コード例 #1
0
def test_set_defaults():
    fake_grid_options_a = {"foo": "bar"}
    set_defaults(show_toolbar=False,
                 precision=4,
                 grid_options=fake_grid_options_a)

    def assert_widget_vals_a(widget):
        assert not widget.show_toolbar
        assert widget.precision == 4
        assert widget.grid_options == fake_grid_options_a

    df = create_df()
    view = show_grid(df)
    assert_widget_vals_a(view)

    view = SpreadsheetWidget(df=df)
    assert_widget_vals_a(view)

    fake_grid_options_b = {"foo": "buzz"}
    set_defaults(show_toolbar=True,
                 precision=2,
                 grid_options=fake_grid_options_b)

    def assert_widget_vals_b(widget):
        assert widget.show_toolbar
        assert widget.precision == 2
        assert widget.grid_options == fake_grid_options_b

    df = create_df()
    view = show_grid(df)
    assert_widget_vals_b(view)

    view = SpreadsheetWidget(df=df)
    assert_widget_vals_b(view)
コード例 #2
0
def test_add_row_button():
    widget = SpreadsheetWidget(df=create_df())
    event_history = init_event_history("row_added", widget=widget)

    widget._handle_view_msg_helper({"type": "add_row"})

    assert event_history == [{
        "name": "row_added",
        "index": 4,
        "source": "gui"
    }]

    # make sure the added row in the internal dataframe contains the
    # expected values
    added_index = event_history[0]["index"]
    expected_values = pd.Series({
        "modin_spreadsheet_unfiltered_index": 4,
        "A": 1,
        "C": 1,
        "D": 3,
        "Date": pd.Timestamp("2013-01-02 00:00:00"),
        "E": "bar",
        "F": "fox",
    })
    sort_idx = widget._df.loc[added_index].index
    assert (widget._df.loc[added_index] == expected_values[sort_idx]).all()
コード例 #3
0
def test_remove_row():
    event_history = init_event_history(All)
    df = create_df()

    widget = SpreadsheetWidget(df=df)
    widget.remove_row(rows=[2])

    assert 2 not in widget._df.index
    assert len(widget._df) == 3

    assert event_history == [
        {
            "name": "instance_created"
        },
        {
            "name": "json_updated",
            "range": (0, 100),
            "triggered_by": "remove_row",
        },
        {
            "name": "row_removed",
            "indices": [2],
            "source": "api"
        },
    ]
コード例 #4
0
def test_edit_cell():
    df = pd.DataFrame({
        "foo": ["hello"],
        "bar": ["world"],
        "baz": [42],
        "boo": [57]
    })
    df.set_index("baz", inplace=True, drop=True)

    q = SpreadsheetWidget(df=df)
    event_history = init_event_history(All)

    q.edit_cell(42, "foo", "hola")

    assert q._df.loc[42, "foo"] == "hola"

    assert event_history == [
        {
            "name": "json_updated",
            "range": (0, 100),
            "triggered_by": "edit_cell",
        },
        {
            "name": "cell_edited",
            "index": 42,
            "column": "foo",
            "old": "hello",
            "new": "hola",
            "source": "api",
        },
    ]
コード例 #5
0
def test_get_selected_df():
    sample_df = create_df()
    selected_rows = [1, 3]
    view = SpreadsheetWidget(df=sample_df)
    view._handle_view_msg_helper({
        "rows": selected_rows,
        "type": "change_selection"
    })
    selected_df = view.get_selected_df()
    assert len(selected_df) == 2
    assert sample_df.iloc[selected_rows].equals(selected_df)
コード例 #6
0
def test_date_index():
    df = create_df()
    df.set_index("Date", inplace=True)
    view = SpreadsheetWidget(df=df)
    view._handle_view_msg_helper({
        "type": "change_filter",
        "field": "A",
        "filter_info": {
            "field": "A",
            "type": "slider",
            "min": 2,
            "max": 3,
        },
    })
コード例 #7
0
def test_integer_index_filter():
    view = SpreadsheetWidget(df=create_df())
    view._handle_view_msg_helper({
        "field": "index",
        "filter_info": {
            "field": "index",
            "max": None,
            "min": 2,
            "type": "slider",
        },
        "type": "change_filter",
    })
    filtered_df = view.get_changed_df()
    assert len(filtered_df) == 2
コード例 #8
0
def test_mixed_type_sort():
    df = pd.DataFrame({
        "Mixed": [pd.Timestamp("2017-02-02"), np.nan, 1e10, 5, "hey"],
        1: [5, 4, "hello", 2, 1],
    })

    spreadsheet = SpreadsheetWidget(df=df)

    spreadsheet._handle_view_msg_helper({
        "type": "change_sort",
        "sort_field": "Mixed",
        "sort_ascending": True
    })

    expected_order_mixed = [1e10, pd.Timestamp("2017-02-02"), 5, "hey", np.nan]
    sorted_order_mixed = list(spreadsheet.get_changed_df()["Mixed"])
    assert expected_order_mixed[:4] == sorted_order_mixed[:4]
    # np.nan != np.nan by definition, so check if value at index 4 are both np.nan
    assert np.isnan(expected_order_mixed[4]) and np.isnan(
        sorted_order_mixed[4])

    # check sorting on number column names works
    spreadsheet._handle_view_msg_helper({
        "type": "change_sort",
        "sort_field": 1,
        "sort_ascending": True
    })
    expected_order_1 = [1, 2, 4, 5, "hello"]
    sorted_order_1 = list(spreadsheet.get_changed_df()[1])
    assert expected_order_1 == sorted_order_1
コード例 #9
0
def test_edit_number():
    old_val = 3
    view = SpreadsheetWidget(df=create_df())

    for idx in range(-10, 10, 1):
        check_edit_success(view, "D", 2, old_val, old_val, idx, idx)
        old_val = idx
コード例 #10
0
def test_row_edit_callback():
    sample_df = create_df()

    def can_edit_row(row):
        return row["E"] == "train" and row["F"] == "bar"

    view = SpreadsheetWidget(df=sample_df, row_edit_callback=can_edit_row)

    view._handle_view_msg_helper({
        "type": "change_sort",
        "sort_field": "index",
        "sort_ascending": True
    })

    expected_dict = {0: False, 1: True, 2: False, 3: False}

    assert expected_dict == view._editable_rows
コード例 #11
0
def test_change_selection():
    widget = SpreadsheetWidget(df=create_large_df(size=10))
    event_history = init_event_history("selection_changed", widget=widget)

    widget._handle_view_msg_helper({"type": "change_selection", "rows": [5]})
    assert widget._selected_rows == [5]

    widget._handle_view_msg_helper({
        "type": "change_selection",
        "rows": [7, 8]
    })
    assert widget._selected_rows == [7, 8]

    widget.change_selection([3, 5, 6])
    assert widget._selected_rows == [3, 5, 6]

    widget.change_selection()
    assert widget._selected_rows == []

    assert event_history == [
        {
            "name": "selection_changed",
            "old": [],
            "new": [5],
            "source": "gui"
        },
        {
            "name": "selection_changed",
            "old": [5],
            "new": [7, 8],
            "source": "gui",
        },
        {
            "name": "selection_changed",
            "old": [7, 8],
            "new": [3, 5, 6],
            "source": "api",
        },
        {
            "name": "selection_changed",
            "old": [3, 5, 6],
            "new": [],
            "source": "api",
        },
    ]
コード例 #12
0
def test_edit_date():
    view = SpreadsheetWidget(df=create_df())
    check_edit_success(
        view,
        "Date",
        3,
        pd.Timestamp("2013-01-02 00:00:00"),
        "2013-01-02T00:00:00.000Z",
        pd.Timestamp("2013-01-16 00:00:00"),
        "2013-01-16T00:00:00.000Z",
    )
コード例 #13
0
def test_index_categorical():
    df = pd.DataFrame({
        "foo": np.random.randn(3),
        "future_index": [22, 13, 87]
    })
    df["future_index"] = df["future_index"].astype("category")
    df = df.set_index("future_index")
    widget = SpreadsheetWidget(df=df)
    grid_data = json.loads(widget._df_json)["data"]

    assert not isinstance(grid_data[0]["future_index"], dict)
    assert not isinstance(grid_data[1]["future_index"], dict)
コード例 #14
0
def test_remove_row_button():
    widget = SpreadsheetWidget(df=create_df())
    event_history = init_event_history(["row_removed", "selection_changed"],
                                       widget=widget)

    selected_rows = [1, 2]
    widget._handle_view_msg_helper({
        "rows": selected_rows,
        "type": "change_selection"
    })

    widget._handle_view_msg_helper({"type": "remove_row"})

    assert event_history == [
        {
            "name": "selection_changed",
            "old": [],
            "new": selected_rows,
            "source": "gui",
        },
        {
            "name": "row_removed",
            "indices": selected_rows,
            "source": "gui"
        },
    ]
コード例 #15
0
def test_object_dtype():
    df = pd.DataFrame({"a": my_object_vals}, index=my_object_vals)
    widget = SpreadsheetWidget(df=df)
    grid_data = json.loads(widget._df_json)["data"]

    widget._handle_view_msg_helper({
        "type": "show_filter_dropdown",
        "field": "a",
        "search_val": None
    })
    widget._handle_view_msg_helper({
        "field": "a",
        "filter_info": {
            "field": "a",
            "selected": [0, 1],
            "type": "text",
            "excluded": [],
        },
        "type": "change_filter",
    })

    filter_table = widget._filter_tables["a"]
    assert not isinstance(filter_table[0], dict)
    assert not isinstance(filter_table[1], dict)

    assert not isinstance(grid_data[0]["a"], dict)
    assert not isinstance(grid_data[1]["a"], dict)

    assert not isinstance(grid_data[0]["index"], dict)
    assert not isinstance(grid_data[1]["index"], dict)
コード例 #16
0
def test_change_viewport():
    widget = SpreadsheetWidget(df=create_large_df())
    event_history = init_event_history(All)

    widget._handle_view_msg_helper({
        "type": "change_viewport",
        "top": 7124,
        "bottom": 7136
    })

    assert event_history == [
        {
            "name": "json_updated",
            "triggered_by": "change_viewport",
            "range": (7024, 7224),
        },
        {
            "name": "viewport_changed",
            "old": (0, 100),
            "new": (7124, 7136)
        },
    ]
コード例 #17
0
def test_add_row():
    event_history = init_event_history(All)
    df = pd.DataFrame({
        "foo": ["hello"],
        "bar": ["world"],
        "baz": [42],
        "boo": [57]
    })
    df.set_index("baz", inplace=True, drop=True)

    q = SpreadsheetWidget(df=df)

    new_row = [
        ("baz", 43),
        ("bar", "new bar"),
        ("boo", 58),
        ("foo", "new foo"),
    ]

    q.add_row(new_row)

    assert q._df.loc[43, "foo"] == "new foo"
    assert q._df.loc[42, "foo"] == "hello"

    assert event_history == [
        {
            "name": "instance_created"
        },
        {
            "name": "json_updated",
            "range": (0, 100),
            "triggered_by": "add_row"
        },
        {
            "name": "row_added",
            "index": 43,
            "source": "api"
        },
    ]
コード例 #18
0
def test_change_filter_viewport():
    widget = SpreadsheetWidget(df=create_large_df())
    event_history = init_event_history(All)

    widget._handle_view_msg_helper({
        "type": "show_filter_dropdown",
        "field": "B (as str)",
        "search_val": None,
    })

    widget._handle_view_msg_helper({
        "type": "change_filter_viewport",
        "field": "B (as str)",
        "top": 556,
        "bottom": 568,
    })

    widget._handle_view_msg_helper({
        "type": "change_filter_viewport",
        "field": "B (as str)",
        "top": 302,
        "bottom": 314,
    })

    assert event_history == [
        {
            "name": "filter_dropdown_shown",
            "column": "B (as str)"
        },
        {
            "name": "text_filter_viewport_changed",
            "column": "B (as str)",
            "old": (0, 200),
            "new": (556, 568),
        },
        {
            "name": "text_filter_viewport_changed",
            "column": "B (as str)",
            "old": (556, 568),
            "new": (302, 314),
        },
    ]
コード例 #19
0
def test_mixed_type_column():
    df = pd.DataFrame({"A": [1.2, "xy", 4], "B": [3, 4, 5]})
    df = df.set_index(pd.Index(["yz", 7, 3.2]))
    view = SpreadsheetWidget(df=df)
    view._handle_view_msg_helper({
        "type": "change_sort",
        "sort_field": "A",
        "sort_ascending": True
    })
    view._handle_view_msg_helper({
        "type": "show_filter_dropdown",
        "field": "A",
        "search_val": None
    })
コード例 #20
0
def test_nans():
    df = pd.DataFrame([(pd.Timestamp("2017-02-02"), np.nan), (4, 2),
                       ("foo", "bar")])
    view = SpreadsheetWidget(df=df)
    view._handle_view_msg_helper({
        "type": "change_sort",
        "sort_field": 1,
        "sort_ascending": True
    })
    view._handle_view_msg_helper({
        "type": "show_filter_dropdown",
        "field": 1,
        "search_val": None
    })
コード例 #21
0
def test_series_of_text_filters():
    view = SpreadsheetWidget(df=create_df())
    view._handle_view_msg_helper({
        "type": "show_filter_dropdown",
        "field": "E",
        "search_val": None
    })
    view._handle_view_msg_helper({
        "field": "E",
        "filter_info": {
            "field": "E",
            "selected": [0, 1],
            "type": "text",
            "excluded": [],
        },
        "type": "change_filter",
    })
    filtered_df = view.get_changed_df()
    assert len(filtered_df) == 2

    # reset the filter...
    view._handle_view_msg_helper({
        "field": "E",
        "filter_info": {
            "field": "E",
            "selected": None,
            "type": "text",
            "excluded": [],
        },
        "type": "change_filter",
    })

    # ...and apply a text filter on a different column
    view._handle_view_msg_helper({
        "type": "show_filter_dropdown",
        "field": "F",
        "search_val": None
    })
    view._handle_view_msg_helper({
        "field": "F",
        "filter_info": {
            "field": "F",
            "selected": [0, 1],
            "type": "text",
            "excluded": [],
        },
        "type": "change_filter",
    })
    filtered_df = view.get_changed_df()
    assert len(filtered_df) == 2
コード例 #22
0
def test_multi_index():
    widget = SpreadsheetWidget(df=create_multi_index_df())
    event_history = init_event_history(
        ["filter_dropdown_shown", "filter_changed", "sort_changed"],
        widget=widget,
    )

    widget._handle_view_msg_helper({
        "type": "show_filter_dropdown",
        "field": "level_0",
        "search_val": None,
    })

    widget._handle_view_msg_helper({
        "type": "show_filter_dropdown",
        "field": 3,
        "search_val": None
    })

    widget._handle_view_msg_helper({
        "type": "change_filter",
        "field": 3,
        "filter_info": {
            "field": 3,
            "type": "slider",
            "min": -0.111,
            "max": None,
        },
    })

    widget._handle_view_msg_helper({
        "type": "change_filter",
        "field": 3,
        "filter_info": {
            "field": 3,
            "type": "slider",
            "min": None,
            "max": None,
        },
    })

    widget._handle_view_msg_helper({
        "type": "change_filter",
        "field": "level_1",
        "filter_info": {
            "field": "level_1",
            "type": "text",
            "selected": [0],
            "excluded": [],
        },
    })

    widget._handle_view_msg_helper({
        "type": "change_sort",
        "sort_field": 3,
        "sort_ascending": True
    })

    widget._handle_view_msg_helper({
        "type": "change_sort",
        "sort_field": "level_0",
        "sort_ascending": True,
    })

    assert event_history == [
        {
            "name": "filter_dropdown_shown",
            "column": "level_0"
        },
        {
            "name": "filter_dropdown_shown",
            "column": 3
        },
        {
            "name": "filter_changed",
            "column": 3
        },
        {
            "name": "filter_changed",
            "column": 3
        },
        {
            "name": "filter_changed",
            "column": "level_1"
        },
        {
            "name": "sort_changed",
            "old": {
                "column": None,
                "ascending": True
            },
            "new": {
                "column": 3,
                "ascending": True
            },
        },
        {
            "name": "sort_changed",
            "old": {
                "column": 3,
                "ascending": True
            },
            "new": {
                "column": "level_0",
                "ascending": True
            },
        },
    ]
コード例 #23
0
def test_get_history():
    spreadsheet = SpreadsheetWidget(df=create_df())

    # Initial history
    last_history = spreadsheet.get_history()[-1]
    expected_history = "unfiltered_df = df.copy()"
    assert last_history == expected_history

    # Edit cell
    spreadsheet._handle_view_msg_helper({
        "column": "A",
        "row_index": 0,
        "type": "edit_cell",
        "unfiltered_index": 0,
        "value": "hello world",
    })
    last_history = spreadsheet.get_history()[-1]
    expected_history = "# Edit cell\ndf.loc[(0, 'A')]='hello world'\nunfiltered_df.loc[(0, 'A')]='hello world'"
    assert last_history == expected_history

    spreadsheet._handle_view_msg_helper(
        {
            "row_index": 0,
            "column": "Date",
            "unfiltered_index": 0,
            "value": "2013-01-10T00:00:00.000+00:00",
            "type": "edit_cell",
        }, )
    last_history = spreadsheet.get_history()[-1]
    expected_history = "# Edit cell\ndf.loc[(0, 'Date')]=Timestamp('2013-01-10 00:00:00')\nunfiltered_df.loc[(0, 'Date')]=Timestamp('2013-01-10 00:00:00')"
    assert last_history == expected_history

    # Remove row
    spreadsheet._handle_view_msg_helper({"type": "remove_row"}, )
    last_history = spreadsheet.get_history()[-1]
    expected_history = (
        "# Remove rows\ndf.drop([], inplace=True)\nunfiltered_df.drop([], inplace=True)"
    )
    assert last_history == expected_history

    # Change selection
    spreadsheet._handle_view_msg_helper(
        {
            "rows": [],
            "type": "change_selection"
        }, )
    spreadsheet._handle_view_msg_helper(
        {
            "rows": [1],
            "type": "change_selection"
        }, )
    spreadsheet._handle_view_msg_helper(
        {
            "rows": [1, 2, 3],
            "type": "change_selection"
        }, )
    # History should not be changed
    last_history = spreadsheet.get_history()[-1]
    assert last_history == expected_history

    # Change viewport
    spreadsheet._handle_view_msg_helper({
        "type": "change_viewport",
        "top": 7124,
        "bottom": 7136
    })
    # History should not be changed
    last_history = spreadsheet.get_history()[-1]
    assert last_history == expected_history

    # Add row
    spreadsheet._handle_view_msg_helper({"type": "add_row"})
    last_history = spreadsheet.get_history()[-1]
    expected_history = "# Add row\nlast = df.loc[max(df.index)].copy()\ndf.loc[last.name+1] = last.values\nunfiltered_df.loc[last.name+1] = last.values"
    assert last_history == expected_history

    # Change filter
    spreadsheet._handle_view_msg_helper(
        {
            "type": "change_filter",
            "field": "Date",
            "filter_info": {
                "field": "Date",
                "type": "date",
                "min": 1351900800000,
                "max": 1357862399999,
            },
        }, )
    last_history = spreadsheet.get_history()[-1]
    expected_history = "# Filter columns\ndf = unfiltered_df[(unfiltered_df['Date'] >= pd.to_datetime(1351900800000, unit='ms'))&(unfiltered_df['Date'] <= pd.to_datetime(1357862399999, unit='ms'))].copy()"
    assert last_history == expected_history

    # Change sort
    spreadsheet._handle_view_msg_helper({
        "type": "change_sort",
        "sort_field": "A",
        "sort_ascending": True
    })
    last_history = spreadsheet.get_history()[-1]
    expected_history = "# Sort mixed type column\ndf['A_modin_spreadsheet_sort_column'] = df['A'].map(str)\ndf.sort_values('A_modin_spreadsheet_sort_column', ascending=True, inplace=True)\ndf.drop(columns='A_modin_spreadsheet_sort_column', inplace=True)"
    assert last_history == expected_history

    # Reset filters
    spreadsheet._handle_view_msg_helper({"type": "reset_filters_end"})
    last_history = spreadsheet.get_history()[-1]
    expected_history = "# Sort mixed type column\ndf['A_modin_spreadsheet_sort_column'] = df['A'].map(str)\ndf.sort_values('A_modin_spreadsheet_sort_column', ascending=True, inplace=True)\ndf.drop(columns='A_modin_spreadsheet_sort_column', inplace=True)"
    assert last_history == expected_history

    # Reset sort
    spreadsheet._handle_view_msg_helper({"type": "reset_sort"})
    last_history = spreadsheet.get_history()[-1]
    expected_history = "# Reset sort\ndf.sort_index(ascending=True, inplace=True)"
    assert last_history == expected_history

    # Clear history
    spreadsheet._handle_view_msg_helper({"type": "clear_history"})
    history = spreadsheet.get_history()
    expected_history = ["unfiltered_df = df.copy()"]
    assert history == expected_history
コード例 #24
0
def test_apply_history():
    df = create_df()
    df_copy = df.copy()
    spreadsheet = SpreadsheetWidget(df=df)

    spreadsheet._handle_view_msg_helper({
        "row_index": 1,
        "column": "A",
        "unfiltered_index": 1,
        "value": 5,
        "type": "edit_cell",
    })
    spreadsheet._handle_view_msg_helper(
        {
            "type": "change_filter",
            "field": "A",
            "filter_info": {
                "field": "A",
                "type": "slider",
                "min": None,
                "max": 4.14
            },
        }, )
    spreadsheet._handle_view_msg_helper({
        "row_index": 2,
        "column": "F",
        "unfiltered_index": 3,
        "value": "foo",
        "type": "edit_cell",
    })
    spreadsheet._handle_view_msg_helper({
        "type": "change_filter",
        "field": "Date",
        "filter_info": {
            "field": "Date",
            "type": "date",
            "min": 1356998400000,
            "max": 1357171199999,
        },
    })
    spreadsheet._handle_view_msg_helper({
        "type": "change_sort",
        "sort_field": "Date",
        "sort_ascending": True
    })
    spreadsheet._handle_view_msg_helper({
        "row_index": 1,
        "column": "A",
        "unfiltered_index": 2,
        "value": 2,
        "type": "edit_cell",
    })
    spreadsheet._handle_view_msg_helper({
        "row_index": 2,
        "column": "A",
        "unfiltered_index": 3,
        "value": 3,
        "type": "edit_cell",
    })

    changed_df = spreadsheet.get_changed_df()

    # Checks that spreadsheet df is not modified in place
    assert not changed_df.equals(df)

    applied_df = spreadsheet.apply_history(df_copy)

    # Checks that apply_history does not modify in place
    assert df.equals(df_copy)

    # Checks that the history is applied properly
    assert changed_df.equals(applied_df)
コード例 #25
0
def test_filter_relevant_history():
    spreadsheet = SpreadsheetWidget(df=create_df())

    # Initial history
    filtered_history = spreadsheet.filter_relevant_history()
    expected_history = ["unfiltered_df = df.copy()"]
    assert filtered_history == expected_history

    # Empty history
    spreadsheet._history = []
    expected_history = []
    filtered_history = spreadsheet.filter_relevant_history()
    assert filtered_history == expected_history

    # Multiple filters
    spreadsheet._history = [
        "# Filter columns\ndf = unfiltered_df[(unfiltered_df['trip_id'] <= 48802)].copy()",
        "# Filter columns\ndf = unfiltered_df[(unfiltered_df['trip_id'] >= 5495)&(unfiltered_df['trip_id'] <= 48802)].copy()",
        "# Filter columns\ndf = unfiltered_df[(unfiltered_df['trip_id'] >= 5495)&(unfiltered_df['trip_id'] <= 48802)&(unfiltered_df['vendor_id'] >= 2)].copy()",
    ]
    expected_history = [
        "# Filter columns\ndf = unfiltered_df[(unfiltered_df['trip_id'] >= 5495)&(unfiltered_df['trip_id'] <= 48802)&(unfiltered_df['vendor_id'] >= 2)].copy()",
    ]
    filtered_history = spreadsheet.filter_relevant_history()
    assert filtered_history == expected_history

    # Multiple sorts
    spreadsheet._history = [
        "# Sort column\ndf.sort_values('trip_id', ascending=True, inplace=True)",
        "# Sort column\ndf.sort_values('vendor_id', ascending=True, inplace=True)",
        "# Sort column\ndf.sort_values('vendor_id', ascending=False, inplace=True)",
    ]
    expected_history = [
        "# Sort column\ndf.sort_values('vendor_id', ascending=False, inplace=True)",
    ]
    filtered_history = spreadsheet.filter_relevant_history()
    assert filtered_history == expected_history

    # Filter after reset
    spreadsheet._history = [
        "# Filter columns\ndf = unfiltered_df[(unfiltered_df['trip_id'] >= 6485)].copy()",
        "# Reset filter\ndf = unfiltered_df.copy()",
        "# Filter columns\ndf = unfiltered_df[(unfiltered_df['trip_id'] <= 41873)].copy()",
    ]
    expected_history = [
        "# Filter columns\ndf = unfiltered_df[(unfiltered_df['trip_id'] <= 41873)].copy()"
    ]
    filtered_history = spreadsheet.filter_relevant_history()
    assert filtered_history == expected_history

    # Sort after reset
    spreadsheet._history = [
        "# Sort column\ndf.sort_values('trip_id', ascending=True, inplace=True)",
        "# Reset sort\ndf.sort_index(ascending=True, inplace=True)",
        "# Sort column\ndf.sort_values('vendor_id', ascending=True, inplace=True)",
    ]
    expected_history = [
        "# Sort column\ndf.sort_values('vendor_id', ascending=True, inplace=True)"
    ]
    filtered_history = spreadsheet.filter_relevant_history()
    assert filtered_history == expected_history

    # None after reset
    spreadsheet._history = [
        "# Sort column\ndf.sort_values('vendor_id', ascending=True, inplace=True)",
        "# Reset sort\ndf.sort_index(ascending=True, inplace=True)",
        "# Filter columns\ndf = unfiltered_df[(unfiltered_df['trip_id'] >= 6980)].copy()",
        "# Reset all filters\ndf = unfiltered_df.copy()",
    ]
    expected_history = []
    filtered_history = spreadsheet.filter_relevant_history()
    assert filtered_history == expected_history

    # Mixed transformation
    mixed_history = [
        "# Edit cell\ndf.loc[(4, 'trip_id')]=10\nunfiltered_df.loc[(4, 'trip_id')]=10",
        "# Sort column\ndf.sort_values('trip_id', ascending=True, inplace=True)",
        "# Sort column\ndf.sort_values('rate_code_id', ascending=True, inplace=True)",
        "# Reset sort\ndf.sort_index(ascending=True, inplace=True)",
        "# Sort index\ndf.sort_index(ascending=True, inplace=True)",
        "# Remove rows\ndf.drop([6], inplace=True)\nunfiltered_df.drop([6], inplace=True)",
        "# Add row\nlast = df.loc[max(df.index)].copy()\ndf.loc[last.name+1] = last.values\nunfiltered_df.loc[last.name+1] = last.values",
        "# Reset all filters\ndf = unfiltered_df.copy()",
        "# Sort index\ndf.sort_index(ascending=True, inplace=True)",
    ]
    expected_filtered_history = [
        "# Edit cell\ndf.loc[(4, 'trip_id')]=10\nunfiltered_df.loc[(4, 'trip_id')]=10",
        "# Remove rows\ndf.drop([6], inplace=True)\nunfiltered_df.drop([6], inplace=True)",
        "# Add row\nlast = df.loc[max(df.index)].copy()\ndf.loc[last.name+1] = last.values\nunfiltered_df.loc[last.name+1] = last.values",
        "# Sort index\ndf.sort_index(ascending=True, inplace=True)",
    ]
    spreadsheet._history = mixed_history
    filtered_history = spreadsheet.filter_relevant_history()
    assert filtered_history == expected_filtered_history

    # Persist filter history
    spreadsheet._history = mixed_history
    spreadsheet.filter_relevant_history(persist=True)
    assert spreadsheet.get_history() == expected_filtered_history

    # Don't persist filter history
    spreadsheet._history = mixed_history
    filtered_history = spreadsheet.filter_relevant_history(persist=False)
    assert filtered_history == expected_filtered_history
    assert spreadsheet.get_history() == mixed_history