Skip to content

Commit

Permalink
Merge pull request #3641 from mathesar-foundation/column_metadata_rpc
Browse files Browse the repository at this point in the history
Add `columns.metadata.list` rpc function
  • Loading branch information
Anish9901 authored Jun 26, 2024
2 parents 98a2f83 + 013e11b commit 90309d0
Show file tree
Hide file tree
Showing 12 changed files with 318 additions and 133 deletions.
1 change: 1 addition & 0 deletions config/settings/common_settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ def pipe_delim(pipe_string):
MODERNRPC_METHODS_MODULES = [
'mathesar.rpc.connections',
'mathesar.rpc.columns',
'mathesar.rpc.columns.metadata',
'mathesar.rpc.schemas',
'mathesar.rpc.tables'
]
Expand Down
25 changes: 16 additions & 9 deletions docs/docs/api/rpc.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,19 +37,19 @@ To use an RPC function:
}
```

---
## Connections

::: mathesar.rpc.connections
::: connections
options:
members:
- add_from_known_connection
- add_from_scratch
- grant_access_to_user
- DBModelReturn

---
## Schemas

::: mathesar.rpc.schemas
::: schemas
options:
members:
- list_
Expand All @@ -59,9 +59,9 @@ To use an RPC function:
- SchemaInfo
- SchemaPatch

---
## Tables

::: mathesar.rpc.tables
::: tables
options:
members:
- list_
Expand All @@ -73,20 +73,27 @@ To use an RPC function:
- TableInfo
- SettableTableInfo

---
## Columns

::: mathesar.rpc.columns
::: columns
options:
members:
- list_
- add
- patch
- delete
- ColumnListReturn
- ColumnInfo
- SettableColumnInfo
- TypeOptions
- ColumnDefault

## Column Metadata

::: columns.metadata
options:
members:
- list_
- ColumnMetaData

## Responses

Expand Down
8 changes: 4 additions & 4 deletions docs/mkdocs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -57,14 +57,14 @@ plugins:
- mkdocstrings:
handlers:
python:
paths: [..]
paths: [../mathesar/rpc/]
options:
heading_level: 3
docstring_style: google
separate_signature: true
show_root_heading: true
show_root_full_path: false
show_root_toc_entry: false
show_root_members_full_path: true
show_source: false
show_symbol_type_heading: true
group_by_category: false

theme:
Expand Down
46 changes: 46 additions & 0 deletions mathesar/migrations/0008_add_metadata_models.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
# Generated by Django 4.2.11 on 2024-06-20 08:02

from django.db import migrations, models
import django.db.models.deletion


class Migration(migrations.Migration):

dependencies = [
('mathesar', '0007_users_permissions_remodel'),
]

operations = [
migrations.CreateModel(
name='ColumnMetaData',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('created_at', models.DateTimeField(auto_now_add=True)),
('updated_at', models.DateTimeField(auto_now=True)),
('table_oid', models.PositiveIntegerField()),
('attnum', models.PositiveIntegerField()),
('bool_input', models.CharField(blank=True, choices=[('dropdown', 'dropdown'), ('checkbox', 'checkbox')])),
('bool_true', models.CharField(default='True')),
('bool_false', models.CharField(default='False')),
('num_min_frac_digits', models.PositiveIntegerField(blank=True)),
('num_max_frac_digits', models.PositiveIntegerField(blank=True)),
('num_show_as_perc', models.BooleanField(default=False)),
('mon_currency_symbol', models.CharField(default='$')),
('mon_currency_location', models.CharField(choices=[('after-minus', 'after-minus'), ('end-with-space', 'end-with-space')], default='after-minus')),
('time_format', models.CharField(blank=True)),
('date_format', models.CharField(blank=True)),
('duration_min', models.CharField(blank=True, max_length=255)),
('duration_max', models.CharField(blank=True, max_length=255)),
('duration_show_units', models.BooleanField(default=True)),
('database', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='mathesar.database')),
],
),
migrations.AddConstraint(
model_name='columnmetadata',
constraint=models.UniqueConstraint(fields=('database', 'table_oid', 'attnum'), name='unique_column_metadata'),
),
migrations.AddConstraint(
model_name='columnmetadata',
constraint=models.CheckConstraint(check=models.Q(('num_max_frac_digits__lte', 20), ('num_min_frac_digits__lte', 20), ('num_min_frac_digits__lte', models.F('num_max_frac_digits'))), name='frac_digits_integrity'),
),
]
41 changes: 41 additions & 0 deletions mathesar/models/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -80,3 +80,44 @@ def connection(self):
user=self.role.name,
password=self.role.password,
)


class ColumnMetaData(BaseModel):
database = models.ForeignKey('Database', on_delete=models.CASCADE)
table_oid = models.PositiveIntegerField()
attnum = models.PositiveIntegerField()
bool_input = models.CharField(
choices=[("dropdown", "dropdown"), ("checkbox", "checkbox")],
blank=True
)
bool_true = models.CharField(default='True')
bool_false = models.CharField(default='False')
num_min_frac_digits = models.PositiveIntegerField(blank=True)
num_max_frac_digits = models.PositiveIntegerField(blank=True)
num_show_as_perc = models.BooleanField(default=False)
mon_currency_symbol = models.CharField(default="$")
mon_currency_location = models.CharField(
choices=[("after-minus", "after-minus"), ("end-with-space", "end-with-space")],
default="after-minus"
)
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)

class Meta:
constraints = [
models.UniqueConstraint(
fields=["database", "table_oid", "attnum"],
name="unique_column_metadata"
),
models.CheckConstraint(
check=(
models.Q(num_max_frac_digits__lte=20)
& models.Q(num_min_frac_digits__lte=20)
& models.Q(num_min_frac_digits__lte=models.F("num_max_frac_digits"))
),
name="frac_digits_integrity"
)
]
1 change: 1 addition & 0 deletions mathesar/rpc/columns/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
from .base import * # noqa
32 changes: 3 additions & 29 deletions mathesar/rpc/columns.py → mathesar/rpc/columns/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@
from db.columns.operations.select import get_column_info_for_table
from mathesar.rpc.exceptions.handlers import handle_rpc_exceptions
from mathesar.rpc.utils import connect
from mathesar.utils.columns import get_raw_display_options


class TypeOptions(TypedDict, total=False):
Expand Down Expand Up @@ -171,49 +170,24 @@ def from_dict(cls, col_info):
)


class ColumnListReturn(TypedDict):
"""
Information about the columns of a table.
Attributes:
column_info: Column information from the user's database.
display_options: Display metadata managed by Mathesar.
"""
column_info: list[ColumnInfo]
display_options: list[dict]


@rpc_method(name="columns.list")
@http_basic_auth_login_required
@handle_rpc_exceptions
def list_(*, table_oid: int, database_id: int, **kwargs) -> ColumnListReturn:
def list_(*, table_oid: int, database_id: int, **kwargs) -> list[ColumnInfo]:
"""
List information about columns for a table. Exposed as `list`.
Also return display options for each column, if they're defined.
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, and a separate list of display options.
A list of column details.
"""
user = kwargs.get(REQUEST_KEY).user
with connect(database_id, user) as conn:
raw_column_info = get_column_info_for_table(table_oid, conn)
column_info, attnums = tuple(
zip(
*[(ColumnInfo.from_dict(col), col['id']) for col in raw_column_info]
)
)
display_options = get_raw_display_options(
database_id, table_oid, attnums, user
)
return ColumnListReturn(
column_info=column_info,
display_options=display_options,
)
return [ColumnInfo.from_dict(col) for col in raw_column_info]


@rpc_method(name="columns.add")
Expand Down
93 changes: 93 additions & 0 deletions mathesar/rpc/columns/metadata.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
"""
Classes and functions exposed to the RPC endpoint for managing column metadata.
"""
from typing import Literal, Optional, TypedDict

from modernrpc.core import rpc_method
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


class ColumnMetaData(TypedDict):
"""
Metadata for a column in a table.
Only the `database`, `table_oid`, and `attnum` keys are required.
Attributes:
database_id: The Django id of the database containing the table.
table_oid: The OID of the table containing the column
attnum: The attnum of the column in the table.
bool_input: How the input for a boolean column should be shown.
bool_true: A string to display for `true` values.
bool_false: A string to display for `false` values.
num_min_frac_digits: Minimum digits shown after the decimal point.
num_max_frac_digits: Maximum digits shown after the decimal point.
num_show_as_perc: Whether to show a numeric value as a percentage.
mon_currency_symbol: The currency symbol shown for money value.
mon_currency_location: Where the currency symbol should be shown.
time_format: A string representing the format of time values.
date_format: A string representing the format of date values.
duration_min: The smallest unit for displaying durations.
duration_max: The largest unit for displaying durations.
duration_show_units: Whether to show the units for durations.
"""
database_id: int
table_oid: int
attnum: int
bool_input: Optional[Literal["dropdown", "checkbox"]]
bool_true: Optional[str]
bool_false: Optional[str]
num_min_frac_digits: Optional[int]
num_max_frac_digits: Optional[int]
num_show_as_perc: Optional[bool]
mon_currency_symbol: Optional[str]
mon_currency_location: Optional[Literal["after-minus", "end-with-space"]]
time_format: Optional[str]
date_format: Optional[str]
duration_min: Optional[str]
duration_max: Optional[str]
duration_show_units: Optional[bool]

@classmethod
def from_model(cls, model):
return cls(
database_id=model.database.id,
table_oid=model.table_oid,
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]:
"""
List metadata associated with columns for a table. Exposed as `list`.
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 meta data objects.
"""
columns_meta_data = get_columns_meta_data(table_oid, database_id)
return [
ColumnMetaData.from_model(model) for model in columns_meta_data
]
Loading

0 comments on commit 90309d0

Please sign in to comment.