コード例 #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))
コード例 #2
0
ファイル: test_formats.py プロジェクト: mikeengland/fireant
    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))
コード例 #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)),
     )
コード例 #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))
コード例 #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))
コード例 #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 []
コード例 #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,
        }
コード例 #8
0
ファイル: test_formats.py プロジェクト: mikeengland/fireant
 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))
コード例 #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))
コード例 #10
0
ファイル: test_formats.py プロジェクト: mikeengland/fireant
 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))
コード例 #11
0
    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)
コード例 #12
0
ファイル: highcharts.py プロジェクト: mikeengland/fireant
    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
        ]
コード例 #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))
コード例 #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))
コード例 #15
0
ファイル: highcharts.py プロジェクト: mikeengland/fireant
 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))
コード例 #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))
コード例 #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))
コード例 #18
0
 def test_wrong_type_passed_to_formatter_returns_value(self):
     result = formats.display_value("abcdef", number_field)
     self.assertEqual(result, "abcdef")
コード例 #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))
コード例 #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)
コード例 #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)))
コード例 #22
0
ファイル: highcharts.py プロジェクト: mikeengland/fireant
    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
コード例 #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))
コード例 #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))
コード例 #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))
コード例 #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))
コード例 #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
コード例 #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)))
コード例 #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))
コード例 #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))