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

feat: Add default value support for table columns #4043

Merged
merged 17 commits into from
Oct 9, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
836e3b5
Add 'type', 'description', and 'default' fields to Table schema and e…
ogabrielluiz Oct 7, 2024
f05f080
Add type-based mapping to formatter validator in table schema
ogabrielluiz Oct 7, 2024
6c5b536
Add default value support for new table rows in TableNodeComponent
ogabrielluiz Oct 7, 2024
88a7dec
Add optional 'description' and 'default' fields to ColumnField interface
ogabrielluiz Oct 7, 2024
73ab116
Add default value inference for table columns in utils.ts
ogabrielluiz Oct 7, 2024
775f3af
Add default table input validation and update formatter logic in Colu…
ogabrielluiz Oct 7, 2024
31a136d
Add unit tests for Column class in table schema module
ogabrielluiz Oct 7, 2024
7f934d6
Merge branch 'main' into feat/add-default-table-input
github-actions[bot] Oct 8, 2024
aa8c64f
Merge branch 'main' into feat/add-default-table-input
Cristhianzl Oct 9, 2024
9885b8f
Merge branch 'main' into feat/add-default-table-input
github-actions[bot] Oct 9, 2024
b3df40e
Merge branch 'main' into feat/add-default-table-input
github-actions[bot] Oct 9, 2024
0425304
Add default value and validator for 'display_name' in Column class
ogabrielluiz Oct 9, 2024
df536a5
Add test for default display_name in Column creation
ogabrielluiz Oct 9, 2024
de41269
Merge branch 'main' into feat/add-default-table-input
github-actions[bot] Oct 9, 2024
381f090
Merge branch 'main' into feat/add-default-table-input
github-actions[bot] Oct 9, 2024
31f04c0
Merge branch 'main' into feat/add-default-table-input
github-actions[bot] Oct 9, 2024
abdff39
Merge branch 'main' into feat/add-default-table-input
github-actions[bot] Oct 9, 2024
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
30 changes: 24 additions & 6 deletions src/backend/base/langflow/schema/table.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
from enum import Enum

from pydantic import BaseModel, Field, field_validator
from pydantic import BaseModel, ConfigDict, Field, field_validator, model_validator

VALID_TYPES = ["date", "number", "text", "json", "integer", "int", "float", "str", "string"]


class FormatterType(str, Enum):
Expand All @@ -11,19 +13,35 @@ class FormatterType(str, Enum):


class Column(BaseModel):
display_name: str
model_config = ConfigDict(populate_by_name=True)
name: str
display_name: str = Field(default="")
sortable: bool = Field(default=True)
filterable: bool = Field(default=True)
formatter: FormatterType | str | None = None

@field_validator("formatter")
formatter: FormatterType | str | None = Field(default=None, alias="type")
description: str | None = None
default: str | None = None

@model_validator(mode="after")
def set_display_name(self):
if not self.display_name:
self.display_name = self.name
return self

@field_validator("formatter", mode="before")
@classmethod
def validate_formatter(cls, value):
if value in ["integer", "int", "float"]:
value = FormatterType.number
if value in ["str", "string"]:
value = FormatterType.text
if value == "dict":
value = FormatterType.json
if isinstance(value, str):
return FormatterType(value)
if isinstance(value, FormatterType):
return value
msg = "Invalid formatter type"
msg = f"Invalid formatter type: {value}. Valid types are: {FormatterType}"
raise ValueError(msg)


Expand Down
60 changes: 60 additions & 0 deletions src/backend/tests/unit/io/test_table_schema.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
# Generated by qodo Gen

import pytest

from langflow.schema.table import Column, FormatterType


@pytest.fixture
def client():
pass


class TestColumn:
# Creating a Column instance without display_name sets it to the name
def test_create_column_without_display_name(self):
column = Column(name="test_column")
assert column.display_name == "test_column"

# Creating a Column instance with valid formatter values
def test_create_column_with_valid_formatter(self):
column = Column(display_name="Test Column", name="test_column", formatter="date")
assert column.formatter == FormatterType.date

# Formatter is set based on provided formatter value
def test_formatter_set_based_on_value(self):
column = Column(display_name="Test Column", name="test_column", formatter="int")
assert column.formatter == FormatterType.number

# Default values for sortable and filterable are set to True
def test_default_sortable_filterable(self):
column = Column(display_name="Test Column", name="test_column")
assert column.sortable is True
assert column.filterable is True

# Ensure formatter field is correctly set when provided a FormatterType
def test_formatter_explicitly_set_to_enum(self):
column = Column(display_name="Date Column", name="date_column", formatter=FormatterType.date)
assert column.formatter == FormatterType.date

# Invalid formatter raises ValueError
def test_invalid_formatter_raises_value_error(self):
with pytest.raises(ValueError):
Column(display_name="Invalid Column", name="invalid_column", formatter="invalid")

# Formatter is None when not provided
def test_formatter_none_when_not_provided(self):
column = Column(display_name="Test Column", name="test_column")
assert column.formatter is None

# Description and default can be set
def test_description_and_default(self):
column = Column(
display_name="Test Column", name="test_column", description="A test column", default="default_value"
)
assert column.description == "A test column"
assert column.default == "default_value"

def test_create_with_type_instead_of_formatter(self):
column = Column(display_name="Test Column", name="test_column", type="date")
assert column.formatter == FormatterType.date
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ export default function TableNodeComponent({
function addRow() {
const newRow = {};
componentColumns.forEach((column) => {
newRow[column.name] = null;
newRow[column.name] = column.default ?? null; // Use the default value if available
});
handleOnNewValue({ value: [...value, newRow] });
}
Expand Down
8 changes: 5 additions & 3 deletions src/frontend/src/types/utils/functions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,12 @@ export enum FormatterType {
json = "json",
}

export type ColumnField = {
display_name: string;
export interface ColumnField {
name: string;
display_name: string;
sortable: boolean;
filterable: boolean;
formatter?: FormatterType;
};
description?: string;
default?: any; // Add this line
}
12 changes: 11 additions & 1 deletion src/frontend/src/utils/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -528,7 +528,18 @@ export function generateBackendColumnsFromValue(rows: Object[]): ColumnField[] {
display_name: column.headerName ?? "",
sortable: true,
filterable: true,
default: null, // Initialize default to null or appropriate value
};

// Attempt to infer the default value from the data, if possible
if (rows.length > 0) {
const sampleValue = rows[0][column.field ?? ""];
if (sampleValue !== undefined) {
newColumn.default = sampleValue;
}
}

// Determine the formatter based on the sample value
if (rows[0] && rows[0][column.field ?? ""]) {
const value = rows[0][column.field ?? ""] as any;
if (typeof value === "string") {
Expand All @@ -538,7 +549,6 @@ export function generateBackendColumnsFromValue(rows: Object[]): ColumnField[] {
newColumn.formatter = FormatterType.text;
}
} else if (typeof value === "object" && value !== null) {
// Check if the object is a Date object
if (
Object.prototype.toString.call(value) === "[object Date]" ||
value instanceof Date
Expand Down
Loading