Skip to content
Draft
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
5 changes: 5 additions & 0 deletions apps/api/src/python/visualizations.ts
Original file line number Diff line number Diff line change
Expand Up @@ -604,6 +604,11 @@ def _briefer_create_visualization(
df = df[df[column_name].isnull()]
elif operator == 'isNotNull':
df = df[df[column_name].notnull()]
elif pd.api.types.is_bool_dtype(df[column_name]):
if operator == 'isTrue':
df = df[df[column_name]]
elif operator == 'isFalse':
df = df[~df[column_name]]
elif pd.api.types.is_datetime64_any_dtype(df[column_name]):
# Convert both DataFrame column and value to UTC safely
df_column_utc, value_utc = _briefer_convert_to_utc_safe(df[column_name], pd.to_datetime(value))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,15 @@ import {
VisualizationDateFilterOperator,
VisualizationNumberFilterOperator,
VisualizationStringFilterOperator,
VisualizationBooleanFilterOperator,
numberFilterOperators,
stringFilterOperators,
dateFilterOperators,
booleanFilterOperators,
VisualizationNumberFilter,
VisualizationStringFilter,
VisualizationDateFilter,
VisualizationBooleanFilter,
toDate,
DataFrame,
VisualizationFilter,
Expand Down Expand Up @@ -49,6 +52,7 @@ function isNumberOperator(
| VisualizationNumberFilterOperator
| VisualizationStringFilterOperator
| VisualizationDateFilterOperator
| VisualizationBooleanFilterOperator
): operator is VisualizationNumberFilterOperator {
return VisualizationNumberFilterOperator.safeParse(operator).success
}
Expand All @@ -58,15 +62,27 @@ function isStringOperator(
| VisualizationNumberFilterOperator
| VisualizationStringFilterOperator
| VisualizationDateFilterOperator
| VisualizationBooleanFilterOperator
): operator is VisualizationStringFilterOperator {
return VisualizationStringFilterOperator.safeParse(operator).success
}

function isBooleanOperator(
operator:
| VisualizationNumberFilterOperator
| VisualizationStringFilterOperator
| VisualizationDateFilterOperator
| VisualizationBooleanFilterOperator
): operator is VisualizationBooleanFilterOperator {
return VisualizationBooleanFilterOperator.safeParse(operator).success
}

function isDateOperator(
operator:
| VisualizationNumberFilterOperator
| VisualizationStringFilterOperator
| VisualizationDateFilterOperator
| VisualizationBooleanFilterOperator
): operator is VisualizationDateFilterOperator {
return VisualizationDateFilterOperator.safeParse(operator).success
}
Expand Down Expand Up @@ -169,6 +185,27 @@ function stringOperatorLabel(
}
}

function booleanOperatorSymbol(
operator: VisualizationBooleanFilterOperator
): string {
switch (operator) {
case 'isTrue':
return 'is true'
case 'isFalse':
return 'is false'
}
}
function booleanOperatorLabel(
operator: VisualizationBooleanFilterOperator
): string {
switch (operator) {
case 'isTrue':
return 'Is True'
case 'isFalse':
return 'Is False'
}
}

function dateOperatorSymbol(operator: VisualizationDateFilterOperator): string {
switch (operator) {
case 'eq':
Expand Down Expand Up @@ -215,6 +252,7 @@ function getOperatorLabel(
| VisualizationStringFilterOperator
| VisualizationNumberFilterOperator
| VisualizationDateFilterOperator
| VisualizationBooleanFilterOperator
): string {
if (isNumberOperator(operator)) {
return numberOperatorLabel(operator)
Expand All @@ -224,6 +262,10 @@ function getOperatorLabel(
return stringOperatorLabel(operator)
}

if (isBooleanOperator(operator)) {
return booleanOperatorLabel(operator)
}

return dateOperatorLabel(operator)
}

Expand All @@ -232,6 +274,7 @@ function searchOperator<
| VisualizationNumberFilterOperator
| VisualizationStringFilterOperator
| VisualizationDateFilterOperator
| VisualizationBooleanFilterOperator
>(options: T[], query: string): T[] {
return options.filter((c) => {
if (isNumberOperator(c)) {
Expand All @@ -248,6 +291,13 @@ function searchOperator<
)
}

if (isBooleanOperator(c)) {
return (
booleanOperatorLabel(c).toLowerCase().includes(query.toLowerCase()) ||
booleanOperatorSymbol(c).toLowerCase().includes(query.toLowerCase())
)
}

return (
dateOperatorLabel(c).toLowerCase().includes(query.toLowerCase()) ||
dateOperatorSymbol(c).toLowerCase().includes(query.toLowerCase())
Expand All @@ -268,9 +318,8 @@ function getOperatorOptions(columnType: DataFrameColumn['type']) {
return dateFilterOperators
}

// TODO: add filtering capabilities for boolean types
if (NumpyBoolTypes.safeParse(columnType).success) {
return []
return booleanFilterOperators
}

// TODO: this should never happen, we should be alerted
Expand All @@ -281,6 +330,7 @@ type Operator =
| VisualizationStringFilterOperator
| VisualizationNumberFilterOperator
| VisualizationDateFilterOperator
| VisualizationBooleanFilterOperator

interface Props {
dataframe: Pick<DataFrame, 'name' | 'columns'>
Expand Down Expand Up @@ -364,6 +414,13 @@ function FilterSelector(props: Props) {
return
}

if (NumpyBoolTypes.safeParse(column.type).success) {
if (!isBooleanOperator(operator)) {
setOperator('isTrue')
}
return
}

if (NumpyDateTypes.safeParse(column.type).success) {
if (!isDateOperator(operator)) {
setOperator('eq')
Expand Down Expand Up @@ -418,6 +475,23 @@ function FilterSelector(props: Props) {
}
}

if (
NumpyBoolTypes.safeParse(column.type).success
) {
if (isBooleanOperator(operator)) {
const filter = VisualizationBooleanFilter.safeParse({
id: props.filter.id,
column,
operator,
value,
})
if (filter.success) {
props.onChange(filter.data)
return
}
}
}

if (NumpyDateTypes.safeParse(column.type).success) {
if (isDateOperator(operator)) {
const filter = VisualizationDateFilter.safeParse({
Expand Down Expand Up @@ -514,7 +588,7 @@ function FilterSelector(props: Props) {
}
}

if (column && (newOp === 'isNull' || newOp === 'isNotNull')) {
if (column && (newOp === 'isNull' || newOp === 'isNotNull' || newOp === 'isTrue' || newOp === 'isFalse')) {
if (
NumpyNumberTypes.or(NumpyTimeDeltaTypes).safeParse(column.type)
.success
Expand All @@ -528,6 +602,12 @@ function FilterSelector(props: Props) {
setValue('filter')
}

if (
NumpyBoolTypes.safeParse(column.type).success
) {
setValue('filter') // FIXME: Improve value handling for boolean filtering
}

if (NumpyDateTypes.safeParse(column.type).success) {
setValue(new Date().toISOString())
}
Expand Down Expand Up @@ -636,7 +716,7 @@ function FilterSelector(props: Props) {
<span>{column?.name ?? 'New filter'}</span>
<span
className={clsx(
operator === 'isNull' || operator === 'isNotNull'
operator === 'isNull' || operator === 'isNotNull' || operator === 'isTrue' || operator === 'isFalse'
? 'pl-0.5'
: 'px-0.5',
props.isInvalid ? 'text-red-400' : 'text-gray-400'
Expand All @@ -647,10 +727,12 @@ function FilterSelector(props: Props) {
? numberOperatorSymbol(operator)
: isStringOperator(operator)
? stringOperatorSymbol(operator)
: isBooleanOperator(operator)
? booleanOperatorSymbol(operator)
: dateOperatorSymbol(operator)
: ''}
</span>
{operator !== 'isNull' && operator !== 'isNotNull' ? (
{operator !== 'isNull' && operator !== 'isNotNull' && operator !== 'isTrue' && operator !== 'isFalse' ? (
<>
{renderedValue ? (
<span className="px-1.5 py-0.5 bg-ceramic-100 text-ceramic-500 rounded-md">
Expand Down Expand Up @@ -719,6 +801,7 @@ function FilterSelector(props: Props) {
| VisualizationNumberFilterOperator
| VisualizationDateFilterOperator
| VisualizationStringFilterOperator
| VisualizationBooleanFilterOperator
>
icon={() => null}
label="Operator"
Expand All @@ -730,7 +813,7 @@ function FilterSelector(props: Props) {
placeholder="Operator"
disabled={props.disabled}
/>
{operator !== 'isNull' && operator !== 'isNotNull' && (
{operator !== 'isNull' && operator !== 'isNotNull' && operator !== 'isTrue' && operator !== 'isFalse' && (
<div className="relative">
{VisualizationStringFilterMultiValuesOperator.safeParse(
operator
Expand Down
26 changes: 26 additions & 0 deletions packages/types/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -397,6 +397,8 @@ export type VisualizationStringFilterSingleValueOperator = z.infer<
export const VisualizationOperatorWithoutValue = z.union([
z.literal('isNull'),
z.literal('isNotNull'),
z.literal('isTrue'),
z.literal('isFalse'),
])
export type VisualizationOperatorWithoutValue = z.infer<
typeof VisualizationOperatorWithoutValue
Expand Down Expand Up @@ -523,10 +525,33 @@ export const VisualizationDateFilter = z.object({
})
export type VisualizationDateFilter = z.infer<typeof VisualizationDateFilter>

export const VisualizationBooleanFilterOperator = z.union([
z.literal('isTrue'),
z.literal('isFalse'),
])
export type VisualizationBooleanFilterOperator = z.infer<
typeof VisualizationBooleanFilterOperator
>
export const booleanFilterOperators: VisualizationBooleanFilterOperator[] = [
'isTrue',
'isFalse',
]

export const VisualizationBooleanFilter = z.object({
id: uuidSchema,
column: DataFrameBooleanColumn,
operator: VisualizationBooleanFilterOperator,
value: z.string().optional(),
renderError: PythonErrorOutput.optional(),
renderedValue: z.string().optional(),
})
export type VisualizationBooleanFilter = z.infer<typeof VisualizationBooleanFilter>

const VisualizationFilterOperator = z.union([
VisualizationNumberFilterOperator,
VisualizationStringFilterOperator,
VisualizationDateFilterOperator,
VisualizationBooleanFilterOperator,
])

export const UnfinishedVisualizationFilter = z.object({
Expand All @@ -544,6 +569,7 @@ export const VisualizationFilter = z.union([
VisualizationStringFilter,
VisualizationNumberFilter,
VisualizationDateFilter,
VisualizationBooleanFilter,
UnfinishedVisualizationFilter,
])
export type VisualizationFilter = z.infer<typeof VisualizationFilter>
Expand Down