diff --git a/lib/galaxy/managers/markdown_util.py b/lib/galaxy/managers/markdown_util.py index d037e2c85e0..26787fc452d 100644 --- a/lib/galaxy/managers/markdown_util.py +++ b/lib/galaxy/managers/markdown_util.py @@ -18,6 +18,13 @@ import os import re import shutil import tempfile +from typing import ( + Any, + Dict, + List, + Match, + Optional, +) import markdown import pkg_resources @@ -26,6 +33,7 @@ try: except Exception: weasyprint = None +from galaxy.config import GalaxyAppConfiguration from galaxy.exceptions import ( MalformedContents, ServerNotConfiguredForRequest, @@ -38,6 +46,7 @@ from galaxy.managers.jobs import ( ) from galaxy.model.item_attrs import get_item_annotation_str from galaxy.model.orm.now import now +from galaxy.schema import PdfDocumentType from galaxy.util.sanitize_html import sanitize_html from .markdown_parse import GALAXY_MARKDOWN_FUNCTION_CALL_LINE, validate_galaxy_markdown @@ -197,6 +206,10 @@ class GalaxyInternalMarkdownDirectiveHandler(metaclass=abc.ABCMeta): def handle_dataset_peek(self, line, hda): pass + @abc.abstractmethod + def handle_dataset_embedded(self, line, hda): + pass + @abc.abstractmethod def handle_dataset_info(self, line, hda): pass @@ -472,7 +485,7 @@ class ToBasicMarkdownDirectiveHandler(GalaxyInternalMarkdownDirectiveHandler): def handle_job_metrics(self, line, job): job_metrics = summarize_job_metrics(self.trans, job) - metrics_by_plugin = {} + metrics_by_plugin: Dict[str, Dict[str, Any]] = {} for job_metric in job_metrics: plugin = job_metric["plugin"] if plugin not in metrics_by_plugin: @@ -557,7 +570,7 @@ class MarkdownFormatHelpers: return f"
{markdown}
" -def to_basic_markdown(trans, internal_galaxy_markdown): +def to_basic_markdown(trans, internal_galaxy_markdown: str) -> str: """Replace Galaxy Markdown extensions with plain Markdown for PDF/HTML export. """ markdown_formatting_helpers = MarkdownFormatHelpers() @@ -566,13 +579,14 @@ def to_basic_markdown(trans, internal_galaxy_markdown): return plain_markdown -def to_html(basic_markdown): +def to_html(basic_markdown: str) -> str: # Allow data: urls so we can embed images. html = sanitize_html(markdown.markdown(basic_markdown, extensions=["tables"]), allow_data_urls=True) return html -def to_pdf(trans, basic_markdown, css_paths=None) -> bytes: +def to_pdf_raw(basic_markdown: str, css_paths: Optional[List[str]] = None) -> bytes: + """Convert RAW markdown with specified CSS paths into bytes of a PDF.""" css_paths = css_paths or [] as_html = to_html(basic_markdown) directory = tempfile.mkdtemp('gxmarkdown') @@ -589,8 +603,6 @@ def to_pdf(trans, basic_markdown, css_paths=None) -> bytes: css = weasyprint.CSS(string=css_content) stylesheets.append(css) return html.write_pdf(stylesheets=stylesheets) - # font_config = FontConfiguration() - # stylesheets=[css], font_config=font_config finally: shutil.rmtree(directory) @@ -605,10 +617,14 @@ def _check_can_convert_to_pdf_or_raise(): raise ServerNotConfiguredForRequest("PDF conversion service not available.") -def internal_galaxy_markdown_to_pdf(trans, internal_galaxy_markdown, document_type) -> bytes: +def internal_galaxy_markdown_to_pdf(trans, internal_galaxy_markdown: str, document_type: PdfDocumentType) -> bytes: _check_can_convert_to_pdf_or_raise() basic_markdown = to_basic_markdown(trans, internal_galaxy_markdown) config = trans.app.config + return to_branded_pdf(basic_markdown, document_type, config) + + +def to_branded_pdf(basic_markdown: str, document_type: PdfDocumentType, config: GalaxyAppConfiguration) -> bytes: document_type_prologue = getattr(config, f"markdown_export_prologue_{document_type}s", '') or '' document_type_epilogue = getattr(config, f"markdown_export_epilogue_{document_type}s", '') or '' general_prologue = config.markdown_export_prologue or '' @@ -617,13 +633,13 @@ def internal_galaxy_markdown_to_pdf(trans, internal_galaxy_markdown, document_ty effective_epilogue = document_type_epilogue or general_epilogue branded_markdown = effective_prologue + basic_markdown + effective_epilogue css_paths = [] - general_css_path = trans.app.config.markdown_export_css + general_css_path = config.markdown_export_css document_type_css_path = getattr(config, f"markdown_export_css_{document_type}s", None) if general_css_path and os.path.exists(general_css_path): css_paths.append(general_css_path) if document_type_css_path and os.path.exists(document_type_css_path): css_paths.append(document_type_css_path) - return to_pdf(trans, branded_markdown, css_paths=css_paths) + return to_pdf_raw(branded_markdown, css_paths=css_paths) def resolve_invocation_markdown(trans, invocation, workflow_markdown): @@ -702,6 +718,8 @@ history_dataset_collection_display(input={}) if group: return group + target_match: Optional[Match] + ref_object: Optional[Any] if output_match: target_match = output_match name = find_non_empty_group(target_match) @@ -719,6 +737,7 @@ history_dataset_collection_display(input={}) target_match = None ref_object = None if ref_object: + assert target_match # tell type system, this is set when ref_object is set if ref_object_type is None: if ref_object.history_content_type == "dataset": ref_object_type = "history_dataset" @@ -772,6 +791,7 @@ def _remap_galaxy_markdown_calls(func, markdown): if matching_line: match = GALAXY_MARKDOWN_FUNCTION_CALL_LINE.match(line) + assert match # already matched return func(match.group(1), f"{matching_line}\n") else: return (container, True) diff --git a/lib/galaxy/schema/__init__.py b/lib/galaxy/schema/__init__.py index c8a40e0afaf..a87fbf2dce4 100644 --- a/lib/galaxy/schema/__init__.py +++ b/lib/galaxy/schema/__init__.py @@ -1,3 +1,4 @@ +from enum import Enum from typing import ( Dict, List, @@ -82,3 +83,8 @@ class SerializationParams(BaseModel): title='Default View', description='The item view that will be used in case none was specified.', ) + + +class PdfDocumentType(str, Enum): + invocation_report = "invocation_report" + page = "page" diff --git a/lib/galaxy/webapps/galaxy/services/pages.py b/lib/galaxy/webapps/galaxy/services/pages.py index 2d43f552bc0..f08fd0f8671 100644 --- a/lib/galaxy/webapps/galaxy/services/pages.py +++ b/lib/galaxy/webapps/galaxy/services/pages.py @@ -14,6 +14,7 @@ from galaxy.managers.pages import ( PageManager, PageSerializer, ) +from galaxy.schema import PdfDocumentType from galaxy.schema.fields import EncodedDatabaseIdField from galaxy.schema.schema import ( CreatePagePayload, @@ -128,4 +129,4 @@ class PagesService(ServiceBase): if page.latest_revision.content_format != PageContentFormat.markdown.value: raise exceptions.RequestParameterInvalidException("PDF export only allowed for Markdown based pages") internal_galaxy_markdown = page.latest_revision.content - return internal_galaxy_markdown_to_pdf(trans, internal_galaxy_markdown, 'page') + return internal_galaxy_markdown_to_pdf(trans, internal_galaxy_markdown, PdfDocumentType.page) diff --git a/lib/galaxy/workflow/reports/generators/__init__.py b/lib/galaxy/workflow/reports/generators/__init__.py index bf63bb1a66f..65adc127c19 100644 --- a/lib/galaxy/workflow/reports/generators/__init__.py +++ b/lib/galaxy/workflow/reports/generators/__init__.py @@ -11,6 +11,7 @@ from galaxy.managers.markdown_util import ( ready_galaxy_markdown_for_export, resolve_invocation_markdown, ) +from galaxy.schema import PdfDocumentType class WorkflowReportGeneratorPlugin(metaclass=ABCMeta): @@ -58,7 +59,7 @@ class WorkflowMarkdownGeneratorPlugin(WorkflowReportGeneratorPlugin, metaclass=A def generate_report_pdf(self, trans, invocation, runtime_report_config_json=None): internal_markdown = self._generate_internal_markdown(trans, invocation, runtime_report_config_json=runtime_report_config_json) - return internal_galaxy_markdown_to_pdf(trans, internal_markdown, 'invocation_report') + return internal_galaxy_markdown_to_pdf(trans, internal_markdown, PdfDocumentType.invocation_report) @abstractmethod def _generate_report_markdown(self, trans, invocation, runtime_report_config_json=None): diff --git a/setup.cfg b/setup.cfg index 651ae1c2970..86da2d71719 100644 --- a/setup.cfg +++ b/setup.cfg @@ -609,8 +609,6 @@ check_untyped_defs = False check_untyped_defs = False [mypy-galaxy.app] check_untyped_defs = False -[mypy-galaxy.managers.markdown_util] -check_untyped_defs = False [mypy-tool_shed.webapp.config] check_untyped_defs = False [mypy-galaxy.main]