From c9ff1d13f903bcfbbce2071d45e29088d9abb978 Mon Sep 17 00:00:00 2001 From: Dominykas Mostauskis Date: Tue, 22 Feb 2022 17:25:26 +0200 Subject: [PATCH 1/9] Split UI datetime type --- mathesar/database/types.py | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/mathesar/database/types.py b/mathesar/database/types.py index 715b33186d..c54ea17489 100644 --- a/mathesar/database/types.py +++ b/mathesar/database/types.py @@ -13,6 +13,8 @@ class MathesarTypeIdentifier(Enum): BOOLEAN = 'boolean' DATETIME = 'datetime' + TIME = 'time' + DATE = 'date' DURATION = 'duration' EMAIL = 'email' MONEY = 'money' @@ -48,12 +50,22 @@ def _get_type_map(): 'name': 'Boolean', 'sa_type_names': [PostgresType.BOOLEAN.value] }, { - 'identifier': MathesarTypeIdentifier.DATETIME.value, - 'name': 'Date & Time', + 'identifier': MathesarTypeIdentifier.DATE.value, + 'name': 'Date', 'sa_type_names': [ PostgresType.DATE.value, + ] + }, { + 'identifier': MathesarTypeIdentifier.TIME.value, + 'name': 'Time', + 'sa_type_names': [ PostgresType.TIME_WITH_TIME_ZONE.value, PostgresType.TIME_WITHOUT_TIME_ZONE.value, + ] + }, { + 'identifier': MathesarTypeIdentifier.DATETIME.value, + 'name': 'Date & Time', + 'sa_type_names': [ PostgresType.TIMESTAMP_WITH_TIME_ZONE.value, PostgresType.TIMESTAMP_WITHOUT_TIME_ZONE.value ] From 8b7fe6396d2500409f8de9005716ec0bdbf09bb8 Mon Sep 17 00:00:00 2001 From: Dominykas Mostauskis Date: Tue, 22 Feb 2022 17:26:29 +0200 Subject: [PATCH 2/9] Add appropriate hints to time-related types --- db/functions/hints.py | 6 ++++++ db/types/base.py | 20 ++++++++++++++++++++ 2 files changed, 26 insertions(+) diff --git a/db/functions/hints.py b/db/functions/hints.py index db50c64f56..5587f45ef2 100644 --- a/db/functions/hints.py +++ b/db/functions/hints.py @@ -64,6 +64,12 @@ def returns(*hints): email = _make_hint("email") +time = _make_hint("time") + + +date = _make_hint("date") + + literal = _make_hint("literal") diff --git a/db/types/base.py b/db/types/base.py index 45c4afb4d2..b0a37c859e 100644 --- a/db/types/base.py +++ b/db/types/base.py @@ -197,6 +197,26 @@ def _add_to_db_type_hintsets(db_types, hints): hints_for_time_related_types = (hints.comparable,) _add_to_db_type_hintsets(time_related_db_types, hints_for_time_related_types) + time_of_day_db_types = ( + PostgresType.TIME, + PostgresType.TIME_WITH_TIME_ZONE, + PostgresType.TIME_WITHOUT_TIME_ZONE, + ) + _add_to_db_type_hintsets(time_of_day_db_types, (hints.time,)) + + date_db_types = ( + PostgresType.DATE, + ) + _add_to_db_type_hintsets(date_db_types, (hints.date,)) + + datetime_db_types = ( + PostgresType.TIMESTAMP, + PostgresType.TIMESTAMP_WITH_TIME_ZONE, + PostgresType.TIMESTAMP_WITHOUT_TIME_ZONE, + ) + _add_to_db_type_hintsets(datetime_db_types, (hints.date, hints.time,)) + + return frozendict(db_types_hinted) From c4dcaf4ede05b5bc4fd438bc56b03eaa143a1cb3 Mon Sep 17 00:00:00 2001 From: Dominykas Mostauskis Date: Tue, 22 Feb 2022 20:33:51 +0200 Subject: [PATCH 3/9] Split datetime display options --- mathesar/api/display_options.py | 15 ++++++++++-- .../api/serializers/shared_serializers.py | 24 ++++++++++++------- 2 files changed, 28 insertions(+), 11 deletions(-) diff --git a/mathesar/api/display_options.py b/mathesar/api/display_options.py index 89d22498da..ba945461c2 100644 --- a/mathesar/api/display_options.py +++ b/mathesar/api/display_options.py @@ -17,6 +17,17 @@ }, MathesarTypeIdentifier.DATETIME.value: { - "options": [{"name": "format", "type": "string"}] - } + "options": [ + {"name": "time_format", "type": "string"}, + {"name": "date_format", "type": "string"}, + ] + }, + MathesarTypeIdentifier.TIME.value: + { + "options": [{"name": "time_format", "type": "string"}] + }, + MathesarTypeIdentifier.DATE.value: + { + "options": [{"name": "date_format", "type": "string"}] + }, } diff --git a/mathesar/api/serializers/shared_serializers.py b/mathesar/api/serializers/shared_serializers.py index f05c369a13..f45b0e9b69 100644 --- a/mathesar/api/serializers/shared_serializers.py +++ b/mathesar/api/serializers/shared_serializers.py @@ -180,8 +180,12 @@ def validate(self, datetime_obj, display_format, serializer_field): return super().validate(datetime_obj, display_format, serializer_field) -class DateDisplayOptionSerializer(MathesarErrorMessageMixin, OverrideRootPartialMixin, serializers.Serializer): - format = serializers.CharField(validators=[DateFormatValidator()]) +class DateDisplayOptionSerializer( + MathesarErrorMessageMixin, + OverrideRootPartialMixin, + serializers.Serializer +): + date_format = serializers.CharField(validators=[DateFormatValidator()]) class TimestampWithoutTimezoneDisplayOptionSerializer( @@ -189,7 +193,8 @@ class TimestampWithoutTimezoneDisplayOptionSerializer( OverrideRootPartialMixin, serializers.Serializer ): - format = serializers.CharField(validators=[TimestampWithoutTimeZoneFormatValidator()]) + time_format = serializers.CharField(validators=[TimeWithoutTimeZoneFormatValidator()]) + date_format = serializers.CharField(validators=[DateFormatValidator()]) class TimestampWithTimezoneDisplayOptionSerializer( @@ -197,7 +202,8 @@ class TimestampWithTimezoneDisplayOptionSerializer( OverrideRootPartialMixin, serializers.Serializer ): - format = serializers.CharField(validators=[TimestampWithTimeZoneFormatValidator()]) + time_format = serializers.CharField(validators=[TimeWithTimeZoneFormatValidator()]) + date_format = serializers.CharField(validators=[DateFormatValidator()]) class TimeWithTimezoneDisplayOptionSerializer( @@ -205,7 +211,7 @@ class TimeWithTimezoneDisplayOptionSerializer( OverrideRootPartialMixin, serializers.Serializer ): - format = serializers.CharField(validators=[TimeWithTimeZoneFormatValidator()]) + time_format = serializers.CharField(validators=[TimeWithTimeZoneFormatValidator()]) class TimeWithoutTimezoneDisplayOptionSerializer( @@ -213,7 +219,7 @@ class TimeWithoutTimezoneDisplayOptionSerializer( OverrideRootPartialMixin, serializers.Serializer ): - format = serializers.CharField(validators=[TimeWithoutTimeZoneFormatValidator()]) + time_format = serializers.CharField(validators=[TimeWithoutTimeZoneFormatValidator()]) class DisplayOptionsMappingSerializer( @@ -228,9 +234,9 @@ class DisplayOptionsMappingSerializer( MathesarTypeIdentifier.DATETIME.value): TimestampWithTimezoneDisplayOptionSerializer, ('timestamp without time zone', MathesarTypeIdentifier.DATETIME.value): TimestampWithoutTimezoneDisplayOptionSerializer, - ('date', MathesarTypeIdentifier.DATETIME.value): DateDisplayOptionSerializer, - ('time with time zone', MathesarTypeIdentifier.DATETIME.value): TimeWithTimezoneDisplayOptionSerializer, - ('time without time zone', MathesarTypeIdentifier.DATETIME.value): TimeWithoutTimezoneDisplayOptionSerializer, + ('date', MathesarTypeIdentifier.DATE.value): DateDisplayOptionSerializer, + ('time with time zone', MathesarTypeIdentifier.TIME.value): TimeWithTimezoneDisplayOptionSerializer, + ('time without time zone', MathesarTypeIdentifier.TIME.value): TimeWithoutTimezoneDisplayOptionSerializer, } def get_mapping_field(self): From b0e5b07738e1281defb1c9c59bcc081f369bb2ff Mon Sep 17 00:00:00 2001 From: Dominykas Mostauskis Date: Tue, 22 Feb 2022 20:34:15 +0200 Subject: [PATCH 4/9] Update hints --- db/functions/hints.py | 3 +++ db/types/base.py | 27 +++++++++++++-------------- 2 files changed, 16 insertions(+), 14 deletions(-) diff --git a/db/functions/hints.py b/db/functions/hints.py index 5587f45ef2..ac67a1ad0e 100644 --- a/db/functions/hints.py +++ b/db/functions/hints.py @@ -64,6 +64,9 @@ def returns(*hints): email = _make_hint("email") +duration = _make_hint("duration") + + time = _make_hint("time") diff --git a/db/types/base.py b/db/types/base.py index b0a37c859e..6330f95bfb 100644 --- a/db/types/base.py +++ b/db/types/base.py @@ -183,20 +183,7 @@ def _add_to_db_type_hintsets(db_types, hints): hints_for_numeric_db_types = (hints.comparable,) _add_to_db_type_hintsets(numeric_db_types, hints_for_numeric_db_types) - # time related types get the "comparable" hint - time_related_db_types = ( - PostgresType.DATE, - PostgresType.TIME, - PostgresType.TIME_WITH_TIME_ZONE, - PostgresType.TIME_WITHOUT_TIME_ZONE, - PostgresType.TIMESTAMP, - PostgresType.TIMESTAMP_WITH_TIME_ZONE, - PostgresType.TIMESTAMP_WITHOUT_TIME_ZONE, - PostgresType.INTERVAL, - ) - hints_for_time_related_types = (hints.comparable,) - _add_to_db_type_hintsets(time_related_db_types, hints_for_time_related_types) - + # time of day db types get the "time" hint time_of_day_db_types = ( PostgresType.TIME, PostgresType.TIME_WITH_TIME_ZONE, @@ -204,11 +191,13 @@ def _add_to_db_type_hintsets(db_types, hints): ) _add_to_db_type_hintsets(time_of_day_db_types, (hints.time,)) + # date db types get the "date" hint date_db_types = ( PostgresType.DATE, ) _add_to_db_type_hintsets(date_db_types, (hints.date,)) + # datetime db types get the "date" and "time" hints datetime_db_types = ( PostgresType.TIMESTAMP, PostgresType.TIMESTAMP_WITH_TIME_ZONE, @@ -216,6 +205,16 @@ def _add_to_db_type_hintsets(db_types, hints): ) _add_to_db_type_hintsets(datetime_db_types, (hints.date, hints.time,)) + # duration db types get the "duration" hints + duration_db_types = ( + PostgresType.INTERVAL, + ) + _add_to_db_type_hintsets(duration_db_types, (hints.duration,)) + + # time related types get the "comparable" hint + hints_for_time_related_types = (hints.comparable,) + for db_types in [time_of_day_db_types, date_db_types, datetime_db_types, duration_db_types]: + _add_to_db_type_hintsets(db_types, hints_for_time_related_types) return frozendict(db_types_hinted) From 1519e6b17ed0e57b17202488b1b188f2c50b52b1 Mon Sep 17 00:00:00 2001 From: Dominykas Mostauskis Date: Tue, 1 Mar 2022 14:38:19 +0200 Subject: [PATCH 5/9] Finish adapting serializers --- mathesar/api/serializers/shared_serializers.py | 14 ++++++++++---- mathesar/tests/api/test_column_api.py | 10 +++++----- 2 files changed, 15 insertions(+), 9 deletions(-) diff --git a/mathesar/api/serializers/shared_serializers.py b/mathesar/api/serializers/shared_serializers.py index f45b0e9b69..51d5ff3109 100644 --- a/mathesar/api/serializers/shared_serializers.py +++ b/mathesar/api/serializers/shared_serializers.py @@ -102,7 +102,7 @@ class CustomBooleanLabelSerializer(MathesarErrorMessageMixin, serializers.Serial FALSE = serializers.CharField() -DISPLAY_OPTIONS_SERIALIZER_MAPPING_KEY = 'mathesar_type' +DISPLAY_OPTIONS_SERIALIZER_MAPPING_KEY = 'db_type' class BooleanDisplayOptionSerializer(MathesarErrorMessageMixin, OverrideRootPartialMixin, serializers.Serializer): @@ -227,6 +227,7 @@ class DisplayOptionsMappingSerializer( ReadWritePolymorphicSerializerMappingMixin, serializers.Serializer ): + # Some mappings are based on Mathesar/UI types, while others are based on DB types serializers_mapping = { MathesarTypeIdentifier.BOOLEAN.value: BooleanDisplayOptionSerializer, MathesarTypeIdentifier.NUMBER.value: NumberDisplayOptionSerializer, @@ -234,14 +235,19 @@ class DisplayOptionsMappingSerializer( MathesarTypeIdentifier.DATETIME.value): TimestampWithTimezoneDisplayOptionSerializer, ('timestamp without time zone', MathesarTypeIdentifier.DATETIME.value): TimestampWithoutTimezoneDisplayOptionSerializer, - ('date', MathesarTypeIdentifier.DATE.value): DateDisplayOptionSerializer, ('time with time zone', MathesarTypeIdentifier.TIME.value): TimeWithTimezoneDisplayOptionSerializer, ('time without time zone', MathesarTypeIdentifier.TIME.value): TimeWithoutTimezoneDisplayOptionSerializer, + MathesarTypeIdentifier.DATE.value: DateDisplayOptionSerializer, } def get_mapping_field(self): mathesar_type = get_mathesar_type_from_db_type(self.context[DISPLAY_OPTIONS_SERIALIZER_MAPPING_KEY]) - if mathesar_type == MathesarTypeIdentifier.DATETIME.value: - return self.context[DISPLAY_OPTIONS_SERIALIZER_MAPPING_KEY].lower(), mathesar_type + # For some db types, their serializer is mapped using a tuple of + # `(db_type, mathesar_type)`, for the rest it's just `mathesar_type`. + # NOTE: the use of a mathesar_type in the `(db_type, mathesar_type)` key is unnecessary, a + # `db_type` can only belong to a single `mathesar_type`, currently. + if mathesar_type in [MathesarTypeIdentifier.DATETIME.value, MathesarTypeIdentifier.TIME.value]: + db_type = self.context[DISPLAY_OPTIONS_SERIALIZER_MAPPING_KEY].lower() + return (db_type, mathesar_type) else: return mathesar_type diff --git a/mathesar/tests/api/test_column_api.py b/mathesar/tests/api/test_column_api.py index d99e0dd2b2..f6ffee9f9c 100644 --- a/mathesar/tests/api/test_column_api.py +++ b/mathesar/tests/api/test_column_api.py @@ -237,13 +237,13 @@ def test_column_create_invalid_default(column_test_table, client): create_display_options_test_list = [ ("BOOLEAN", {"input": "dropdown"}), ("BOOLEAN", {"input": "checkbox", "custom_labels": {"TRUE": "yes", "FALSE": "no"}}), - ("DATE", {'format': 'YYYY-MM-DD'}), + ("DATE", {'date_format': 'YYYY-MM-DD'}), ("NUMERIC", {"show_as_percentage": True}), ("NUMERIC", {"show_as_percentage": True, "locale": "en_US"}), - ("TIMESTAMP WITH TIME ZONE", {'format': 'YYYY-MM-DD hh:mm'}), - ("TIMESTAMP WITHOUT TIME ZONE", {'format': 'YYYY-MM-DD hh:mm'}), - ("TIME WITHOUT TIME ZONE", {'format': 'hh:mm'}), - ("TIME WITH TIME ZONE", {'format': 'hh:mm Z'}), + ("TIMESTAMP WITH TIME ZONE", {'date_format': 'YYYY-MM-DD', 'time_format': 'hh:mm'}), + ("TIMESTAMP WITHOUT TIME ZONE", {'date_format': 'YYYY-MM-DD', 'time_format': 'hh:mm'}), + ("TIME WITHOUT TIME ZONE", {'time_format': 'hh:mm'}), + ("TIME WITH TIME ZONE", {'time_format': 'hh:mm Z'}), ] From 3cc27ebd34b7fbbce2b4b919412d349e22917545 Mon Sep 17 00:00:00 2001 From: Dominykas Mostauskis Date: Tue, 1 Mar 2022 17:36:24 +0200 Subject: [PATCH 6/9] Revert diplay_options changes; reduce validation to checking length --- .../api/serializers/shared_serializers.py | 133 ++---------------- mathesar/tests/api/test_column_api.py | 26 ++-- 2 files changed, 26 insertions(+), 133 deletions(-) diff --git a/mathesar/api/serializers/shared_serializers.py b/mathesar/api/serializers/shared_serializers.py index 51d5ff3109..a4c7a3ca00 100644 --- a/mathesar/api/serializers/shared_serializers.py +++ b/mathesar/api/serializers/shared_serializers.py @@ -1,6 +1,3 @@ -from abc import ABC, abstractmethod - -import arrow from django.core.exceptions import ImproperlyConfigured from rest_framework import serializers @@ -115,111 +112,19 @@ class NumberDisplayOptionSerializer(MathesarErrorMessageMixin, OverrideRootParti locale = serializers.CharField(required=False) -class AbstractDateTimeFormatValidator(ABC): - requires_context = True - - def __init__(self): - pass - - def __call__(self, value, serializer_field): - self.date_format_validator(value, serializer_field) - - def date_format_validator(self, value, serializer_field): - try: - timestamp_with_tz_obj = arrow.get('2013-09-30T15:34:00.000-07:00') - parsed_datetime_str = timestamp_with_tz_obj.format(value) - datetime_object = arrow.get(parsed_datetime_str, value) - except ValueError: - raise serializers.ValidationError(f"{value} is not a valid format used for parsing a datetime.") - else: - self.validate(datetime_object, value, serializer_field) - - @abstractmethod - def validate(self, datetime_obj, display_format, serializer_field): - pass - - -class TimestampWithTimeZoneFormatValidator(AbstractDateTimeFormatValidator): - - def validate(self, datetime_obj, display_format, serializer_field): - pass - - -class TimestampWithoutTimeZoneFormatValidator(AbstractDateTimeFormatValidator): - - def validate(self, datetime_obj, display_format, serializer_field): - if 'z' in display_format.lower(): - raise serializers.ValidationError( - "Timestamp without timezone column cannot contain timezone display format" - ) - - -class DateFormatValidator(AbstractDateTimeFormatValidator): - - def validate(self, datetime_obj, display_format, serializer_field): - date_obj = arrow.get('2013-09-30') - if datetime_obj.time() != date_obj.time(): - raise serializers.ValidationError("Date column cannot contain time or timezone display format") - - -class TimeWithTimeZoneFormatValidator(AbstractDateTimeFormatValidator): +def is_time_format_valid(format): + if isinstance(format, str) and len(format) <= 255: + return True + else: + raise serializers.ValidationError("Time format string not a string or longer than 255 characters.") - def validate(self, datetime_obj, display_format, serializer_field): - time_only_format = 'HH:mm:ssZZ' - time_str = arrow.get('2013-09-30T15:34:00.000-07:00').format(time_only_format) - parsed_time_str = arrow.get(time_str, time_only_format) - if parsed_time_str.date() != datetime_obj.date(): - raise serializers.ValidationError("Time column cannot contain date display format") - -class TimeWithoutTimeZoneFormatValidator(TimeWithTimeZoneFormatValidator): - - def validate(self, datetime_obj, display_format, serializer_field): - if 'z' in display_format.lower(): - raise serializers.ValidationError("Time without timezone column cannot contain timezone display format") - return super().validate(datetime_obj, display_format, serializer_field) - - -class DateDisplayOptionSerializer( +class TimeFormatDisplayOptionSerializer( MathesarErrorMessageMixin, OverrideRootPartialMixin, serializers.Serializer ): - date_format = serializers.CharField(validators=[DateFormatValidator()]) - - -class TimestampWithoutTimezoneDisplayOptionSerializer( - MathesarErrorMessageMixin, - OverrideRootPartialMixin, - serializers.Serializer -): - time_format = serializers.CharField(validators=[TimeWithoutTimeZoneFormatValidator()]) - date_format = serializers.CharField(validators=[DateFormatValidator()]) - - -class TimestampWithTimezoneDisplayOptionSerializer( - MathesarErrorMessageMixin, - OverrideRootPartialMixin, - serializers.Serializer -): - time_format = serializers.CharField(validators=[TimeWithTimeZoneFormatValidator()]) - date_format = serializers.CharField(validators=[DateFormatValidator()]) - - -class TimeWithTimezoneDisplayOptionSerializer( - MathesarErrorMessageMixin, - OverrideRootPartialMixin, - serializers.Serializer -): - time_format = serializers.CharField(validators=[TimeWithTimeZoneFormatValidator()]) - - -class TimeWithoutTimezoneDisplayOptionSerializer( - MathesarErrorMessageMixin, - OverrideRootPartialMixin, - serializers.Serializer -): - time_format = serializers.CharField(validators=[TimeWithoutTimeZoneFormatValidator()]) + format = serializers.CharField(validators=[is_time_format_valid]) class DisplayOptionsMappingSerializer( @@ -227,27 +132,15 @@ class DisplayOptionsMappingSerializer( ReadWritePolymorphicSerializerMappingMixin, serializers.Serializer ): - # Some mappings are based on Mathesar/UI types, while others are based on DB types serializers_mapping = { MathesarTypeIdentifier.BOOLEAN.value: BooleanDisplayOptionSerializer, MathesarTypeIdentifier.NUMBER.value: NumberDisplayOptionSerializer, - ('timestamp with time zone', - MathesarTypeIdentifier.DATETIME.value): TimestampWithTimezoneDisplayOptionSerializer, - ('timestamp without time zone', - MathesarTypeIdentifier.DATETIME.value): TimestampWithoutTimezoneDisplayOptionSerializer, - ('time with time zone', MathesarTypeIdentifier.TIME.value): TimeWithTimezoneDisplayOptionSerializer, - ('time without time zone', MathesarTypeIdentifier.TIME.value): TimeWithoutTimezoneDisplayOptionSerializer, - MathesarTypeIdentifier.DATE.value: DateDisplayOptionSerializer, + MathesarTypeIdentifier.DATETIME.value: TimeFormatDisplayOptionSerializer, + MathesarTypeIdentifier.DATE.value: TimeFormatDisplayOptionSerializer, + MathesarTypeIdentifier.TIME.value: TimeFormatDisplayOptionSerializer, } def get_mapping_field(self): - mathesar_type = get_mathesar_type_from_db_type(self.context[DISPLAY_OPTIONS_SERIALIZER_MAPPING_KEY]) - # For some db types, their serializer is mapped using a tuple of - # `(db_type, mathesar_type)`, for the rest it's just `mathesar_type`. - # NOTE: the use of a mathesar_type in the `(db_type, mathesar_type)` key is unnecessary, a - # `db_type` can only belong to a single `mathesar_type`, currently. - if mathesar_type in [MathesarTypeIdentifier.DATETIME.value, MathesarTypeIdentifier.TIME.value]: - db_type = self.context[DISPLAY_OPTIONS_SERIALIZER_MAPPING_KEY].lower() - return (db_type, mathesar_type) - else: - return mathesar_type + db_type = self.context[DISPLAY_OPTIONS_SERIALIZER_MAPPING_KEY] + mathesar_type = get_mathesar_type_from_db_type(db_type) + return mathesar_type diff --git a/mathesar/tests/api/test_column_api.py b/mathesar/tests/api/test_column_api.py index f6ffee9f9c..66c3721aba 100644 --- a/mathesar/tests/api/test_column_api.py +++ b/mathesar/tests/api/test_column_api.py @@ -237,13 +237,13 @@ def test_column_create_invalid_default(column_test_table, client): create_display_options_test_list = [ ("BOOLEAN", {"input": "dropdown"}), ("BOOLEAN", {"input": "checkbox", "custom_labels": {"TRUE": "yes", "FALSE": "no"}}), - ("DATE", {'date_format': 'YYYY-MM-DD'}), + ("DATE", {'format': 'YYYY-MM-DD'}), ("NUMERIC", {"show_as_percentage": True}), ("NUMERIC", {"show_as_percentage": True, "locale": "en_US"}), - ("TIMESTAMP WITH TIME ZONE", {'date_format': 'YYYY-MM-DD', 'time_format': 'hh:mm'}), - ("TIMESTAMP WITHOUT TIME ZONE", {'date_format': 'YYYY-MM-DD', 'time_format': 'hh:mm'}), - ("TIME WITHOUT TIME ZONE", {'time_format': 'hh:mm'}), - ("TIME WITH TIME ZONE", {'time_format': 'hh:mm Z'}), + ("TIMESTAMP WITH TIME ZONE", {'format': 'YYYY-MM-DD hh:mm'}), + ("TIMESTAMP WITHOUT TIME ZONE", {'format': 'YYYY-MM-DD hh:mm'}), + ("TIME WITHOUT TIME ZONE", {'format': 'hh:mm'}), + ("TIME WITH TIME ZONE", {'format': 'hh:mm Z'}), ] @@ -265,18 +265,18 @@ def test_column_create_display_options( assert actual_new_col["display_options"] == display_options +_too_long_string = "x" * 256 + + create_display_options_invalid_test_list = [ ("BOOLEAN", {"input": "invalid", "use_custom_columns": False}), ("BOOLEAN", {"input": "checkbox", "use_custom_columns": True, "custom_labels": {"yes": "yes", "1": "no"}}), - ("DATE", {'format': 'YYYY-MM-DD hh:mm Z'}), - ("DATE", {'format': 'hh:mm Z'}), ("NUMERIC", {"show_as_percentage": "wrong value type"}), - ("TIMESTAMP WITH TIME ZONE", {'format': 'xyz'}), - ("TIMESTAMP WITHOUT TIME ZONE", {'format': 'xyz'}), - ("TIMESTAMP WITHOUT TIME ZONE", {'format': 'YYYY-MM-DD hh:mm Z'}), - ("TIME WITH TIME ZONE", {'format': 'YYYY-MM-DD hh:mm Z'}), - ("TIME WITHOUT TIME ZONE", {'format': 'YYYY-MM-DD hh:mm'}), - ("TIME WITHOUT TIME ZONE", {'format': 'hh:mm Z'}), + ("DATE", {'format': _too_long_string}), + ("TIMESTAMP WITH TIME ZONE", {'format': []}), + ("TIMESTAMP WITHOUT TIME ZONE", {'format': _too_long_string}), + ("TIME WITH TIME ZONE", {'format': _too_long_string}), + ("TIME WITHOUT TIME ZONE", {'format': {}}), ] From 27f75caa9a17270e0dbfed119001286ec401c181 Mon Sep 17 00:00:00 2001 From: Dominykas Mostauskis Date: Thu, 3 Mar 2022 18:12:38 +0200 Subject: [PATCH 7/9] Switch to defacto way to specify max_length --- mathesar/api/serializers/shared_serializers.py | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/mathesar/api/serializers/shared_serializers.py b/mathesar/api/serializers/shared_serializers.py index fc0cfc1323..7fb3159830 100644 --- a/mathesar/api/serializers/shared_serializers.py +++ b/mathesar/api/serializers/shared_serializers.py @@ -111,29 +111,24 @@ class NumberDisplayOptionSerializer(MathesarErrorMessageMixin, OverrideRootParti show_as_percentage = serializers.BooleanField(default=False) locale = serializers.CharField(required=False) + def raise_if_duration_format_invalid(format): if 'z' in format.lower(): raise serializers.ValidationError( "Duration column cannot contain timezone display format" ) -def raise_if_time_format_invalid(format): - if isinstance(format, str) and len(format) <= 255: - return - else: - raise serializers.ValidationError("Time format string not a string or longer than 255 characters.") - class TimeFormatDisplayOptionSerializer( MathesarErrorMessageMixin, OverrideRootPartialMixin, serializers.Serializer ): - format = serializers.CharField(validators=[raise_if_time_format_invalid]) + format = serializers.CharField(max_length=255) class DurationDisplayOptionSerializer(MathesarErrorMessageMixin, OverrideRootPartialMixin, serializers.Serializer): - format = serializers.CharField(validators=[raise_if_duration_format_invalid]) + format = serializers.CharField(max_length=255, validators=[raise_if_duration_format_invalid]) class DisplayOptionsMappingSerializer( From ea6050a067d1a366a24473f7093d90a8ef82c096 Mon Sep 17 00:00:00 2001 From: Dominykas Mostauskis Date: Tue, 8 Mar 2022 15:04:57 +0200 Subject: [PATCH 8/9] Remove custom time/duration format validation --- mathesar/api/serializers/shared_serializers.py | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/mathesar/api/serializers/shared_serializers.py b/mathesar/api/serializers/shared_serializers.py index 7fb3159830..d25097df17 100644 --- a/mathesar/api/serializers/shared_serializers.py +++ b/mathesar/api/serializers/shared_serializers.py @@ -112,13 +112,6 @@ class NumberDisplayOptionSerializer(MathesarErrorMessageMixin, OverrideRootParti locale = serializers.CharField(required=False) -def raise_if_duration_format_invalid(format): - if 'z' in format.lower(): - raise serializers.ValidationError( - "Duration column cannot contain timezone display format" - ) - - class TimeFormatDisplayOptionSerializer( MathesarErrorMessageMixin, OverrideRootPartialMixin, @@ -128,7 +121,7 @@ class TimeFormatDisplayOptionSerializer( class DurationDisplayOptionSerializer(MathesarErrorMessageMixin, OverrideRootPartialMixin, serializers.Serializer): - format = serializers.CharField(max_length=255, validators=[raise_if_duration_format_invalid]) + format = serializers.CharField(max_length=255) class DisplayOptionsMappingSerializer( From 791897131d6dffa79c96c71d7a79be86057a86e1 Mon Sep 17 00:00:00 2001 From: Dominykas Mostauskis Date: Tue, 8 Mar 2022 15:05:09 +0200 Subject: [PATCH 9/9] Remove arrow library --- requirements.txt | 1 - 1 file changed, 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 22acd8ad96..1290ff8865 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,5 +1,4 @@ alembic==1.6.5 -arrow==1.2.1 charset-normalizer==2.0.7 clevercsv==0.6.8 Django==3.1.14