Skip to content

Commit

Permalink
Add visualisation filtering for boolean values
Browse files Browse the repository at this point in the history
  • Loading branch information
DanFitzgibbon committed Nov 9, 2024
1 parent d5f129f commit a34d8e1
Show file tree
Hide file tree
Showing 3 changed files with 120 additions and 6 deletions.
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

0 comments on commit a34d8e1

Please sign in to comment.