Skip to content

Commit

Permalink
Merge pull request #3703 from mathesar-foundation/col_with_meta
Browse files Browse the repository at this point in the history
RPC endpoint for column info with metadata
  • Loading branch information
seancolsen authored Jul 24, 2024
2 parents fee9d57 + 773ccbe commit 8688414
Show file tree
Hide file tree
Showing 8 changed files with 164 additions and 97 deletions.
7 changes: 4 additions & 3 deletions docs/docs/api/rpc.md
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,7 @@ To use an RPC function:
- add
- patch
- delete
- list_with_metadata
- ColumnInfo
- ColumnListReturn
- CreatableColumnInfo
Expand All @@ -148,9 +149,9 @@ To use an RPC function:
options:
members:
- list_
- patch
- ColumnMetaData
- SettableColumnMetaData
- set_
- ColumnMetaDataRecord
- ColumnMetaDataBlob

## Types

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
# Generated by Django 4.2.11 on 2024-07-24 10:53

from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
('mathesar', '0012_merge_20240718_0628'),
]

operations = [
migrations.AlterField(
model_name='columnmetadata',
name='bool_false',
field=models.CharField(null=True),
),
migrations.AlterField(
model_name='columnmetadata',
name='bool_input',
field=models.CharField(choices=[('dropdown', 'dropdown'), ('checkbox', 'checkbox')], null=True),
),
migrations.AlterField(
model_name='columnmetadata',
name='bool_true',
field=models.CharField(null=True),
),
migrations.AlterField(
model_name='columnmetadata',
name='date_format',
field=models.CharField(null=True),
),
migrations.AlterField(
model_name='columnmetadata',
name='duration_max',
field=models.CharField(max_length=255, null=True),
),
migrations.AlterField(
model_name='columnmetadata',
name='duration_min',
field=models.CharField(max_length=255, null=True),
),
migrations.AlterField(
model_name='columnmetadata',
name='duration_show_units',
field=models.BooleanField(null=True),
),
migrations.AlterField(
model_name='columnmetadata',
name='mon_currency_location',
field=models.CharField(choices=[('after-minus', 'after-minus'), ('end-with-space', 'end-with-space')], null=True),
),
migrations.AlterField(
model_name='columnmetadata',
name='mon_currency_symbol',
field=models.CharField(null=True),
),
migrations.AlterField(
model_name='columnmetadata',
name='num_max_frac_digits',
field=models.PositiveIntegerField(null=True),
),
migrations.AlterField(
model_name='columnmetadata',
name='num_min_frac_digits',
field=models.PositiveIntegerField(null=True),
),
migrations.AlterField(
model_name='columnmetadata',
name='num_show_as_perc',
field=models.BooleanField(null=True),
),
migrations.AlterField(
model_name='columnmetadata',
name='time_format',
field=models.CharField(null=True),
),
]
26 changes: 13 additions & 13 deletions mathesar/models/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -88,23 +88,23 @@ class ColumnMetaData(BaseModel):
attnum = models.SmallIntegerField()
bool_input = models.CharField(
choices=[("dropdown", "dropdown"), ("checkbox", "checkbox")],
blank=True
null=True
)
bool_true = models.CharField(default='True')
bool_false = models.CharField(default='False')
num_min_frac_digits = models.PositiveIntegerField(default=0)
num_max_frac_digits = models.PositiveIntegerField(default=20)
num_show_as_perc = models.BooleanField(default=False)
mon_currency_symbol = models.CharField(default="$")
bool_true = models.CharField(null=True)
bool_false = models.CharField(null=True)
num_min_frac_digits = models.PositiveIntegerField(null=True)
num_max_frac_digits = models.PositiveIntegerField(null=True)
num_show_as_perc = models.BooleanField(null=True)
mon_currency_symbol = models.CharField(null=True)
mon_currency_location = models.CharField(
choices=[("after-minus", "after-minus"), ("end-with-space", "end-with-space")],
default="after-minus"
null=True
)
time_format = models.CharField(blank=True)
date_format = models.CharField(blank=True)
duration_min = models.CharField(max_length=255, blank=True)
duration_max = models.CharField(max_length=255, blank=True)
duration_show_units = models.BooleanField(default=True)
time_format = models.CharField(null=True)
date_format = models.CharField(null=True)
duration_min = models.CharField(max_length=255, null=True)
duration_max = models.CharField(max_length=255, null=True)
duration_show_units = models.BooleanField(null=True)

class Meta:
constraints = [
Expand Down
24 changes: 24 additions & 0 deletions mathesar/rpc/columns/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,10 @@
from db.columns.operations.create import add_columns_to_table
from db.columns.operations.drop import drop_columns_from_table
from db.columns.operations.select import get_column_info_for_table
from mathesar.rpc.columns.metadata import ColumnMetaDataBlob
from mathesar.rpc.exceptions.handlers import handle_rpc_exceptions
from mathesar.rpc.utils import connect
from mathesar.utils.columns import get_columns_meta_data


class TypeOptions(TypedDict, total=False):
Expand Down Expand Up @@ -282,3 +284,25 @@ def delete(
user = kwargs.get(REQUEST_KEY).user
with connect(database_id, user) as conn:
return drop_columns_from_table(table_oid, column_attnums, conn)


@rpc_method(name="columns.list_with_metadata")
@http_basic_auth_login_required
@handle_rpc_exceptions
def list_with_metadata(*, table_oid: int, database_id: int, **kwargs) -> list:
"""
List information about columns for a table, along with the metadata associated with each column.
Args:
table_oid: Identity of the table in the user's database.
database_id: The Django id of the database containing the table.
Returns:
A list of column details.
"""
user = kwargs.get(REQUEST_KEY).user
with connect(database_id, user) as conn:
column_info = get_column_info_for_table(table_oid, conn)
column_metadata = get_columns_meta_data(table_oid, database_id)
metadata_map = {
c.attnum: ColumnMetaDataBlob.from_model(c) for c in column_metadata
}
return [col | {"metadata": metadata_map.get(col["id"])} for col in column_info]
57 changes: 35 additions & 22 deletions mathesar/rpc/columns/metadata.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,10 @@
from modernrpc.auth.basic import http_basic_auth_login_required

from mathesar.rpc.exceptions.handlers import handle_rpc_exceptions
from mathesar.utils.columns import get_columns_meta_data, patch_columns_meta_data
from mathesar.utils.columns import get_columns_meta_data, set_columns_meta_data


class ColumnMetaData(TypedDict):
class ColumnMetaDataRecord(TypedDict):
"""
Metadata for a column in a table.
Expand Down Expand Up @@ -73,9 +73,9 @@ def from_model(cls, model):
)


class SettableColumnMetaData(TypedDict):
class ColumnMetaDataBlob(TypedDict):
"""
Settable metadata fields for a column in a table.
The metadata fields which can be set for a column in a table.
Attributes:
attnum: The attnum of the column in the table.
Expand Down Expand Up @@ -108,11 +108,30 @@ class SettableColumnMetaData(TypedDict):
duration_max: Optional[str]
duration_show_units: Optional[bool]

@classmethod
def from_model(cls, model):
return cls(
attnum=model.attnum,
bool_input=model.bool_input,
bool_true=model.bool_true,
bool_false=model.bool_false,
num_min_frac_digits=model.num_min_frac_digits,
num_max_frac_digits=model.num_max_frac_digits,
num_show_as_perc=model.num_show_as_perc,
mon_currency_symbol=model.mon_currency_symbol,
mon_currency_location=model.mon_currency_location,
time_format=model.time_format,
date_format=model.date_format,
duration_min=model.duration_min,
duration_max=model.duration_max,
duration_show_units=model.duration_show_units
)


@rpc_method(name="columns.metadata.list")
@http_basic_auth_login_required
@handle_rpc_exceptions
def list_(*, table_oid: int, database_id: int, **kwargs) -> list[ColumnMetaData]:
def list_(*, table_oid: int, database_id: int, **kwargs) -> list[ColumnMetaDataRecord]:
"""
List metadata associated with columns for a table. Exposed as `list`.
Expand All @@ -125,34 +144,28 @@ def list_(*, table_oid: int, database_id: int, **kwargs) -> list[ColumnMetaData]
"""
columns_meta_data = get_columns_meta_data(table_oid, database_id)
return [
ColumnMetaData.from_model(model) for model in columns_meta_data
ColumnMetaDataRecord.from_model(model) for model in columns_meta_data
]


@rpc_method(name="columns.metadata.patch")
@rpc_method(name="columns.metadata.set")
@http_basic_auth_login_required
@handle_rpc_exceptions
def patch(
*,
column_meta_data_list: list[SettableColumnMetaData],
table_oid: int,
database_id: int,
**kwargs
) -> list[ColumnMetaData]:
def set_(
*,
column_meta_data_list: list[ColumnMetaDataBlob],
table_oid: int,
database_id: int,
**kwargs
) -> None:
"""
Alter metadata settings associated with columns of a table for a database.
Set metadata associated with columns of a table for a database. Exposed as `set`.
Args:
column_meta_data_list: A list describing desired metadata alterations.
table_oid: Identity of the table whose metadata we'll modify.
database_id: The Django id of the database containing the table.
Returns:
List of altered metadata objects.
"""
columns_meta_data = patch_columns_meta_data(
set_columns_meta_data(
column_meta_data_list, table_oid, database_id
)
return [
ColumnMetaData.from_model(model) for model in columns_meta_data
]
58 changes: 2 additions & 56 deletions mathesar/tests/rpc/columns/test_c_metadata.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,15 +38,15 @@ def mock_get_columns_meta_data(_table_oid, _database_id):
monkeypatch.setattr(metadata, "get_columns_meta_data", mock_get_columns_meta_data)

expect_metadata_list = [
metadata.ColumnMetaData(
metadata.ColumnMetaDataRecord(
database_id=database_id, table_oid=table_oid, attnum=2,
bool_input="dropdown", bool_true="TRUE", bool_false="FALSE",
num_min_frac_digits=5, num_max_frac_digits=10, num_show_as_perc=False,
mon_currency_symbol="EUR", mon_currency_location="end-with-space",
time_format=None, date_format=None,
duration_min=None, duration_max=None, duration_show_units=True,
),
metadata.ColumnMetaData(
metadata.ColumnMetaDataRecord(
database_id=database_id, table_oid=table_oid, attnum=8,
bool_input="checkbox", bool_true="true", bool_false="false",
num_min_frac_digits=2, num_max_frac_digits=8, num_show_as_perc=True,
Expand All @@ -57,57 +57,3 @@ def mock_get_columns_meta_data(_table_oid, _database_id):
]
actual_metadata_list = metadata.list_(table_oid=table_oid, database_id=database_id)
assert actual_metadata_list == expect_metadata_list


# TODO consider mocking out ColumnMetaData queryset for this test
def test_columns_meta_data_patch(monkeypatch):
database_id = 2
table_oid = 123456
expect_metadata_list = [
metadata.ColumnMetaData(
database_id=database_id, table_oid=table_oid, attnum=2,
bool_input="dropdown", bool_true="TRUE", bool_false="FALSE",
num_min_frac_digits=5, num_max_frac_digits=10, num_show_as_perc=False,
mon_currency_symbol="EUR", mon_currency_location="end-with-space",
time_format=None, date_format=None,
duration_min=None, duration_max=None, duration_show_units=True,
),
metadata.ColumnMetaData(
database_id=database_id, table_oid=table_oid, attnum=8,
bool_input="checkbox", bool_true="true", bool_false="false",
num_min_frac_digits=2, num_max_frac_digits=8, num_show_as_perc=True,
mon_currency_symbol="$", mon_currency_location="after-minus",
time_format=None, date_format=None,
duration_min=None, duration_max=None, duration_show_units=True,
),
]

def mock_patch_columns_meta_data(column_meta_data_list, _table_oid, _database_id):
server_model = Server(id=2, host='example.com', port=5432)
db_model = Database(id=_database_id, name='mymathesardb', server=server_model)
return [
ColumnMetaData(
database=db_model, table_oid=_table_oid, attnum=2,
bool_input="dropdown", bool_true="TRUE", bool_false="FALSE",
num_min_frac_digits=5, num_max_frac_digits=10, num_show_as_perc=False,
mon_currency_symbol="EUR", mon_currency_location="end-with-space",
time_format=None, date_format=None,
duration_min=None, duration_max=None, duration_show_units=True,
),
ColumnMetaData(
database=db_model, table_oid=_table_oid, attnum=8,
bool_input="checkbox", bool_true="true", bool_false="false",
num_min_frac_digits=2, num_max_frac_digits=8, num_show_as_perc=True,
mon_currency_symbol="$", mon_currency_location="after-minus",
time_format=None, date_format=None,
duration_min=None, duration_max=None, duration_show_units=True,
)
]

monkeypatch.setattr(metadata, "patch_columns_meta_data", mock_patch_columns_meta_data)
actual_metadata_list = metadata.patch(
column_meta_data_list=expect_metadata_list,
table_oid=table_oid,
database_id=database_id
)
assert actual_metadata_list == expect_metadata_list
9 changes: 7 additions & 2 deletions mathesar/tests/rpc/test_endpoints.py
Original file line number Diff line number Diff line change
Expand Up @@ -64,14 +64,19 @@
"columns.add",
[user_is_authenticated]
),
(
columns.list_with_metadata,
"columns.list_with_metadata",
[user_is_authenticated]
),
(
columns.metadata.list_,
"columns.metadata.list",
[user_is_authenticated]
),
(
columns.metadata.patch,
"columns.metadata.patch",
columns.metadata.set_,
"columns.metadata.set",
[user_is_authenticated]
),
(
Expand Down
2 changes: 1 addition & 1 deletion mathesar/utils/columns.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ def get_columns_meta_data(table_oid, database_id):
)


def patch_columns_meta_data(column_meta_data_list, table_oid, database_id):
def set_columns_meta_data(column_meta_data_list, table_oid, database_id):
db_model = Database.objects.get(id=database_id)
for meta_data_dict in column_meta_data_list:
# TODO decide if this is worth the trouble of doing in bulk.
Expand Down

0 comments on commit 8688414

Please sign in to comment.