Home > OS >  Check if user input for date ranges has been made in consecutive, chronological pairs
Check if user input for date ranges has been made in consecutive, chronological pairs

Time:05-19

With PyWebIO I have created a user form that takes up to six dates, each pair composing a date range from start date sdt to end date edt. The first two dates are required, the remaining four are optional. This form can be validated via a function; check_form in my example.

I would like to use this function to allow the user only the input of consecutive, chronological pairs.

Example form:

from pywebio import *

form = input.input_group("form", [
    input.input("Period 1 - Start",
        name="sdt_1",
        type=input.DATE,
        required=True),
    input.input("Period 1 - End",
        name="edt_1",
        type=input.DATE,
        required=True),
    input.input("Period 2 - Start (optional)",
        name="sdt_2",
        type=input.DATE),
    input.input("Period 2 - End (optional)",
        name="edt_2",
        type=input.DATE),
    input.input("Period 3 - Start (optional)",
        name="sdt_3",
        type=input.DATE),
    input.input("Period 3 - End (optional)",
        name="edt_3",
        type=input.DATE),
    ], validate = check_form)

PyWebIO sorts the input into a dict, using the name as key and the input as value.

Output of print(form):

form = {
    'sdt_1': '2020-01-01',
    'edt_1': '2020-08-08',
    'sdt_2': '2021-01-01',
    'edt_2': '2021-08-08',
    'sdt_3': '2023-01-01',
    'edt_3': '2023-08-08'
}

I have come up with a solution that checks for chronological order and for an equal amount of entries. However, this solution still allows the user to make nonsensical input, such as only entering a date for sdt_1, edt_1, sdt_3, sdt_3 or sdt_1, edt_1, sdt_2, edt_3

def check_form(data):
    data_lst = []
    for key in data.keys():
        if data[key] != "":
            data_lst.append(data[key])
    if sorted(data_lst) != data_lst:
        return ("sdt_1", "ERROR: Entries not in chronological order.")
    if len(data_lst) % 2:
        return ("sdt_1", "ERROR: Uneven amount of entries.")

Any help is much appreciated!

CodePudding user response:

Since the first pair is required and the remaining two pairs are optional, you should enforce that logic.

def check_form(data):
    required_pairs = {1} # Specify all the required pairs in this set
    max_pairs = 3        # Max numbber of pairs
    data_lst = []
    for i in range(1, max_pairs 1):
        s_key = f"sdt_{i}"
        e_key = f"edt_{i}"

        # Get the data from the dict. If the key doesn't exist, treat it as an empty string
        s_val = data.get(s_key, "")
        e_val = data.get(e_key, "")

        if i in required_pairs or s_val or e_val:
            if i in required_pairs: 
                # This is a required pair and input is missing
                if not s_val: return (s_key, "ERROR: Missing required input")
                elif not e_val: return (e_key, "ERROR: Missing required input")
            elif e_val and not s_val:
                # e_key is specified but not s_key
                return (s_key, "ERROR: Uneven pair of inputs")
            elif s_val and not e_val:
                # s_key is specified but not e_key
                return (e_key, "ERROR: Uneven pair of inputs")
            
            # We didn't return an error, so this data must be valid
            data_lst.append(data[s_key])
            data_lst.append(data[e_key])

    if sorted(data_lst) != data_lst:
        return ("sdt_1", "ERROR: Entries not in chronological order")

Note that you can check if a string s is empty by simply if s (empty strings are falsy)

Let's test this:

##### VALID INPUTS:
# Case 1: Specify all inputs
assert check_form({
    'sdt_1': '2020-01-01',
    'edt_1': '2020-08-08',
    'sdt_2': '2021-01-01',
    'edt_2': '2021-08-08',
    'sdt_3': '2023-01-01',
    'edt_3': '2023-08-08'
}) == None

# Case 2: Specify only required inputs
assert check_form({
    'sdt_1': '2020-01-01',
    'edt_1': '2020-08-08',
    'sdt_2': '',
    'edt_2': '',
    'sdt_3': '',
    'edt_3': ''
}) == None

# Case 3.1: Specify required inputs and one optional input
assert check_form({
    'sdt_1': '2020-01-01',
    'edt_1': '2020-08-08',
    'sdt_2': '2021-01-01',
    'edt_2': '2021-01-08',
    'sdt_3': '',
    'edt_3': ''
}) == None

# Case 3.2: Specify required inputs and one optional input
assert check_form({
    'sdt_1': '2020-01-01',
    'edt_1': '2020-08-08',
    'sdt_2': '',
    'edt_2': '',
    'sdt_3': '2021-01-01',
    'edt_3': '2021-01-08',
}) == None


#### INVALID INPUTS
# Case 4.1: Skip required inputs
assert check_form({
    'sdt_1': '',
    'edt_1': '2020-08-08',
    'sdt_2': '2021-01-01',
    'edt_2': '2021-08-08',
    'sdt_3': '2022-01-01',
    'edt_3': '2022-01-08',
}) == ('sdt_1', "ERROR: Missing required input")

# Case 4.2: Skip required inputs
assert check_form({
    'sdt_1': '2020-01-01',
    'edt_1': '',
    'sdt_2': '2021-01-01',
    'edt_2': '2021-08-08',
    'sdt_3': '2022-01-01',
    'edt_3': '2022-01-08',
}) == ('edt_1', "ERROR: Missing required input")

# Case 4.3: Skip required inputs
assert check_form({
    'sdt_1': '',
    'edt_1': '',
    'sdt_2': '2021-01-01',
    'edt_2': '2021-08-08',
    'sdt_3': '2022-01-01',
    'edt_3': '2022-01-08',
}) == ('sdt_1', "ERROR: Missing required input")

# Case 5.1: Incomplete pair of optional inputs
assert check_form({
    'sdt_1': '2020-01-01',
    'edt_1': '2020-08-08',
    'sdt_2': '',
    'edt_2': '2021-08-08',
    'sdt_3': '2022-01-01',
    'edt_3': '2022-01-08',
}) == ('sdt_2', "ERROR: Uneven pair of inputs")

# Case 5.2: Incomplete pair of optional inputs
assert check_form({
    'sdt_1': '2020-01-01',
    'edt_1': '2020-08-08',
    'sdt_2': '2021-01-01',
    'edt_2': '',
    'sdt_3': '2022-01-01',
    'edt_3': '2022-01-08',
}) == ('edt_2', "ERROR: Uneven pair of inputs")

# Case 5.3: Incomplete pair of optional inputs
assert check_form({
    'sdt_1': '2020-01-01',
    'edt_1': '2020-08-08',
    'sdt_2': '2021-01-01',
    'edt_2': '2021-08-08',
    'sdt_3': '',
    'edt_3': '2022-01-08',
}) == ('sdt_3', "ERROR: Uneven pair of inputs")

# Case 5.4: Incomplete pair of optional inputs
assert check_form({
    'sdt_1': '2020-01-01',
    'edt_1': '2020-08-08',
    'sdt_2': '2021-01-01',
    'edt_2': '2021-08-08',
    'sdt_3': '2022-01-01',
    'edt_3': '',
}) == ('edt_3', "ERROR: Uneven pair of inputs")

# Case 5.5: Incomplete pair of optional inputs
assert check_form({
    'sdt_1': '2020-01-01',
    'edt_1': '2020-08-08',
    'sdt_2': '2021-01-01',
    'edt_2': '',
    'sdt_3': '2022-01-01',
    'edt_3': '',
}) == ('edt_2', "ERROR: Uneven pair of inputs")

# Case 5.6: Incomplete pair of optional inputs
assert check_form({
    'sdt_1': '2020-01-01',
    'edt_1': '2020-08-08',
    'sdt_2': '',
    'edt_2': '2021-08-08',
    'sdt_3': '',
    'edt_3': '2022-08-08',
}) == ('sdt_2', "ERROR: Uneven pair of inputs")

# Case 6.1: Non-chronological inputs
assert check_form({
    'sdt_1': '2020-01-01',
    'edt_1': '2019-08-08',
    'sdt_2': '2021-01-01',
    'edt_2': '2021-08-08',
    'sdt_3': '2022-01-01',
    'edt_3': '2022-08-08',
}) == ('sdt_1', "ERROR: Entries not in chronological order")

# Case 6.2: Non-chronological inputs
assert check_form({
    'sdt_1': '2020-01-01',
    'edt_1': '2020-08-08',
    'sdt_2': '2021-01-01',
    'edt_2': '2022-08-08',
    'sdt_3': '2022-01-01',
    'edt_3': '2022-08-08',
}) == ('sdt_1', "ERROR: Entries not in chronological order")

CodePudding user response:

I have come up with a solution. Put the dict into a list and check from the second index on if the previous index was empty. Like so:

def check_form(data):
    data_lst = []
    data_lst_2 = list(form.values())
    for key in data.keys():
        if data[key] != "":
            data_lst.append(data[key])
    if sorted(data_lst) != data_lst:
        return ("sdt_1", "ERROR: Entries not in chronological order.")
    if len(data_lst) % 2:
        return ("sdt_1", "ERROR: Uneven amount of entries.")
    for item in data_lst_2[1:]:
        if data_lst_2[data_lst_2.index(item) - 1] == "":
            return ("sdt_1", "ERROR: Entries not consecutive.")
  • Related