From 02ec39975f316f539cb1a060adc6375820d77e9e Mon Sep 17 00:00:00 2001 From: jrycw Date: Wed, 11 Sep 2024 23:56:45 +0800 Subject: [PATCH 1/3] Refactor `GT.opt_all_caps()` --- great_tables/_options.py | 45 +++++++++++++++------------------------- 1 file changed, 17 insertions(+), 28 deletions(-) diff --git a/great_tables/_options.py b/great_tables/_options.py index b1e7c11b2..072951b0f 100644 --- a/great_tables/_options.py +++ b/great_tables/_options.py @@ -940,36 +940,25 @@ def opt_all_caps( # Set new options for `locations` selected, or, reset to default options # if `all_caps` is False - # TODO: the code constantly reassigns res, in order to prepare for a - # world where options are not mutating the GT options object. - # TODO: is there a way to set multiple options at once? res = self - if all_caps: - if "column_labels" in locations: - res = tab_options(res, column_labels_font_size="80%") - res = tab_options(res, column_labels_font_weight="bolder") - res = tab_options(res, column_labels_text_transform="uppercase") - - if "stub" in locations: - res = tab_options(res, stub_font_size="80%") - res = tab_options(res, stub_font_weight="bolder") - res = tab_options(res, stub_text_transform="uppercase") - - if "row_group" in locations: - res = tab_options(res, row_group_font_size="80%") - res = tab_options(res, row_group_font_weight="bolder") - res = tab_options(res, row_group_text_transform="uppercase") - else: - res = tab_options(res, column_labels_font_size="100%") - res = tab_options(res, column_labels_font_weight="normal") - res = tab_options(res, column_labels_text_transform="inherit") - res = tab_options(res, stub_font_size="100%") - res = tab_options(res, stub_font_weight="initial") - res = tab_options(res, stub_text_transform="inherit") - res = tab_options(res, row_group_font_size="100%") - res = tab_options(res, row_group_font_weight="initial") - res = tab_options(res, row_group_text_transform="inherit") + # Predicates for locations + p1 = all_caps and "column_labels" in locations + p2 = all_caps and "stub" in locations + p3 = all_caps and "row_group" in locations + + res = tab_options( + res, + column_labels_font_size="80%" if p1 else "100%", + column_labels_font_weight="bolder" if p1 else "normal", + column_labels_text_transform="uppercase" if p1 else "inherit", + stub_font_size="80%" if p2 else "100%", + stub_font_weight="bolder" if p2 else "initial", + stub_text_transform="uppercase" if p2 else "inherit", + row_group_font_size="80%" if p3 else "100%", + row_group_font_weight="bolder" if p3 else "initial", + row_group_text_transform="uppercase" if p3 else "inherit", + ) return res From c42c1adf2d0f9aa980ec6c289c95d437b856e0a9 Mon Sep 17 00:00:00 2001 From: jrycw Date: Thu, 12 Sep 2024 08:30:07 +0800 Subject: [PATCH 2/3] Update flawed logic --- great_tables/_options.py | 67 +++++++++++++++++++++++++++------------- 1 file changed, 46 insertions(+), 21 deletions(-) diff --git a/great_tables/_options.py b/great_tables/_options.py index 072951b0f..c7ccc07f2 100644 --- a/great_tables/_options.py +++ b/great_tables/_options.py @@ -938,29 +938,54 @@ def opt_all_caps( # TODO: Ensure that all values within `locations` are valid - # Set new options for `locations` selected, or, reset to default options - # if `all_caps` is False - res = self + # if `all_caps` is False, reset options to default, or, set new options + # for `locations` selected + if not all_caps: + return tab_options( + self, + column_labels_font_size="100%", + column_labels_font_weight="normal", + column_labels_text_transform="inherit", + stub_font_size="100%", + stub_font_weight="initial", + stub_text_transform="inherit", + row_group_font_size="100%", + row_group_font_weight="initial", + row_group_text_transform="inherit", + ) - # Predicates for locations - p1 = all_caps and "column_labels" in locations - p2 = all_caps and "stub" in locations - p3 = all_caps and "row_group" in locations - - res = tab_options( - res, - column_labels_font_size="80%" if p1 else "100%", - column_labels_font_weight="bolder" if p1 else "normal", - column_labels_text_transform="uppercase" if p1 else "inherit", - stub_font_size="80%" if p2 else "100%", - stub_font_weight="bolder" if p2 else "initial", - stub_text_transform="uppercase" if p2 else "inherit", - row_group_font_size="80%" if p3 else "100%", - row_group_font_weight="bolder" if p3 else "initial", - row_group_text_transform="uppercase" if p3 else "inherit", - ) + info = [ + ( + "column_labels", + { + "column_labels_font_size": "80%", + "column_labels_font_weight": "bolder", + "column_labels_text_transform": "uppercase", + }, + ), + ( + "stub", + { + "stub_font_size": "80%", + "stub_font_weight": "bolder", + "stub_text_transform": "uppercase", + }, + ), + ( + "row_group", + { + "row_group_font_size": "80%", + "row_group_font_weight": "bolder", + "row_group_text_transform": "uppercase", + }, + ), + ] + d = {} + for location, params in info: + if location in locations: + d |= params - return res + return tab_options(self, **d) def opt_table_outline( From f0ae1a7e9cc4bae104d5b4b251ccf1648709e773 Mon Sep 17 00:00:00 2001 From: jrycw Date: Thu, 12 Sep 2024 18:35:45 +0800 Subject: [PATCH 3/3] Modify the `locations` parameter of `opt_all_caps()` to accept `Loc` objects --- great_tables/_options.py | 59 +++++++++++++++++++++++++------- great_tables/loc.py | 3 +- tests/test_options.py | 74 ++++++++++++++++++++++++++++++++++++++-- 3 files changed, 120 insertions(+), 16 deletions(-) diff --git a/great_tables/_options.py b/great_tables/_options.py index c7ccc07f2..fc30d59b4 100644 --- a/great_tables/_options.py +++ b/great_tables/_options.py @@ -893,8 +893,8 @@ def opt_all_caps( locations Which locations should undergo this text transformation? By default it includes all of - the `"column_labels"`, the `"stub"`, and the `"row_group"` locations. However, we could - just choose one or two of those. + the `loc.column_labels`, the `loc.stub"`, and the `loc.row_group` locations. However, we + could just choose one or two of those. Returns ------- @@ -909,7 +909,7 @@ def opt_all_caps( in all row groups is transformed to all caps using the `opt_all_caps()` method. ```{python} - from great_tables import GT, exibble, md + from great_tables import GT, exibble, loc, md ( GT( @@ -927,16 +927,49 @@ def opt_all_caps( .opt_all_caps() ) ``` + `opt_all_caps()` accepts a `locations` parameter that allows us to specify which components + should be transformed. For example, if we only want to ensure that all text in the stub and all + row groups is converted to all caps: + ```{python} + ( + GT( + exibble[["num", "char", "currency", "row", "group"]], + rowname_col="row", + groupname_col="group" + ) + .tab_header( + title=md("Data listing from **exibble**"), + subtitle=md("`exibble` is a **Great Tables** dataset.") + ) + .fmt_number(columns="num") + .fmt_currency(columns="currency") + .tab_source_note(source_note="This is only a subset of the dataset.") + .opt_all_caps(locations=[loc.stub, loc.row_group]) + ) + ``` """ + # Importing `great_tables._locations` at the top will cause a circular import error. + # The type annotation for `locations` should be: + # `Loc | list[Loc] = [LocColumnLabels, LocStub, LocRowGroups]` + from great_tables._locations import Loc, LocColumnLabels, LocStub, LocRowGroups - # If providing a scalar string value, normalize it to be in a list - if not isinstance(locations, list): - locations = _utils._str_scalar_to_list(cast(str, locations)) + if not locations: + locations = [LocColumnLabels, LocStub, LocRowGroups] - # Ensure that the `locations` value is a list of strings - _utils._assert_str_list(locations) - - # TODO: Ensure that all values within `locations` are valid + # If providing a Loc object, normalize it to be in a list + if not isinstance(locations, list): + locations = [locations] + + # Ensure that all values within `locations` are valid + # A `try-except` block is needed here because the first argument of `issubclass()` must be a + # class. + for location in locations: + try: + issubclass(location, Loc) + except TypeError as exc: + raise AssertionError( + "Only `loc.column_labels`, `loc.stub` and `loc.row_group` are allowed in the locations." + ) from exc # if `all_caps` is False, reset options to default, or, set new options # for `locations` selected @@ -956,7 +989,7 @@ def opt_all_caps( info = [ ( - "column_labels", + LocColumnLabels, { "column_labels_font_size": "80%", "column_labels_font_weight": "bolder", @@ -964,7 +997,7 @@ def opt_all_caps( }, ), ( - "stub", + LocStub, { "stub_font_size": "80%", "stub_font_weight": "bolder", @@ -972,7 +1005,7 @@ def opt_all_caps( }, ), ( - "row_group", + LocRowGroups, { "row_group_font_size": "80%", "row_group_font_weight": "bolder", diff --git a/great_tables/loc.py b/great_tables/loc.py index eec0149b7..a09936972 100644 --- a/great_tables/loc.py +++ b/great_tables/loc.py @@ -4,6 +4,7 @@ LocBody as body, LocStub as stub, LocColumnLabels as column_labels, + LocRowGroups as row_group, ) -__all__ = ("body", "stub", "column_labels") +__all__ = ("body", "stub", "column_labels", "row_group") diff --git a/tests/test_options.py b/tests/test_options.py index a92422325..813e44adb 100644 --- a/tests/test_options.py +++ b/tests/test_options.py @@ -1,6 +1,6 @@ import pandas as pd import pytest -from great_tables import GT, exibble, md +from great_tables import GT, exibble, loc, md from great_tables._scss import compile_scss from great_tables._gt_data import default_fonts_list @@ -328,7 +328,6 @@ def test_scss_from_opt_table_outline(gt_tbl: GT, snapshot): def test_opt_table_font_add_font(): - gt_tbl = GT(exibble).opt_table_font(font="Arial", weight="bold", style="italic") assert gt_tbl._options.table_font_names.value == ["Arial"] + default_fonts_list @@ -369,3 +368,74 @@ def test_opt_table_font_raises(): GT(exibble).opt_table_font(font=None, stack=None) assert "Either `font=` or `stack=` must be provided." in exc_info.value.args[0] + + +def test_opt_all_caps(gt_tbl: GT): + tbl = gt_tbl.opt_all_caps(locations=loc.column_labels) + + assert tbl._options.column_labels_font_size.value == "80%" + assert tbl._options.column_labels_font_weight.value == "bolder" + assert tbl._options.column_labels_text_transform.value == "uppercase" + + tbl = gt_tbl.opt_all_caps(locations=[loc.column_labels, loc.stub]) + + assert tbl._options.column_labels_font_size.value == "80%" + assert tbl._options.column_labels_font_weight.value == "bolder" + assert tbl._options.column_labels_text_transform.value == "uppercase" + + assert tbl._options.stub_font_size.value == "80%" + assert tbl._options.stub_font_weight.value == "bolder" + assert tbl._options.stub_text_transform.value == "uppercase" + + tbl = gt_tbl.opt_all_caps(locations=[loc.column_labels, loc.stub, loc.row_group]) + + assert tbl._options.column_labels_font_size.value == "80%" + assert tbl._options.column_labels_font_weight.value == "bolder" + assert tbl._options.column_labels_text_transform.value == "uppercase" + + assert tbl._options.stub_font_size.value == "80%" + assert tbl._options.stub_font_weight.value == "bolder" + assert tbl._options.stub_text_transform.value == "uppercase" + + assert tbl._options.row_group_font_size.value == "80%" + assert tbl._options.row_group_font_weight.value == "bolder" + assert tbl._options.row_group_text_transform.value == "uppercase" + + # Activate the following tests once the circular import issue is resolved. + # tbl = gt_tbl.opt_all_caps() + + # assert tbl._options.column_labels_font_size.value == "80%" + # assert tbl._options.column_labels_font_weight.value == "bolder" + # assert tbl._options.column_labels_text_transform.value == "uppercase" + + # assert tbl._options.stub_font_size.value == "80%" + # assert tbl._options.stub_font_weight.value == "bolder" + # assert tbl._options.stub_text_transform.value == "uppercase" + + # assert tbl._options.row_group_font_size.value == "80%" + # assert tbl._options.row_group_font_weight.value == "bolder" + # assert tbl._options.row_group_text_transform.value == "uppercase" + + # tbl = gt_tbl.opt_all_caps(all_caps=False) + + # assert tbl._options.column_labels_font_size.value == "100%" + # assert tbl._options.column_labels_font_weight.value == "normal" + # assert tbl._options.column_labels_text_transform.value == "inherit" + + # assert tbl._options.stub_font_size.value == "100%" + # assert tbl._options.stub_font_weight.value == "initial" + # assert tbl._options.stub_text_transform.value == "inherit" + + # assert tbl._options.row_group_font_size.value == "100%" + # assert tbl._options.row_group_font_weight.value == "initial" + # assert tbl._options.row_group_text_transform.value == "inherit" + + +def test_opt_all_caps_raises(gt_tbl: GT): + with pytest.raises(AssertionError) as exc_info: + gt_tbl.opt_all_caps(locations="column_labels") + + assert ( + "Only `loc.column_labels`, `loc.stub` and `loc.row_group` are allowed in the locations." + in exc_info.value.args[0] + )