Fixate icon indicating copy to clipboard operation
Fixate copied to clipboard

Check Functions do not display datetime comparisons helpfully

Open pazzarpj opened this issue 6 years ago • 7 comments

Comparing datetime functions in a chk_* will show .3g instead of the repr for the datetime object. This could overflow into other object time comparisons that are not float or integer based.

pazzarpj avatar Apr 01 '19 04:04 pazzarpj

Had a go at replicating this issue but without any luck. Ended up writing some tests for chk_smaller. They're in my fix-time-checks fork in Fixate/test/core/test_checks.py if it helps.

christopherwallis avatar Apr 02 '19 11:04 christopherwallis

@pazzarpj, have you got a script that shows the error? or can you write a failing test?

clint-lawrence avatar Apr 03 '19 00:04 clint-lawrence

from datetime import datetime, timedelta
from fixate.core.checks import CheckClass, _in_range
from fixate.ui_cmdline.cmd_line import _print_comparisons
chk1 = CheckClass(_min=2, _max=3, test_val=2.5, target=_in_range)
date_thing = datetime(2019, 1, 1, 0, 0)
chk2 = CheckClass(_min=date_thing - timedelta(seconds=2), _max=datetime.now() + timedelta(seconds=2), test_val=date_thing, target=_in_range)

_print_comparisons(True, chk1, 1, None)
_print_comparisons(True, chk2, 2, None)

Expected output

Check 1: PASS when comparing 2.5 in range 2 - 3 :
Check 2: PASS when comparing 2019-01-01 00:00:00 in range 2018-12-31 23:59:58 - 2019-01-01 00:00:02 :

Actual

Check 1: PASS when comparing 2.5 in range 2 - 3 :
Check 2: PASS when comparing .3g in range .3g - .3g :

pazzarpj avatar Apr 05 '19 04:04 pazzarpj

There seems to be an issue with timestruct objects as well in that script. They're displayed with their data but always pass:

t = time.localtime(time.time())
chk3 = CheckClass(_min=time.localtime(1545925760), _max=time.localtime(1545925777), test_val=t, target=_in_range)

_print_comparisons(True, chk3, 3, None)
Check 3: PASS when comparing time.struct_time(tm_year=2019, tm_mon=4,
tm_mday=5, tm_hour=19, tm_min=50, tm_sec=33, tm_wday=4, tm_yday=95,
tm_isdst=1) in range time.struct_time(tm_year=2018, tm_mon=12, tm_mday=28,
tm_hour=2, tm_min=49, tm_sec=20, tm_wday=4, tm_yday=362, tm_isdst=1) -
time.struct_time(tm_year=2018, tm_mon=12, tm_mday=28, tm_hour=2, tm_min=49,
tm_sec=37, tm_wday=4, tm_yday=362, tm_isdst=1)

However the issue doesn't seem to occur when using chk_in_range for either timestruct or datetime objects as the following tests pass:

def test_time_struct_check_true():
    t = time.localtime(1545925765)
    chk = chk_in_range(t, time.localtime(1545925760), time.localtime(1545925777), "Time is in range")
    assert chk


def test_time_struct_check_false():
    with pytest.raises(CheckFail):
        t = time.localtime(1554452763)
        chk = chk_in_range(t, time.localtime(1545925760), time.localtime(1545925777), "Time is NOT in range")


def test_datetime_struct_check_true():
    t_min = datetime.now()
    time.sleep(1)
    t = datetime.now()
    time.sleep(1)
    t_max = datetime.now()
    chk = chk_in_range(t, t_min, t_max, "DateTime is in range")
    assert chk


def test_datetime_struct_check_false():
    with pytest.raises(CheckFail):
        t_min = datetime.now()
        time.sleep(1)
        t_max = datetime.now()
        time.sleep(1)
        t = datetime.now()
        chk = chk_in_range(t, t_min, t_max, "DateTime is not in range")
        assert chk

christopherwallis avatar Apr 05 '19 08:04 christopherwallis

The script I provided was to demonstrate the display issue, the CheckClass doesn't actually run the check.

_print_comparisons first parameter determines if it displays as pass/fail.

The issue is that the _print_comparisons function assumes a number with the {:.3g} string formatter.

pazzarpj avatar Apr 05 '19 09:04 pazzarpj

Riiight, i finally get it now. I'll stop barking up the wrong tree. Will look into it when i get a chance. Thanks for slow walking me there.

christopherwallis avatar Apr 05 '19 12:04 christopherwallis

Issue found in ui_cmdline\cmd_line.py line 304:

def round_to_3_sig_figures(chk):
    """
    Tries to round elements to 3 significant figures for formatting
    :param chk:
    :return:
    """
    ret_dict = {}
    for element in ["_min", "_max", "test_val", "nominal", "tol"]:
        ret_dict[element] = getattr(chk, element, None)
        try:
            ret_dict[element] = "{:.3g}".format(ret_dict[element])
        except:
            pass
    return ret_dict

format with decimal places does not produce the desired output but does not raise an error when datetime object is used.

Adding an assert math.isclose within the try: rejects the change of format, allowing the original formatting (standard datetime object output) to be shown:

Check 1: PASS when comparing 2.5 in range 2 - 3 :

Check 2: PASS when comparing 2019-01-01 00:00:00 in range 2018-12-31
23:59:58 - 2019-04-08 21:52:25.021197 :

Will run a few more tests and upload the code.

christopherwallis avatar Apr 08 '19 12:04 christopherwallis