"""
@Author = 'Michael Stanley'

============ Change Log ============
2025-Jan-13 = Created. 

============ License ============
Copyright (C) 2025 Michael Stanley

This file is part of wmul_rivendell.

wmul_rivendell is free software: you can redistribute it and/or modify it 
under the terms of the GNU General Public License as published by the Free 
Software Foundation, either version 3 of the License, or (at your option) any 
later version.

wmul_rivendell is distributed in the hope that it will be useful, but WITHOUT 
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.

You should have received a copy of the GNU General Public License along with 
wmul_rivendell. If not, see <https://www.gnu.org/licenses/>. 
"""
import pytest
from wmul_rivendell.LoadCartDataDump import RivendellCart, CartType
from wmul_rivendell.DatabaseStatistics import RivendellGroupStatistics, StatisticsLimits, _remove_outliers
from wmul_test_utils import make_namedtuple, generate_true_false_matrix_from_list_of_strings


@pytest.fixture(scope="function")
def setup_rivendellgroupstatistics():
    carts = [
        RivendellCart(cart_number='', cut_number='1', type=CartType.Audio, group_name='EXPLICABO', title='perferendis enim harum repudiandae', artist='nisi ipsum', album='', year='', isrc='', isci='', label='', client='', agency='', publisher='', composer='', conductor='', song_id='', user_defined='', description='', outcue='', filename='', length='4:22', start_point='', end_point='', segue_start_point='', segue_end_point='', hook_start_point='', hook_end_point='', talk_start_point='', talk_end_point='', fadeup_point='', fadedown_point='', sched_codes=''),
        RivendellCart(cart_number='', cut_number='1', type=CartType.Audio, group_name='EXPLICABO', title='sapiente praesentium voluptates expedita', artist='tenetur assumenda', album='', year='', isrc='', isci='', label='', client='', agency='', publisher='', composer='', conductor='', song_id='', user_defined='', description='', outcue='', filename='', length='2:28', start_point='', end_point='', segue_start_point='', segue_end_point='', hook_start_point='', hook_end_point='', talk_start_point='', talk_end_point='', fadeup_point='', fadedown_point='', sched_codes=''),
        RivendellCart(cart_number='', cut_number='1', type=CartType.Audio, group_name='EXPLICABO', title='perferendis laudantium dolorum vel', artist='veritatis debitis', album='', year='', isrc='', isci='', label='', client='', agency='', publisher='', composer='', conductor='', song_id='', user_defined='', description='', outcue='', filename='', length='4:43', start_point='', end_point='', segue_start_point='', segue_end_point='', hook_start_point='', hook_end_point='', talk_start_point='', talk_end_point='', fadeup_point='', fadedown_point='', sched_codes=''),
        RivendellCart(cart_number='', cut_number='1', type=CartType.Audio, group_name='EXPLICABO', title='minus esse tempore occaecati', artist='assumenda odio', album='', year='', isrc='', isci='', label='', client='', agency='', publisher='', composer='', conductor='', song_id='', user_defined='', description='', outcue='', filename='', length='4:35', start_point='', end_point='', segue_start_point='', segue_end_point='', hook_start_point='', hook_end_point='', talk_start_point='', talk_end_point='', fadeup_point='', fadedown_point='', sched_codes=''),
        RivendellCart(cart_number='', cut_number='1', type=CartType.Audio, group_name='EXPLICABO', title='placeat at pariatur mollitia', artist='possimus ut', album='', year='', isrc='', isci='', label='', client='', agency='', publisher='', composer='', conductor='', song_id='', user_defined='', description='', outcue='', filename='', length='3:59', start_point='', end_point='', segue_start_point='', segue_end_point='', hook_start_point='', hook_end_point='', talk_start_point='', talk_end_point='', fadeup_point='', fadedown_point='', sched_codes=''),
        RivendellCart(cart_number='', cut_number='1', type=CartType.Audio, group_name='EXPLICABO', title='omnis rem dolore eius', artist='accusantium officia', album='', year='', isrc='', isci='', label='', client='', agency='', publisher='', composer='', conductor='', song_id='', user_defined='', description='', outcue='', filename='', length='4:13', start_point='', end_point='', segue_start_point='', segue_end_point='', hook_start_point='', hook_end_point='', talk_start_point='', talk_end_point='', fadeup_point='', fadedown_point='', sched_codes=''),
        RivendellCart(cart_number='', cut_number='1', type=CartType.Audio, group_name='EXPLICABO', title='doloremque nostrum fugiat nisi', artist='voluptatum quibusdam', album='', year='', isrc='', isci='', label='', client='', agency='', publisher='', composer='', conductor='', song_id='', user_defined='', description='', outcue='', filename='', length='4:47', start_point='', end_point='', segue_start_point='', segue_end_point='', hook_start_point='', hook_end_point='', talk_start_point='', talk_end_point='', fadeup_point='', fadedown_point='', sched_codes=''),
        RivendellCart(cart_number='', cut_number='1', type=CartType.Audio, group_name='EXPLICABO', title='dolor dignissimos minima ipsa', artist='vero rem', album='', year='', isrc='', isci='', label='', client='', agency='', publisher='', composer='', conductor='', song_id='', user_defined='', description='', outcue='', filename='', length='5:53', start_point='', end_point='', segue_start_point='', segue_end_point='', hook_start_point='', hook_end_point='', talk_start_point='', talk_end_point='', fadeup_point='', fadedown_point='', sched_codes=''),
        RivendellCart(cart_number='', cut_number='1', type=CartType.Audio, group_name='EXPLICABO', title='odio dolor sint ex', artist='minus aut', album='', year='', isrc='', isci='', label='', client='', agency='', publisher='', composer='', conductor='', song_id='', user_defined='', description='', outcue='', filename='', length='1:59', start_point='', end_point='', segue_start_point='', segue_end_point='', hook_start_point='', hook_end_point='', talk_start_point='', talk_end_point='', fadeup_point='', fadedown_point='', sched_codes=''),
        RivendellCart(cart_number='', cut_number='1', type=CartType.Audio, group_name='EXPLICABO', title='ipsum repellendus aspernatur qui', artist='voluptatibus qui', album='', year='', isrc='', isci='', label='', client='', agency='', publisher='', composer='', conductor='', song_id='', user_defined='', description='', outcue='', filename='', length='3:09', start_point='', end_point='', segue_start_point='', segue_end_point='', hook_start_point='', hook_end_point='', talk_start_point='', talk_end_point='', fadeup_point='', fadedown_point='', sched_codes=''),
        RivendellCart(cart_number='', cut_number='1', type=CartType.Audio, group_name='EXPLICABO', title='provident quod repellendus architecto', artist='ad amet', album='', year='', isrc='', isci='', label='', client='', agency='', publisher='', composer='', conductor='', song_id='', user_defined='', description='', outcue='', filename='', length='3:45', start_point='', end_point='', segue_start_point='', segue_end_point='', hook_start_point='', hook_end_point='', talk_start_point='', talk_end_point='', fadeup_point='', fadedown_point='', sched_codes=''),
        RivendellCart(cart_number='', cut_number='1', type=CartType.Audio, group_name='EXPLICABO', title='nobis totam architecto molestias', artist='soluta quasi', album='', year='', isrc='', isci='', label='', client='', agency='', publisher='', composer='', conductor='', song_id='', user_defined='', description='', outcue='', filename='', length='3:49', start_point='', end_point='', segue_start_point='', segue_end_point='', hook_start_point='', hook_end_point='', talk_start_point='', talk_end_point='', fadeup_point='', fadedown_point='', sched_codes=''),
        RivendellCart(cart_number='', cut_number='1', type=CartType.Audio, group_name='EXPLICABO', title='ducimus cum dolore accusantium', artist='dolores iusto', album='', year='', isrc='', isci='', label='', client='', agency='', publisher='', composer='', conductor='', song_id='', user_defined='', description='', outcue='', filename='', length='4:13', start_point='', end_point='', segue_start_point='', segue_end_point='', hook_start_point='', hook_end_point='', talk_start_point='', talk_end_point='', fadeup_point='', fadedown_point='', sched_codes=''),
        RivendellCart(cart_number='', cut_number='1', type=CartType.Audio, group_name='EXPLICABO', title='iusto dolores ab dolore', artist='beatae excepturi', album='', year='', isrc='', isci='', label='', client='', agency='', publisher='', composer='', conductor='', song_id='', user_defined='', description='', outcue='', filename='', length='3:27', start_point='', end_point='', segue_start_point='', segue_end_point='', hook_start_point='', hook_end_point='', talk_start_point='', talk_end_point='', fadeup_point='', fadedown_point='', sched_codes=''),
        RivendellCart(cart_number='', cut_number='1', type=CartType.Audio, group_name='EXPLICABO', title='est eum molestiae eveniet', artist='veniam modi', album='', year='', isrc='', isci='', label='', client='', agency='', publisher='', composer='', conductor='', song_id='', user_defined='', description='', outcue='', filename='', length='5:08', start_point='', end_point='', segue_start_point='', segue_end_point='', hook_start_point='', hook_end_point='', talk_start_point='', talk_end_point='', fadeup_point='', fadedown_point='', sched_codes=''),
        RivendellCart(cart_number='', cut_number='1', type=CartType.Audio, group_name='EXPLICABO', title='dolores ducimus odit doloremque', artist='ab possimus', album='', year='', isrc='', isci='', label='', client='', agency='', publisher='', composer='', conductor='', song_id='', user_defined='', description='', outcue='', filename='', length='4:40', start_point='', end_point='', segue_start_point='', segue_end_point='', hook_start_point='', hook_end_point='', talk_start_point='', talk_end_point='', fadeup_point='', fadedown_point='', sched_codes=''),
        RivendellCart(cart_number='', cut_number='1', type=CartType.Audio, group_name='EXPLICABO', title='enim veniam a aspernatur', artist='nulla at', album='', year='', isrc='', isci='', label='', client='', agency='', publisher='', composer='', conductor='', song_id='', user_defined='', description='', outcue='', filename='', length='2:39', start_point='', end_point='', segue_start_point='', segue_end_point='', hook_start_point='', hook_end_point='', talk_start_point='', talk_end_point='', fadeup_point='', fadedown_point='', sched_codes=''),
        RivendellCart(cart_number='', cut_number='1', type=CartType.Audio, group_name='EXPLICABO', title='eligendi commodi ex corrupti', artist='rem nobis', album='', year='', isrc='', isci='', label='', client='', agency='', publisher='', composer='', conductor='', song_id='', user_defined='', description='', outcue='', filename='', length='3:31', start_point='', end_point='', segue_start_point='', segue_end_point='', hook_start_point='', hook_end_point='', talk_start_point='', talk_end_point='', fadeup_point='', fadedown_point='', sched_codes=''),
        RivendellCart(cart_number='', cut_number='1', type=CartType.Audio, group_name='EXPLICABO', title='ex beatae facilis alias', artist='error eligendi', album='', year='', isrc='', isci='', label='', client='', agency='', publisher='', composer='', conductor='', song_id='', user_defined='', description='', outcue='', filename='', length='4:37', start_point='', end_point='', segue_start_point='', segue_end_point='', hook_start_point='', hook_end_point='', talk_start_point='', talk_end_point='', fadeup_point='', fadedown_point='', sched_codes=''),
        RivendellCart(cart_number='', cut_number='1', type=CartType.Audio, group_name='EXPLICABO', title='sunt veniam ab dicta', artist='minus sed', album='', year='', isrc='', isci='', label='', client='', agency='', publisher='', composer='', conductor='', song_id='', user_defined='', description='', outcue='', filename='', length='3:02', start_point='', end_point='', segue_start_point='', segue_end_point='', hook_start_point='', hook_end_point='', talk_start_point='', talk_end_point='', fadeup_point='', fadedown_point='', sched_codes=''),
        RivendellCart(cart_number='', cut_number='1', type=CartType.Audio, group_name='EXPLICABO', title='cumque excepturi eum nostrum', artist='amet aperiam', album='', year='', isrc='', isci='', label='', client='', agency='', publisher='', composer='', conductor='', song_id='', user_defined='', description='', outcue='', filename='', length='10:53', start_point='', end_point='', segue_start_point='', segue_end_point='', hook_start_point='', hook_end_point='', talk_start_point='', talk_end_point='', fadeup_point='', fadedown_point='', sched_codes=''),
        RivendellCart(cart_number='', cut_number='1', type=CartType.Audio, group_name='EXPLICABO', title='maiores tenetur dolor sequi', artist='sequi quae', album='', year='', isrc='', isci='', label='', client='', agency='', publisher='', composer='', conductor='', song_id='', user_defined='', description='', outcue='', filename='', length='3:17', start_point='', end_point='', segue_start_point='', segue_end_point='', hook_start_point='', hook_end_point='', talk_start_point='', talk_end_point='', fadeup_point='', fadedown_point='', sched_codes=''),
        RivendellCart(cart_number='', cut_number='1', type=CartType.Audio, group_name='EXPLICABO', title='aperiam consequatur magnam numquam', artist='ut pariatur', album='', year='', isrc='', isci='', label='', client='', agency='', publisher='', composer='', conductor='', song_id='', user_defined='', description='', outcue='', filename='', length='0:14', start_point='', end_point='', segue_start_point='', segue_end_point='', hook_start_point='', hook_end_point='', talk_start_point='', talk_end_point='', fadeup_point='', fadedown_point='', sched_codes=''),
        RivendellCart(cart_number='', cut_number='1', type=CartType.Audio, group_name='EXPLICABO', title='similique doloribus molestias adipisci', artist='commodi vitae', album='', year='', isrc='', isci='', label='', client='', agency='', publisher='', composer='', conductor='', song_id='', user_defined='', description='', outcue='', filename='', length='2:32', start_point='', end_point='', segue_start_point='', segue_end_point='', hook_start_point='', hook_end_point='', talk_start_point='', talk_end_point='', fadeup_point='', fadedown_point='', sched_codes=''),
        RivendellCart(cart_number='', cut_number='1', type=CartType.Audio, group_name='EXPLICABO', title='dolorum pariatur esse quia', artist='recusandae mollitia', album='', year='', isrc='', isci='', label='', client='', agency='', publisher='', composer='', conductor='', song_id='', user_defined='', description='', outcue='', filename='', length='3:04', start_point='', end_point='', segue_start_point='', segue_end_point='', hook_start_point='', hook_end_point='', talk_start_point='', talk_end_point='', fadeup_point='', fadedown_point='', sched_codes=''),
    ]

    stats_limits = StatisticsLimits(smallest_stdev=15)

    group_name = "EXPLICABO"
    group_stats = RivendellGroupStatistics(group_name=group_name, songs_in_group=carts, stats_limits=stats_limits)

    expected_number_of_songs = 25
    expected_shortest_song_length = 14 # 0:15
    expected_longest_song_length = 653 # 10:53

    expected_lower_outlier_limit = 44 # 44.5 rounded by python 
    expected_upper_outlier_limit = 416 # 416.5 rounded by python
    expected_outlier_limits = (expected_lower_outlier_limit, expected_upper_outlier_limit)

    expected_mean = 229 # 229.217 rounded
    expected_stdev = 57 # 56.8866 rounded

    expected_lower_bound = 150 # 143.5 rounded to the nearest 15 seconds
    expected_upper_bound = 405 # 400.0 rounded to the nearest 15 seconds

    expected_number_of_songs_shorter_than_lower_bound = 3
    expected_number_of_songs_longer_than_upper_bound = 1

    expected_percentage_of_songs_excluded = 16.0

    return make_namedtuple(
        "setup_rivendellgroupstatistics",
        group_name=group_name,
        group_stats=group_stats,
        expected_number_of_songs=expected_number_of_songs,
        expected_shortest_song_length=expected_shortest_song_length,
        expected_longest_song_length=expected_longest_song_length,
        expected_outlier_limits=expected_outlier_limits,
        expected_mean=expected_mean,
        expected_stdev=expected_stdev,
        expected_lower_bound=expected_lower_bound,
        expected_upper_bound=expected_upper_bound,
        expected_number_of_songs_shorter_than_lower_bound=expected_number_of_songs_shorter_than_lower_bound,
        expected_number_of_songs_longer_than_upper_bound=expected_number_of_songs_longer_than_upper_bound,
        expected_percentage_of_songs_excluded=expected_percentage_of_songs_excluded
    )

def test_group_name_correct(setup_rivendellgroupstatistics):
    group_stats = setup_rivendellgroupstatistics.group_stats
    assert group_stats.group_name == setup_rivendellgroupstatistics.group_name

def test_number_of_songs_correct(setup_rivendellgroupstatistics):
    group_stats = setup_rivendellgroupstatistics.group_stats
    assert group_stats.number_of_songs == setup_rivendellgroupstatistics.expected_number_of_songs
    
def test_shortest_song_length_in_this_group_correct(setup_rivendellgroupstatistics):
    group_stats = setup_rivendellgroupstatistics.group_stats
    assert group_stats.shortest_song_length == setup_rivendellgroupstatistics.expected_shortest_song_length

def test_longest_song_length_in_this_group_correct(setup_rivendellgroupstatistics):
    group_stats = setup_rivendellgroupstatistics.group_stats
    assert group_stats.longest_song_length == setup_rivendellgroupstatistics.expected_longest_song_length
    
def test_outlier_limits_correct(setup_rivendellgroupstatistics):
    group_stats = setup_rivendellgroupstatistics.group_stats
    assert group_stats.outlier_limits == setup_rivendellgroupstatistics.expected_outlier_limits

def test_mean_correct(setup_rivendellgroupstatistics):
    group_stats = setup_rivendellgroupstatistics.group_stats
    assert group_stats.mean == setup_rivendellgroupstatistics.expected_mean

def test_stdev_correct(setup_rivendellgroupstatistics):
    group_stats = setup_rivendellgroupstatistics.group_stats
    assert group_stats.stdev == setup_rivendellgroupstatistics.expected_stdev

def test_lower_bound_correct(setup_rivendellgroupstatistics):
    group_stats = setup_rivendellgroupstatistics.group_stats
    assert group_stats.lower_bound == setup_rivendellgroupstatistics.expected_lower_bound

def test_upper_bound_correct(setup_rivendellgroupstatistics):
    group_stats = setup_rivendellgroupstatistics.group_stats
    assert group_stats.upper_bound == setup_rivendellgroupstatistics.expected_upper_bound

def test_number_of_songs_shorter_than_lower_bound_correct(setup_rivendellgroupstatistics):
    group_stats = setup_rivendellgroupstatistics.group_stats
    assert (group_stats.number_of_songs_shorter_than_lower_bound == 
        setup_rivendellgroupstatistics.expected_number_of_songs_shorter_than_lower_bound)

def test_number_of_songs_longer_than_upper_bound_correct(setup_rivendellgroupstatistics):
    group_stats = setup_rivendellgroupstatistics.group_stats
    assert (group_stats.number_of_songs_longer_than_upper_bound == 
        setup_rivendellgroupstatistics.expected_number_of_songs_longer_than_upper_bound)
    
def test_percentage_excluded_correct(setup_rivendellgroupstatistics):
    group_stats = setup_rivendellgroupstatistics.group_stats
    assert (group_stats.percentage_of_songs_excluded == 
            setup_rivendellgroupstatistics.expected_percentage_of_songs_excluded)

def test_stdev_is_nan():
    """
    The standard deviation of a single item is Not a Number (NaN). RivendellGroupStatistics.stdev should be 0 in 
    this case.
    """    
    carts = [
        RivendellCart(cart_number='', cut_number='1', type=CartType.Audio, group_name='EXPLICABO', title='perferendis enim harum repudiandae', artist='nisi ipsum', album='', year='', isrc='', isci='', label='', client='', agency='', publisher='', composer='', conductor='', song_id='', user_defined='', description='', outcue='', filename='', length='4:22', start_point='', end_point='', segue_start_point='', segue_end_point='', hook_start_point='', hook_end_point='', talk_start_point='', talk_end_point='', fadeup_point='', fadedown_point='', sched_codes=''),
    ]

    stats_limits = StatisticsLimits()

    group_name = "EXPLICABO"
    group_stats = RivendellGroupStatistics(group_name=group_name, songs_in_group=carts, stats_limits=stats_limits)

    assert group_stats.stdev == 0


def test_lower_bound_is_less_than_zero():
    """
    If the calculated lower bound is below zero, RivendellGroupStatistics.lower_bound should be 0.
    """
    carts = [
        RivendellCart(cart_number='', cut_number='1', type=CartType.Audio, group_name='VOLUPTATIB', title='quo architecto asperiores minus', artist='ullam enim', album='', year='', isrc='', isci='', label='', client='', agency='', publisher='', composer='', conductor='', song_id='', user_defined='', description='', outcue='', filename='', length=':08', start_point='', end_point='', segue_start_point='', segue_end_point='', hook_start_point='', hook_end_point='', talk_start_point='', talk_end_point='', fadeup_point='', fadedown_point='', sched_codes=''),
        RivendellCart(cart_number='', cut_number='1', type=CartType.Audio, group_name='VOLUPTATIB', title='dignissimos illum eius maxime', artist='id repellat', album='', year='', isrc='', isci='', label='', client='', agency='', publisher='', composer='', conductor='', song_id='', user_defined='', description='', outcue='', filename='', length=':09', start_point='', end_point='', segue_start_point='', segue_end_point='', hook_start_point='', hook_end_point='', talk_start_point='', talk_end_point='', fadeup_point='', fadedown_point='', sched_codes=''),
        RivendellCart(cart_number='', cut_number='1', type=CartType.Audio, group_name='VOLUPTATIB', title='fugiat sunt in tenetur', artist='amet quam', album='', year='', isrc='', isci='', label='', client='', agency='', publisher='', composer='', conductor='', song_id='', user_defined='', description='', outcue='', filename='', length=':09', start_point='', end_point='', segue_start_point='', segue_end_point='', hook_start_point='', hook_end_point='', talk_start_point='', talk_end_point='', fadeup_point='', fadedown_point='', sched_codes=''),
        RivendellCart(cart_number='', cut_number='1', type=CartType.Audio, group_name='VOLUPTATIB', title='voluptates praesentium eveniet tempora', artist='quod culpa', album='', year='', isrc='', isci='', label='', client='', agency='', publisher='', composer='', conductor='', song_id='', user_defined='', description='', outcue='', filename='', length=':07', start_point='', end_point='', segue_start_point='', segue_end_point='', hook_start_point='', hook_end_point='', talk_start_point='', talk_end_point='', fadeup_point='', fadedown_point='', sched_codes=''),
        RivendellCart(cart_number='', cut_number='1', type=CartType.Audio, group_name='VOLUPTATIB', title='cum ipsam labore harum', artist='ullam praesentium', album='', year='', isrc='', isci='', label='', client='', agency='', publisher='', composer='', conductor='', song_id='', user_defined='', description='', outcue='', filename='', length=':10', start_point='', end_point='', segue_start_point='', segue_end_point='', hook_start_point='', hook_end_point='', talk_start_point='', talk_end_point='', fadeup_point='', fadedown_point='', sched_codes=''),
        RivendellCart(cart_number='', cut_number='1', type=CartType.Audio, group_name='VOLUPTATIB', title='alias ratione similique laudantium', artist='dolor iure', album='', year='', isrc='', isci='', label='', client='', agency='', publisher='', composer='', conductor='', song_id='', user_defined='', description='', outcue='', filename='', length=':07', start_point='', end_point='', segue_start_point='', segue_end_point='', hook_start_point='', hook_end_point='', talk_start_point='', talk_end_point='', fadeup_point='', fadedown_point='', sched_codes=''),
        RivendellCart(cart_number='', cut_number='1', type=CartType.Audio, group_name='VOLUPTATIB', title='tempora hic quos eius', artist='sed ullam', album='', year='', isrc='', isci='', label='', client='', agency='', publisher='', composer='', conductor='', song_id='', user_defined='', description='', outcue='', filename='', length=':07', start_point='', end_point='', segue_start_point='', segue_end_point='', hook_start_point='', hook_end_point='', talk_start_point='', talk_end_point='', fadeup_point='', fadedown_point='', sched_codes=''),
        RivendellCart(cart_number='', cut_number='1', type=CartType.Audio, group_name='VOLUPTATIB', title='reprehenderit minima quidem veritatis', artist='vero laboriosam', album='', year='', isrc='', isci='', label='', client='', agency='', publisher='', composer='', conductor='', song_id='', user_defined='', description='', outcue='', filename='', length=':05', start_point='', end_point='', segue_start_point='', segue_end_point='', hook_start_point='', hook_end_point='', talk_start_point='', talk_end_point='', fadeup_point='', fadedown_point='', sched_codes=''),
        RivendellCart(cart_number='', cut_number='1', type=CartType.Audio, group_name='VOLUPTATIB', title='veritatis debitis tempora deserunt', artist='doloremque exercitationem', album='', year='', isrc='', isci='', label='', client='', agency='', publisher='', composer='', conductor='', song_id='', user_defined='', description='', outcue='', filename='', length=':06', start_point='', end_point='', segue_start_point='', segue_end_point='', hook_start_point='', hook_end_point='', talk_start_point='', talk_end_point='', fadeup_point='', fadedown_point='', sched_codes=''),
        RivendellCart(cart_number='', cut_number='1', type=CartType.Audio, group_name='VOLUPTATIB', title='ea exercitationem officiis ipsum', artist='occaecati totam', album='', year='', isrc='', isci='', label='', client='', agency='', publisher='', composer='', conductor='', song_id='', user_defined='', description='', outcue='', filename='', length=':07', start_point='', end_point='', segue_start_point='', segue_end_point='', hook_start_point='', hook_end_point='', talk_start_point='', talk_end_point='', fadeup_point='', fadedown_point='', sched_codes=''),
        RivendellCart(cart_number='', cut_number='1', type=CartType.Audio, group_name='VOLUPTATIB', title='minus corrupti blanditiis facilis', artist='quisquam officia', album='', year='', isrc='', isci='', label='', client='', agency='', publisher='', composer='', conductor='', song_id='', user_defined='', description='', outcue='', filename='', length=':04', start_point='', end_point='', segue_start_point='', segue_end_point='', hook_start_point='', hook_end_point='', talk_start_point='', talk_end_point='', fadeup_point='', fadedown_point='', sched_codes=''),
        RivendellCart(cart_number='', cut_number='1', type=CartType.Audio, group_name='VOLUPTATIB', title='tempore corrupti saepe alias', artist='delectus minus', album='', year='', isrc='', isci='', label='', client='', agency='', publisher='', composer='', conductor='', song_id='', user_defined='', description='', outcue='', filename='', length=':05', start_point='', end_point='', segue_start_point='', segue_end_point='', hook_start_point='', hook_end_point='', talk_start_point='', talk_end_point='', fadeup_point='', fadedown_point='', sched_codes=''),
        RivendellCart(cart_number='', cut_number='1', type=CartType.Audio, group_name='VOLUPTATIB', title='at occaecati itaque sint', artist='sint nisi', album='', year='', isrc='', isci='', label='', client='', agency='', publisher='', composer='', conductor='', song_id='', user_defined='', description='', outcue='', filename='', length=':05', start_point='', end_point='', segue_start_point='', segue_end_point='', hook_start_point='', hook_end_point='', talk_start_point='', talk_end_point='', fadeup_point='', fadedown_point='', sched_codes=''),
        RivendellCart(cart_number='', cut_number='1', type=CartType.Audio, group_name='VOLUPTATIB', title='ducimus id asperiores eum', artist='optio ratione', album='', year='', isrc='', isci='', label='', client='', agency='', publisher='', composer='', conductor='', song_id='', user_defined='', description='', outcue='', filename='', length=':09', start_point='', end_point='', segue_start_point='', segue_end_point='', hook_start_point='', hook_end_point='', talk_start_point='', talk_end_point='', fadeup_point='', fadedown_point='', sched_codes=''),
        RivendellCart(cart_number='', cut_number='1', type=CartType.Audio, group_name='VOLUPTATIB', title='consectetur illo tempore alias', artist='perspiciatis maxime', album='', year='', isrc='', isci='', label='', client='', agency='', publisher='', composer='', conductor='', song_id='', user_defined='', description='', outcue='', filename='', length=':08', start_point='', end_point='', segue_start_point='', segue_end_point='', hook_start_point='', hook_end_point='', talk_start_point='', talk_end_point='', fadeup_point='', fadedown_point='', sched_codes=''),
        RivendellCart(cart_number='', cut_number='1', type=CartType.Audio, group_name='VOLUPTATIB', title='consectetur incidunt nihil alias', artist='cupiditate culpa', album='', year='', isrc='', isci='', label='', client='', agency='', publisher='', composer='', conductor='', song_id='', user_defined='', description='', outcue='', filename='', length=':08', start_point='', end_point='', segue_start_point='', segue_end_point='', hook_start_point='', hook_end_point='', talk_start_point='', talk_end_point='', fadeup_point='', fadedown_point='', sched_codes=''),
        RivendellCart(cart_number='', cut_number='1', type=CartType.Audio, group_name='VOLUPTATIB', title='amet ipsam veniam suscipit', artist='adipisci at', album='', year='', isrc='', isci='', label='', client='', agency='', publisher='', composer='', conductor='', song_id='', user_defined='', description='', outcue='', filename='', length=':08', start_point='', end_point='', segue_start_point='', segue_end_point='', hook_start_point='', hook_end_point='', talk_start_point='', talk_end_point='', fadeup_point='', fadedown_point='', sched_codes=''),
        RivendellCart(cart_number='', cut_number='1', type=CartType.Audio, group_name='VOLUPTATIB', title='delectus aliquam assumenda autem', artist='harum delectus', album='', year='', isrc='', isci='', label='', client='', agency='', publisher='', composer='', conductor='', song_id='', user_defined='', description='', outcue='', filename='', length=':11', start_point='', end_point='', segue_start_point='', segue_end_point='', hook_start_point='', hook_end_point='', talk_start_point='', talk_end_point='', fadeup_point='', fadedown_point='', sched_codes=''),
        RivendellCart(cart_number='', cut_number='1', type=CartType.Audio, group_name='VOLUPTATIB', title='odit ex hic molestiae', artist='debitis voluptas', album='', year='', isrc='', isci='', label='', client='', agency='', publisher='', composer='', conductor='', song_id='', user_defined='', description='', outcue='', filename='', length=':08', start_point='', end_point='', segue_start_point='', segue_end_point='', hook_start_point='', hook_end_point='', talk_start_point='', talk_end_point='', fadeup_point='', fadedown_point='', sched_codes=''),
    ]

    stats_limits = StatisticsLimits()

    group_name = "VOLUPTATIB"  
    group_stats = RivendellGroupStatistics(group_name=group_name, songs_in_group=carts, stats_limits=stats_limits)

    assert group_stats.lower_bound == 0


def test_remove_outliers():
    import numpy as np

    carts = [
        RivendellCart(cart_number='', cut_number='1', type=CartType.Audio, group_name='EXPLICABO', title='atque numquam neque animi', artist='animi voluptas', album='', year='', isrc='', isci='', label='', client='', agency='', publisher='', composer='', conductor='', song_id='', user_defined='', description='', outcue='', filename='', length='4:22', start_point='', end_point='', segue_start_point='', segue_end_point='', hook_start_point='', hook_end_point='', talk_start_point='', talk_end_point='', fadeup_point='', fadedown_point='', sched_codes=''),
        RivendellCart(cart_number='', cut_number='1', type=CartType.Audio, group_name='EXPLICABO', title='quos doloremque nulla eligendi', artist='totam voluptatem', album='', year='', isrc='', isci='', label='', client='', agency='', publisher='', composer='', conductor='', song_id='', user_defined='', description='', outcue='', filename='', length='2:28', start_point='', end_point='', segue_start_point='', segue_end_point='', hook_start_point='', hook_end_point='', talk_start_point='', talk_end_point='', fadeup_point='', fadedown_point='', sched_codes=''),
        RivendellCart(cart_number='', cut_number='1', type=CartType.Audio, group_name='EXPLICABO', title='unde quidem quam debitis', artist='adipisci qui', album='', year='', isrc='', isci='', label='', client='', agency='', publisher='', composer='', conductor='', song_id='', user_defined='', description='', outcue='', filename='', length='4:43', start_point='', end_point='', segue_start_point='', segue_end_point='', hook_start_point='', hook_end_point='', talk_start_point='', talk_end_point='', fadeup_point='', fadedown_point='', sched_codes=''),
        RivendellCart(cart_number='', cut_number='1', type=CartType.Audio, group_name='EXPLICABO', title='explicabo commodi maxime nobis', artist='hic veniam', album='', year='', isrc='', isci='', label='', client='', agency='', publisher='', composer='', conductor='', song_id='', user_defined='', description='', outcue='', filename='', length='4:35', start_point='', end_point='', segue_start_point='', segue_end_point='', hook_start_point='', hook_end_point='', talk_start_point='', talk_end_point='', fadeup_point='', fadedown_point='', sched_codes=''),
        RivendellCart(cart_number='', cut_number='1', type=CartType.Audio, group_name='EXPLICABO', title='architecto consequuntur hic occaecati', artist='aut odit', album='', year='', isrc='', isci='', label='', client='', agency='', publisher='', composer='', conductor='', song_id='', user_defined='', description='', outcue='', filename='', length='3:59', start_point='', end_point='', segue_start_point='', segue_end_point='', hook_start_point='', hook_end_point='', talk_start_point='', talk_end_point='', fadeup_point='', fadedown_point='', sched_codes=''),
        RivendellCart(cart_number='', cut_number='1', type=CartType.Audio, group_name='EXPLICABO', title='enim natus expedita in', artist='amet similique', album='', year='', isrc='', isci='', label='', client='', agency='', publisher='', composer='', conductor='', song_id='', user_defined='', description='', outcue='', filename='', length='4:13', start_point='', end_point='', segue_start_point='', segue_end_point='', hook_start_point='', hook_end_point='', talk_start_point='', talk_end_point='', fadeup_point='', fadedown_point='', sched_codes=''),
        RivendellCart(cart_number='', cut_number='1', type=CartType.Audio, group_name='EXPLICABO', title='vero labore soluta fugiat', artist='nesciunt incidunt', album='', year='', isrc='', isci='', label='', client='', agency='', publisher='', composer='', conductor='', song_id='', user_defined='', description='', outcue='', filename='', length='4:47', start_point='', end_point='', segue_start_point='', segue_end_point='', hook_start_point='', hook_end_point='', talk_start_point='', talk_end_point='', fadeup_point='', fadedown_point='', sched_codes=''),
        RivendellCart(cart_number='', cut_number='1', type=CartType.Audio, group_name='EXPLICABO', title='quae repellat placeat quas', artist='quam illum', album='', year='', isrc='', isci='', label='', client='', agency='', publisher='', composer='', conductor='', song_id='', user_defined='', description='', outcue='', filename='', length='5:53', start_point='', end_point='', segue_start_point='', segue_end_point='', hook_start_point='', hook_end_point='', talk_start_point='', talk_end_point='', fadeup_point='', fadedown_point='', sched_codes=''),
        RivendellCart(cart_number='', cut_number='1', type=CartType.Audio, group_name='EXPLICABO', title='culpa sunt velit itaque', artist='sed quaerat', album='', year='', isrc='', isci='', label='', client='', agency='', publisher='', composer='', conductor='', song_id='', user_defined='', description='', outcue='', filename='', length='1:59', start_point='', end_point='', segue_start_point='', segue_end_point='', hook_start_point='', hook_end_point='', talk_start_point='', talk_end_point='', fadeup_point='', fadedown_point='', sched_codes=''),
        RivendellCart(cart_number='', cut_number='1', type=CartType.Audio, group_name='EXPLICABO', title='quia possimus assumenda libero', artist='eos itaque', album='', year='', isrc='', isci='', label='', client='', agency='', publisher='', composer='', conductor='', song_id='', user_defined='', description='', outcue='', filename='', length='3:09', start_point='', end_point='', segue_start_point='', segue_end_point='', hook_start_point='', hook_end_point='', talk_start_point='', talk_end_point='', fadeup_point='', fadedown_point='', sched_codes=''),
        RivendellCart(cart_number='', cut_number='1', type=CartType.Audio, group_name='EXPLICABO', title='voluptas minima exercitationem officiis', artist='quam et', album='', year='', isrc='', isci='', label='', client='', agency='', publisher='', composer='', conductor='', song_id='', user_defined='', description='', outcue='', filename='', length='3:45', start_point='', end_point='', segue_start_point='', segue_end_point='', hook_start_point='', hook_end_point='', talk_start_point='', talk_end_point='', fadeup_point='', fadedown_point='', sched_codes=''),
        RivendellCart(cart_number='', cut_number='1', type=CartType.Audio, group_name='EXPLICABO', title='quod dolorem ut illum', artist='culpa impedit', album='', year='', isrc='', isci='', label='', client='', agency='', publisher='', composer='', conductor='', song_id='', user_defined='', description='', outcue='', filename='', length='3:49', start_point='', end_point='', segue_start_point='', segue_end_point='', hook_start_point='', hook_end_point='', talk_start_point='', talk_end_point='', fadeup_point='', fadedown_point='', sched_codes=''),
        RivendellCart(cart_number='', cut_number='1', type=CartType.Audio, group_name='EXPLICABO', title='tempore doloremque consectetur occaecati', artist='totam aliquam', album='', year='', isrc='', isci='', label='', client='', agency='', publisher='', composer='', conductor='', song_id='', user_defined='', description='', outcue='', filename='', length='4:13', start_point='', end_point='', segue_start_point='', segue_end_point='', hook_start_point='', hook_end_point='', talk_start_point='', talk_end_point='', fadeup_point='', fadedown_point='', sched_codes=''),
        RivendellCart(cart_number='', cut_number='1', type=CartType.Audio, group_name='EXPLICABO', title='doloremque quae beatae eum', artist='sint odio', album='', year='', isrc='', isci='', label='', client='', agency='', publisher='', composer='', conductor='', song_id='', user_defined='', description='', outcue='', filename='', length='3:27', start_point='', end_point='', segue_start_point='', segue_end_point='', hook_start_point='', hook_end_point='', talk_start_point='', talk_end_point='', fadeup_point='', fadedown_point='', sched_codes=''),
        RivendellCart(cart_number='', cut_number='1', type=CartType.Audio, group_name='EXPLICABO', title='deserunt aspernatur quod perferendis', artist='provident nobis', album='', year='', isrc='', isci='', label='', client='', agency='', publisher='', composer='', conductor='', song_id='', user_defined='', description='', outcue='', filename='', length='5:08', start_point='', end_point='', segue_start_point='', segue_end_point='', hook_start_point='', hook_end_point='', talk_start_point='', talk_end_point='', fadeup_point='', fadedown_point='', sched_codes=''),
        RivendellCart(cart_number='', cut_number='1', type=CartType.Audio, group_name='EXPLICABO', title='modi earum sequi quod', artist='excepturi velit', album='', year='', isrc='', isci='', label='', client='', agency='', publisher='', composer='', conductor='', song_id='', user_defined='', description='', outcue='', filename='', length='4:40', start_point='', end_point='', segue_start_point='', segue_end_point='', hook_start_point='', hook_end_point='', talk_start_point='', talk_end_point='', fadeup_point='', fadedown_point='', sched_codes=''),
        RivendellCart(cart_number='', cut_number='1', type=CartType.Audio, group_name='EXPLICABO', title='sit dignissimos quisquam reiciendis', artist='necessitatibus incidunt', album='', year='', isrc='', isci='', label='', client='', agency='', publisher='', composer='', conductor='', song_id='', user_defined='', description='', outcue='', filename='', length='2:39', start_point='', end_point='', segue_start_point='', segue_end_point='', hook_start_point='', hook_end_point='', talk_start_point='', talk_end_point='', fadeup_point='', fadedown_point='', sched_codes=''),
        RivendellCart(cart_number='', cut_number='1', type=CartType.Audio, group_name='EXPLICABO', title='sed excepturi porro aspernatur', artist='nisi minus', album='', year='', isrc='', isci='', label='', client='', agency='', publisher='', composer='', conductor='', song_id='', user_defined='', description='', outcue='', filename='', length='3:31', start_point='', end_point='', segue_start_point='', segue_end_point='', hook_start_point='', hook_end_point='', talk_start_point='', talk_end_point='', fadeup_point='', fadedown_point='', sched_codes=''),
        RivendellCart(cart_number='', cut_number='1', type=CartType.Audio, group_name='EXPLICABO', title='pariatur blanditiis sunt corrupti', artist='unde dicta', album='', year='', isrc='', isci='', label='', client='', agency='', publisher='', composer='', conductor='', song_id='', user_defined='', description='', outcue='', filename='', length='4:37', start_point='', end_point='', segue_start_point='', segue_end_point='', hook_start_point='', hook_end_point='', talk_start_point='', talk_end_point='', fadeup_point='', fadedown_point='', sched_codes=''),
        RivendellCart(cart_number='', cut_number='1', type=CartType.Audio, group_name='EXPLICABO', title='eius illum consequuntur aperiam', artist='voluptatem cum', album='', year='', isrc='', isci='', label='', client='', agency='', publisher='', composer='', conductor='', song_id='', user_defined='', description='', outcue='', filename='', length='3:02', start_point='', end_point='', segue_start_point='', segue_end_point='', hook_start_point='', hook_end_point='', talk_start_point='', talk_end_point='', fadeup_point='', fadedown_point='', sched_codes=''),
        RivendellCart(cart_number='', cut_number='1', type=CartType.Audio, group_name='EXPLICABO', title='natus sunt laborum esse', artist='repellendus aut', album='', year='', isrc='', isci='', label='', client='', agency='', publisher='', composer='', conductor='', song_id='', user_defined='', description='', outcue='', filename='', length='10:53', start_point='', end_point='', segue_start_point='', segue_end_point='', hook_start_point='', hook_end_point='', talk_start_point='', talk_end_point='', fadeup_point='', fadedown_point='', sched_codes=''),
        RivendellCart(cart_number='', cut_number='1', type=CartType.Audio, group_name='EXPLICABO', title='distinctio provident similique placeat', artist='sed veniam', album='', year='', isrc='', isci='', label='', client='', agency='', publisher='', composer='', conductor='', song_id='', user_defined='', description='', outcue='', filename='', length='3:17', start_point='', end_point='', segue_start_point='', segue_end_point='', hook_start_point='', hook_end_point='', talk_start_point='', talk_end_point='', fadeup_point='', fadedown_point='', sched_codes=''),
        RivendellCart(cart_number='', cut_number='1', type=CartType.Audio, group_name='EXPLICABO', title='ullam magni commodi quaerat', artist='placeat rerum', album='', year='', isrc='', isci='', label='', client='', agency='', publisher='', composer='', conductor='', song_id='', user_defined='', description='', outcue='', filename='', length='0:14', start_point='', end_point='', segue_start_point='', segue_end_point='', hook_start_point='', hook_end_point='', talk_start_point='', talk_end_point='', fadeup_point='', fadedown_point='', sched_codes=''),
        RivendellCart(cart_number='', cut_number='1', type=CartType.Audio, group_name='EXPLICABO', title='quaerat minus unde aliquam', artist='voluptatibus ex', album='', year='', isrc='', isci='', label='', client='', agency='', publisher='', composer='', conductor='', song_id='', user_defined='', description='', outcue='', filename='', length='2:32', start_point='', end_point='', segue_start_point='', segue_end_point='', hook_start_point='', hook_end_point='', talk_start_point='', talk_end_point='', fadeup_point='', fadedown_point='', sched_codes=''),
        RivendellCart(cart_number='', cut_number='1', type=CartType.Audio, group_name='EXPLICABO', title='porro ad ipsum repudiandae', artist='explicabo eum', album='', year='', isrc='', isci='', label='', client='', agency='', publisher='', composer='', conductor='', song_id='', user_defined='', description='', outcue='', filename='', length='3:04', start_point='', end_point='', segue_start_point='', segue_end_point='', hook_start_point='', hook_end_point='', talk_start_point='', talk_end_point='', fadeup_point='', fadedown_point='', sched_codes='')
    ]

    times_of_this_group = np.array([this_item.length_in_seconds() for this_item in carts])

    expected_lower_outlier_limit = 44.5 
    expected_upper_outlier_limit = 416.5 

    stats_limits = StatisticsLimits()

    outliers_excluded, lower_limit, upper_limit = _remove_outliers(
        times_of_this_group=times_of_this_group, 
        stats_limits=stats_limits
    )
    
    carts_without_outliers = [
        RivendellCart(cart_number='100000', cut_number='1', type=CartType.Audio, group_name='ALTERNATIV', title="There's a Star", artist='Ash', album='', year='', isrc='', isci='', label='', client='', agency='', publisher='', composer='Imported from WOAFR: A00/0001', conductor='', song_id='', user_defined='', description="There's a Star", outcue='', filename='100000_001.wav', length='4:22', start_point='0', end_point='262000', segue_start_point='260000', segue_end_point='262000', hook_start_point='-1', hook_end_point='-1', talk_start_point='0', talk_end_point='10000', fadeup_point='-1', fadedown_point='-1', sched_codes='2000s|Flashback'),
        RivendellCart(cart_number='100001', cut_number='1', type=CartType.Audio, group_name='ALTERNATIV', title='Six Feet Under', artist='No Doubt', album='', year='', isrc='', isci='', label='', client='', agency='', publisher='', composer='Imported from WOAFR: A00/0002', conductor='', song_id='', user_defined='', description='Six Feet Under', outcue='', filename='100001_001.wav', length='2:28', start_point='0', end_point='148000', segue_start_point='146000', segue_end_point='148000', hook_start_point='-1', hook_end_point='-1', talk_start_point='0', talk_end_point='6000', fadeup_point='-1', fadedown_point='-1', sched_codes='2000s|Flashback'),
        RivendellCart(cart_number='100002', cut_number='1', type=CartType.Audio, group_name='ALTERNATIV', title='Portions for Foxes', artist='Rilo Kiley', album='', year='2004', isrc='', isci='', label='', client='', agency='', publisher='', composer='Imported from WOAFR: A00/0003', conductor='', song_id='', user_defined='', description='Portions for Foxes', outcue='', filename='100002_001.wav', length='4:43', start_point='0', end_point='283000', segue_start_point='280000', segue_end_point='283000', hook_start_point='-1', hook_end_point='-1', talk_start_point='0', talk_end_point='34000', fadeup_point='-1', fadedown_point='-1', sched_codes='2000s'),
        RivendellCart(cart_number='100003', cut_number='1', type=CartType.Audio, group_name='ALTERNATIV', title='Maybe Tomorrow', artist='Stereophonics', album='', year='2004', isrc='', isci='', label='', client='', agency='', publisher='', composer='Imported from WOAFR: A00/0004', conductor='', song_id='', user_defined='', description='Maybe Tomorrow', outcue='', filename='100003_001.wav', length='4:35', start_point='0', end_point='275000', segue_start_point='271000', segue_end_point='275000', hook_start_point='-1', hook_end_point='-1', talk_start_point='0', talk_end_point='30000', fadeup_point='-1', fadedown_point='-1', sched_codes='2000s|Flashback'),
        RivendellCart(cart_number='100004', cut_number='1', type=CartType.Audio, group_name='ALTERNATIV', title='Existentialism on Prom Night', artist='Straylight Run', album='', year='2004', isrc='', isci='', label='', client='', agency='', publisher='', composer='Imported from WOAFR: A00/0005', conductor='', song_id='', user_defined='', description='Existentialism on Prom Night', outcue='', filename='100004_001.wav', length='3:59', start_point='0', end_point='239000', segue_start_point='234000', segue_end_point='239000', hook_start_point='-1', hook_end_point='-1', talk_start_point='0', talk_end_point='28000', fadeup_point='-1', fadedown_point='-1', sched_codes='2000s|Flashback'),

        RivendellCart(cart_number='100005', cut_number='1', type=CartType.Audio, group_name='ALTERNATIV', title='Your Ex-Lover is Dead', artist='Stars', album='', year='2004', isrc='', isci='', label='', client='', agency='', publisher='', composer='Imported from WOAFR: A00/0006', conductor='', song_id='', user_defined='', description='Your Ex-Lover is Dead', outcue='', filename='100005_001.wav', length='4:13', start_point='0', end_point='253000', segue_start_point='251000', segue_end_point='253000', hook_start_point='-1', hook_end_point='-1', talk_start_point='0', talk_end_point='0', fadeup_point='-1', fadedown_point='-1', sched_codes='2000s|Flashback'),
        RivendellCart(cart_number='100006', cut_number='1', type=CartType.Audio, group_name='ALTERNATIV', title='Neighborhood #1 (Tunnels)', artist='Arcade Fire', album='', year='2004', isrc='', isci='', label='', client='', agency='', publisher='', composer='Imported from WOAFR: A00/0007', conductor='', song_id='', user_defined='', description='Neighborhood #1 (Tunnels)', outcue='', filename='100006_001.wav', length='4:47', start_point='0', end_point='287000', segue_start_point='285000', segue_end_point='287000', hook_start_point='-1', hook_end_point='-1', talk_start_point='0', talk_end_point='25000', fadeup_point='-1', fadedown_point='-1', sched_codes='2000s|Flashback'),
        RivendellCart(cart_number='100007', cut_number='1', type=CartType.Audio, group_name='ALTERNATIV', title='How to Disappear Completely', artist='Radiohead', album='', year='2000', isrc='', isci='', label='', client='', agency='', publisher='', composer='Imported from WOAFR: A00/0008', conductor='', song_id='', user_defined='', description='How to Disappear Completely', outcue='', filename='100007_001.wav', length='5:53', start_point='0', end_point='353000', segue_start_point='351000', segue_end_point='353000', hook_start_point='-1', hook_end_point='-1', talk_start_point='0', talk_end_point='41000', fadeup_point='-1', fadedown_point='-1', sched_codes='2000s|Flashback'),
        RivendellCart(cart_number='100008', cut_number='1', type=CartType.Audio, group_name='ALTERNATIV', title='Pin', artist='Yeah Yeah Yeahs', album='', year='2003', isrc='', isci='', label='', client='', agency='', publisher='', composer='Imported from WOAFR: A00/0009', conductor='', song_id='', user_defined='', description='Pin', outcue='', filename='100008_001.wav', length='1:59', start_point='0', end_point='119000', segue_start_point='117000', segue_end_point='119000', hook_start_point='-1', hook_end_point='-1', talk_start_point='0', talk_end_point='22000', fadeup_point='-1', fadedown_point='-1', sched_codes='2000s|Flashback'),
        RivendellCart(cart_number='100009', cut_number='1', type=CartType.Audio, group_name='ALTERNATIV', title='Juxtapozed with U', artist='Super Furry Animals', album='', year='2001', isrc='', isci='', label='', client='', agency='', publisher='', composer='Imported from WOAFR: A00/0010', conductor='', song_id='', user_defined='', description='Juxtapozed with U', outcue='', filename='100009_001.wav', length='3:09', start_point='0', end_point='189000', segue_start_point='188000', segue_end_point='189000', hook_start_point='-1', hook_end_point='-1', talk_start_point='0', talk_end_point='25000', fadeup_point='-1', fadedown_point='-1', sched_codes='2000s|Flashback'),
        
        RivendellCart(cart_number='100010', cut_number='1', type=CartType.Audio, group_name='ALTERNATIV', title='New Slang', artist='The Shins', album='', year='', isrc='', isci='', label='', client='', agency='', publisher='', composer='Imported from WOAFR: A00/0011', conductor='', song_id='', user_defined='', description='New Slang', outcue='', filename='100010_001.wav', length='3:45', start_point='0', end_point='225000', segue_start_point='221000', segue_end_point='225000', hook_start_point='-1', hook_end_point='-1', talk_start_point='0', talk_end_point='36000', fadeup_point='-1', fadedown_point='-1', sched_codes='2000s|Flashback'),
        RivendellCart(cart_number='100011', cut_number='1', type=CartType.Audio, group_name='ALTERNATIV', title='War on War', artist='Wilco', album='', year='2002', isrc='', isci='', label='', client='', agency='', publisher='', composer='Imported from WOAFR: A00/0012', conductor='', song_id='', user_defined='', description='War on War', outcue='', filename='100011_001.wav', length='3:49', start_point='0', end_point='229000', segue_start_point='227000', segue_end_point='229000', hook_start_point='-1', hook_end_point='-1', talk_start_point='0', talk_end_point='50000', fadeup_point='-1', fadedown_point='-1', sched_codes='2000s|Flashback'),
        RivendellCart(cart_number='100012', cut_number='1', type=CartType.Audio, group_name='ALTERNATIV', title='Drift Away', artist='Uncle Kracker', album='', year='', isrc='', isci='', label='', client='', agency='', publisher='', composer='Imported from WOAFR: A00/0013', conductor='', song_id='', user_defined='', description='Drift Away', outcue='', filename='100012_001.wav', length='4:13', start_point='0', end_point='253000', segue_start_point='246000', segue_end_point='253000', hook_start_point='-1', hook_end_point='-1', talk_start_point='0', talk_end_point='12000', fadeup_point='-1', fadedown_point='-1', sched_codes='2000s|Flashback'),
        RivendellCart(cart_number='100013', cut_number='1', type=CartType.Audio, group_name='ALTERNATIV', title='Neighborhood #2 (Laika)', artist='Arcade Fire', album='', year='', isrc='', isci='', label='', client='', agency='', publisher='', composer='Imported from WOAFR: A00/0014', conductor='', song_id='', user_defined='', description='Neighborhood #2 (Laika)', outcue='', filename='100013_001.wav', length='3:27', start_point='0', end_point='207000', segue_start_point='205000', segue_end_point='207000', hook_start_point='-1', hook_end_point='-1', talk_start_point='0', talk_end_point='23000', fadeup_point='-1', fadedown_point='-1', sched_codes='2000s|Flashback'),
        RivendellCart(cart_number='100014', cut_number='1', type=CartType.Audio, group_name='ALTERNATIV', title='Neighborhood #3 (Power Out)', artist='Arcade Fire', album='', year='', isrc='', isci='', label='', client='', agency='', publisher='', composer='Imported from WOAFR: A00/0015', conductor='', song_id='', user_defined='', description='Neighborhood #3 (Power Out)', outcue='', filename='100014_001.wav', length='5:08', start_point='0', end_point='308000', segue_start_point='308000', segue_end_point='308000', hook_start_point='-1', hook_end_point='-1', talk_start_point='0', talk_end_point='15000', fadeup_point='-1', fadedown_point='-1', sched_codes='2000s|Flashback'),
        
        RivendellCart(cart_number='100015', cut_number='1', type=CartType.Audio, group_name='ALTERNATIV', title='Neighborhood #4 (7 Kettles)', artist='Arcade Fire', album='', year='', isrc='', isci='', label='', client='', agency='', publisher='', composer='Imported from WOAFR: A00/0016', conductor='', song_id='', user_defined='', description='Neighborhood #4 (7 Kettles)', outcue='', filename='100015_001.wav', length='4:40', start_point='0', end_point='280000', segue_start_point='272000', segue_end_point='280000', hook_start_point='-1', hook_end_point='-1', talk_start_point='0', talk_end_point='22000', fadeup_point='-1', fadedown_point='-1', sched_codes='2000s|Flashback'),
        RivendellCart(cart_number='100016', cut_number='1', type=CartType.Audio, group_name='ALTERNATIV', title='5/4', artist='Gorillaz', album='', year='', isrc='', isci='', label='', client='', agency='', publisher='', composer='Imported from WOAFR: A00/0017', conductor='', song_id='', user_defined='', description='5/4', outcue='', filename='100016_001.wav', length='2:39', start_point='0', end_point='159000', segue_start_point='156000', segue_end_point='159000', hook_start_point='-1', hook_end_point='-1', talk_start_point='0', talk_end_point='12000', fadeup_point='-1', fadedown_point='-1', sched_codes='2000s|Flashback'),
        RivendellCart(cart_number='100017', cut_number='1', type=CartType.Audio, group_name='ALTERNATIV', title='Cute Without the E', artist='Taking Back Sunday', album='', year='', isrc='', isci='', label='', client='', agency='', publisher='', composer='Imported from WOAFR: A00/0018', conductor='', song_id='', user_defined='', description='Cute Without the E', outcue='', filename='100017_001.wav', length='3:31', start_point='0', end_point='211000', segue_start_point='207000', segue_end_point='211000', hook_start_point='-1', hook_end_point='-1', talk_start_point='0', talk_end_point='5000', fadeup_point='-1', fadedown_point='-1', sched_codes='2000s|Flashback'),
        RivendellCart(cart_number='100018', cut_number='1', type=CartType.Audio, group_name='ALTERNATIV', title='Pinch Me', artist='Barenaked Ladies', album='', year='', isrc='', isci='', label='', client='', agency='', publisher='', composer='Imported from WOAFR: A00/0019', conductor='', song_id='', user_defined='', description='Pinch Me', outcue='', filename='100018_001.wav', length='4:37', start_point='0', end_point='277000', segue_start_point='270000', segue_end_point='277000', hook_start_point='-1', hook_end_point='-1', talk_start_point='0', talk_end_point='13000', fadeup_point='-1', fadedown_point='-1', sched_codes='2000s|Flashback'),
        RivendellCart(cart_number='100019', cut_number='1', type=CartType.Audio, group_name='ALTERNATIV', title='Move Your Feet', artist='Junior Senior', album='', year='', isrc='', isci='', label='', client='', agency='', publisher='', composer='Imported from WOAFR: A00/0020', conductor='', song_id='', user_defined='', description='Move Your Feet', outcue='', filename='100019_001.wav', length='3:02', start_point='0', end_point='182000', segue_start_point='180000', segue_end_point='182000', hook_start_point='-1', hook_end_point='-1', talk_start_point='0', talk_end_point='6000', fadeup_point='-1', fadedown_point='-1', sched_codes='2000s|Flashback'),

        RivendellCart(cart_number='100021', cut_number='1', type=CartType.Audio, group_name='ALTERNATIV', title='Everything Hits At Once', artist='Spoon', album='', year='', isrc='', isci='', label='', client='', agency='', publisher='', composer='Imported from WOAFR: A00/0022', conductor='', song_id='', user_defined='', description='Everything Hits At Once', outcue='', filename='100021_001.wav', length='3:17', start_point='0', end_point='197000', segue_start_point='195000', segue_end_point='197000', hook_start_point='-1', hook_end_point='-1', talk_start_point='0', talk_end_point='17000', fadeup_point='-1', fadedown_point='-1', sched_codes='2000s|Flashback'),
        RivendellCart(cart_number='100023', cut_number='1', type=CartType.Audio, group_name='ALTERNATIV', title='Call of the Yeti', artist='The Mighty Boosh', album='', year='', isrc='', isci='', label='', client='', agency='', publisher='', composer='Imported from WOAFR: A00/0024', conductor='', song_id='', user_defined='', description='Call of the Yeti', outcue='', filename='100023_001.wav', length='2:32', start_point='0', end_point='152000', segue_start_point='148000', segue_end_point='152000', hook_start_point='-1', hook_end_point='-1', talk_start_point='0', talk_end_point='0', fadeup_point='-1', fadedown_point='-1', sched_codes='2000s|Flashback'),
        RivendellCart(cart_number='100024', cut_number='1', type=CartType.Audio, group_name='ALTERNATIV', title='Spiky Seeds', artist='Pillows', album='', year='', isrc='', isci='', label='', client='', agency='', publisher='', composer='Imported from WOAFR: A00/0025', conductor='', song_id='', user_defined='', description='Spiky Seeds', outcue='', filename='100024_001.wav', length='3:04', start_point='0', end_point='184000', segue_start_point='180000', segue_end_point='184000', hook_start_point='-1', hook_end_point='-1', talk_start_point='0', talk_end_point='20000', fadeup_point='-1', fadedown_point='-1', sched_codes='2000s')
    ]

    times_without_outliers = np.array([this_item.length_in_seconds() for this_item in carts_without_outliers])
    times_without_outliers.sort()

    assert (outliers_excluded == times_without_outliers).all()
    assert lower_limit == expected_lower_outlier_limit
    assert upper_limit == expected_upper_outlier_limit


def test_remove_outliers_not_enough_population():
    """
    If the population is < 4, then the remove_outliers function should return the list unchanged.
    The lower limit should be 0 and the upper limit should be 86,399 (23 hours, 59 minutes, 59 seconds).
    """
    import numpy as np

    carts = [
        RivendellCart(cart_number='', cut_number='1', type=CartType.Audio, group_name='EXPLICABO', title='atque numquam neque animi', artist='animi voluptas', album='', year='', isrc='', isci='', label='', client='', agency='', publisher='', composer='', conductor='', song_id='', user_defined='', description='', outcue='', filename='', length='4:22', start_point='', end_point='', segue_start_point='', segue_end_point='', hook_start_point='', hook_end_point='', talk_start_point='', talk_end_point='', fadeup_point='', fadedown_point='', sched_codes=''),
        RivendellCart(cart_number='', cut_number='1', type=CartType.Audio, group_name='EXPLICABO', title='quos doloremque nulla eligendi', artist='totam voluptatem', album='', year='', isrc='', isci='', label='', client='', agency='', publisher='', composer='', conductor='', song_id='', user_defined='', description='', outcue='', filename='', length='2:28', start_point='', end_point='', segue_start_point='', segue_end_point='', hook_start_point='', hook_end_point='', talk_start_point='', talk_end_point='', fadeup_point='', fadedown_point='', sched_codes=''),
        RivendellCart(cart_number='', cut_number='1', type=CartType.Audio, group_name='EXPLICABO', title='unde quidem quam debitis', artist='adipisci qui', album='', year='', isrc='', isci='', label='', client='', agency='', publisher='', composer='', conductor='', song_id='', user_defined='', description='', outcue='', filename='', length='4:43', start_point='', end_point='', segue_start_point='', segue_end_point='', hook_start_point='', hook_end_point='', talk_start_point='', talk_end_point='', fadeup_point='', fadedown_point='', sched_codes=''),
    ]

    times_of_this_group = np.array([this_item.length_in_seconds() for this_item in carts])

    expected_lower_outlier_limit = 0 
    expected_upper_outlier_limit = 86_399 # 23:59:59

    stats_limits = StatisticsLimits()

    outliers_excluded, lower_limit, upper_limit = _remove_outliers(
        times_of_this_group=times_of_this_group, 
        stats_limits=stats_limits
    )
    
    assert (outliers_excluded == times_of_this_group).all()
    assert lower_limit == expected_lower_outlier_limit
    assert upper_limit == expected_upper_outlier_limit


def test_remove_ouliers_too_small_stdev():
    """
    If the standard deviation of the population is < 15 seconds, don't try to calculate outliers.
    """
    import numpy as np

    carts = [
        RivendellCart(cart_number='', cut_number='1', type=CartType.Audio, group_name='DOLORES', title='magni voluptatum saepe architecto', artist='recusandae temporibus', album='', year='', isrc='', isci='', label='', client='', agency='', publisher='', composer='', conductor='', song_id='', user_defined='', description='', outcue='', filename='', length=':05', start_point='', end_point='', segue_start_point='', segue_end_point='', hook_start_point='', hook_end_point='', talk_start_point='', talk_end_point='', fadeup_point='', fadedown_point='', sched_codes=''),
        RivendellCart(cart_number='', cut_number='1', type=CartType.Audio, group_name='DOLORES', title='ipsum doloremque eveniet odio', artist='repellat rem', album='', year='', isrc='', isci='', label='', client='', agency='', publisher='', composer='', conductor='', song_id='', user_defined='', description='', outcue='', filename='', length=':05', start_point='', end_point='', segue_start_point='', segue_end_point='', hook_start_point='', hook_end_point='', talk_start_point='', talk_end_point='', fadeup_point='', fadedown_point='', sched_codes=''),
        RivendellCart(cart_number='', cut_number='1', type=CartType.Audio, group_name='DOLORES', title='doloremque veniam ex a', artist='praesentium delectus', album='', year='', isrc='', isci='', label='', client='', agency='', publisher='', composer='', conductor='', song_id='', user_defined='', description='', outcue='', filename='', length=':04', start_point='', end_point='', segue_start_point='', segue_end_point='', hook_start_point='', hook_end_point='', talk_start_point='', talk_end_point='', fadeup_point='', fadedown_point='', sched_codes=''),
        RivendellCart(cart_number='', cut_number='1', type=CartType.Audio, group_name='DOLORES', title='quas accusantium reiciendis illo', artist='quibusdam delectus', album='', year='', isrc='', isci='', label='', client='', agency='', publisher='', composer='', conductor='', song_id='', user_defined='', description='', outcue='', filename='', length=':05', start_point='', end_point='', segue_start_point='', segue_end_point='', hook_start_point='', hook_end_point='', talk_start_point='', talk_end_point='', fadeup_point='', fadedown_point='', sched_codes=''),
        RivendellCart(cart_number='', cut_number='1', type=CartType.Audio, group_name='DOLORES', title='a fugit natus maiores', artist='nemo dolor', album='', year='', isrc='', isci='', label='', client='', agency='', publisher='', composer='', conductor='', song_id='', user_defined='', description='', outcue='', filename='', length=':12', start_point='', end_point='', segue_start_point='', segue_end_point='', hook_start_point='', hook_end_point='', talk_start_point='', talk_end_point='', fadeup_point='', fadedown_point='', sched_codes=''),
        RivendellCart(cart_number='', cut_number='1', type=CartType.Audio, group_name='DOLORES', title='eum nihil eaque autem', artist='reprehenderit sapiente', album='', year='', isrc='', isci='', label='', client='', agency='', publisher='', composer='', conductor='', song_id='', user_defined='', description='', outcue='', filename='', length=':09', start_point='', end_point='', segue_start_point='', segue_end_point='', hook_start_point='', hook_end_point='', talk_start_point='', talk_end_point='', fadeup_point='', fadedown_point='', sched_codes=''),
        RivendellCart(cart_number='', cut_number='1', type=CartType.Audio, group_name='DOLORES', title='a suscipit labore eos', artist='hic ut', album='', year='', isrc='', isci='', label='', client='', agency='', publisher='', composer='', conductor='', song_id='', user_defined='', description='', outcue='', filename='', length=':09', start_point='', end_point='', segue_start_point='', segue_end_point='', hook_start_point='', hook_end_point='', talk_start_point='', talk_end_point='', fadeup_point='', fadedown_point='', sched_codes=''),
        RivendellCart(cart_number='', cut_number='1', type=CartType.Audio, group_name='DOLORES', title='nostrum cupiditate eaque maiores', artist='consequuntur voluptatum', album='', year='', isrc='', isci='', label='', client='', agency='', publisher='', composer='', conductor='', song_id='', user_defined='', description='', outcue='', filename='', length=':05', start_point='', end_point='', segue_start_point='', segue_end_point='', hook_start_point='', hook_end_point='', talk_start_point='', talk_end_point='', fadeup_point='', fadedown_point='', sched_codes=''),
        RivendellCart(cart_number='', cut_number='1', type=CartType.Audio, group_name='DOLORES', title='voluptate quis animi rerum', artist='repudiandae libero', album='', year='', isrc='', isci='', label='', client='', agency='', publisher='', composer='', conductor='', song_id='', user_defined='', description='', outcue='', filename='', length=':17', start_point='', end_point='', segue_start_point='', segue_end_point='', hook_start_point='', hook_end_point='', talk_start_point='', talk_end_point='', fadeup_point='', fadedown_point='', sched_codes=''),
        RivendellCart(cart_number='', cut_number='1', type=CartType.Audio, group_name='DOLORES', title='quos perspiciatis quasi corporis', artist='ducimus fugiat', album='', year='', isrc='', isci='', label='', client='', agency='', publisher='', composer='', conductor='', song_id='', user_defined='', description='', outcue='', filename='', length=':09', start_point='', end_point='', segue_start_point='', segue_end_point='', hook_start_point='', hook_end_point='', talk_start_point='', talk_end_point='', fadeup_point='', fadedown_point='', sched_codes=''),
        RivendellCart(cart_number='', cut_number='1', type=CartType.Audio, group_name='DOLORES', title='doloremque sit eligendi vitae', artist='ut omnis', album='', year='', isrc='', isci='', label='', client='', agency='', publisher='', composer='', conductor='', song_id='', user_defined='', description='', outcue='', filename='', length=':05', start_point='', end_point='', segue_start_point='', segue_end_point='', hook_start_point='', hook_end_point='', talk_start_point='', talk_end_point='', fadeup_point='', fadedown_point='', sched_codes=''),
        RivendellCart(cart_number='', cut_number='1', type=CartType.Audio, group_name='DOLORES', title='ex quisquam laborum natus', artist='autem sequi', album='', year='', isrc='', isci='', label='', client='', agency='', publisher='', composer='', conductor='', song_id='', user_defined='', description='', outcue='', filename='', length=':06', start_point='', end_point='', segue_start_point='', segue_end_point='', hook_start_point='', hook_end_point='', talk_start_point='', talk_end_point='', fadeup_point='', fadedown_point='', sched_codes=''),
        RivendellCart(cart_number='', cut_number='1', type=CartType.Audio, group_name='DOLORES', title='mollitia eos id architecto', artist='adipisci voluptas', album='', year='', isrc='', isci='', label='', client='', agency='', publisher='', composer='', conductor='', song_id='', user_defined='', description='', outcue='', filename='', length=':10', start_point='', end_point='', segue_start_point='', segue_end_point='', hook_start_point='', hook_end_point='', talk_start_point='', talk_end_point='', fadeup_point='', fadedown_point='', sched_codes=''),
        RivendellCart(cart_number='', cut_number='1', type=CartType.Audio, group_name='DOLORES', title='beatae ipsa impedit illo', artist='repellat impedit', album='', year='', isrc='', isci='', label='', client='', agency='', publisher='', composer='', conductor='', song_id='', user_defined='', description='', outcue='', filename='', length=':10', start_point='', end_point='', segue_start_point='', segue_end_point='', hook_start_point='', hook_end_point='', talk_start_point='', talk_end_point='', fadeup_point='', fadedown_point='', sched_codes=''),
        RivendellCart(cart_number='', cut_number='1', type=CartType.Audio, group_name='DOLORES', title='illum hic nostrum quia', artist='culpa cum', album='', year='', isrc='', isci='', label='', client='', agency='', publisher='', composer='', conductor='', song_id='', user_defined='', description='', outcue='', filename='', length=':15', start_point='', end_point='', segue_start_point='', segue_end_point='', hook_start_point='', hook_end_point='', talk_start_point='', talk_end_point='', fadeup_point='', fadedown_point='', sched_codes=''),
        RivendellCart(cart_number='', cut_number='1', type=CartType.Audio, group_name='DOLORES', title='nemo accusamus aliquam pariatur', artist='architecto facilis', album='', year='', isrc='', isci='', label='', client='', agency='', publisher='', composer='', conductor='', song_id='', user_defined='', description='', outcue='', filename='', length=':09', start_point='', end_point='', segue_start_point='', segue_end_point='', hook_start_point='', hook_end_point='', talk_start_point='', talk_end_point='', fadeup_point='', fadedown_point='', sched_codes=''),
        RivendellCart(cart_number='', cut_number='1', type=CartType.Audio, group_name='DOLORES', title='dignissimos accusamus quod corrupti', artist='molestiae ipsa', album='', year='', isrc='', isci='', label='', client='', agency='', publisher='', composer='', conductor='', song_id='', user_defined='', description='', outcue='', filename='', length=':11', start_point='', end_point='', segue_start_point='', segue_end_point='', hook_start_point='', hook_end_point='', talk_start_point='', talk_end_point='', fadeup_point='', fadedown_point='', sched_codes=''),
        RivendellCart(cart_number='', cut_number='1', type=CartType.Audio, group_name='DOLORES', title='molestiae fugit veritatis suscipit', artist='numquam ullam', album='', year='', isrc='', isci='', label='', client='', agency='', publisher='', composer='', conductor='', song_id='', user_defined='', description='', outcue='', filename='', length=':09', start_point='', end_point='', segue_start_point='', segue_end_point='', hook_start_point='', hook_end_point='', talk_start_point='', talk_end_point='', fadeup_point='', fadedown_point='', sched_codes=''),
        RivendellCart(cart_number='', cut_number='1', type=CartType.Audio, group_name='DOLORES', title='consequatur officia nostrum cupiditate', artist='rem distinctio', album='', year='', isrc='', isci='', label='', client='', agency='', publisher='', composer='', conductor='', song_id='', user_defined='AMC 9/1/2013', description='', outcue='', filename='', length=':13', start_point='', end_point='', segue_start_point='', segue_end_point='', hook_start_point='', hook_end_point='', talk_start_point='', talk_end_point='', fadeup_point='', fadedown_point='', sched_codes=''),
        RivendellCart(cart_number='', cut_number='1', type=CartType.Audio, group_name='DOLORES', title='alias magnam deleniti sunt', artist='maxime facere', album='', year='', isrc='', isci='', label='', client='', agency='', publisher='', composer='', conductor='', song_id='', user_defined='AMC 8/20/2013', description='', outcue='', filename='', length=':08', start_point='', end_point='', segue_start_point='', segue_end_point='', hook_start_point='', hook_end_point='', talk_start_point='', talk_end_point='', fadeup_point='', fadedown_point='', sched_codes=''),
    ]

    times_of_this_group = np.array([this_item.length_in_seconds() for this_item in carts])

    expected_lower_outlier_limit = 0 
    expected_upper_outlier_limit = 86_399 # 23:59:59

    stats_limits = StatisticsLimits()

    outliers_excluded, lower_limit, upper_limit = _remove_outliers(
        times_of_this_group=times_of_this_group, 
        stats_limits=stats_limits
    )
    
    assert (outliers_excluded == times_of_this_group).all()
    assert lower_limit == expected_lower_outlier_limit
    assert upper_limit == expected_upper_outlier_limit


def test_nearest_15():
    test_cases = [
        (0, 0),
        (1, 0),
        (7, 0),
        (8, 15),
        (22, 15),
        (23, 30),
        (30, 30)
    ]

    for test_value, expected_result in test_cases:
        result = RivendellGroupStatistics._nearest_15(test_value)
        assert result == expected_result


def test_main_calculations_small_stdev():
    carts = [
        RivendellCart(cart_number='', cut_number='1', type=CartType.Audio, group_name='DOLORES', title='magni voluptatum saepe architecto', artist='recusandae temporibus', album='', year='', isrc='', isci='', label='', client='', agency='', publisher='', composer='', conductor='', song_id='', user_defined='', description='', outcue='', filename='', length=':05', start_point='', end_point='', segue_start_point='', segue_end_point='', hook_start_point='', hook_end_point='', talk_start_point='', talk_end_point='', fadeup_point='', fadedown_point='', sched_codes=''),
        RivendellCart(cart_number='', cut_number='1', type=CartType.Audio, group_name='DOLORES', title='ipsum doloremque eveniet odio', artist='repellat rem', album='', year='', isrc='', isci='', label='', client='', agency='', publisher='', composer='', conductor='', song_id='', user_defined='', description='', outcue='', filename='', length=':05', start_point='', end_point='', segue_start_point='', segue_end_point='', hook_start_point='', hook_end_point='', talk_start_point='', talk_end_point='', fadeup_point='', fadedown_point='', sched_codes=''),
        RivendellCart(cart_number='', cut_number='1', type=CartType.Audio, group_name='DOLORES', title='doloremque veniam ex a', artist='praesentium delectus', album='', year='', isrc='', isci='', label='', client='', agency='', publisher='', composer='', conductor='', song_id='', user_defined='', description='', outcue='', filename='', length=':04', start_point='', end_point='', segue_start_point='', segue_end_point='', hook_start_point='', hook_end_point='', talk_start_point='', talk_end_point='', fadeup_point='', fadedown_point='', sched_codes=''),
        RivendellCart(cart_number='', cut_number='1', type=CartType.Audio, group_name='DOLORES', title='quas accusantium reiciendis illo', artist='quibusdam delectus', album='', year='', isrc='', isci='', label='', client='', agency='', publisher='', composer='', conductor='', song_id='', user_defined='', description='', outcue='', filename='', length=':05', start_point='', end_point='', segue_start_point='', segue_end_point='', hook_start_point='', hook_end_point='', talk_start_point='', talk_end_point='', fadeup_point='', fadedown_point='', sched_codes=''),
        RivendellCart(cart_number='', cut_number='1', type=CartType.Audio, group_name='DOLORES', title='a fugit natus maiores', artist='nemo dolor', album='', year='', isrc='', isci='', label='', client='', agency='', publisher='', composer='', conductor='', song_id='', user_defined='', description='', outcue='', filename='', length=':12', start_point='', end_point='', segue_start_point='', segue_end_point='', hook_start_point='', hook_end_point='', talk_start_point='', talk_end_point='', fadeup_point='', fadedown_point='', sched_codes=''),
        RivendellCart(cart_number='', cut_number='1', type=CartType.Audio, group_name='DOLORES', title='eum nihil eaque autem', artist='reprehenderit sapiente', album='', year='', isrc='', isci='', label='', client='', agency='', publisher='', composer='', conductor='', song_id='', user_defined='', description='', outcue='', filename='', length=':09', start_point='', end_point='', segue_start_point='', segue_end_point='', hook_start_point='', hook_end_point='', talk_start_point='', talk_end_point='', fadeup_point='', fadedown_point='', sched_codes=''),
        RivendellCart(cart_number='', cut_number='1', type=CartType.Audio, group_name='DOLORES', title='a suscipit labore eos', artist='hic ut', album='', year='', isrc='', isci='', label='', client='', agency='', publisher='', composer='', conductor='', song_id='', user_defined='', description='', outcue='', filename='', length=':09', start_point='', end_point='', segue_start_point='', segue_end_point='', hook_start_point='', hook_end_point='', talk_start_point='', talk_end_point='', fadeup_point='', fadedown_point='', sched_codes=''),
        RivendellCart(cart_number='', cut_number='1', type=CartType.Audio, group_name='DOLORES', title='nostrum cupiditate eaque maiores', artist='consequuntur voluptatum', album='', year='', isrc='', isci='', label='', client='', agency='', publisher='', composer='', conductor='', song_id='', user_defined='', description='', outcue='', filename='', length=':05', start_point='', end_point='', segue_start_point='', segue_end_point='', hook_start_point='', hook_end_point='', talk_start_point='', talk_end_point='', fadeup_point='', fadedown_point='', sched_codes=''),
        RivendellCart(cart_number='', cut_number='1', type=CartType.Audio, group_name='DOLORES', title='voluptate quis animi rerum', artist='repudiandae libero', album='', year='', isrc='', isci='', label='', client='', agency='', publisher='', composer='', conductor='', song_id='', user_defined='', description='', outcue='', filename='', length=':17', start_point='', end_point='', segue_start_point='', segue_end_point='', hook_start_point='', hook_end_point='', talk_start_point='', talk_end_point='', fadeup_point='', fadedown_point='', sched_codes=''),
        RivendellCart(cart_number='', cut_number='1', type=CartType.Audio, group_name='DOLORES', title='quos perspiciatis quasi corporis', artist='ducimus fugiat', album='', year='', isrc='', isci='', label='', client='', agency='', publisher='', composer='', conductor='', song_id='', user_defined='', description='', outcue='', filename='', length=':09', start_point='', end_point='', segue_start_point='', segue_end_point='', hook_start_point='', hook_end_point='', talk_start_point='', talk_end_point='', fadeup_point='', fadedown_point='', sched_codes=''),
        RivendellCart(cart_number='', cut_number='1', type=CartType.Audio, group_name='DOLORES', title='doloremque sit eligendi vitae', artist='ut omnis', album='', year='', isrc='', isci='', label='', client='', agency='', publisher='', composer='', conductor='', song_id='', user_defined='', description='', outcue='', filename='', length=':05', start_point='', end_point='', segue_start_point='', segue_end_point='', hook_start_point='', hook_end_point='', talk_start_point='', talk_end_point='', fadeup_point='', fadedown_point='', sched_codes=''),
        RivendellCart(cart_number='', cut_number='1', type=CartType.Audio, group_name='DOLORES', title='ex quisquam laborum natus', artist='autem sequi', album='', year='', isrc='', isci='', label='', client='', agency='', publisher='', composer='', conductor='', song_id='', user_defined='', description='', outcue='', filename='', length=':06', start_point='', end_point='', segue_start_point='', segue_end_point='', hook_start_point='', hook_end_point='', talk_start_point='', talk_end_point='', fadeup_point='', fadedown_point='', sched_codes=''),
        RivendellCart(cart_number='', cut_number='1', type=CartType.Audio, group_name='DOLORES', title='mollitia eos id architecto', artist='adipisci voluptas', album='', year='', isrc='', isci='', label='', client='', agency='', publisher='', composer='', conductor='', song_id='', user_defined='', description='', outcue='', filename='', length=':10', start_point='', end_point='', segue_start_point='', segue_end_point='', hook_start_point='', hook_end_point='', talk_start_point='', talk_end_point='', fadeup_point='', fadedown_point='', sched_codes=''),
        RivendellCart(cart_number='', cut_number='1', type=CartType.Audio, group_name='DOLORES', title='beatae ipsa impedit illo', artist='repellat impedit', album='', year='', isrc='', isci='', label='', client='', agency='', publisher='', composer='', conductor='', song_id='', user_defined='', description='', outcue='', filename='', length=':10', start_point='', end_point='', segue_start_point='', segue_end_point='', hook_start_point='', hook_end_point='', talk_start_point='', talk_end_point='', fadeup_point='', fadedown_point='', sched_codes=''),
        RivendellCart(cart_number='', cut_number='1', type=CartType.Audio, group_name='DOLORES', title='illum hic nostrum quia', artist='culpa cum', album='', year='', isrc='', isci='', label='', client='', agency='', publisher='', composer='', conductor='', song_id='', user_defined='', description='', outcue='', filename='', length=':15', start_point='', end_point='', segue_start_point='', segue_end_point='', hook_start_point='', hook_end_point='', talk_start_point='', talk_end_point='', fadeup_point='', fadedown_point='', sched_codes=''),
        RivendellCart(cart_number='', cut_number='1', type=CartType.Audio, group_name='DOLORES', title='nemo accusamus aliquam pariatur', artist='architecto facilis', album='', year='', isrc='', isci='', label='', client='', agency='', publisher='', composer='', conductor='', song_id='', user_defined='', description='', outcue='', filename='', length=':09', start_point='', end_point='', segue_start_point='', segue_end_point='', hook_start_point='', hook_end_point='', talk_start_point='', talk_end_point='', fadeup_point='', fadedown_point='', sched_codes=''),
        RivendellCart(cart_number='', cut_number='1', type=CartType.Audio, group_name='DOLORES', title='dignissimos accusamus quod corrupti', artist='molestiae ipsa', album='', year='', isrc='', isci='', label='', client='', agency='', publisher='', composer='', conductor='', song_id='', user_defined='', description='', outcue='', filename='', length=':11', start_point='', end_point='', segue_start_point='', segue_end_point='', hook_start_point='', hook_end_point='', talk_start_point='', talk_end_point='', fadeup_point='', fadedown_point='', sched_codes=''),
        RivendellCart(cart_number='', cut_number='1', type=CartType.Audio, group_name='DOLORES', title='molestiae fugit veritatis suscipit', artist='numquam ullam', album='', year='', isrc='', isci='', label='', client='', agency='', publisher='', composer='', conductor='', song_id='', user_defined='', description='', outcue='', filename='', length=':09', start_point='', end_point='', segue_start_point='', segue_end_point='', hook_start_point='', hook_end_point='', talk_start_point='', talk_end_point='', fadeup_point='', fadedown_point='', sched_codes=''),
        RivendellCart(cart_number='', cut_number='1', type=CartType.Audio, group_name='DOLORES', title='consequatur officia nostrum cupiditate', artist='rem distinctio', album='', year='', isrc='', isci='', label='', client='', agency='', publisher='', composer='', conductor='', song_id='', user_defined='AMC 9/1/2013', description='', outcue='', filename='', length=':13', start_point='', end_point='', segue_start_point='', segue_end_point='', hook_start_point='', hook_end_point='', talk_start_point='', talk_end_point='', fadeup_point='', fadedown_point='', sched_codes=''),
        RivendellCart(cart_number='', cut_number='1', type=CartType.Audio, group_name='DOLORES', title='alias magnam deleniti sunt', artist='maxime facere', album='', year='', isrc='', isci='', label='', client='', agency='', publisher='', composer='', conductor='', song_id='', user_defined='AMC 8/20/2013', description='', outcue='', filename='', length=':08', start_point='', end_point='', segue_start_point='', segue_end_point='', hook_start_point='', hook_end_point='', talk_start_point='', talk_end_point='', fadeup_point='', fadedown_point='', sched_codes=''),
     ]

    stats_limits = StatisticsLimits()

    group_stats = RivendellGroupStatistics("DOLORES", carts, stats_limits)

    assert group_stats.lower_bound == 0
    assert group_stats.number_of_songs_shorter_than_lower_bound == 0
    assert group_stats.upper_bound == 86_399
    assert group_stats.number_of_songs_longer_than_upper_bound == 0
    assert group_stats.percentage_of_songs_excluded == 0


to_pandas_params, to_pandas_ids = \
    generate_true_false_matrix_from_list_of_strings(
        "to_pandas",
        [
            "write_full_statistics"
        ]

    )

@pytest.mark.parametrize("write_full_statistics", to_pandas_params, ids=to_pandas_ids)
def test_to_pandas_correct(write_full_statistics):
    carts = [
        RivendellCart(cart_number='', cut_number='1', type=CartType.Audio, group_name='EXPLICABO', title='perferendis enim harum repudiandae', artist='nisi ipsum', album='', year='', isrc='', isci='', label='', client='', agency='', publisher='', composer='', conductor='', song_id='', user_defined='', description='', outcue='', filename='', length='4:22', start_point='', end_point='', segue_start_point='', segue_end_point='', hook_start_point='', hook_end_point='', talk_start_point='', talk_end_point='', fadeup_point='', fadedown_point='', sched_codes=''),
        RivendellCart(cart_number='', cut_number='1', type=CartType.Audio, group_name='EXPLICABO', title='sapiente praesentium voluptates expedita', artist='tenetur assumenda', album='', year='', isrc='', isci='', label='', client='', agency='', publisher='', composer='', conductor='', song_id='', user_defined='', description='', outcue='', filename='', length='2:28', start_point='', end_point='', segue_start_point='', segue_end_point='', hook_start_point='', hook_end_point='', talk_start_point='', talk_end_point='', fadeup_point='', fadedown_point='', sched_codes=''),
        RivendellCart(cart_number='', cut_number='1', type=CartType.Audio, group_name='EXPLICABO', title='perferendis laudantium dolorum vel', artist='veritatis debitis', album='', year='', isrc='', isci='', label='', client='', agency='', publisher='', composer='', conductor='', song_id='', user_defined='', description='', outcue='', filename='', length='4:43', start_point='', end_point='', segue_start_point='', segue_end_point='', hook_start_point='', hook_end_point='', talk_start_point='', talk_end_point='', fadeup_point='', fadedown_point='', sched_codes=''),
        RivendellCart(cart_number='', cut_number='1', type=CartType.Audio, group_name='EXPLICABO', title='minus esse tempore occaecati', artist='assumenda odio', album='', year='', isrc='', isci='', label='', client='', agency='', publisher='', composer='', conductor='', song_id='', user_defined='', description='', outcue='', filename='', length='4:35', start_point='', end_point='', segue_start_point='', segue_end_point='', hook_start_point='', hook_end_point='', talk_start_point='', talk_end_point='', fadeup_point='', fadedown_point='', sched_codes=''),
        RivendellCart(cart_number='', cut_number='1', type=CartType.Audio, group_name='EXPLICABO', title='placeat at pariatur mollitia', artist='possimus ut', album='', year='', isrc='', isci='', label='', client='', agency='', publisher='', composer='', conductor='', song_id='', user_defined='', description='', outcue='', filename='', length='3:59', start_point='', end_point='', segue_start_point='', segue_end_point='', hook_start_point='', hook_end_point='', talk_start_point='', talk_end_point='', fadeup_point='', fadedown_point='', sched_codes=''),
        
        RivendellCart(cart_number='', cut_number='1', type=CartType.Audio, group_name='EXPLICABO', title='omnis rem dolore eius', artist='accusantium officia', album='', year='', isrc='', isci='', label='', client='', agency='', publisher='', composer='', conductor='', song_id='', user_defined='', description='', outcue='', filename='', length='4:13', start_point='', end_point='', segue_start_point='', segue_end_point='', hook_start_point='', hook_end_point='', talk_start_point='', talk_end_point='', fadeup_point='', fadedown_point='', sched_codes=''),
        RivendellCart(cart_number='', cut_number='1', type=CartType.Audio, group_name='EXPLICABO', title='doloremque nostrum fugiat nisi', artist='voluptatum quibusdam', album='', year='', isrc='', isci='', label='', client='', agency='', publisher='', composer='', conductor='', song_id='', user_defined='', description='', outcue='', filename='', length='4:47', start_point='', end_point='', segue_start_point='', segue_end_point='', hook_start_point='', hook_end_point='', talk_start_point='', talk_end_point='', fadeup_point='', fadedown_point='', sched_codes=''),
        RivendellCart(cart_number='', cut_number='1', type=CartType.Audio, group_name='EXPLICABO', title='dolor dignissimos minima ipsa', artist='vero rem', album='', year='', isrc='', isci='', label='', client='', agency='', publisher='', composer='', conductor='', song_id='', user_defined='', description='', outcue='', filename='', length='5:53', start_point='', end_point='', segue_start_point='', segue_end_point='', hook_start_point='', hook_end_point='', talk_start_point='', talk_end_point='', fadeup_point='', fadedown_point='', sched_codes=''),
        RivendellCart(cart_number='', cut_number='1', type=CartType.Audio, group_name='EXPLICABO', title='odio dolor sint ex', artist='minus aut', album='', year='', isrc='', isci='', label='', client='', agency='', publisher='', composer='', conductor='', song_id='', user_defined='', description='', outcue='', filename='', length='1:59', start_point='', end_point='', segue_start_point='', segue_end_point='', hook_start_point='', hook_end_point='', talk_start_point='', talk_end_point='', fadeup_point='', fadedown_point='', sched_codes=''),
        RivendellCart(cart_number='', cut_number='1', type=CartType.Audio, group_name='EXPLICABO', title='ipsum repellendus aspernatur qui', artist='voluptatibus qui', album='', year='', isrc='', isci='', label='', client='', agency='', publisher='', composer='', conductor='', song_id='', user_defined='', description='', outcue='', filename='', length='3:09', start_point='', end_point='', segue_start_point='', segue_end_point='', hook_start_point='', hook_end_point='', talk_start_point='', talk_end_point='', fadeup_point='', fadedown_point='', sched_codes=''),
        
        RivendellCart(cart_number='', cut_number='1', type=CartType.Audio, group_name='EXPLICABO', title='provident quod repellendus architecto', artist='ad amet', album='', year='', isrc='', isci='', label='', client='', agency='', publisher='', composer='', conductor='', song_id='', user_defined='', description='', outcue='', filename='', length='3:45', start_point='', end_point='', segue_start_point='', segue_end_point='', hook_start_point='', hook_end_point='', talk_start_point='', talk_end_point='', fadeup_point='', fadedown_point='', sched_codes=''),
        RivendellCart(cart_number='', cut_number='1', type=CartType.Audio, group_name='EXPLICABO', title='nobis totam architecto molestias', artist='soluta quasi', album='', year='', isrc='', isci='', label='', client='', agency='', publisher='', composer='', conductor='', song_id='', user_defined='', description='', outcue='', filename='', length='3:49', start_point='', end_point='', segue_start_point='', segue_end_point='', hook_start_point='', hook_end_point='', talk_start_point='', talk_end_point='', fadeup_point='', fadedown_point='', sched_codes=''),
        RivendellCart(cart_number='', cut_number='1', type=CartType.Audio, group_name='EXPLICABO', title='ducimus cum dolore accusantium', artist='dolores iusto', album='', year='', isrc='', isci='', label='', client='', agency='', publisher='', composer='', conductor='', song_id='', user_defined='', description='', outcue='', filename='', length='4:13', start_point='', end_point='', segue_start_point='', segue_end_point='', hook_start_point='', hook_end_point='', talk_start_point='', talk_end_point='', fadeup_point='', fadedown_point='', sched_codes=''),
        RivendellCart(cart_number='', cut_number='1', type=CartType.Audio, group_name='EXPLICABO', title='iusto dolores ab dolore', artist='beatae excepturi', album='', year='', isrc='', isci='', label='', client='', agency='', publisher='', composer='', conductor='', song_id='', user_defined='', description='', outcue='', filename='', length='3:27', start_point='', end_point='', segue_start_point='', segue_end_point='', hook_start_point='', hook_end_point='', talk_start_point='', talk_end_point='', fadeup_point='', fadedown_point='', sched_codes=''),
        RivendellCart(cart_number='', cut_number='1', type=CartType.Audio, group_name='EXPLICABO', title='est eum molestiae eveniet', artist='veniam modi', album='', year='', isrc='', isci='', label='', client='', agency='', publisher='', composer='', conductor='', song_id='', user_defined='', description='', outcue='', filename='', length='5:08', start_point='', end_point='', segue_start_point='', segue_end_point='', hook_start_point='', hook_end_point='', talk_start_point='', talk_end_point='', fadeup_point='', fadedown_point='', sched_codes=''),
        
        RivendellCart(cart_number='', cut_number='1', type=CartType.Audio, group_name='EXPLICABO', title='dolores ducimus odit doloremque', artist='ab possimus', album='', year='', isrc='', isci='', label='', client='', agency='', publisher='', composer='', conductor='', song_id='', user_defined='', description='', outcue='', filename='', length='4:40', start_point='', end_point='', segue_start_point='', segue_end_point='', hook_start_point='', hook_end_point='', talk_start_point='', talk_end_point='', fadeup_point='', fadedown_point='', sched_codes=''),
        RivendellCart(cart_number='', cut_number='1', type=CartType.Audio, group_name='EXPLICABO', title='enim veniam a aspernatur', artist='nulla at', album='', year='', isrc='', isci='', label='', client='', agency='', publisher='', composer='', conductor='', song_id='', user_defined='', description='', outcue='', filename='', length='2:39', start_point='', end_point='', segue_start_point='', segue_end_point='', hook_start_point='', hook_end_point='', talk_start_point='', talk_end_point='', fadeup_point='', fadedown_point='', sched_codes=''),
        RivendellCart(cart_number='', cut_number='1', type=CartType.Audio, group_name='EXPLICABO', title='eligendi commodi ex corrupti', artist='rem nobis', album='', year='', isrc='', isci='', label='', client='', agency='', publisher='', composer='', conductor='', song_id='', user_defined='', description='', outcue='', filename='', length='3:31', start_point='', end_point='', segue_start_point='', segue_end_point='', hook_start_point='', hook_end_point='', talk_start_point='', talk_end_point='', fadeup_point='', fadedown_point='', sched_codes=''),
        RivendellCart(cart_number='', cut_number='1', type=CartType.Audio, group_name='EXPLICABO', title='ex beatae facilis alias', artist='error eligendi', album='', year='', isrc='', isci='', label='', client='', agency='', publisher='', composer='', conductor='', song_id='', user_defined='', description='', outcue='', filename='', length='4:37', start_point='', end_point='', segue_start_point='', segue_end_point='', hook_start_point='', hook_end_point='', talk_start_point='', talk_end_point='', fadeup_point='', fadedown_point='', sched_codes=''),
        RivendellCart(cart_number='', cut_number='1', type=CartType.Audio, group_name='EXPLICABO', title='sunt veniam ab dicta', artist='minus sed', album='', year='', isrc='', isci='', label='', client='', agency='', publisher='', composer='', conductor='', song_id='', user_defined='', description='', outcue='', filename='', length='3:02', start_point='', end_point='', segue_start_point='', segue_end_point='', hook_start_point='', hook_end_point='', talk_start_point='', talk_end_point='', fadeup_point='', fadedown_point='', sched_codes=''),
        
        RivendellCart(cart_number='', cut_number='1', type=CartType.Audio, group_name='EXPLICABO', title='cumque excepturi eum nostrum', artist='amet aperiam', album='', year='', isrc='', isci='', label='', client='', agency='', publisher='', composer='', conductor='', song_id='', user_defined='', description='', outcue='', filename='', length='10:53', start_point='', end_point='', segue_start_point='', segue_end_point='', hook_start_point='', hook_end_point='', talk_start_point='', talk_end_point='', fadeup_point='', fadedown_point='', sched_codes=''),
        RivendellCart(cart_number='', cut_number='1', type=CartType.Audio, group_name='EXPLICABO', title='maiores tenetur dolor sequi', artist='sequi quae', album='', year='', isrc='', isci='', label='', client='', agency='', publisher='', composer='', conductor='', song_id='', user_defined='', description='', outcue='', filename='', length='3:17', start_point='', end_point='', segue_start_point='', segue_end_point='', hook_start_point='', hook_end_point='', talk_start_point='', talk_end_point='', fadeup_point='', fadedown_point='', sched_codes=''),
        RivendellCart(cart_number='', cut_number='1', type=CartType.Audio, group_name='EXPLICABO', title='aperiam consequatur magnam numquam', artist='ut pariatur', album='', year='', isrc='', isci='', label='', client='', agency='', publisher='', composer='', conductor='', song_id='', user_defined='', description='', outcue='', filename='', length='0:14', start_point='', end_point='', segue_start_point='', segue_end_point='', hook_start_point='', hook_end_point='', talk_start_point='', talk_end_point='', fadeup_point='', fadedown_point='', sched_codes=''),
        RivendellCart(cart_number='', cut_number='1', type=CartType.Audio, group_name='EXPLICABO', title='similique doloribus molestias adipisci', artist='commodi vitae', album='', year='', isrc='', isci='', label='', client='', agency='', publisher='', composer='', conductor='', song_id='', user_defined='', description='', outcue='', filename='', length='2:32', start_point='', end_point='', segue_start_point='', segue_end_point='', hook_start_point='', hook_end_point='', talk_start_point='', talk_end_point='', fadeup_point='', fadedown_point='', sched_codes=''),
        RivendellCart(cart_number='', cut_number='1', type=CartType.Audio, group_name='EXPLICABO', title='dolorum pariatur esse quia', artist='recusandae mollitia', album='', year='', isrc='', isci='', label='', client='', agency='', publisher='', composer='', conductor='', song_id='', user_defined='', description='', outcue='', filename='', length='3:04', start_point='', end_point='', segue_start_point='', segue_end_point='', hook_start_point='', hook_end_point='', talk_start_point='', talk_end_point='', fadeup_point='', fadedown_point='', sched_codes='')
    ]

    stats_limits = StatisticsLimits()

    group_name = "EXPLICABO"
    group_stats = RivendellGroupStatistics(group_name=group_name, songs_in_group=carts, stats_limits=stats_limits)

    import pandas as pd

    if write_full_statistics:
        expected_pandas_series = pd.Series(
            {
                "Number of Songs": 25,
                "Shortest Song Length": "0:00:14",
                "Longest Song Length": "0:10:53",
                "Outlier Limits": ("0:00:44", "0:06:56"),
                "Mean": "0:03:49",
                "Standard Deviation": "0:00:57",
                "Lower Bound": "0:02:30",
                "Number of Songs < Lower Bound": 3,
                "Upper Bound": "0:06:45",
                "Number of Songs > Upper Bound": 1,
                "Percent of Songs Excluded": 16.0
            }
        )
    else:
        expected_pandas_series = pd.Series(
            {
                "Number of Songs": 25,
                "Number of Songs < Lower Bound": 3,
                "Number of Songs > Upper Bound": 1
            }
        )

    result_series = group_stats.to_pandas_series(write_full_statistics)

    assert (result_series == expected_pandas_series).all()
