Skip to content
Open
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
12 changes: 9 additions & 3 deletions apps/api/services/workbook/conditions.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@

import logging
import re
from decimal import Decimal, InvalidOperation
from typing import Dict

logger = logging.getLogger("workbook.conditions")
Expand Down Expand Up @@ -192,15 +193,20 @@ def _compare(left: str, right: str, op: str) -> bool:
"""Compare two values with the given operator."""
# Try numeric comparison
try:
left_num = float(left)
right_num = float(right)
left_num = Decimal(left)
right_num = Decimal(right)
# Finite decimal operands retain the digits supplied by the caller.
# Preserve the existing float semantics for NaN and infinities.
if not left_num.is_finite() or not right_num.is_finite():
left_num = float(left)
right_num = float(right)
if op == "==": return left_num == right_num
if op == "!=": return left_num != right_num
if op == ">": return left_num > right_num
if op == "<": return left_num < right_num
if op == ">=": return left_num >= right_num
if op == "<=": return left_num <= right_num
except (ValueError, TypeError):
except (InvalidOperation, ValueError, TypeError):
pass

# String comparison
Expand Down
41 changes: 41 additions & 0 deletions tests/test_workbook_condition_precision.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
"""Public workbook conditions preserve numeric distinctions in actual row values."""

import pytest

from apps.api.services.workbook.conditions import evaluate_condition


@pytest.mark.parametrize("value, threshold, operator, expected", [
("9007199254740993", "9007199254740992", "==", False),
("9007199254740993", "9007199254740992", "!=", True),
("9007199254740993", "9007199254740992", ">", True),
("9007199254740993", "9007199254740992", "<", False),
("9007199254740993", "9007199254740992", ">=", True),
("9007199254740993", "9007199254740992", "<=", False),
("-9007199254740993", "-9007199254740992", "==", False),
("-9007199254740993", "-9007199254740992", "!=", True),
("-9007199254740993", "-9007199254740992", ">", False),
("-9007199254740993", "-9007199254740992", "<", True),
("-9007199254740993", "-9007199254740992", ">=", False),
("-9007199254740993", "-9007199254740992", "<=", True),
("9007199254740992.5", "9007199254740992.25", "==", False),
("9007199254740992.5", "9007199254740992.25", "!=", True),
("9007199254740992.5", "9007199254740992.25", ">", True),
("9007199254740992.5", "9007199254740992.25", "<", False),
("9007199254740992.5", "9007199254740992.25", ">=", True),
("9007199254740992.5", "9007199254740992.25", "<=", False),
(9007199254740993, "9007199254740992", "==", False),
(-9007199254740993, "-9007199254740992", "<", True),
("3.50", "3.5", "==", True),
("1e3", "1000", "==", True),
("009", "9", "==", True),
("-4.25", "-4", "<", True),
("Acme", '"Acme"', "==", True),
("Other", '"Acme"', "!=", True),
])
def test_numeric_conditions_keep_original_operand_distinctions(
value, threshold, operator, expected,
):
cells = {"score": {"value": value, "status": "complete"}}
expression = f"{{score}} {operator} {threshold}"
assert evaluate_condition(expression, cells, []) is expected
Loading