Skip to content

Commit 83356bd

Browse files
Merge pull request #288 from DashAISoftware/feat/ux-dataset
Feat/ux dataset
2 parents 0c730a3 + a5adf15 commit 83356bd

28 files changed

Lines changed: 369 additions & 89 deletions

DashAI/back/api/api_v1/endpoints/datasets.py

Lines changed: 3 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@
1919
from DashAI.back.dataloaders.classes.dashai_dataset import (
2020
get_columns_spec,
2121
get_dataset_info,
22-
update_columns_spec,
2322
)
2423
from DashAI.back.dependencies.database.models import Dataset, Experiment
2524

@@ -531,20 +530,16 @@ async def update_dataset(
531530
session_factory: sessionmaker = Depends(lambda: di["session_factory"]),
532531
config: Dict[str, Any] = Depends(lambda: di["config"]),
533532
):
534-
"""Updates the name and/or task name of a dataset with the provided ID.
533+
"""Updates the name of a dataset with the provided ID.
535534
536535
Parameters
537536
----------
538537
dataset_id : int
539538
ID of the dataset to update.
540539
params : DatasetUpdateParams
541540
A dictionary containing the new values for the dataset.
542-
name : str, optional
541+
name : str
543542
New name for the dataset.
544-
task_name : str, optional
545-
New task name for the dataset.
546-
columns : Dict[str, ColumnSpecItemParams], optional
547-
New column specification for the dataset.
548543
session_factory : Callable[..., ContextManager[Session]]
549544
A factory that creates a context manager that handles a SQLAlchemy session.
550545
The generated session can be used to access and query the database.
@@ -557,12 +552,8 @@ async def update_dataset(
557552
with session_factory() as db:
558553
try:
559554
dataset = db.get(Dataset, dataset_id)
560-
if params.columns:
561-
update_columns_spec(f"{dataset.file_path}/dataset", params.columns)
562-
elif params.name:
555+
if params.name and params.name != dataset.name:
563556
setattr(dataset, "name", params.name)
564-
new_folder_path = config["DATASETS_PATH"] / params.name
565-
os.rename(dataset.file_path, new_folder_path)
566557
db.commit()
567558
db.refresh(dataset)
568559
return dataset

DashAI/back/api/api_v1/endpoints/notebook.py

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,10 @@ def create_notebook(
6969
shutil.copytree(dataset_folder, new_folder_path, dirs_exist_ok=True)
7070

7171
notebook_data = params.model_dump()
72+
notebook_data = {
73+
**notebook_data,
74+
"name": notebook_data.get("name") or "Untitled Notebook",
75+
}
7276
notebook_data["file_path"] = new_folder_path
7377
notebook_model = Notebook(**notebook_data)
7478
db.add(notebook_model)
@@ -338,3 +342,56 @@ async def create_dataset_from_notebook(
338342
) from e
339343

340344
return dataset
345+
346+
347+
@router.patch("/{notebook_id}")
348+
@inject
349+
async def update_notebook(
350+
notebook_id: int,
351+
params: schemas.NotebookUpdateParams,
352+
session_factory: sessionmaker = Depends(lambda: di["session_factory"]),
353+
):
354+
"""Updates the name of a notebook with the provided ID.
355+
356+
Parameters
357+
----------
358+
notebook_id : int
359+
ID of the notebook to update.
360+
params : NotebookUpdateParams
361+
A dictionary containing the new values for the notebook.
362+
name : str
363+
New name for the notebook.
364+
session_factory : Callable[..., ContextManager[Session]]
365+
A factory that creates a context manager that handles a SQLAlchemy session.
366+
The generated session can be used to access and query the database.
367+
368+
Returns
369+
-------
370+
Dict
371+
A dictionary containing the updated dataset record.
372+
"""
373+
with session_factory() as db:
374+
try:
375+
notebook = db.get(Notebook, notebook_id)
376+
if not notebook:
377+
raise HTTPException(
378+
status_code=status.HTTP_404_NOT_FOUND,
379+
detail="Notebook not found",
380+
)
381+
if not params.name or params.name == notebook.name:
382+
raise HTTPException(
383+
status_code=status.HTTP_304_NOT_MODIFIED,
384+
detail="No fields to update",
385+
)
386+
387+
setattr(notebook, "name", params.name)
388+
db.commit()
389+
db.refresh(notebook)
390+
except Exception as e:
391+
log.error(f"Error updating notebook {notebook_id}: {e}")
392+
raise HTTPException(
393+
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
394+
detail="Failed to update notebook",
395+
) from e
396+
397+
return notebook

DashAI/back/api/api_v1/schemas/converter_params.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,15 +6,15 @@
66
class ConverterParams(BaseModel):
77
order: int = 0
88
params: Dict[str, Union[str, int, float, bool, None]] = None
9-
scope: Dict[str, List[int]] = None
10-
target_index: int = None
9+
scope: Dict[str, Union[List[int], List[Dict[str, Any]]]] = None
10+
target: Union[Dict[str, Any], None] = None
1111

1212
def serialize(self) -> Dict[str, Any]:
1313
return {
1414
"order": self.order,
1515
"params": self.params,
1616
"scope": self.scope,
17-
"target_index": self.target_index,
17+
"target": self.target,
1818
}
1919

2020

DashAI/back/api/api_v1/schemas/datasets_params.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,6 @@ class ColumnsSpecParams(BaseModel):
2222

2323
class DatasetUpdateParams(BaseModel):
2424
name: str = None
25-
columns: Dict[str, ColumnSpecItemParams] = None
2625

2726

2827
class DatasetUploadFromNotebookParams(BaseModel):

DashAI/back/api/api_v1/schemas/notebook_params.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,3 +19,7 @@ class Notebook(NotebookBase):
1919
created: datetime
2020
last_modified: datetime
2121
file_path: str
22+
23+
24+
class NotebookUpdateParams(BaseModel):
25+
name: str = None

DashAI/back/job/converter_job.py

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -211,7 +211,14 @@ def instantiate_chain(
211211
# dataset to edit
212212
dataset_path = f"{converter_list.notebook.file_path}/dataset"
213213
loaded_dataset = load_dataset(dataset_path)
214-
target_column_index = converter_list.parameters.pop("target_index")
214+
print("Pre target column")
215+
params = converter_list.parameters or {}
216+
target_column_index = (
217+
params["target"].get("idx")
218+
if params.get("target") is not None
219+
else None
220+
)
221+
print(target_column_index)
215222

216223
if not loaded_dataset:
217224
raise JobError(f"Dataset with path {dataset_path} not found")
@@ -348,7 +355,9 @@ def instantiate_chain(
348355
converter_scope = converter_info["scope"]
349356

350357
# Process columns scope
351-
columns_scope = [column - 1 for column in converter_scope["columns"]]
358+
columns_scope = [
359+
column["idx"] - 1 for column in converter_scope["columns"]
360+
]
352361
scope_column_indexes = sorted(set(columns_scope))
353362

354363
# If no columns specified, use all columns

DashAI/back/job/dataset_job.py

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import logging
44
import os
55
import shutil
6+
import uuid
67
from typing import Any, Dict
78

89
from kink import inject
@@ -57,15 +58,16 @@ async def run(
5758

5859
parsed_params = parse_params(DatasetParams, json.dumps(params))
5960
dataloader = component_registry[parsed_params.dataloader]["class"]()
60-
folder_path = config["DATASETS_PATH"] / parsed_params.name
61+
random_name = str(uuid.uuid4())
62+
folder_path = config["DATASETS_PATH"] / random_name
6163

6264
try:
6365
log.debug("Trying to create a new dataset path: %s", folder_path)
6466
folder_path.mkdir(parents=True)
6567
except FileExistsError as e:
6668
log.exception(e)
6769
raise JobError(
68-
f"A dataset with the name {parsed_params.name} already exists."
70+
f"A dataset with the name {random_name} already exists."
6971
) from e
7072

7173
try:
@@ -90,7 +92,11 @@ async def run(
9092
try:
9193
folder_path = os.path.realpath(folder_path)
9294
new_dataset = Dataset(
93-
name=parsed_params.name,
95+
name=(
96+
parsed_params.name
97+
if parsed_params.name
98+
else "Untitled Dataset"
99+
),
94100
file_path=folder_path,
95101
)
96102
db.add(new_dataset)

DashAI/front/src/api/notebook.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,3 +53,14 @@ export const createDatasetFromNotebook = async (
5353

5454
return response.data;
5555
};
56+
57+
export const updateNotebook = async (
58+
id: number,
59+
formData: object,
60+
): Promise<INotebook> => {
61+
console.log("updating notebook with id:", id, "and formData:", formData);
62+
const response = await api.patch(`${notebookEndpoint}/${id}`, {
63+
...formData,
64+
});
65+
return response.data;
66+
};

DashAI/front/src/components/notebooks/LeftBar.jsx

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,10 @@ export default function DatasetsNotebooksBar({
1717
selectedNotebookId,
1818
onDatasetClick,
1919
onDatasetDelete,
20+
onDatasetEdit,
2021
onNotebookClick,
2122
onNotebookDelete,
23+
onNotebookEdit,
2224
handleNewSessionButton,
2325
}) {
2426
const [searchQuery, setSearchQuery] = useState("");
@@ -50,6 +52,25 @@ export default function DatasetsNotebooksBar({
5052
}
5153
};
5254

55+
const getDatasetDescription = (dataset) => {
56+
return (
57+
dataset.description ||
58+
`${dataset.total_rows || 0} rows, ${dataset.total_columns || 0} cols`
59+
);
60+
};
61+
62+
const getNotebookDescription = (notebook) => {
63+
if (notebook.dataset_id && datasets.length > 0) {
64+
const associatedDataset = datasets.find(
65+
(dataset) => dataset.id === notebook.dataset_id,
66+
);
67+
return associatedDataset?.name
68+
? `from ${associatedDataset.name} dataset`
69+
: "No dataset";
70+
}
71+
return notebook.description || "";
72+
};
73+
5374
return (
5475
<SideBar>
5576
{/* Header */}
@@ -88,9 +109,11 @@ export default function DatasetsNotebooksBar({
88109
selectedItemId={selectedDatasetId}
89110
onItemClick={onDatasetClick}
90111
onItemDelete={onDatasetDelete}
112+
onItemEdit={onDatasetEdit}
91113
defaultOpen={true}
92114
title="Available Datasets"
93115
Icon={StorageIcon}
116+
getItemDescription={getDatasetDescription}
94117
/>
95118

96119
<Divider sx={{ width: "90%", bgcolor: "#252836", mx: "auto" }} />
@@ -100,10 +123,13 @@ export default function DatasetsNotebooksBar({
100123
selectedItemId={selectedNotebookId}
101124
onItemClick={onNotebookClick}
102125
onItemDelete={onNotebookDelete}
126+
onItemEdit={onNotebookEdit}
103127
onItemInfo={handleNotebookInfo}
104128
defaultOpen={true}
105129
title="Notebooks"
106130
Icon={DescriptionIcon}
131+
datasets={datasets}
132+
getItemDescription={getNotebookDescription}
107133
/>
108134
</Box>
109135

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
import { Box, Typography } from "@mui/material";
2+
3+
export default function NoteBox({ message }) {
4+
return (
5+
<Box
6+
sx={{
7+
mt: 2,
8+
p: 2,
9+
bgcolor: "#212121",
10+
borderRadius: 1,
11+
border: "1px solid rgba(255, 255, 255, 0.1)",
12+
mb: 2,
13+
}}
14+
>
15+
<Typography variant="subtitle2" sx={{ color: "#00BEBB", mb: 1 }}>
16+
Note:
17+
</Typography>
18+
<Typography variant="body2">{message}</Typography>
19+
</Box>
20+
);
21+
}

0 commit comments

Comments
 (0)