Skip to content

Commit

Permalink
Merge pull request #3731 from mathesar-foundation/exp_replace
Browse files Browse the repository at this point in the history
Implement explorations `add` & `replace` endpoint
  • Loading branch information
mathemancer authored Aug 7, 2024
2 parents 021a587 + 2ae2ec1 commit 1f5b940
Show file tree
Hide file tree
Showing 4 changed files with 91 additions and 53 deletions.
2 changes: 2 additions & 0 deletions docs/docs/api/rpc.md
Original file line number Diff line number Diff line change
Expand Up @@ -200,7 +200,9 @@ To use an RPC function:
members:
- list_
- get
- add
- delete
- replace
- run
- run_saved
- ExplorationInfo
Expand Down
79 changes: 44 additions & 35 deletions mathesar/rpc/explorations.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,9 @@
get_exploration,
delete_exploration,
run_exploration,
run_saved_exploration
run_saved_exploration,
replace_exploration,
create_exploration
)


Expand All @@ -28,7 +30,7 @@ class ExplorationInfo(TypedDict):
base_table_oid: The OID of the base table of the exploration on the database.
initial_columns: A list describing the columns to be included in the exploration.
transformations: A list describing the transformations to be made on the included columns.
display_options: A list desrcibing metadata for the columns in the explorations.
display_options: A list describing metadata for the columns in the explorations.
display_names: A map between the actual column names on the database and the alias to be displayed(if any).
description: The description of the exploration.
"""
Expand Down Expand Up @@ -62,44 +64,23 @@ class ExplorationDef(TypedDict):
Definition about a runnable exploration.
Attributes:
database_id: The Django id of the database containing the exploration.
name: The name of the exploration.
base_table_oid: The OID of the base table of the exploration on the database.
initial_columns: A list describing the columns to be included in the exploration.
display_names: A map between the actual column names on the database and the alias to be displayed.
transformations: A list describing the transformations to be made on the included columns.
limit: Specifies the number of rows to return.(default 100)
offset: Specifies the number of rows to skip.(default 0)
filter: A dict describing filters to be applied to an exploration.
e.g. Here is a dict describing getting records from exploration where "col1" = NULL and "col2" = "abc"
```
{"and": [
{"null": [
{"column_name": ["col1"]},
]},
{"equal": [
{"to_lowercase": [
{"column_name": ["col2"]},
]},
{"literal": ["abc"]},
]},
]}
```
Refer to db/functions/base.py for all the possible filters.
order_by: A list of dicts, where each dict has a `field` and `direction` field.
Here the value for `field` should be column name and `direction` should be either `asc` or `desc`.
search: A list of dicts, where each dict has a `column` and `literal` field.
Here the value for `column` should be a column name and `literal` should be a string to be searched in the aforementioned column.
duplicate_only: A list of column names for which you want duplicate records.
display_options: A list describing metadata for the columns in the explorations.
display_names: A map between the actual column names on the database and the alias to be displayed(if any).
description: The description of the exploration.
"""
database_id: int
name: str
base_table_oid: int
initial_columns: list
display_names: dict
transformations: Optional[list]
limit: Optional[int]
offset: Optional[int]
filter: Optional[dict]
order_by: Optional[list[dict]]
search: Optional[list[dict]]
duplicate_only: Optional[list]
display_options: Optional[list]
display_names: Optional[dict]
description: Optional[str]


class ExplorationResult(TypedDict):
Expand Down Expand Up @@ -195,7 +176,7 @@ def delete(*, exploration_id: int, **kwargs) -> None:
@rpc_method(name="explorations.run")
@http_basic_auth_login_required
@handle_rpc_exceptions
def run(*, exploration_def: ExplorationDef, database_id: int, **kwargs) -> ExplorationResult:
def run(*, exploration_def: ExplorationDef, database_id: int, limit: int = 100, offset: int = 0, **kwargs) -> ExplorationResult:
"""
Run an exploration.
Expand All @@ -205,7 +186,7 @@ def run(*, exploration_def: ExplorationDef, database_id: int, **kwargs) -> Explo
"""
user = kwargs.get(REQUEST_KEY).user
with connect(database_id, user) as conn:
exploration_result = run_exploration(exploration_def, database_id, conn)
exploration_result = run_exploration(exploration_def, database_id, conn, limit, offset)
return ExplorationResult.from_dict(exploration_result)


Expand All @@ -226,3 +207,31 @@ def run_saved(*, exploration_id: int, database_id: int, limit: int = 100, offset
with connect(database_id, user) as conn:
exploration_result = run_saved_exploration(exploration_id, limit, offset, database_id, conn)
return ExplorationResult.from_dict(exploration_result)


@rpc_method(name='explorations.replace')
@http_basic_auth_login_required
@handle_rpc_exceptions
def replace(*, new_exploration: ExplorationInfo) -> ExplorationInfo:
"""
Replace a saved exploration.
Args:
new_exploration: A dict describing the exploration to replace, including the updated fields.
"""
replaced_exp_model = replace_exploration(new_exploration)
return ExplorationInfo.from_model(replaced_exp_model)


@rpc_method(name='explorations.add')
@http_basic_auth_login_required
@handle_rpc_exceptions
def add(*, exploration_def: ExplorationDef) -> ExplorationInfo:
"""
Add a new exploration.
Args:
exploration_def: A dict describing the exploration to create.
"""
exp_model = create_exploration(exploration_def)
return ExplorationInfo.from_model(exp_model)
10 changes: 10 additions & 0 deletions mathesar/tests/rpc/test_endpoints.py
Original file line number Diff line number Diff line change
Expand Up @@ -164,11 +164,21 @@
"explorations.get",
[user_is_authenticated]
),
(
explorations.add,
"explorations.add",
[user_is_authenticated]
),
(
explorations.delete,
"explorations.delete",
[user_is_authenticated]
),
(
explorations.replace,
"explorations.replace",
[user_is_authenticated]
),
(
explorations.run,
"explorations.run",
Expand Down
53 changes: 35 additions & 18 deletions mathesar/utils/explorations.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
from db.queries.base import DBQuery, InitialColumn, JoinParameter
from db.tables.operations.select import get_table
from mathesar.api.utils import process_annotated_records
from mathesar.models.base import Explorations, ColumnMetaData
from mathesar.models.base import Explorations, ColumnMetaData, Database
from mathesar.rpc.columns.metadata import ColumnMetaDataRecord
from mathesar.state import get_cached_metadata

Expand All @@ -20,7 +20,34 @@ def delete_exploration(exploration_id):
Explorations.objects.get(id=exploration_id).delete()


def run_exploration(exploration_def, database_id, conn):
def replace_exploration(new_exploration):
Explorations.objects.filter(id=new_exploration["id"]).update(
database=Database.objects.get(id=new_exploration["database_id"]),
name=new_exploration["name"],
base_table_oid=new_exploration["base_table_oid"],
initial_columns=new_exploration["initial_columns"],
transformations=new_exploration.get("transformations"),
display_options=new_exploration.get("display_options"),
display_names=new_exploration.get("display_names", {}),
description=new_exploration.get("description")
)
return get_exploration(new_exploration["id"])


def create_exploration(exploration_def):
return Explorations.objects.create(
database=Database.objects.get(id=exploration_def["database_id"]),
name=exploration_def["name"],
base_table_oid=exploration_def["base_table_oid"],
initial_columns=exploration_def["initial_columns"],
transformations=exploration_def.get("transformations"),
display_options=exploration_def.get("display_options"),
display_names=exploration_def.get("display_names", {}),
description=exploration_def.get("description")
)


def run_exploration(exploration_def, database_id, conn, limit=100, offset=0):
engine = create_future_engine_with_custom_types(
conn.info.user,
conn.info.password,
Expand Down Expand Up @@ -60,12 +87,8 @@ def run_exploration(exploration_def, database_id, conn):
metadata=metadata
)
records = db_query.get_records(
limit=exploration_def.get('limit', 100),
offset=exploration_def.get('offset', 0),
filter=exploration_def.get('filter', None),
order_by=exploration_def.get('order_by', []),
search=exploration_def.get('search', []),
duplicate_only=exploration_def.get('duplicate_only', None)
limit=limit,
offset=offset
)
processed_records = process_annotated_records(records)[0]
column_metadata = _get_exploration_column_metadata(
Expand All @@ -89,12 +112,8 @@ def run_exploration(exploration_def, database_id, conn):
},
"output_columns": tuple(sa_col.name for sa_col in db_query.sa_output_columns),
"column_metadata": column_metadata,
"limit": exploration_def.get('limit', 100),
"offset": exploration_def.get('offset', 0),
"filter": exploration_def.get('filter', None),
"order_by": exploration_def.get('order_by', []),
"search": exploration_def.get('search', []),
"duplicate_only": exploration_def.get('duplicate_only', None)
"limit": limit,
"offset": offset
}


Expand All @@ -104,11 +123,9 @@ def run_saved_exploration(exploration_id, limit, offset, database_id, conn):
"base_table_oid": exp_model["base_table_oid"],
"initial_columns": exp_model["initial_columns"],
"display_names": exp_model["display_names"],
"transformations": exp_model["transformations"],
"limit": limit,
"offset": offset
"transformations": exp_model["transformations"]
}
return run_exploration(exploration_def, database_id, conn)
return run_exploration(exploration_def, database_id, conn, limit, offset)


def _get_exploration_column_metadata(
Expand Down

0 comments on commit 1f5b940

Please sign in to comment.