示例#1
0
        def _make_columns(columns_frame, previous_level_values=()):
            """
            This function recursively creates the individual column definitions for React Table with the above tree
            structure depending on how many index levels there are in the columns.

            :param columns_frame:
                A data frame representing the columns of the result set data frame.
            :param previous_level_values:
                A tuple containing the higher level index level values used for building the data accessor path
            """
            f_dimension_alias = columns_frame.index.names[0]

            # Group the columns if they are multi-index so we can get the proper sub-column values. This will yield
            # one group per dimension value with the group data frame containing only the relevant sub-columns
            groups = (columns_frame.groupby(
                level=0) if isinstance(columns_frame.index, pd.MultiIndex) else
                      [(level, None) for level in columns_frame.index])

            columns = []
            for column_value, group in groups:
                if column_value in hide_metric_aliases:
                    continue

                is_totals = column_value in TOTALS_MARKERS | {TOTALS_LABEL}

                # All column definitions have a header
                column = {
                    "Header":
                    get_header(column_value, f_dimension_alias, is_totals)
                }

                level_values = previous_level_values + (column_value, )

                if group is not None:
                    # If there is a group, then drop this index level from the group data frame and recurse to build
                    # sub column definitions
                    next_level_df = group.reset_index(level=0, drop=True)
                    column["columns"] = _make_columns(next_level_df,
                                                      level_values)

                else:
                    column["accessor"] = ".".join(
                        safe_value(value) for value in level_values)
                    # path_accessor can be useful for front-end code e.g. if specifying a custom accessor function.
                    # Use cases for this include if the value of the nested column name includes a '.' character,
                    # which breaks the default nested column access functionality.
                    column["path_accessor"] = [
                        safe_value(value) for value in level_values
                    ]

                if is_totals:
                    column["className"] = "fireant-totals"

                columns.append(column)

            return columns
示例#2
0
        def get_header(column_value, f_dimension_alias, is_totals):
            if f_dimension_alias == F_METRICS_DIMENSION_ALIAS or is_totals:
                item = field_map[column_value]
                return getattr(item, "label", item.alias)

            if f_dimension_alias in field_map:
                field = field_map[f_dimension_alias]
                return _display_value(column_value,
                                      field) or safe_value(column_value)

            if f_dimension_alias is None:
                return ""

            return safe_value(column_value)
示例#3
0
    def transform_row_values(series, fields, is_transposed):
        # Add the values to the row
        index_names = series.index.names or []

        row = {}
        for key, value in series.iteritems():
            key = wrap_list(key)

            # Get the field for the metric
            metric_alias = wrap_list(
                series.name)[0] if is_transposed else key[0]
            field = fields[metric_alias]

            data = {RAW_VALUE: raw_value(value, field)}
            display = _display_value(value, field, date_as=return_none)
            if display is not None:
                data["display"] = display

            accessor_fields = [
                fields[field_alias] for field_alias in index_names
                if field_alias is not None
            ]

            accessor = [
                safe_value(value)
                for value, field in zip(key, accessor_fields)
            ] or key

            setdeepattr(row, accessor, data)

        return row
示例#4
0
    def transform_row_index(index_values, field_map,
                            dimension_hyperlink_templates):
        # Add the index to the row
        row = {}
        for key, value in index_values.items():
            if key is None:
                continue

            field_alias = key
            field = field_map[field_alias]

            data = {RAW_VALUE: raw_value(value, field)}
            display = _display_value(value, field)
            if display is not None:
                data["display"] = display

            # If the dimension has a hyperlink template, then apply the template by formatting it with the dimension
            # values for this row. The values contained in `index_values` will always contain all of the required values
            # at this point, otherwise the hyperlink template will not be included.
            if key in dimension_hyperlink_templates:
                data["hyperlink"] = dimension_hyperlink_templates[key].format(
                    **index_values)

            safe_key = safe_value(key)
            row[safe_key] = data

        return row
示例#5
0
    def transform_index_column_headers(data_frame, field_map):
        """
        Convert the un-pivoted dimensions into ReactTable column header definitions.

        :param data_frame:
            The result set data frame
        :param field_map:
        :return:
            A list of column header definitions with the following structure.

        .. code-block:: jsx

            columns = [{
              Header: 'Column A',
              accessor: 'a',
            }, {
              Header: 'Column B',
              accessor: 'b',
            }]
        """
        columns = []
        if (not isinstance(data_frame.index, pd.MultiIndex)
                and data_frame.index.name is None):
            return columns

        for f_dimension_alias in data_frame.index.names:
            dimension = field_map[f_dimension_alias]
            header = getattr(dimension, "label", dimension.alias)

            columns.append({
                "Header": json_value(header),
                "accessor": safe_value(f_dimension_alias),
            })

        return columns
示例#6
0
 def test_decimal_value_is_returned_with_decimal_point_replaced(self):
     tests = [
         (0.0, "0$0"),
         (-1.1, "-1$1"),
         (1.1, "1$1"),
         (0.123456789, "0$123456789"),
         (-0.123456789, "-0$123456789"),
     ]
     for value, expected in tests:
         with self.subTest("using value" + str(value)):
             self.assertEqual(expected, formats.safe_value(value))
示例#7
0
    def _get_row_value_accessor(series, fields, key):
        index_names = series.index.names or []

        accessor_fields = [
            fields[field_alias] for field_alias in index_names
            if field_alias is not None
        ]
        accessor = [
            safe_value(value) for value, field in zip(key, accessor_fields)
        ] or key

        return accessor
示例#8
0
 def test_string_value_is_returned_with_only_safe_characters_replaced(self):
     tests = [
         ("abcdefghijklmnopqrstuvwxyz", "abcdefghijklmnopqrstuvwxyz"),
         ("ABCDEFGHIJKLMNOPQRSTUVWXYZ", "ABCDEFGHIJKLMNOPQRSTUVWXYZ"),
         (" ", " "),
         ("-0123456789", "-0123456789"),
         (".[]", "$$$"),
         ("a.1", "a$1"),
         ("b[0]", "b$0$"),
     ]
     for value, expected in tests:
         with self.subTest("using value" + value):
             self.assertEqual(expected, formats.safe_value(value))
示例#9
0
    def transform_row_index(
        index_values,
        field_map: Dict[str, Field],
        dimension_hyperlink_templates: Dict[str, str],
        hide_dimension_aliases: List[str],
        row_colors: List[str],
    ):
        # Add the index to the row
        row = {}
        for key, value in index_values.items():
            if key is None or key not in field_map:
                continue

            field_alias = key
            field = field_map[field_alias]

            data = {RAW_VALUE: raw_value(value, field)}
            display = _display_value(value, field)
            if display is not None:
                data["display"] = display
            if row_colors is not None:
                data["color"], data["text_color"] = row_colors

            is_totals = display == TOTALS_LABEL

            # If the dimension has a hyperlink template, then apply the template by formatting it with the dimension
            # values for this row. The values contained in `index_values` will always contain all of the required values
            # at this point, otherwise the hyperlink template will not be included.
            if not is_totals and key in dimension_hyperlink_templates:
                try:
                    data["hyperlink"] = dimension_hyperlink_templates[
                        key].format(**index_values)
                except KeyError:
                    pass

            safe_key = safe_value(key)
            row[safe_key] = data

        for dimension_alias in hide_dimension_aliases:
            if dimension_alias in row:
                del row[dimension_alias]

        return row
示例#10
0
    def transform_index_column_headers(
            data_frame: pd.DataFrame, field_map: Dict[str, Field],
            hide_dimension_aliases: List[str]) -> List[dict]:
        """
        Convert the un-pivoted dimensions into ReactTable column header definitions.

        :param data_frame:
            The result set data frame.
        :param field_map:
            A map to find metrics/operations based on their keys found in the data frame.
        :param hide_dimension_aliases:
            A set with hide dimension aliases.
        :return:
            A list of column header definitions with the following structure.

        .. code-block:: jsx

            columns = [{
              Header: 'Column A',
              accessor: 'a',
            }, {
              Header: 'Column B',
              accessor: 'b',
            }]
        """
        columns = []
        if not isinstance(data_frame.index,
                          pd.MultiIndex) and data_frame.index.name is None:
            return columns

        for f_dimension_alias in data_frame.index.names:
            if f_dimension_alias not in field_map or f_dimension_alias in hide_dimension_aliases:
                continue

            dimension = field_map[f_dimension_alias]
            header = getattr(dimension, "label", dimension.alias)

            columns.append({
                "Header": json_value(header),
                "accessor": safe_value(f_dimension_alias),
            })

        return columns
示例#11
0
 def test_inf_returned_as_inf_label(self):
     with self.subTest("positive inf"):
         self.assertEqual("inf", formats.safe_value(np.inf))
     with self.subTest("negative inf"):
         self.assertEqual("inf", formats.safe_value(-np.inf))
示例#12
0
 def test_nan_returned_as_null_label(self):
     self.assertEqual("null", formats.safe_value(np.nan))
示例#13
0
 def test_none_returned_as_null_label(self):
     self.assertEqual("null", formats.safe_value(None))