Beispiel #1
0
 def test_none_returned_as_nan_value_arg(self):
     for expected in ("", "abcdefg"):
         self.assertEqual(
             expected, formats.display_value(None,
                                             None,
                                             null_value=expected))
     self.assertEqual("null", formats.display_value(None, None))
Beispiel #2
0
    def test_converts_percentage_to_decimal_when_use_raw_value_True_and_suffix_is_percentage(self):
        with self.subTest('when no precision'):
            field = Field("number", None, data_type=DataType.number, suffix="%")
            self.assertEqual("0.87123123131", formats.display_value(87.123123131, field, use_raw_value=True))

        with self.subTest('when precision'):
            field = Field("number", None, data_type=DataType.number, suffix="%", precision=2)
            self.assertEqual("0.0739", formats.display_value(7.38652, field, use_raw_value=True))
Beispiel #3
0
 def test_date_value_with_hour_interval_is_returned_as_date_string_to_the_minute_rounded_to_hour(
     self, ):
     self.assertEqual(
         "2019-01-01 12:00",
         formats.display_value(datetime(2019, 1, 1, 12, 30, 2),
                               hour(date_field)),
     )
Beispiel #4
0
 def test_style_numbers_with_thousands_separator(self):
     euro_field = Field("number",
                        None,
                        data_type=DataType.number,
                        thousands=",")
     self.assertEqual("1,000,000",
                      formats.display_value(1000000, euro_field))
Beispiel #5
0
 def test_raw_value_when_precision_specified(self):
     field = Field("number", None, data_type=DataType.number, precision=4)
     self.assertEqual(
         "1244996.1138",
         formats.display_value(1244996.1138000000000000,
                               field,
                               use_raw_value=True))
Beispiel #6
0
    def _categories(self, data_frame, dimension_map, dimension_alias=None):
        is_mi = isinstance(data_frame.index, pd.MultiIndex)
        levels = data_frame.index.levels if is_mi else [data_frame.index]

        first_level = None

        if dimension_alias:
            level_index = 0

            for i, level in enumerate(levels):
                if level.name == dimension_alias:
                    level_index = i
                    break

            first_level = levels[level_index]
        else:
            first_level = levels[0]

        if first_level is not None and first_level.name is not None:
            dimension_alias = first_level.name
            dimension = dimension_map[dimension_alias]
            return [
                formats.display_value(category, dimension) or category
                if not pd.isnull(category) else None
                for category in first_level
            ]

        return []
Beispiel #7
0
    def _render_x_axis(self, data_frame, dimensions, fields):
        """
        Renders the xAxis configuration.

        https://api.highcharts.com/highcharts/xAxis

        :param data_frame:
        :param dimensions:
        :return:
        """
        is_mi = isinstance(data_frame.index, pd.MultiIndex)
        first_level = data_frame.index.levels[0] if is_mi else data_frame.index

        is_timeseries = dimensions and dimensions[0].data_type == DataType.date
        if is_timeseries:
            return {
                "type": "datetime",
                "visible": self.x_axis_visible,
            }

        categories = ["All"]
        if first_level.name is not None:
            dimension_alias = utils.alias_for_alias_selector(first_level.name)
            dimension = fields[dimension_alias]
            categories = [
                formats.display_value(category, dimension) or category
                for category in first_level
            ]

        return {
            "type": "category",
            "categories": categories,
            "visible": self.x_axis_visible,
        }
Beispiel #8
0
 def test_totals_markers_are_returned_as_text_totals_label(self):
     for marker, field in [
         (TEXT_TOTALS, text_field),
         (NUMBER_TOTALS, number_field),
         (DATE_TOTALS, date_field),
     ]:
         with self.subTest(field.data_type):
             self.assertEqual("Totals", formats.display_value(marker, field))
Beispiel #9
0
 def test_use_raw_value_int(self):
     field = Field("number",
                   None,
                   data_type=DataType.number,
                   suffix="€",
                   prefix="$")
     self.assertEqual(
         "1234", formats.display_value(1234, field, use_raw_value=True))
Beispiel #10
0
 def test_style_numbers_with_mixed(self):
     euro_field = Field(
         "number",
         None,
         data_type=DataType.number,
         prefix="$",
         thousands=",",
         precision=2,
     )
     self.assertEqual("$-1,000,000.00", formats.display_value(-1000000, euro_field))
    def fetch(self, hint=None, force_include=()) -> List[str]:
        """
        Fetch the data for this query and transform it into the widgets.

        :param hint:
            For database vendors that support it, add a query hint to collect analytics on the queries triggered by
            fireant.
        :param force_include:
            A list of dimension values to include in the result set. This can be used to avoid having necessary results
            cut off due to the pagination.  These results will be returned at the head of the results.
        :return:
            A list of dict (JSON) objects containing the widget configurations.
        """
        query = add_hints(self.sql, hint)[0]
        dimension = self.dimensions[0]
        alias_definition = dimension.definition.as_(
            alias_selector(dimension.alias))
        dimension_definition = dimension.definition

        if self.hint_table:
            alias_definition = alias_definition.replace_table(
                alias_definition.table, self.hint_table)
            dimension_definition = dimension.definition.replace_table(
                dimension_definition.table, self.hint_table)

        if force_include:
            include = self.dataset.database.to_char(dimension_definition).isin(
                [str(x) for x in force_include])

            # Ensure that these values are included
            query = query.orderby(include, order=Order.desc)

        # Filter out NULL values from choices
        query = query.where(dimension_definition.notnull())

        # Order by the dimension definition that the choices are for
        query = query.orderby(alias_definition)
        max_rows_returned, data = fetch_data(self.dataset.database, [query],
                                             self.dimensions)

        if len(data.index.names) > 1:
            display_alias = data.index.names[1]
            data.reset_index(display_alias, inplace=True)
            choices = data[display_alias]

        else:
            data["display"] = data.index.tolist()
            choices = data["display"]

        dimension_display = self.dimensions[-1]
        choices = choices.map(
            lambda raw: display_value(raw, dimension_display) or raw)
        return self._transform_for_return(choices,
                                          max_rows_returned=max_rows_returned)
Beispiel #12
0
    def _categories(self,
                    data_frame: pd.DataFrame,
                    dimension_map: Dict[str, Field],
                    dimension_alias: Optional[str] = None) -> List[str]:
        values, dimension = self._values_and_dimension(data_frame,
                                                       dimension_map,
                                                       dimension_alias)

        return [
            formats.display_value(value, dimension) or value
            for value in values
        ]
Beispiel #13
0
 def test_decimal_value_is_returned_as_string_self_with_excess_zeroes_stripped_rounded_to_6_places(
     self, ):
     tests = [
         (0.0, "0"),
         (-1.1, "-1.1"),
         (1.1, "1.1"),
         (0.123456789, "0.123457"),
         (-0.123456789, "-0.123457"),
     ]
     for value, expected in tests:
         with self.subTest("using value" + str(value)):
             self.assertEqual(expected,
                              formats.display_value(value, number_field))
Beispiel #14
0
 def test_raw_value_formats_integers_with_trailing_zeros(self):
     field = Field("number", None, data_type=DataType.number)
     self.assertEqual(
         "126500", formats.display_value(126500, field, use_raw_value=True))
Beispiel #15
0
 def _format_dimension_values(dimensions: List[Field],
                              dimension_values: list) -> str:
     return ", ".join(
         str.strip(formats.display_value(value, dimension) or str(value))
         for value, dimension in zip(dimension_values, dimensions))
Beispiel #16
0
 def test_style_numbers_with_precision(self):
     euro_field = Field("number",
                        None,
                        data_type=DataType.number,
                        precision=2)
     self.assertEqual("1.23", formats.display_value(1.234567, euro_field))
Beispiel #17
0
 def test_style_numbers_with_suffix(self):
     euro_field = Field("number",
                        None,
                        data_type=DataType.number,
                        suffix="€")
     self.assertEqual("1€", formats.display_value(1, euro_field))
Beispiel #18
0
 def test_wrong_type_passed_to_formatter_returns_value(self):
     result = formats.display_value("abcdef", number_field)
     self.assertEqual(result, "abcdef")
Beispiel #19
0
 def test_boolean_value_is_returned_as_self_lower_case(self):
     for value in (True, False):
         with self.subTest("using value=" + str(value)):
             self.assertEqual(
                 str(value).lower(),
                 formats.display_value(value, boolean_field))
Beispiel #20
0
 def test_date_value_with_quarter_interval_is_returned_as_date_string_to_the_quarter_and_year(
         self):
     dates = (date(2019, m, 1) for m in range(1, 13))
     result = [formats.display_value(d, quarter(date_field)) for d in dates]
     self.assertEqual(result, ['Q1 2019'] * 3 + ['Q2 2019'] * 3 +
                      ['Q3 2019'] * 3 + ['Q4 2019'] * 3)
Beispiel #21
0
 def test_date_value_with_week_interval_is_returned_as_date_string_to_the_year_and_week_number(
     self, ):
     for d in (date(2019, 2, 25), datetime(2019, 2, 25, 12, 30, 2)):
         with self.subTest("with " + d.__class__.__name__):
             self.assertEqual("W08 2019-02-25",
                              formats.display_value(d, week(date_field)))
Beispiel #22
0
    def transform(
        self,
        data_frame: pd.DataFrame,
        dimensions: List[Field],
        references: List[Reference],
        annotation_frame: Optional[pd.DataFrame] = None,
    ):
        """
        - Main entry point -

        Transforms a data frame into HighCharts JSON format.

        See https://api.highcharts.com/highcharts/

        :param data_frame:
            The data frame containing the data. Index must match the dimensions parameter.
        :param dimensions:
            A list of dimensions that are being rendered.
        :param references:
            A list of references that are being rendered.
        :param annotation_frame:
            A data frame containing annotation data.
        :return:
            A dict or a list of dicts meant to be dumped as JSON.
        """
        result_df = data_frame.copy()

        hide_aliases = self.hide_aliases(dimensions)
        self.hide_data_frame_indexes(result_df, hide_aliases)

        dimension_map = {
            alias_selector(dimension.alias): dimension
            for dimension in dimensions
        }

        # Nan/None values in the index can break split dimension feature,
        # because xs method cannot
        result_df.rename(index={np.nan: formats.BLANK_VALUE}, inplace=True)

        render_group = []
        split_dimension = self.split_dimension

        if split_dimension and split_dimension.data_type != DataType.date:
            split_dimension_alias = alias_selector(split_dimension.alias)

            # Categories method cannot be reused here, given the totals label wouldn't be correctly
            # mapped to the totals value in the split dimension column.
            values, _ = self._values_and_dimension(result_df, dimension_map,
                                                   split_dimension_alias)

            for value in values:
                render_group.append([
                    result_df.xs(value or '',
                                 level=split_dimension_alias,
                                 drop_level=False),
                    formats.display_value(value, split_dimension) or value,
                ])

        if not render_group:
            render_group = [(result_df, None)]

        num_charts = len(render_group)

        charts = [
            self._render_individual_chart(
                chart_df,
                dimensions,
                references,
                annotation_frame=annotation_frame,
                title_suffix=title_suffix,
                num_charts=num_charts,
            ) for chart_df, title_suffix in render_group
        ]

        return charts[0] if num_charts == 1 else charts
Beispiel #23
0
 def test_nan_returned_as_string(self):
     for expected in ("", "abcdefg"):
         self.assertEqual(
             expected,
             formats.display_value(np.nan, None, nan_value=expected))
     self.assertEqual("NaN", formats.display_value(np.nan, None))
Beispiel #24
0
 def test_int_value_is_returned_as_string_self(self):
     for value in (0, -1, 1, 100):
         with self.subTest("using value" + str(value)):
             self.assertEqual(str(value),
                              formats.display_value(value, number_field))
Beispiel #25
0
 def test_text_value_is_returned_as_none(self):
     for value in ("abc", " dc23d- 0f30fi", ""):
         with self.subTest("using value" + value):
             self.assertIsNone(formats.display_value(value, text_field))
Beispiel #26
0
 def test_raw_value_does_not_trim_zero_value(self):
     field = Field("number", None, data_type=DataType.number)
     self.assertEqual("0",
                      formats.display_value(0, field, use_raw_value=True))
Beispiel #27
0
    def transform(self,
                  data_frame,
                  dimensions,
                  references,
                  annotation_frame=None):
        """
        - Main entry point -

        Transforms a data frame into HighCharts JSON format.

        See https://api.highcharts.com/highcharts/

        :param data_frame:
            The data frame containing the data. Index must match the dimensions parameter.
        :param dimensions:
            A list of dimensions that are being rendered.
        :param references:
            A list of references that are being rendered.
        :param annotation_frame:
            A data frame containing annotation data.
        :return:
            A dict or a list of dicts meant to be dumped as JSON.
        """
        result_df = data_frame.copy()

        hide_dimensions = {
            dimension
            for dimension in dimensions if dimension.fetch_only
        }
        self.hide_data_frame_indexes(result_df, hide_dimensions)

        dimension_map = {
            alias_selector(dimension.alias): dimension
            for dimension in dimensions
        }

        render_group = []
        split_dimension = self.split_dimension

        if split_dimension:
            split_dimension_alias = alias_selector(split_dimension.alias)

            categories = self._categories(
                result_df,
                dimension_map,
                split_dimension_alias,
            )

            for category in categories:
                render_group.append([
                    result_df.xs(category,
                                 level=split_dimension_alias,
                                 drop_level=False),
                    formats.display_value(category, split_dimension)
                    or category,
                ])

        if not render_group:
            render_group = [(result_df, None)]

        num_charts = len(render_group)

        charts = [
            self._render_individual_chart(
                chart_df,
                dimensions,
                references,
                annotation_frame=annotation_frame,
                titleSuffix=titleSuffix,
                num_charts=num_charts,
            ) for chart_df, titleSuffix in render_group
        ]

        return charts[0] if num_charts == 1 else charts
Beispiel #28
0
 def test_date_value_with_year_interval_is_returned_as_date_string_to_the_year(
         self):
     for d in (date(2019, 1, 1), datetime(2019, 1, 1, 12, 30, 2)):
         with self.subTest("with " + d.__class__.__name__):
             self.assertEqual("2019",
                              formats.display_value(d, year(date_field)))
Beispiel #29
0
 def test_style_negative_numbers_with_prefix(self):
     dollar_field = Field("number",
                          None,
                          data_type=DataType.number,
                          prefix="$")
     self.assertEqual("$-1", formats.display_value(-1, dollar_field))
Beispiel #30
0
 def test_inf_returned_as_inf_label(self):
     with self.subTest("positive inf"):
         self.assertEqual("Inf", formats.display_value(np.inf, None))
     with self.subTest("negative inf"):
         self.assertEqual("Inf", formats.display_value(-np.inf, None))