Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add columns.metadata.list rpc function #3641

Merged
merged 15 commits into from
Jun 26, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading