Esempio n. 1
0
    def test_quick_fixes(self):
        err = PromptingError([
            PromptingError.WrongColumnType(["A"], "text",
                                           frozenset({"number"})),
            PromptingError.WrongColumnType(["B", "C"], "datetime",
                                           frozenset({"number"})),
        ])
        quick_fixes_result = err.as_quick_fixes()
        self.assertEqual(
            quick_fixes_result,
            [
                QuickFix(
                    I18nMessage.TODO_i18n("Convert Text to Numbers"),
                    QuickFixAction.PrependStep("converttexttonumber",
                                               {"colnames": ["A"]}),
                ),
                QuickFix(
                    I18nMessage.TODO_i18n("Convert Dates & Times to Numbers"),
                    QuickFixAction.PrependStep("converttexttonumber",
                                               {"colnames": ["B", "C"]}),
                ),
            ],
        )

        error_result = err.as_error_str()
        self.assertEqual(
            error_result,
            ("The column “A” must be converted from Text to Numbers.\n\n"
             "The columns “B” and “C” must be converted from Dates & Times to Numbers."
             ),
        )
Esempio n. 2
0
 def test_quick_fixes(self):
     err = PromptingError([
         PromptingError.WrongColumnType(["A"], "text",
                                        frozenset({"number"})),
         PromptingError.WrongColumnType(["B", "C"], "text",
                                        frozenset({"number"})),
     ])
     result = err.as_render_errors()
     self.assertEqual(
         result,
         [
             RenderError(
                 I18nMessage(
                     "py.renderer.execute.types.PromptingError.WrongColumnType.general.message.before_convert_buttons",
                     {
                         "columns": 1,
                         "0": "A",
                         "found_type": "text",
                     },
                     None,
                 ),
                 [
                     QuickFix(
                         I18nMessage(
                             "py.renderer.execute.types.PromptingError.WrongColumnType.general.quick_fix",
                             {"wanted_type": "number"},
                             None,
                         ),
                         QuickFixAction.PrependStep("converttexttonumber",
                                                    {"colnames": ["A"]}),
                     )
                 ],
             ),
             RenderError(
                 I18nMessage(
                     "py.renderer.execute.types.PromptingError.WrongColumnType.general.message.before_convert_buttons",
                     {
                         "columns": 2,
                         "0": "B",
                         "1": "C",
                         "found_type": "text",
                     },
                     None,
                 ),
                 [
                     QuickFix(
                         I18nMessage(
                             "py.renderer.execute.types.PromptingError.WrongColumnType.general.quick_fix",
                             {"wanted_type": "number"},
                             None,
                         ),
                         QuickFixAction.PrependStep(
                             "converttexttonumber",
                             {"colnames": ["B", "C"]}),
                     )
                 ],
             ),
         ],
     )
Esempio n. 3
0
 def test_quick_fixes_multiple_conversions(self):
     # For example, "linechart" X axis may be temporal or number
     err = PromptingError([
         PromptingError.WrongColumnType(
             ["A"], "text", frozenset({"number", "date", "timestamp"}))
     ])
     result = err.as_render_errors()
     self.assertEqual(
         result,
         [
             RenderError(
                 I18nMessage(
                     "py.renderer.execute.types.PromptingError.WrongColumnType.general.message.before_convert_buttons",
                     {
                         "columns": 1,
                         "0": "A",
                         "found_type": "text",
                     },
                     None,
                 ),
                 [
                     QuickFix(
                         I18nMessage(
                             "py.renderer.execute.types.PromptingError.WrongColumnType.general.quick_fix",
                             {"wanted_type": "date"},
                             None,
                         ),
                         QuickFixAction.PrependStep("converttexttodate",
                                                    {"colnames": ["A"]}),
                     ),
                     QuickFix(
                         I18nMessage(
                             "py.renderer.execute.types.PromptingError.WrongColumnType.general.quick_fix",
                             {"wanted_type": "number"},
                             None,
                         ),
                         QuickFixAction.PrependStep("converttexttonumber",
                                                    {"colnames": ["A"]}),
                     ),
                     QuickFix(
                         I18nMessage(
                             "py.renderer.execute.types.PromptingError.WrongColumnType.general.quick_fix",
                             {"wanted_type": "timestamp"},
                             None,
                         ),
                         QuickFixAction.PrependStep("convert-date",
                                                    {"colnames": ["A"]}),
                     ),
                 ],
             ),
         ],
     )
Esempio n. 4
0
 def test_quick_fixes_convert_to_text(self):
     err = PromptingError([
         PromptingError.WrongColumnType(["A", "B"], None,
                                        frozenset({"text"}))
     ])
     result = err.as_render_errors()
     self.assertEqual(
         result,
         [
             RenderError(
                 I18nMessage(
                     "py.renderer.execute.types.PromptingError.WrongColumnType.as_error_message.shouldBeText",
                     {
                         "columns": 2,
                         "0": "A",
                         "1": "B"
                     },
                     None,
                 ),
                 [
                     QuickFix(
                         I18nMessage(
                             "py.renderer.execute.types.PromptingError.WrongColumnType.as_quick_fixes.shouldBeText",
                             {},
                             None,
                         ),
                         QuickFixAction.PrependStep(
                             "converttotext", {"colnames": ["A", "B"]}),
                     )
                 ],
             )
         ],
     )
Esempio n. 5
0
    def test_cache_render_result(self):
        with arrow_table_context(make_column("A", [1])) as (table_path, table):
            result = LoadedRenderResult(
                path=table_path,
                table=table,
                columns=[Column("A", ColumnType.Number(format="{:,}"))],
                errors=[
                    RenderError(
                        I18nMessage("e1", {"text": "hi"}, None),
                        [
                            QuickFix(
                                I18nMessage("q1", {"var": 2}, None),
                                QuickFixAction.PrependStep("filter", {"a": "x"}),
                            )
                        ],
                    ),
                    RenderError(I18nMessage("e2", {}, None), []),
                ],
                json={"foo": "bar"},
            )
            cache_render_result(self.workflow, self.step, 1, result)

        cached = self.step.cached_render_result
        self.assertEqual(cached.step_id, self.step.id)
        self.assertEqual(cached.delta_id, 1)

        self.assertEqual(
            crr_parquet_key(cached),
            f"wf-{self.workflow.id}/wfm-{self.step.id}/delta-1.dat",
        )

        # Reading completely freshly from the DB should give the same thing
        db_step = Step.objects.get(id=self.step.id)
        from_db = db_step.cached_render_result
        self.assertEqual(from_db, cached)

        with open_cached_render_result(from_db) as result2:
            assert_arrow_table_equals(
                result2.table, make_table(make_column("A", [1], format="{:,}"))
            )
            self.assertEqual(
                result2.columns, [Column("A", ColumnType.Number(format="{:,}"))]
            )
Esempio n. 6
0
        def as_render_errors(self) -> List[RenderError]:
            """Build RenderError(s) that describe this error. 
            
            Render errors will include a QuickFix that would resolve this error."""
            if self.should_be_text:
                message = I18nMessage.trans(
                    "py.renderer.execute.types.PromptingError.WrongColumnType.as_quick_fixes.shouldBeText",
                    default="Convert to Text.",
                )
            else:
                # i18n: The parameters {found_type} and {best_wanted_type} will have values among "text", "number", "datetime"; however, including an (possibly empty) "other" case is mandatory.
                message = I18nMessage.trans(
                    "py.renderer.execute.types.PromptingError.WrongColumnType.as_quick_fixes.general",
                    default=
                    "Convert { found_type, select, text {Text} number {Numbers} datetime {Dates & Times} other {}} to {best_wanted_type, select, text {Text} number {Numbers} datetime {Dates & Times} other{}}.",
                    args={
                        "found_type": self.found_type,
                        "best_wanted_type": self.best_wanted_type_id,
                    },
                )

            params = {"colnames": self.column_names}

            if "text" in self.wanted_types:
                module_id = "converttotext"
            elif "number" in self.wanted_types:
                module_id = "converttexttonumber"
            elif "datetime" in self.wanted_types:
                module_id = "convert-date"
            else:
                raise RuntimeError(
                    f"Unhandled wanted_types: {self.wanted_types}")

            return [
                RenderError(
                    self._as_i18n_message(),
                    [
                        QuickFix(message,
                                 QuickFixAction.PrependStep(module_id, params))
                    ],
                )
            ]
Esempio n. 7
0
    def test_quick_fixes_convert_to_text(self):
        err = PromptingError([
            PromptingError.WrongColumnType(["A", "B"], None,
                                           frozenset({"text"}))
        ])
        quick_fixes_result = err.as_quick_fixes()
        self.assertEqual(
            quick_fixes_result,
            [
                QuickFix(
                    I18nMessage.TODO_i18n("Convert to Text"),
                    QuickFixAction.PrependStep("converttotext",
                                               {"colnames": ["A", "B"]}),
                )
            ],
        )

        error_result = err.as_error_str()
        self.assertEqual(error_result,
                         "The columns “A” and “B” must be converted to Text.")
Esempio n. 8
0
        def as_quick_fix(self):
            """Build a QuickFix that would resolve this error."""
            if self.should_be_text:
                prompt = f"Convert to {self.best_wanted_type_name}"
            else:
                prompt = (
                    f"Convert {self.found_type_name} to {self.best_wanted_type_name}"
                )
            params = {"colnames": self.column_names}

            if "text" in self.wanted_types:
                module_id = "converttotext"
            elif "number" in self.wanted_types:
                module_id = "converttexttonumber"
            elif "datetime" in self.wanted_types:
                module_id = "convert-date"
            else:
                raise RuntimeError(f"Unhandled wanted_types: {self.wanted_types}")

            return QuickFix(
                I18nMessage.TODO_i18n(prompt),
                QuickFixAction.PrependStep(module_id, params),
            )
Esempio n. 9
0
    def test_cache_render_result(self):
        result = RenderResult(
            arrow_table({"A": [1]}),
            [
                RenderError(
                    I18nMessage("e1", [1, "x"]),
                    [
                        QuickFix(
                            I18nMessage("q1", []),
                            QuickFixAction.PrependStep("filter", {"a": "x"}),
                        )
                    ],
                ),
                RenderError(I18nMessage("e2", []), []),
            ],
            {"foo": "bar"},
        )
        cache_render_result(self.workflow, self.wf_module, self.delta.id,
                            result)

        cached = self.wf_module.cached_render_result
        self.assertEqual(cached.wf_module_id, self.wf_module.id)
        self.assertEqual(cached.delta_id, self.delta.id)

        self.assertEqual(
            crr_parquet_key(cached),
            f"wf-{self.workflow.id}/wfm-{self.wf_module.id}/delta-{self.delta.id}.dat",
        )

        # Reading completely freshly from the DB should give the same thing
        db_wf_module = WfModule.objects.get(id=self.wf_module.id)
        from_db = db_wf_module.cached_render_result
        self.assertEqual(from_db, cached)

        with open_cached_render_result(from_db) as result2:
            assert_render_result_equals(result2, result)
Esempio n. 10
0
def _dict_to_quick_fix_action(value: Dict[str, Any]) -> QuickFixAction:
    if value["type"] == "prependStep":
        return QuickFixAction.PrependStep(value["moduleSlug"],
                                          value["partialParams"])
    else:
        raise ValueError("Unhandled type in QuickFixAction: %r", value)
Esempio n. 11
0
        def as_render_error(self) -> RenderError:
            """Build RenderError(s) that describe this error.

            Render errors should include at least one QuickFix to resolve the error.

            Errors the user can see:

                (wanted_types = {date, timestamp})
                "A", "B" and 2 others are Text.
                     [Convert to Date]
                     [Convert to Timestamp]

                (wanted_types = {number})
                "A" and "B" are Date. Select Number.

                (wanted_types = {text} - special case because all types convert to text)
                "A" is not Text.
                     [Convert to Text]
            """
            if self.should_be_text:
                icu_args = {
                    "columns": len(self.column_names),
                    **{
                        str(i): name
                        for i, name in enumerate(self.column_names)
                    },
                }
                # i18n: The parameter {columns} will contain the total number of columns that need to be converted; you will also receive the column names as {0}, {1}, {2}, etc.
                message = trans(
                    "py.renderer.execute.types.PromptingError.WrongColumnType.as_error_message.shouldBeText",
                    default="{ columns, plural, offset:2"
                    " =1 {“{0}” is not Text.}"
                    " =2 {“{0}” and “{1}” are not Text.}"
                    " =3 {“{0}”, “{1}” and “{2}” are not Text.}"
                    " other {“{0}”, “{1}” and # others are not Text.}}",
                    arguments=icu_args,
                )
                return RenderError(
                    message,
                    quick_fixes=[
                        QuickFix(
                            trans(
                                "py.renderer.execute.types.PromptingError.WrongColumnType.as_quick_fixes.shouldBeText",
                                default="Convert to Text",
                            ),
                            QuickFixAction.PrependStep(
                                "converttotext",
                                dict(colnames=self.column_names)),
                        )
                    ],
                )
            else:
                icu_args = {
                    "columns": len(self.column_names),
                    "found_type": self.found_type,
                    **{
                        str(i): name
                        for i, name in enumerate(self.column_names)
                    },
                }

                quick_fixes = [
                    QuickFix(
                        # i18n: The parameter {wanted_type} will have values among "text", "number", "date", "timestamp" or "other". ("other" may translate to "".)
                        trans(
                            "py.renderer.execute.types.PromptingError.WrongColumnType.general.quick_fix",
                            default=
                            "Convert to {wanted_type, select, text {Text} number {Number} date {Date} timestamp {Timestamp} other {}}",
                            arguments=dict(wanted_type=wanted_type),
                        ),
                        QuickFixAction.PrependStep(
                            _QUICK_FIX_CONVERSIONS[(self.found_type,
                                                    wanted_type)],
                            dict(colnames=self.column_names),
                        ),
                    ) for wanted_type in sorted(
                        self.wanted_types)  # sort for determinism
                    if (self.found_type, wanted_type) in _QUICK_FIX_CONVERSIONS
                ]

                if quick_fixes:
                    # i18n: The parameter {columns} will contain the total number of columns that need to be converted; you will also receive the column names: {0}, {1}, {2}, etc. The parameter {found_type} will be "date", "text", "number", "timestamp" or "other". ("other" may translate to "".)
                    message = trans(
                        "py.renderer.execute.types.PromptingError.WrongColumnType.general.message.before_convert_buttons",
                        default="{columns, plural, offset:2"
                        " =1 {“{0}” is {found_type, select, text {Text} number {Number} timestamp {Timestamp} date {Date} other {}}.}"
                        " =2 {“{0}” and “{1}” are {found_type, select, text {Text} number {Number} date {Date} timestamp {Timestamp} other {}}.}"
                        " =3 {“{0}”, “{1}” and “{2}” are {found_type, select, text {Text} number {Number} date {Date} timestamp {Timestamp} other {}}.}"
                        " other {“{0}”, “{1}” and # others are {found_type, select, text {Text} number {Number} date {Date} timestamp {Timestamp} other {}}.}}",
                        arguments=icu_args,
                    )
                else:
                    # i18n: The parameter {columns} will contain the total number of columns that need to be converted; you will also receive the column names: {0}, {1}, {2}, etc. The parameters {found_type} and {best_wanted_type} will be "date", "text", "number", "timestamp" or "other". ("other" may translate to "".)
                    message = trans(
                        "py.renderer.execute.types.PromptingError.WrongColumnType.general.message.without_convert_buttons",
                        default="{columns, plural, offset:2"
                        " =1 {“{0}” is {found_type, select, text {Text} number {Number} timestamp {Timestamp} date {Date} other {}}. Select {best_wanted_type, select, text {Text} number {Number} date {Date} timestamp {Timestamp} other {}}.}"
                        " =2 {“{0}” and “{1}” are {found_type, select, text {Text} number {Number} date {Date} timestamp {Timestamp} other {}}. Select {best_wanted_type, select, text {Text} number {Number} date {Date} timestamp {Timestamp} other {}}.}"
                        " =3 {“{0}”, “{1}” and “{2}” are {found_type, select, text {Text} number {Number} date {Date} timestamp {Timestamp} other {}}. Select {best_wanted_type, select, text {Text} number {Number} date {Date} timestamp {Timestamp} other {}}.}"
                        " other {“{0}”, “{1}” and # others are {found_type, select, text {Text} number {Number} date {Date} timestamp {Timestamp} other {}}. Select {best_wanted_type, select, text {Text} number {Number} date {Date} timestamp {Timestamp} other {}}.}}",
                        arguments=dict(best_wanted_type=self.best_wanted_type,
                                       **icu_args),
                    )

                return RenderError(message, quick_fixes=quick_fixes)