def add_html_context_to_job()

in src/wagtail_localize_smartling/api/client.py [0:0]


    def add_html_context_to_job(self, *, job: "Job"):
        """
        To help with translation, Smartling supports the idea of a
        "visual context" for translators, which effectively gives them
        a real-time/WYSIWYG view of the page they are translating.

        We push info about the context, then trigger its processing, via
        a special combined-action API endpoint:
        https://api-reference.smartling.com/#tag/Context/operation/uploadAndMatchVisualContext

        As for how we get the info to send as a visual context, that is up to the
        implementation that is using wagtail-localize-smartling to decide, via
        the use of a configurable callback function - see `VISUAL_CONTEXT_CALLBACK`
        in the settings or the README.

        If the callback is defined, it will be used to generate the the visual
        context to send to Smartling.

        The callback must take the Job instance and return two values:

        1. A full, absolute URL for the page that shows the content used
           to generate that Job
        2. The HTML of that same page

        e.g.

            from wagtail.models import Page
            from wagtail_localize.models import Job
            from wagtail_localize_smartling.exceptions import IncapableVisualContextCallback

            def get_visual_context(job: Job) -> tuple[str, str]:

                # This assumes the page is live and visible. If the page is a
                # draft, you will need a some custom work to expose the draft
                # version of the page

                content_obj = job.translation_source.get_source_instance()

                # IMPORTANT: if your translatable objects include some where a visual
                # context is not available or appropriate (eg a Snippet, rather than
                # a Page), then your settings.VISUAL_CONTEXT_CALLBACK function should
                # raise IncapableVisualContextCallback with an explaination
                #
                # Below, we simply check if the object is a Page, but depending
                # on how your objects are previewable, you could instead use
                # isinstance(content_obj, PreviewableMixin)

                if not isinstance(content_obj, Page):
                    raise IncapableVisualContextCallback(
                        "Object was not visually previewable"
                    )

                page_url = page.full_url

                html = # code to render that page instance

                return page_url, html

        """  # noqa: E501

        if not (
            visual_context_callback_fn := smartling_settings.VISUAL_CONTEXT_CALLBACK
        ):
            logger.info("No visual context callback configured")
            return

        try:
            url, html = visual_context_callback_fn(job)
        except IncapableVisualContextCallback as ex:
            logger.info(
                "Visual context callback refused to provide values. "
                f"Reason: {str(ex)}. Not sending visual context."
            )
            return

        # data:
        # `name` - url of the page the Job is for
        # `matchparams` - config params for Smartling's string matching
        # `content` - the HTML of the relevant Page for this Job, as bytes

        data_payload: dict[str, Any] = {
            "name": url,
            "matchparams": {
                "translationJobUids": [job.translation_job_uid],
            },
        }

        # The file payload contains the rendered HTML of the page
        # being translated. It needs to be send as multipart form
        # data, so we turn the HTML string into a bytearray
        # and pass it along with a filename based on the slug
        # of the page

        if isinstance(html, str):
            html = bytearray(html, "utf-8")

        filename = utils.get_filename_for_visual_context(url)

        file_payload: dict[str, tuple[str, bytearray, str]] = {
            "content": (filename, html, "text/html"),
        }

        logger.info(
            "Sending visual context to Smartling for Job %s for URL %s",
            job.translation_job_uid,
            url,
        )

        result = self._request(
            method="POST",
            path=f"/context-api/v2/projects/{quote(job.project.project_id)}/contexts/upload-and-match-async",
            response_serializer_class=AddVisualContextToJobSerializer,
            files=file_payload,
            data=data_payload,
        )

        logger.info(
            "Visual context sent. processUid returned: %s", result.get("processUid")
        )

        return result