This is part of a larger working system which I'm now trying to clean up. In the interests of making the system more test-able I'm splitting up the functions which can be split, but this 100-line function is messing me up.Short overview, this takes 5 inputs:-matchedslots - A list of 4 times (nearest-match actual reportingschedule to the expected schedule in realslots)realslots - A list of 4 times (expected reporting schedule)leave - Fraction of the day that employee is on leave (0.2 is a 2hour break, 0.5 is half-day leave, 1 is full-day leave) daytype - used elsewhere, but in this context 3 and above indicates anon-working daypaid_lunch_period - special case for a certain class of employees whoget paid for the lunch hourAnd produces 6 outputs:-absent - 1, 0.5, or 0 indicating full/half/no absence on this dayworking - total number of hours workedshortearly - number of minutes the employee left early (for lunch or at day's end)shortlate - number of minutes the employee arrived late (morning or after lunch)overtime - number of minutes worked past closing timeabsentminutes - number of minutes the employee missed today (total)The sheer number of if/else statements (and function length) convince me there must be a much better and more test-able way to write this, but I'm stumped.def _calc_hours(matchedslots, realslots, leave, daytype, paid_lunch_period=False):    '''    Based on the matched slots, calculate how many days absent, how many    minutes spent working (non-OT), how many minutes short, and how many    minutes overtime done (pass through _rounding_OT first)    Special case needs to be made for guard, where paid_lunch_period is    set to True. In that case he gets an additional 1 working hour per    day (assuming he was present).    '''    absent, working, shortearly, shortlate, overtime = 0, 0, 0, 0, 0    extratime = 0  # Used to track possible extra time for non-working days                   # This time is only present around the lunch period    if leave not in (0, 0.2, 0.5, 1):        raise(ValueError('Leave can only be 0, 0.2, 0.5, or 1'))    # Variables to be used for calculating working time, may be overridden    # later if staff late/early    (clockin, lunchout, lunchin, clockout) = realslots    if lunchout:        morningminutes = _time_subtract(lunchout, clockin)        eveningminutes = _time_subtract(clockout, lunchin)        lunchduration = _time_subtract(lunchin, lunchout)    else:  # Saturdays don't have lunch slots for some staff        morningminutes = _time_subtract(clockout, clockin)        eveningminutes = 0        lunchduration = 0    if matchedslots[0]:  # Signed in in the morning        # Check for being late        late = _time_subtract(matchedslots[0], realslots[0])        if late > fuzzygap:  # fuzzygap leeway given for morning            shortlate += late            clockin = matchedslots[0]        # Check if there IS a lunch time slot, and whether signout occured        if realslots[1] and matchedslots[1]:            # Check for possible short            early = _time_subtract(realslots[1], matchedslots[1])            if early > 0:                shortearly += early                lunchout = matchedslots[1]            else:                early = early + lunchduration                extratime += -early if early < 0 else 0    else:  # No morning sign-in, at least half-day absent        absent += 0.5        clockin = None    if matchedslots[3]:  # Signed out sometime in evening/at night        # Check if there IS a lunch time slot, and whether signin occured        if realslots[2] and matchedslots[2]:            # Check for possible short            late = _time_subtract(matchedslots[2], realslots[2])            if late > 0:                shortlate += late                lunchin = matchedslots[2]            else:                late = late + lunchduration                extratime += -late if late < 0 else 0        # Check for early signout        early = _time_subtract(realslots[3], matchedslots[3])        if early > 0:            shortearly += early            clockout = matchedslots[3]        else:  # Did not leave early, check for overtime            overtime = -early    else:  # No evening/night sign-out, at least half-day absent        absent += 0.5        clockout = None    # Calculate working hours    if lunchout and lunchin:  # Not none meaning a normal day        if clockin:            working += _time_subtract(lunchout, clockin)        if clockout:            working += _time_subtract(clockout, lunchin)            if ((_time_subtract(clockout, time(15,15)) > 0) and                    (_time_subtract(time(15,0), lunchin) > 0) and                    not paid_lunch_period):                working -= 15  # tea break period    else:  # A day without lunch hours        if clockin and clockout:            working += _time_subtract(clockout, clockin)            # If there's overtime, subtract up to an hour because lunch hour            # is not taken into account for this case yet            overtime = overtime - 60 if overtime >= 60 else 0    # If leave applied, this counts against absent    absent = absent - leave if absent >= leave else 0    if paid_lunch_period and absent == 0:        if lunchout and lunchin:            working += _time_subtract(lunchin, lunchout)    if daytype > 2:  # Not a working day        # Add in the extra time        working += extratime        # Hours worked are counted as overtime        overtime += working        working = 0        # Can't be absent or early/late on non-working days        absent, shortearly, shortlate = 0, 0, 0    # Round out overtime hours    overtime = _rounding_OT(overtime)    # Calculate number of minutes absent    absentminutes = 0    if absent == 1:  # Full day absence        absentminutes = morningminutes + eveningminutes    elif absent == 0.5:  # Half day absence        if matchedslots[0]:  # Signed in in morning, absence is evening            absentminutes = morningminutes    if leave == 0.2:  # Time Off (max 2 hours)        if shortearly > shortlate:            shortearly = 0 if shortearly < 120 else shortearly - 120        else:            shortlate = 0 if shortlate < 120 else shortlate - 120    return absent, working, shortearly, shortlate, overtime, absentminutesBasically what's being done should be clear from the comments. If statements check for a combination of cases (some staff forget to sign in/out, and management doesn't penalise that in the case of lunch-time hours; Saturdays don't have lunch time slots for staff whose shift is only till noon; half-day leave can still result in half-day absence).Here's the test function I use to verify that it works (sorry, its a bit verbose). The first format of matchedslots and realslots can be seen to be a list of 4 datetime.time() instances. leave is one of the values (0, 0.2, 0.5, 1), daytype is 2 and below or 3 and above, paid_lunch_period is a boolean value.from datetime import timedef test_calc_hours():    daytype = {'work': 1, 'nonwork': 3}    normalschedule = [time(8,30), time(12,30), time(13,30), time(18,0)]    fridayadmin = [time(8,30), time(12,30), time(13,30), time(18,0)]    fridaymuslim = [time(8,30), time(13,0), time(14,30), time(18,0)]    fridaynon = [time(8,30), time(13,0), time(14,0), time(18,0)]    saturdayadmin = [time(8,30), None, None, time(12,30)]    saturdayothers = [time(8,30), time(12,30), time(13,30), time(15,0)]    matchedslots = [time(8,31), time(12,31), time(13,6), time(18,16)]    (absent, working, shortearly, shortlate, overtime, absentminutes            ) = _calc_hours(matchedslots, normalschedule, 0,                                        daytype['work'])    assert (absent, working, shortearly, shortlate) == (0, 8*60 + 15, 0, 0)    assert (overtime, absentminutes) == (0, 0)    matchedslots = [time(9,45), time(12,31), time(13,6), time(18,16)]    (absent, working, shortearly, shortlate, overtime, absentminutes            ) = _calc_hours(matchedslots, normalschedule, 0.2,                                        daytype['work'])    assert (absent, working, shortearly, shortlate) == (0, 7*60, 0, 0)    assert (overtime, absentminutes) == (0, 0)    matchedslots = [time(8,31), time(12,31), time(12,46), time(16,1)]    (absent, working, shortearly, shortlate, overtime, absentminutes            ) = _calc_hours(matchedslots, normalschedule, 0,                                        daytype['nonwork'])    assert (absent, working, shortearly, shortlate) == (0, 0, 0, 0)    assert (overtime, absentminutes) == (6*60+15, 0)    matchedslots = [time(8,17), None, None, time(12,14)]    (absent, working, shortearly, shortlate, overtime, absentminutes            ) = _calc_hours(matchedslots, saturdayadmin, 0,                                        daytype['work'])    assert (absent, working) == (0, 224)    assert (shortearly, shortlate) == (16, 0)    assert (overtime, absentminutes) == (0, 0)    matchedslots = [time(8,17), None, None, time(13,44)]    (absent, working, shortearly, shortlate, overtime, absentminutes            ) = _calc_hours(matchedslots, saturdayadmin, 0,                                        daytype['work'])    assert (absent, working) == (0, 240)    assert (shortearly, shortlate) == (0, 0)    assert (overtime, absentminutes) == (0, 0)    matchedslots = [time(8,17), None, None, time(18,1)]    (absent, working, shortearly, shortlate, overtime, absentminutes            ) = _calc_hours(matchedslots, saturdayadmin, 0,                                        daytype['nonwork'])    assert (absent, working) == (0, 0)    assert (shortearly, shortlate) == (0, 0)    assert (overtime, absentminutes) == (8*60+30, 0)    matchedslots = [time(8,39), None, None, time(17,0)]    (absent, working, shortearly, shortlate, overtime, absentminutes            ) = _calc_hours(matchedslots, saturdayothers, 0,                                        daytype['work'])    assert (absent, working) == (0, 5*60+21)    assert (shortearly, shortlate) == (0, 9)    assert (overtime, absentminutes) == (2*60+0, 0)
Calculate attendance compliance based on attendance record
python;datetime;unit testing;interval