Skip to content

Commit

Permalink
Merge branch 'master' into first_letter_grouping
Browse files Browse the repository at this point in the history
  • Loading branch information
dmos62 authored May 4, 2022
2 parents 093ca7f + 275240a commit ca3e848
Show file tree
Hide file tree
Showing 119 changed files with 5,598 additions and 1,875 deletions.
1 change: 1 addition & 0 deletions db/columns/operations/alter.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ def alter_column_type(
table_oid, column_name, engine, connection, target_type_str,
type_options={}, friendly_names=True,
):
type_options = type_options if type_options is not None else {}
table = reflect_table_from_oid(table_oid, engine, connection)
_preparer = engine.dialect.identifier_preparer
supported_types = get_supported_alter_column_types(
Expand Down
6 changes: 5 additions & 1 deletion mathesar/api/display_options.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,10 @@
},
MathesarTypeIdentifier.DURATION.value:
{
"options": [{"name": "format", "type": "string"}]
"options": [
{"name": "min", "type": "string"},
{"name": "max", "type": "string"},
{"name": "show_units", "type": "boolean"},
]
}
}
5 changes: 4 additions & 1 deletion mathesar/api/exceptions/mixins.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ def build_pretty_errors(self, errors, serializer=None):
else:
pretty.extend(self.get_non_field_error_entries(errors[error_type]))
else:
field = self.fields.fields[error_type]
field = self.get_serializer_fields().fields[error_type]
if isinstance(field, Serializer) and type(errors[error_type]) == dict:
field.initial_data = self.initial_data[error_type]
child_errors = field.build_pretty_errors(errors[error_type])
Expand All @@ -43,6 +43,9 @@ def build_pretty_errors(self, errors, serializer=None):
return pretty
return []

def get_serializer_fields(self):
return self.fields

def _run_validator(self, validator, field, message):
"""
This method build on top of `_run_validator` method of the superclass
Expand Down
2 changes: 1 addition & 1 deletion mathesar/api/serializers/columns.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ class TypeOptionSerializer(MathesarErrorMessageMixin, serializers.Serializer):

def run_validation(self, data=empty):
# Ensure that there are no unknown type options passed in.
if data is not empty:
if data is not empty and data is not None:
unknown = set(data) - set(self.fields)
if unknown:
errors = ['Unknown field: {}'.format(field) for field in unknown]
Expand Down
29 changes: 21 additions & 8 deletions mathesar/api/serializers/shared_serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,10 +36,9 @@ def __init__(self, *args, **kwargs):
def to_representation(self, instance):
serializer = self.serializers_mapping.get(self.get_mapping_field(), None)
if serializer is not None:
self.__class__ = self.serializers_cls_mapping.get(self.get_mapping_field())
return serializer.to_representation(instance)
else:
raise Exception(f"Cannot find a matching serializer for the specified type {self.get_mapping_field()}")
return instance

def get_mapping_field(self):
mapping_field = getattr(self, "mapping_field", None)
Expand All @@ -55,10 +54,10 @@ class ReadWritePolymorphicSerializerMappingMixin(ReadOnlyPolymorphicSerializerMa
def to_internal_value(self, data):
serializer = self.serializers_mapping.get(self.get_mapping_field())
if serializer is not None:
self.__class__ = self.serializers_cls_mapping.get(self.get_mapping_field())
return serializer.to_internal_value(data=data)
else:
raise Exception(f"Cannot find a matching serializer for the specified type {self.get_mapping_field()}")
data = {}
return data


class MonkeyPatchPartial:
Expand Down Expand Up @@ -94,6 +93,11 @@ def run_validation(self, *args, **kwargs):
return super().run_validation(*args, **kwargs)


class MathesarPolymorphicErrorMixin(MathesarErrorMessageMixin):
def get_serializer_fields(self):
return self.serializers_mapping[self.get_mapping_field()].fields


class CustomBooleanLabelSerializer(MathesarErrorMessageMixin, serializers.Serializer):
TRUE = serializers.CharField()
FALSE = serializers.CharField()
Expand All @@ -107,9 +111,16 @@ class BooleanDisplayOptionSerializer(MathesarErrorMessageMixin, OverrideRootPart
custom_labels = CustomBooleanLabelSerializer(required=False)


class NumberDisplayOptionSerializer(MathesarErrorMessageMixin, OverrideRootPartialMixin, serializers.Serializer):
class AbstractNumberDisplayOptionSerializer(serializers.Serializer):
number_format = serializers.ChoiceField(allow_null=True, required=False, choices=['english', 'german', 'french', 'hindi', 'swiss'])


class NumberDisplayOptionSerializer(
MathesarErrorMessageMixin,
OverrideRootPartialMixin,
AbstractNumberDisplayOptionSerializer
):
show_as_percentage = serializers.BooleanField(default=False)
locale = serializers.CharField(required=False)


class TimeFormatDisplayOptionSerializer(
Expand All @@ -121,12 +132,14 @@ class TimeFormatDisplayOptionSerializer(


class DurationDisplayOptionSerializer(MathesarErrorMessageMixin, OverrideRootPartialMixin, serializers.Serializer):
format = serializers.CharField(max_length=255)
min = serializers.CharField(max_length=255)
max = serializers.CharField(max_length=255)
show_units = serializers.BooleanField()


class DisplayOptionsMappingSerializer(
MathesarErrorMessageMixin,
OverrideRootPartialMixin,
MathesarPolymorphicErrorMixin,
ReadWritePolymorphicSerializerMappingMixin,
serializers.Serializer
):
Expand Down
71 changes: 66 additions & 5 deletions mathesar/tests/api/test_column_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -63,8 +63,8 @@ def column_test_table_with_service_layer_options(patent_schema):
]
column_data_list = [{},
{'display_options': {'input': "dropdown", "custom_labels": {"TRUE": "yes", "FALSE": "no"}}},
{'display_options': {"show_as_percentage": True, "locale": "en_US"}},
{},
{'display_options': {'show_as_percentage': True, 'number_format': "english"}},
{'display_options': None},
{},
{},
{'display_options': {'format': 'YYYY-MM-DD hh:mm'}}]
Expand Down Expand Up @@ -255,9 +255,8 @@ def test_column_create_invalid_default(column_test_table, client):
("BOOLEAN", {"input": "dropdown"}),
("BOOLEAN", {"input": "checkbox", "custom_labels": {"TRUE": "yes", "FALSE": "no"}}),
("DATE", {'format': 'YYYY-MM-DD'}),
("INTERVAL", {'format': 'DD HH:mm:ss.SSS'}),
("NUMERIC", {"show_as_percentage": True}),
("NUMERIC", {"show_as_percentage": True, "locale": "en_US"}),
("INTERVAL", {'min': 's', 'max': 'h', 'show_units': True}),
("NUMERIC", {"show_as_percentage": True, 'number_format': "english"}),
("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'}),
Expand Down Expand Up @@ -290,6 +289,7 @@ def test_column_create_display_options(
("BOOLEAN", {"input": "invalid"}),
("BOOLEAN", {"input": "checkbox", "custom_labels": {"yes": "yes", "1": "no"}}),
("NUMERIC", {"show_as_percentage": "wrong value type"}),
("NUMERIC", {'number_format': "wrong"}),
("DATE", {'format': _too_long_string}),
("TIMESTAMP WITH TIME ZONE", {'format': []}),
("TIMESTAMP WITHOUT TIME ZONE", {'format': _too_long_string}),
Expand Down Expand Up @@ -497,6 +497,23 @@ def test_column_update_type_invalid_display_options(column_test_table_with_servi
assert response.status_code == 400


def test_column_update_type_get_all_columns(column_test_table_with_service_layer_options, client):
cache.clear()
table, columns = column_test_table_with_service_layer_options
colum_name = "mycolumn2"
column = _get_columns_by_name(table, [colum_name])[0]
column_id = column.id
display_options_data = {'type': 'BOOLEAN'}
client.patch(
f"/api/db/v0/tables/{table.id}/columns/{column_id}/",
display_options_data,
)
new_columns_response = client.get(
f"/api/db/v0/tables/{table.id}/columns/"
)
assert new_columns_response.status_code == 200


def test_column_display_options_type_on_reflection(column_test_table,
client, engine):
cache.clear()
Expand Down Expand Up @@ -847,3 +864,47 @@ def test_column_duplicate_no_parameters(column_test_table, client):
assert response.status_code == 400
assert response_data[0]["message"] == "This field is required."
assert response_data[0]["field"] == "type"


def test_column_update_type_with_display_and_type_options_as_null(column_test_table, client):
cache.clear()
type_ = "MATHESAR_TYPES.URI"
display_options = None
type_options = None
data = {
"type": type_,
"display_options": display_options,
"type_options": type_options
}
column = _get_columns_by_name(column_test_table, ['mycolumn3'])[0]
response = client.patch(
f"/api/db/v0/tables/{column_test_table.id}/columns/{column.id}/",
data=data,
)
assert response.status_code == 200
response_json = response.json()
assert response_json["type"] == type_
assert response_json["display_options"] == display_options
assert response_json["type_options"] == type_options


def test_column_update_type_with_display_and_type_options_as_empty_objects(column_test_table, client):
cache.clear()
type_ = "MATHESAR_TYPES.URI"
display_options = {}
type_options = {}
data = {
"type": type_,
"display_options": display_options,
"type_options": type_options
}
column = _get_columns_by_name(column_test_table, ['mycolumn3'])[0]
response = client.patch(
f"/api/db/v0/tables/{column_test_table.id}/columns/{column.id}/",
data=data,
)
assert response.status_code == 200
response_json = response.json()
assert response_json["type"] == type_
assert response_json["display_options"] == {}
assert response_json["type_options"] is None
24 changes: 24 additions & 0 deletions mathesar/tests/integration/test_columns.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
from playwright.sync_api import expect
# from mathesar.tests.integration.utils.table_actions import open_column_options, get_default_value_checkbox
# from mathesar.tests.integration.utils.validators import expect_tab_to_be_visible


def test_add_column(page, go_to_patents_data_table):
Expand Down Expand Up @@ -53,3 +55,25 @@ def test_group_by_column(page, go_to_patents_data_table):
expect(locator_group_count).not_to_be_visible()
expect(locator_group_header).not_to_be_visible()
expect(page.locator("button:has-text('Group')")).to_be_visible()


# Column patch requests from frontend currently fails
# Commented until https://github.com/centerofci/mathesar/issues/1276 is merged
# def test_set_column_default_value(page, go_to_all_types_table):
# expect_tab_to_be_visible(page, "All datatypes table")
# open_column_options(page, "text", "Text")
# default_value_cb_locator = get_default_value_checkbox(page)
# default_value_cb_handle = default_value_cb_locator.element_handle()
# assert default_value_cb_handle.is_checked() is False
# default_value_cb_handle.click()
# assert default_value_cb_handle.is_checked() is True
# page.pause()
# default_value_input = page.locator("span:has-text('Default Value') textarea")
# expect(default_value_input).to_be_empty()
# default_value_input.fill("some default value")
# page.click("button:has-text('Save')")
# expect(page.locator(".type-options-content")).not_to_be_visible()
# page.click("button:has-text('New Record')")
# created_row = page.locator(".row.created")
# expect(created_row).to_be_visible()
# expect(created_row.locator(".cell:has-text('some default value')")).to_be_visible()
10 changes: 4 additions & 6 deletions mathesar/tests/integration/test_scroll_state.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import pytest
from playwright.sync_api import expect
from mathesar.tests.integration.utils.locators import get_table_entry
from mathesar.tests.integration.utils.table_actions import create_empty_table, rename_column
Expand Down Expand Up @@ -26,15 +25,14 @@ def test_scroll_preserving_on_active_table_updating(page, go_to_patents_data_tab
expect(thirtieth_element).to_be_visible()


# TODO: add 'Filter' when implemented
@pytest.mark.parametrize("action", ["Sort", "Group"])
def test_scroll_resetting_on_sort_group_applying(page, go_to_patents_data_table, action):
# TODO: add 'Filter', 'Group'
def test_scroll_reset_on_sort(page, go_to_patents_data_table):
thirtieth_element = page.locator("span.number:text-is('30')")
expect(thirtieth_element).not_to_be_visible()
page.locator(".ps__rail-y").click()
expect(thirtieth_element).to_be_visible()
page.locator(f"button:has-text('{action}')").click()
page.locator(f"button:has-text('Add new {action} column')").click()
page.locator("button:has-text('Sort')").click()
page.locator("button:has-text('Add new Sort column')").click()
page.locator("td.action >> button:first-child").click()
expect(thirtieth_element).not_to_be_visible()

Expand Down
7 changes: 7 additions & 0 deletions mathesar/tests/integration/utils/table_actions.py
Original file line number Diff line number Diff line change
Expand Up @@ -65,3 +65,10 @@ def get_cell_selector(page, table, row_number, column_name):
cell_locator = page.locator(cell_selector)
expect(cell_locator).to_be_visible()
return cell_selector


def get_default_value_checkbox(page):
default_value_cb_selector = "span:has-text('Set Default Value') input[type='checkbox']"
cb_locator = page.locator(default_value_cb_selector)
expect(cb_locator).to_be_visible()
return cb_locator
1 change: 1 addition & 0 deletions mathesar_ui/jest.config.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ function getAlias() {
}

module.exports = {
testEnvironment: 'jsdom',
transform: {
'^.+\\.svelte$': [
'svelte-jester',
Expand Down
Loading

0 comments on commit ca3e848

Please sign in to comment.