Ejemplo n.º 1
0
def test_write_excel__raises_error_on_invalid_style_spec(
        tmp_path, err_msg_match, style_spec):
    # Make a table
    t = Table(name="foo")
    t["place"] = ["home", "work", "beach", "wonderland"]
    t.add_column("distance", list(range(3)) + [float("nan")], "km")

    # Invalid font size
    with pytest.raises(ValueError, match=err_msg_match):
        write_excel([t],
                    tmp_path / "foo_invalid_style.xlsx",
                    styles=style_spec)
Ejemplo n.º 2
0
def test__table_to_csv():
    # Make a table with content of various units
    t = Table(name="foo")
    t["place"] = ["home", "work", "beach", "wonderland"]
    t.add_column("distance", list(range(3)) + [float("nan")], "km")
    t.add_column(
        "ETA",
        pd.to_datetime([
            "2020-08-04 08:00", "2020-08-04 09:00", "2020-08-04 17:00", pd.NaT
        ]),
        "datetime",
    )
    t.add_column("is_hot", [True, False, True, False], "onoff")

    # Write table to stream
    with io.StringIO() as out:
        _table_to_csv(t, out, ";", "-")
        # Assert stream content is as expected
        assert out.getvalue() == dedent("""\
            **foo;
            all
            place;distance;ETA;is_hot
            text;km;datetime;onoff
            home;0.0;2020-08-04 08:00:00;1
            work;1.0;2020-08-04 09:00:00;0
            beach;2.0;2020-08-04 17:00:00;1
            wonderland;-;-;0

            """)
Ejemplo n.º 3
0
def test__table_to_csv__writes_transposed_table():
    # Make a TRANSPOSED table with content of various units
    t = Table(name="foo")
    t["place"] = ["home", "work", "beach", "wonderland"]
    t.add_column("distance", list(range(3)) + [float("nan")], "km")
    t.add_column(
        "ETA",
        pd.to_datetime(["2020-08-04 08:00", "2020-08-04 09:00", "2020-08-04 17:00", pd.NaT]),
        "datetime",
    )
    t.add_column("is_hot", [True, False, True, False], "onoff")
    t.metadata.transposed = True  # <<<< aha

    # Write transposed table to stream
    with io.StringIO() as out:
        _table_to_csv(t, out, ";", "-")
        # Stream content is as expected
        assert out.getvalue() == dedent(
            """\
            **foo*;
            all
            place;text;home;work;beach;wonderland
            distance;km;0.0;1.0;2.0;-
            ETA;datetime;2020-08-04 08:00:00;2020-08-04 09:00:00;2020-08-04 17:00:00;-
            is_hot;onoff;1;0;1;0

            """
        )
Ejemplo n.º 4
0
def test_write_csv__writes_to_file(tmp_path):
    # Make a table
    t2 = Table(
        pd.DataFrame({
            "number": [1, 6, 42],
            "spelling": ["one", "six", "forty-two"]
        }),
        name="bar",
        units=["-", "text"],
    )

    # Write to file
    out_path = tmp_path / "write_csv_to_file.csv"
    write_csv(t2, out_path)

    # Now check file contents
    assert out_path.read_text() == dedent("""\
        **bar;
        all
        number;spelling
        -;text
        1;one
        6;six
        42;forty-two

        """)
    # Teardown
    out_path.unlink()
Ejemplo n.º 5
0
def test_write_csv__leaves_stream_open_if_caller_passes_stream():
    # Make a table
    t2 = Table(
        pd.DataFrame({
            "number": [1, 6, 42],
            "spelling": ["one", "six", "forty-two"]
        }),
        name="bar",
        units=["-", "text"],
    )

    # Check write_csv single table and leave stream open for business
    with io.StringIO() as out:
        write_csv(t2, out)
        out.write("Fin\n")
        assert out.getvalue() == dedent("""\
            **bar;
            all
            number;spelling
            -;text
            1;one
            6;six
            42;forty-two

            Fin
            """)
Ejemplo n.º 6
0
def test_write_csv__with_format_specs():
    # Make a table
    t2 = Table(
        pd.DataFrame({
            "numbers": [1, 6, 42],
            "same_numbers": [1, 6, 42],
            "others": [1, 123.456, 42]
        }),
        name="bar",
        units=["-", "-", "-"],
    )

    # Give formats to some columns, leave some without formats
    t2.column_metadata["same_numbers"].display_format = ColumnFormat(2)
    t2.column_metadata["others"].display_format = ColumnFormat("14.3e")

    with io.StringIO() as out:
        write_csv(t2, out)
        assert out.getvalue() == dedent("""\
            **bar;
            all
            numbers;same_numbers;others
            -;-;-
            1;1.00;     1.000e+00
            6;6.00;     1.235e+02
            42;42.00;     4.200e+01
            
            """)
Ejemplo n.º 7
0
def test_write_excel__transposed_table_units_and_values_are_centered_by_default(
        tmp_path):
    # Make a table
    t = Table(name="foo")
    t["place"] = ["home", "work", "beach", "wonderland"]
    t.add_column("distance", list(range(3)) + [float("nan")], "km")
    t.metadata.transposed = True
    nc = len(t.column_names)
    nr = len(t.df)

    # DEFAULT ALIGNMENT: CENTER
    # Write tables to workbook with default styles, save, and re-load
    out_path = tmp_path / "foo_custom_style.xlsx"
    write_excel([t], out_path, styles=True)  # <<< Default style
    wb = openpyxl.load_workbook(out_path)
    ws = wb.active

    # column units and values are centered
    assert [ws.cell(2 + c, 2).alignment.horizontal
            for c in range(1, nc + 1)] == ["center"] * nc
    assert [[
        ws.cell(2 + c, 2 + r).alignment.horizontal for c in range(1, nc + 1)
    ] for r in range(1, nr + 1)] == [["center"] * nc] * nr

    # DEFAULT ALIGNMENT NOT APPLIED when custom alignment is specified
    # Write tables to workbook with custom alignment styles, save, and re-load
    out_path = tmp_path / "foo_custom_style.xlsx"
    left = {"alignment": {"horizontal": "left"}}
    write_excel([t], out_path, styles={
        "units": left,
        "values": left
    })  # << Custom alignment
    wb = openpyxl.load_workbook(out_path)
    ws = wb.active

    # column units and values are not centered
    assert [ws.cell(2 + c, 2).alignment.horizontal
            for c in range(1, nc + 1)] == ["left"] * nc
    assert [[
        ws.cell(2 + c, 2 + r).alignment.horizontal for c in range(1, nc + 1)
    ] for r in range(1, nr + 1)] == [["left"] * nc] * nr
Ejemplo n.º 8
0
def make_table(cells: CellGrid, origin: Optional[TableOriginCSV] = None, **kwargs) -> Table:
    """Parses cell grid into a pdtable-style Table block object."""
    json_precursor = make_table_json_precursor(cells, origin=origin, **kwargs)
    return Table(
        frame.make_table_dataframe(
            pd.DataFrame(json_precursor["columns"]),
            units=json_precursor["units"],
            table_metadata=TableMetadata(
                name=json_precursor["name"],
                destinations=set(json_precursor["destinations"].keys()),
                origin=json_precursor["origin"],
            ),
        )
    )
Ejemplo n.º 9
0
def test_make_table__with_backslashes():
    cells = [[cell.strip() for cell in line.split(";")] for line in dedent(r"""
**input_files_derived;
all;
file_bytes;file_date;file_name;has_table;
-;text;text;onoff;
15373;20190516T104445;PISA_Library\results\check_Soil_Plastic_ULS1-PISA_C1.csv;1;
15326;20190516T104445;PISA_Library\results\check_Soil_Plastic_ULS1-PISA_C2.csv;1;
""").strip().split("\n")]

    t = make_table(cells).df
    assert t.file_bytes[0] == 15373

    tt = Table(t)
    assert tt.name == "input_files_derived"
    assert set(tt.metadata.destinations) == {"all"}
    assert tt.units == ["-", "text", "text", "onoff"]
Ejemplo n.º 10
0
def test_make_table__parses_onoff_column():
    cells = [[cell.strip() for cell in line.split(";")] for line in dedent(r"""
    **input_files_derived;
    all;
    file_bytes;file_date;has_table;
    -;text;onoff;
    15373;a;0;
    15326;b;1;
    """).strip().split("\n")]

    table_df = make_table(cells).df
    assert table_df.file_bytes[0] == 15373
    assert not table_df.has_table[0]
    assert table_df.has_table[1]
    tt = Table(table_df)
    assert tt.name == "input_files_derived"
    assert set(tt.metadata.destinations) == {"all"}
    assert tt.units == ["-", "text", "onoff"]
Ejemplo n.º 11
0
def test__append_table_to_openpyxl_worksheet():
    # Make a table with content of various units
    t = Table(name="foo")
    t["place"] = ["home", "work", "beach", "wonderland"]
    t.add_column("distance", list(range(3)) + [float("nan")], "km")
    t.add_column(
        "ETA",
        pd.to_datetime([
            "2020-08-04 08:00", "2020-08-04 09:00", "2020-08-04 17:00", pd.NaT
        ]),
        "datetime",
    )
    t.add_column("is_hot", [True, False, True, False], "onoff")

    wb = openpyxl.Workbook()
    ws = wb.active

    # Act
    _append_table_to_openpyxl_worksheet(t, ws, sep_lines=1)

    # Assert worksheet looks as expected:
    # table header by row
    assert ws["A1"].value == "**foo"
    assert ws["A2"].value == "all"
    assert [ws.cell(3, c).value
            for c in range(1, 5)] == ["place", "distance", "ETA", "is_hot"]
    assert [ws.cell(4, c).value
            for c in range(1, 5)] == ["text", "km", "datetime", "onoff"]

    # table data by column
    assert [ws.cell(r, 1).value
            for r in range(5, 9)] == ["home", "work", "beach", "wonderland"]
    assert [ws.cell(r, 2).value for r in range(5, 9)] == [0, 1, 2, "-"]
    assert [ws.cell(r, 3).value for r in range(5, 9)] == list(
        pd.to_datetime([
            "2020-08-04 08:00", "2020-08-04 09:00", "2020-08-04 17:00"
        ])) + ["-"]
    assert [ws.cell(r, 4).value for r in range(5, 9)] == [1, 0, 1, 0]
Ejemplo n.º 12
0
def test__write_csv__uses_altered_default_csv_separator(monkeypatch):

    # Change the default CSV separator
    # Using monkeypatch in lieu of just 'tables.CSV_SEP = ","'
    # so that this alteration of the default is only visible in this test.
    monkeypatch.setattr(pdtable, "CSV_SEP", ",")

    # Make a table with content of various units
    t = Table(
        pd.DataFrame(
            {
                "place": ["home", "work", "beach", "wonderland"],
                "distance": list(range(3)) + [float("nan")],
                "ETA": pd.to_datetime(
                    ["2020-08-04 08:00", "2020-08-04 09:00", "2020-08-04 17:00", pd.NaT]
                ),
                "is_hot": [True, False, True, False],
            }
        ),
        name="foo",
        units=["text", "km", "datetime", "onoff"],
    )

    # Write table to stream
    with io.StringIO() as out:
        write_csv(t, out)
        # Assert stream content is as expected
        assert out.getvalue() == dedent(
            """\
            **foo,
            all
            place,distance,ETA,is_hot
            text,km,datetime,onoff
            home,0.0,2020-08-04 08:00:00,1
            work,1.0,2020-08-04 09:00:00,0
            beach,2.0,2020-08-04 17:00:00,1
            wonderland,-,-,0

            """
        )
Ejemplo n.º 13
0
def test_write_excel(tmp_path):
    # Make a couple of tables
    t = Table(name="foo")
    t["place"] = ["home", "work", "beach", "wonderland"]
    t.add_column("distance", list(range(3)) + [float("nan")], "km")
    t.add_column(
        "ETA",
        pd.to_datetime([
            "2020-08-04 08:00", "2020-08-04 09:00", "2020-08-04 17:00", pd.NaT
        ]),
        "datetime",
    )
    t.add_column("is_hot", [True, False, True, False], "onoff")

    # This one is transposed
    t2 = Table(name="bar")
    t2.add_column("digit", [1, 6, 42], "-")
    t2.add_column("spelling", ["one", "six", "forty-two"], "text")
    t2.metadata.transposed = True

    # Write tables to workbook, save, and re-load
    out_path = tmp_path / "foo.xlsx"
    write_excel([t, t2], out_path)
    wb = openpyxl.load_workbook(out_path)
    ws = wb.active

    # First table is written as expected
    # - table header by row
    assert ws["A1"].value == "**foo"
    assert ws["A2"].value == "all"
    assert [ws.cell(3, c).value
            for c in range(1, 5)] == ["place", "distance", "ETA", "is_hot"]
    assert [ws.cell(4, c).value
            for c in range(1, 5)] == ["text", "km", "datetime", "onoff"]

    # - table data by column
    assert [ws.cell(r, 1).value
            for r in range(5, 9)] == ["home", "work", "beach", "wonderland"]
    assert [ws.cell(r, 2).value for r in range(5, 9)] == [0, 1, 2, "-"]
    for r, d in zip(
            range(5, 8),
            pd.to_datetime(
                ["2020-08-04 08:00", "2020-08-04 09:00", "2020-08-04 17:00"])):
        # workaround openpyxl bug: https://foss.heptapod.net/openpyxl/openpyxl/-/issues/1493
        # openpyxl adds a spurious microsecond to some datetimes.
        assert abs(ws.cell(r, 3).value -
                   d) <= datetime.timedelta(microseconds=1)
    assert ws.cell(8, 3).value == "-"
    assert [ws.cell(r, 4).value for r in range(5, 9)] == [1, 0, 1, 0]

    # Second table is there as well
    assert ws["A10"].value == "**bar*"
    assert ws["A11"].value == "all"
    # column headers (transposed)
    assert [ws.cell(r, 1).value
            for r in range(12, 14)] == ["digit", "spelling"]
    assert [ws.cell(r, 2).value for r in range(12, 14)] == ["-", "text"]
    # column values (transposed)
    assert [ws.cell(12, c).value for c in range(3, 6)] == [1, 6, 42]
    assert [ws.cell(13, c).value
            for c in range(3, 6)] == ["one", "six", "forty-two"]

    # Teardown
    out_path.unlink()
Ejemplo n.º 14
0
def test_write_excel__multiple_sheets(tmp_path):
    """write_excel() can write tables to multiple sheets in a workbook"""

    # Make a couple of tables
    t = Table(name="foo")
    t["place"] = ["home", "work", "beach", "wonderland"]
    t.add_column("distance", list(range(3)) + [float("nan")], "km")
    t.add_column(
        "ETA",
        pd.to_datetime([
            "2020-08-04 08:00", "2020-08-04 09:00", "2020-08-04 17:00", pd.NaT
        ]),
        "datetime",
    )
    t.add_column("is_hot", [True, False, True, False], "onoff")

    t2 = Table(name="bar")
    t2.add_column("digit", [1, 6, 42], "-")
    t2.add_column("spelling", ["one", "six", "forty-two"], "text")

    # Write tables to workbook, save, and re-load
    out_path = tmp_path / "foo.xlsx"
    write_excel({"sheet_one": [t, t2], "sheet_two": t2}, out_path)
    wb = openpyxl.load_workbook(out_path)

    # Workbook has the expected sheets
    assert len(wb.worksheets) == 2
    assert wb.sheetnames == ["sheet_one", "sheet_two"]
    # First sheet contains the expected tables
    assert wb.worksheets[0]["A1"].value == "**foo"
    assert wb.worksheets[0]["A10"].value == "**bar"
    # Second sheet contains the expected tables
    assert wb.worksheets[1]["A1"].value == "**bar"
    # Table details are tested elsewhere.

    # Teardown
    out_path.unlink()
Ejemplo n.º 15
0
def test_write_excel__sep_lines(tmp_path):
    # Make a couple of tables
    t = Table(name="foo")
    t["place"] = ["home", "work", "beach", "wonderland"]
    t.add_column("distance", list(range(3)) + [float("nan")], "km")
    t.add_column(
        "ETA",
        pd.to_datetime([
            "2020-08-04 08:00", "2020-08-04 09:00", "2020-08-04 17:00", pd.NaT
        ]),
        "datetime",
    )
    t.add_column("is_hot", [True, False, True, False], "onoff")

    # This one is transposed
    t2 = Table(name="bar")
    t2.add_column("digit", [1, 6, 42], "-")
    t2.add_column("spelling", ["one", "six", "forty-two"], "text")
    t2.metadata.transposed = True

    # This one is also transposed
    t3 = Table(name="bas")
    t3.add_column("digit", [1, 6, 42], "-")
    t3.add_column("spelling", ["one", "six", "forty-two"], "text")
    t3.metadata.transposed = True

    # Write tables to workbook, save, and re-load
    out_path = tmp_path / "foo.xlsx"
    write_excel([t, t2, t3], out_path, sep_lines=2)
    wb = openpyxl.load_workbook(out_path)
    ws = wb.active

    # Tables start on the expected rows
    assert ws["A1"].value == "**foo"
    assert ws["A11"].value == "**bar*"
    assert ws["A17"].value == "**bas*"
Ejemplo n.º 16
0
def test_write_excel__custom_style(tmp_path):
    # Make a table
    t = Table(name="foo")
    t["place"] = ["home", "work", "beach", "wonderland"]
    t.add_column("distance", list(range(3)) + [float("nan")], "km")
    nc = len(t.column_names)
    nr = len(t.df)

    # Make a style specification as a JSON-like data structure
    style_spec = {
        "table_name": {
            "font": {
                "name": "Times New Roman",
                "size": 24,
                "color": "FF0000",  # RGB hex color code
                "bold": True,
            },
            "fill": {
                "color":
                "00AAAAAA",  # leading 'aa' transparency values are accepted (but unused)
            },
        },
        "destinations": {
            "font": {
                "italic": True,
                "color": "0000FF",
            },
            "fill": {
                "color": "888888",
            },
        },
        "column_names": {
            "font": {
                "color": "444400",
                "bold": True,
            },
            "fill": {
                "color": "777777",
            },
        },
        "units": {
            "font": {
                "color": "440044",
            },  # --------------------- fill unspecified, leave untouched
        },
        "values": {
            "alignment": {
                "horizontal": "left",
            },
            "fill": {
                "color": "EEEEEE",
            },  # --------------------- font unspecified, leave untouched
        },
    }

    # Write tables to workbook, save, and re-load
    out_path = tmp_path / "foo_custom_style.xlsx"
    write_excel([t], out_path, styles=style_spec)
    wb = openpyxl.load_workbook(out_path)
    ws = wb.active

    # Check table formatting
    # table name
    assert ws["A1"].fill.fill_type == "solid"
    assert ws["A1"].fill.start_color.value == "00AAAAAA"
    assert ws["A1"].font.size == 24
    assert ws["A1"].font.color.value == "00FF0000"
    assert ws["A1"].font.name == "Times New Roman"
    assert ws["A1"].font.bold is True

    # destinations
    assert ws["A2"].fill.fill_type == "solid"
    assert ws["A2"].fill.start_color.value == "00888888"
    assert ws["A2"].font.color.value == "000000FF"
    assert ws["A2"].font.bold is False
    assert ws["A2"].font.italic is True

    # column names
    assert [ws.cell(3, c).fill.fill_type
            for c in range(1, nc + 1)] == ["solid"] * nc
    assert [ws.cell(3, c).fill.start_color.value
            for c in range(1, nc + 1)] == ["00777777"] * nc
    assert [ws.cell(3, c).font.color.value
            for c in range(1, nc + 1)] == ["00444400"] * nc
    assert [ws.cell(3, c).font.bold for c in range(1, nc + 1)] == [True] * nc

    # column units
    assert [ws.cell(4, c).fill.fill_type
            for c in range(1, nc + 1)] == [None] * nc  # left as default
    assert [ws.cell(4, c).font.color.value
            for c in range(1, nc + 1)] == ["00440044"] * nc
    assert [ws.cell(4, c).font.bold for c in range(1, nc + 1)] == [False] * nc
    assert [ws.cell(4, c).alignment.horizontal
            for c in range(1, nc + 1)] == [None] * nc

    # values
    assert [[ws.cell(4 + r, c).fill.fill_type for c in range(1, nc + 1)]
            for r in range(1, nr + 1)] == [["solid"] * nc] * nr
    assert [[
        ws.cell(4 + r, c).fill.start_color.value for c in range(1, nc + 1)
    ] for r in range(1, nr + 1)] == [["00EEEEEE"] * nc] * nr
    assert [[ws.cell(4 + r, c).alignment.horizontal for c in range(1, nc + 1)]
            for r in range(1, nr + 1)] == [["left"] * nc] * nr
Ejemplo n.º 17
0
def test_write_csv__writes_two_tables():
    # Make a couple of tables
    t = Table(name="foo")
    t["place"] = ["home", "work", "beach", "wonderland"]
    t.add_column("distance", list(range(3)) + [float("nan")], "km")
    t.add_column(
        "ETA",
        pd.to_datetime([
            "2020-08-04 08:00", "2020-08-04 09:00", "2020-08-04 17:00", pd.NaT
        ]),
        "datetime",
    )
    t.add_column("is_hot", [True, False, True, False], "onoff")

    t2 = Table(name="bar")
    t2.add_column("number", [1, 6, 42], "-")
    t2.add_column("spelling", ["one", "six", "forty-two"], "text")

    # Write tables to stream
    with io.StringIO() as out:
        write_csv([t, t2], out)
        # Assert stream content is as expected
        assert out.getvalue() == dedent("""\
            **foo;
            all
            place;distance;ETA;is_hot
            text;km;datetime;onoff
            home;0.0;2020-08-04 08:00:00;1
            work;1.0;2020-08-04 09:00:00;0
            beach;2.0;2020-08-04 17:00:00;1
            wonderland;-;-;0

            **bar;
            all
            number;spelling
            -;text
            1;one
            6;six
            42;forty-two

            """)