1
0
Fork 0
mirror of https://gitlab.com/bramw/baserow.git synced 2025-04-15 01:28:30 +00:00

Resolve "isNan/whenNan formulas"

This commit is contained in:
Alexander Haller 2023-02-02 16:11:55 +00:00
parent 935ba12f25
commit c1cfdb235b
7 changed files with 102 additions and 1 deletions
backend
src/baserow/contrib/database/formula/ast
tests/baserow/contrib/database/formula
changelog.md
web-frontend
locales
modules/database
test/unit/database/formula

View file

@ -1,6 +1,6 @@
from abc import ABC
from decimal import Decimal
from typing import List, Optional, Type
from typing import List, Optional, Type, Union
from django.contrib.postgres.aggregates import JSONBAgg
from django.db.models import (
@ -147,6 +147,8 @@ def register_formula_functions(registry):
registry.register(BaserowCeil())
registry.register(BaserowFloor())
registry.register(BaserowSign())
registry.register(BaserowIsNaN())
registry.register(BaserowWhenNan())
# Boolean functions
registry.register(BaserowIf())
registry.register(BaserowEqual())
@ -769,6 +771,44 @@ def int_like_numeric_output_field() -> fields.DecimalField:
)
class BaserowIsNaN(OneArgumentBaserowFunction):
type = "is_nan"
arg_type = [BaserowFormulaNumberType]
def type_function(
self,
func_call: BaserowFunctionCall[UnTyped],
arg: Union[BaserowExpression[BaserowFormulaNumberType]],
) -> BaserowExpression[BaserowFormulaType]:
return func_call.with_valid_type(BaserowFormulaBooleanType())
def to_django_expression(self, arg: Expression) -> Expression:
return EqualsExpr(
arg,
Value(Decimal("NaN")),
output_field=fields.BooleanField(),
)
class BaserowWhenNan(TwoArgumentBaserowFunction):
type = "when_nan"
arg1_type = [BaserowFormulaNumberType]
arg2_type = [BaserowFormulaNumberType]
def type_function(
self,
func_call: BaserowFunctionCall[UnTyped],
arg1: Union[BaserowExpression[BaserowFormulaNumberType]],
arg2: Union[BaserowExpression[BaserowFormulaNumberType]],
) -> BaserowExpression[BaserowFormulaType]:
return func_call.with_valid_type(
calculate_number_type([arg1.expression_type, arg2.expression_type])
)
def to_django_expression(self, arg1: Expression, arg2: Expression) -> Expression:
return handle_arg_being_nan(arg1, arg2, arg1)
class BaserowInt(BaserowTrunc):
"""
Kept for backwards compatability as was introduced in v3 of formula language but

View file

@ -263,6 +263,12 @@ VALID_FORMULA_TESTS = [
("exp(-1.00)", "0.37"),
("exp(1/0)", "NaN"),
("exp(tonumber('invalid'))", "NaN"),
("is_nan(1/0)", True),
("is_nan(1)", False),
("when_nan(1/0, 4)", "4.0000000000"),
("when_nan(1.0, 4)", "1.0"),
("when_nan(1, 4.0)", "1.0"),
("when_nan(1/0, 1/0)", "NaN"),
("int(1.1234)", "1"),
("int(1.56)", "1"),
("int(-1.56)", "-1"),

View file

@ -16,6 +16,7 @@ For example:
* Can add a row with textual values for single select, multiple select and link row field. [#1312](https://gitlab.com/bramw/baserow/-/issues/1312)
* Add e2e tests. [#820](https://gitlab.com/bramw/baserow/-/issues/820)
* Added missing actions for audit log. [#1500](https://gitlab.com/bramw/baserow/-/issues/1500)
* Add `is_nan` and `when_nan` formula functions [#1527](https://gitlab.com/bramw/baserow/-/issues/1527)
### Bug Fixes
* Add missing `procps` system package to all-in-one docker image fixing `/baserow/supervisor/docker-postgres-setup.sh run` (#1512)[https://gitlab.com/bramw/baserow/-/issues/1512]

View file

@ -381,6 +381,8 @@
"sqrtDescription": "Returns the square root of the argument provided.",
"roundDescription": "Returns first argument rounded to the number of digits specified by the second argument.",
"truncDescription": "Returns only the first argument converted into an integer by truncating any decimal places.",
"isNanDescription": "Returns true if the argument is 'NaN', returns false otherwise.",
"wheNanDescription": "Returns the first argument if it's not 'NaN'. Returns the second argument if the first argument is 'NaN'",
"absDescription": "Returns the absolute value for the argument number provided.",
"ceilDescription": "Returns the smallest integer that is greater than or equal the argument number provided.",
"floorDescription": "Returns the largest integer that is less than or equal the argument number provided."

View file

@ -1594,6 +1594,52 @@ export class BaserowTrunc extends BaserowFunctionDefinition {
}
}
export class BaserowIsNaN extends BaserowFunctionDefinition {
static getType() {
return 'is_nan'
}
getDescription() {
const { i18n } = this.app
return i18n.t('formulaFunctions.isNanDescription')
}
getSyntaxUsage() {
return ['is_nan(number)']
}
getExamples() {
return ['is_nan(1 / 0) = true', 'is_nan(1) = false']
}
getFormulaType() {
return 'number'
}
}
export class BaserowWhenNaN extends BaserowFunctionDefinition {
static getType() {
return 'when_nan'
}
getDescription() {
const { i18n } = this.app
return i18n.t('formulaFunctions.whenNanDescription')
}
getSyntaxUsage() {
return ['when_nan(number, fallback)']
}
getExamples() {
return ['when_nan(1 / 0, 4) = 4', 'when_nan(1, 4) = 1']
}
getFormulaType() {
return 'number'
}
}
export class BaserowLeast extends BaserowFunctionDefinition {
static getType() {
return 'least'

View file

@ -155,6 +155,8 @@ import {
BaserowContains,
BaserowFilter,
BaserowTrunc,
BaserowIsNaN,
BaserowWhenNaN,
BaserowEven,
BaserowOdd,
BaserowCeil,
@ -458,6 +460,8 @@ export default (context) => {
app.$registry.register('formula_function', new BaserowSum(context))
app.$registry.register('formula_function', new BaserowFilter(context))
app.$registry.register('formula_function', new BaserowTrunc(context))
app.$registry.register('formula_function', new BaserowIsNaN(context))
app.$registry.register('formula_function', new BaserowWhenNaN(context))
app.$registry.register('formula_function', new BaserowEven(context))
app.$registry.register('formula_function', new BaserowOdd(context))
app.$registry.register('formula_function', new BaserowAbs(context))

View file

@ -47,6 +47,7 @@ describe('Formula Functions Test', () => {
'if',
'equal',
'isblank',
'is_nan',
'not',
'not_equal',
'greater_than',
@ -67,6 +68,7 @@ describe('Formula Functions Test', () => {
'minus',
'row_id',
'when_empty',
'when_nan',
'any',
'every',
'max',