Skip to content
Merged
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
8 changes: 7 additions & 1 deletion backend/src/baserow/core/apps.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ def ready(self):
from baserow.core.formula.registries import formula_runtime_function_registry
from baserow.core.formula.runtime_formula_types import (
RuntimeAdd,
RuntimeAnd,
RuntimeCapitalize,
RuntimeConcat,
RuntimeDateTimeFormat,
Expand All @@ -53,13 +54,15 @@ def ready(self):
RuntimeIsEven,
RuntimeIsOdd,
RuntimeLessThan,
RuntimeLessThanOrEqual,
RuntimeLower,
RuntimeMinus,
RuntimeMinute,
RuntimeMonth,
RuntimeMultiply,
RuntimeNotEqual,
RuntimeNow,
RuntimeOr,
RuntimeRandomBool,
RuntimeRandomFloat,
RuntimeRandomInt,
Expand All @@ -78,8 +81,9 @@ def ready(self):
formula_runtime_function_registry.register(RuntimeDivide())
formula_runtime_function_registry.register(RuntimeEqual())
formula_runtime_function_registry.register(RuntimeNotEqual())
formula_runtime_function_registry.register(RuntimeGreaterThan())
formula_runtime_function_registry.register(RuntimeLessThan())
formula_runtime_function_registry.register(RuntimeLessThanOrEqual())
formula_runtime_function_registry.register(RuntimeGreaterThan())
formula_runtime_function_registry.register(RuntimeGreaterThanOrEqual())
formula_runtime_function_registry.register(RuntimeUpper())
formula_runtime_function_registry.register(RuntimeLower())
Expand All @@ -102,6 +106,8 @@ def ready(self):
formula_runtime_function_registry.register(RuntimeRandomBool())
formula_runtime_function_registry.register(RuntimeGenerateUUID())
formula_runtime_function_registry.register(RuntimeIf())
formula_runtime_function_registry.register(RuntimeAnd())
formula_runtime_function_registry.register(RuntimeOr())

from baserow.core.permission_manager import (
AllowIfTemplatePermissionManagerType,
Expand Down
23 changes: 6 additions & 17 deletions backend/src/baserow/core/formula/argument_types.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,10 @@ def parse(self, value):


class NumberBaserowRuntimeFormulaArgumentType(BaserowRuntimeFormulaArgumentType):
def __init__(self, *args, **kwargs):
self.cast_to_int = kwargs.pop("cast_to_int", False)
super().__init__(*args, **kwargs)

def test(self, value):
try:
ensure_numeric(value)
Expand All @@ -33,7 +37,8 @@ def test(self, value):
return False

def parse(self, value):
return ensure_numeric(value)
value = ensure_numeric(value)
return int(value) if self.cast_to_int else value


class TextBaserowRuntimeFormulaArgumentType(BaserowRuntimeFormulaArgumentType):
Expand All @@ -48,22 +53,6 @@ def parse(self, value):
return ensure_string(value)


class AddableBaserowRuntimeFormulaArgumentType(BaserowRuntimeFormulaArgumentType):
def test(self, value):
return hasattr(value, "__add__")

def parse(self, value):
return value


class SubtractableBaserowRuntimeFormulaArgumentType(BaserowRuntimeFormulaArgumentType):
def test(self, value):
return hasattr(value, "__sub__")

def parse(self, value):
return value


class DateTimeBaserowRuntimeFormulaArgumentType(BaserowRuntimeFormulaArgumentType):
def test(self, value):
try:
Expand Down
4 changes: 4 additions & 0 deletions backend/src/baserow/core/formula/parser/python_executor.py
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,10 @@ def visitBinaryOp(self, ctx: BaserowFormula.BinaryOpContext):
op = "greater_than_or_equal"
elif ctx.LTE():
op = "less_than_or_equal"
elif ctx.AMP_AMP():
op = "and"
elif ctx.PIPE_PIPE():
op = "or"
else:
raise UnknownOperator(ctx.getText())

Expand Down
125 changes: 60 additions & 65 deletions backend/src/baserow/core/formula/runtime_formula_types.py
Original file line number Diff line number Diff line change
@@ -1,20 +1,16 @@
import operator
import random
import uuid
from functools import reduce
from typing import Optional
from zoneinfo import ZoneInfo

from django.utils import timezone

from baserow.core.formula.argument_types import (
AddableBaserowRuntimeFormulaArgumentType,
AnyBaserowRuntimeFormulaArgumentType,
BooleanBaserowRuntimeFormulaArgumentType,
DateTimeBaserowRuntimeFormulaArgumentType,
DictBaserowRuntimeFormulaArgumentType,
NumberBaserowRuntimeFormulaArgumentType,
SubtractableBaserowRuntimeFormulaArgumentType,
TextBaserowRuntimeFormulaArgumentType,
TimezoneBaserowRuntimeFormulaArgumentType,
)
Expand All @@ -28,7 +24,7 @@ class RuntimeConcat(RuntimeFormulaFunction):
type = "concat"

def validate_type_of_args(self, args) -> Optional[FormulaArg]:
arg_type = AddableBaserowRuntimeFormulaArgumentType()
arg_type = TextBaserowRuntimeFormulaArgumentType()
return next(
(arg for arg in args if not arg_type.test(arg)),
None,
Expand All @@ -52,61 +48,47 @@ def execute(self, context: FormulaContext, args: FormulaArgs):
class RuntimeAdd(RuntimeFormulaFunction):
type = "add"

def validate_type_of_args(self, args) -> Optional[FormulaArg]:
arg_type = AddableBaserowRuntimeFormulaArgumentType()
return next(
(arg for arg in args if not arg_type.test(arg)),
None,
)

def validate_number_of_args(self, args):
return len(args) >= 1
args = [
NumberBaserowRuntimeFormulaArgumentType(),
NumberBaserowRuntimeFormulaArgumentType(),
]

def execute(self, context: FormulaContext, args: FormulaArgs):
return reduce(operator.add, args)
return args[0] + args[1]


class RuntimeMinus(RuntimeFormulaFunction):
type = "minus"

def validate_type_of_args(self, args) -> Optional[FormulaArg]:
arg_type = SubtractableBaserowRuntimeFormulaArgumentType()
return next(
(arg for arg in args if not arg_type.test(arg)),
None,
)

def validate_number_of_args(self, args):
return len(args) > 1
args = [
NumberBaserowRuntimeFormulaArgumentType(),
NumberBaserowRuntimeFormulaArgumentType(),
]

def execute(self, context: FormulaContext, args: FormulaArgs):
return reduce(operator.sub, args)
return args[0] - args[1]


class RuntimeMultiply(RuntimeFormulaFunction):
type = "multiply"

args = [
NumberBaserowRuntimeFormulaArgumentType(),
NumberBaserowRuntimeFormulaArgumentType(),
]

def validate_number_of_args(self, args):
return len(args) == 2

def execute(self, context: FormulaContext, args: FormulaArgs):
return args[0] * args[1]


class RuntimeDivide(RuntimeFormulaFunction):
type = "divide"

args = [
NumberBaserowRuntimeFormulaArgumentType(),
NumberBaserowRuntimeFormulaArgumentType(),
]

def validate_number_of_args(self, args):
return len(args) == 2

def execute(self, context: FormulaContext, args: FormulaArgs):
return args[0] / args[1]

Expand All @@ -118,9 +100,6 @@ class RuntimeEqual(RuntimeFormulaFunction):
AnyBaserowRuntimeFormulaArgumentType(),
]

def validate_number_of_args(self, args):
return len(args) == 2

def execute(self, context: FormulaContext, args: FormulaArgs):
return args[0] == args[1]

Expand All @@ -132,65 +111,50 @@ class RuntimeNotEqual(RuntimeFormulaFunction):
AnyBaserowRuntimeFormulaArgumentType(),
]

def validate_number_of_args(self, args):
return len(args) == 2

def execute(self, context: FormulaContext, args: FormulaArgs):
return args[0] != args[1]


class RuntimeGreaterThan(RuntimeFormulaFunction):
type = "greater_than"
args = [
NumberBaserowRuntimeFormulaArgumentType(),
NumberBaserowRuntimeFormulaArgumentType(),
AnyBaserowRuntimeFormulaArgumentType(),
AnyBaserowRuntimeFormulaArgumentType(),
]

def validate_number_of_args(self, args):
return len(args) == 2

def execute(self, context: FormulaContext, args: FormulaArgs):
return args[0] > args[1]


class RuntimeLessThan(RuntimeFormulaFunction):
type = "less_than"
args = [
NumberBaserowRuntimeFormulaArgumentType(),
NumberBaserowRuntimeFormulaArgumentType(),
AnyBaserowRuntimeFormulaArgumentType(),
AnyBaserowRuntimeFormulaArgumentType(),
]

def validate_number_of_args(self, args):
return len(args) == 2

def execute(self, context: FormulaContext, args: FormulaArgs):
return args[0] < args[1]


class RuntimeGreaterThanOrEqual(RuntimeFormulaFunction):
type = "greater_than_or_equal"
args = [
NumberBaserowRuntimeFormulaArgumentType(),
NumberBaserowRuntimeFormulaArgumentType(),
AnyBaserowRuntimeFormulaArgumentType(),
AnyBaserowRuntimeFormulaArgumentType(),
]

def validate_number_of_args(self, args):
return len(args) == 2

def execute(self, context: FormulaContext, args: FormulaArgs):
return args[0] >= args[1]


class RuntimeLessThanOrEqual(RuntimeFormulaFunction):
type = "less_than_or_equal"
args = [
NumberBaserowRuntimeFormulaArgumentType(),
NumberBaserowRuntimeFormulaArgumentType(),
AnyBaserowRuntimeFormulaArgumentType(),
AnyBaserowRuntimeFormulaArgumentType(),
]

def validate_number_of_args(self, args):
return len(args) == 2

def execute(self, context: FormulaContext, args: FormulaArgs):
return args[0] <= args[1]

Expand Down Expand Up @@ -227,7 +191,7 @@ class RuntimeRound(RuntimeFormulaFunction):

args = [
NumberBaserowRuntimeFormulaArgumentType(),
NumberBaserowRuntimeFormulaArgumentType(),
NumberBaserowRuntimeFormulaArgumentType(optional=True, cast_to_int=True),
]

def execute(self, context: FormulaContext, args: FormulaArgs):
Expand Down Expand Up @@ -272,7 +236,7 @@ def execute(self, context: FormulaContext, args: FormulaArgs):
datetime_obj = args[0]
moment_format = args[1]

if (len(args)) == 2:
if len(args) == 2:
timezone_name = context.get_timezone_name()
else:
timezone_name = args[2]
Expand Down Expand Up @@ -350,13 +314,17 @@ def execute(self, context: FormulaContext, args: FormulaArgs):
class RuntimeNow(RuntimeFormulaFunction):
type = "now"

args = []

def execute(self, context: FormulaContext, args: FormulaArgs):
return timezone.now()


class RuntimeToday(RuntimeFormulaFunction):
type = "today"

args = []

def execute(self, context: FormulaContext, args: FormulaArgs):
return timezone.localdate()

Expand Down Expand Up @@ -400,26 +368,53 @@ def execute(self, context: FormulaContext, args: FormulaArgs):
class RuntimeRandomBool(RuntimeFormulaFunction):
type = "random_bool"

args = []

def execute(self, context: FormulaContext, args: FormulaArgs):
return random.choice([True, False]) # nosec: B311


class RuntimeGenerateUUID(RuntimeFormulaFunction):
type = "generate_uuid"

args = []

def execute(self, context: FormulaContext, args: FormulaArgs):
return str(uuid.uuid4())


class RuntimeIf(RuntimeFormulaFunction):
type = "if"

def validate_type_of_args(self, args) -> Optional[FormulaArg]:
arg_type = BooleanBaserowRuntimeFormulaArgumentType()
if not arg_type.test(args[0]):
return args[0]

return None
args = [
BooleanBaserowRuntimeFormulaArgumentType(),
AnyBaserowRuntimeFormulaArgumentType(),
AnyBaserowRuntimeFormulaArgumentType(),
]

def execute(self, context: FormulaContext, args: FormulaArgs):
return args[1] if args[0] else args[2]


class RuntimeAnd(RuntimeFormulaFunction):
type = "and"

args = [
BooleanBaserowRuntimeFormulaArgumentType(),
BooleanBaserowRuntimeFormulaArgumentType(),
]

def execute(self, context: FormulaContext, args: FormulaArgs):
return args[0] and args[1]


class RuntimeOr(RuntimeFormulaFunction):
type = "or"

args = [
BooleanBaserowRuntimeFormulaArgumentType(),
BooleanBaserowRuntimeFormulaArgumentType(),
]

def execute(self, context: FormulaContext, args: FormulaArgs):
return args[0] or args[1]
Loading
Loading