diff --git a/strictdoc/core/traceability_index.py b/strictdoc/core/traceability_index.py index ec26434df..c8665d44a 100644 --- a/strictdoc/core/traceability_index.py +++ b/strictdoc/core/traceability_index.py @@ -31,6 +31,7 @@ SingleValidationError, ) from strictdoc.core.tree_cycle_detector import TreeCycleDetector +from strictdoc.core.validation_index import ValidationIndex from strictdoc.helpers.cast import assert_cast, assert_optional_cast from strictdoc.helpers.file_modification_time import set_file_modification_time from strictdoc.helpers.mid import MID @@ -54,7 +55,7 @@ def __init__( self._file_traceability_index: FileTraceabilityIndex = ( file_traceability_index ) - + self.validation_index: ValidationIndex = ValidationIndex() self.graph_database: GraphDatabase = graph_database self.document_tree: DocumentTree = document_tree self.asset_manager: Optional[AssetManager] = None diff --git a/strictdoc/core/validation_index.py b/strictdoc/core/validation_index.py new file mode 100644 index 000000000..27007e379 --- /dev/null +++ b/strictdoc/core/validation_index.py @@ -0,0 +1,50 @@ +from typing import Dict, List, Optional, Union + +from strictdoc.backend.sdoc.models.model import ( + SDocDocumentIF, + SDocNodeIF, +) + + +class ValidationIndex: + def __init__(self) -> None: + self.node_issues: Dict[ + Union[SDocNodeIF, SDocDocumentIF], Dict[Optional[str], List[str]] + ] = {} + + def add_issue( + self, + node: Union[SDocNodeIF, SDocDocumentIF], + issue: str, + field: Optional[str] = None, + subject: Optional[str] = None, + ) -> None: + self.node_issues.setdefault(node, {}).setdefault(field, []).append( + issue + ) + + if isinstance(node, SDocNodeIF): + document_node = node.get_parent_or_including_document() + self.node_issues.setdefault(document_node, {}).setdefault( + None, [] + ).append(issue) + + print_issue = issue + if subject is not None: + print_issue = f"{issue} {subject}" + print(f"warning: {print_issue}") # noqa: T201 + + def get_issues( + self, + node: Union[SDocNodeIF, SDocDocumentIF], + field: Optional[str] = None, + ) -> Optional[List[str]]: + if node not in self.node_issues: + return None + node_issues = self.node_issues[node] + if field not in node_issues: + return None + issues = node_issues[field] + if isinstance(node, SDocDocumentIF) and not node.document_is_included(): + return [f"Document has {len(issues)} issues."] + return issues diff --git a/strictdoc/export/html/_static/element.css b/strictdoc/export/html/_static/element.css index fc5c6af86..ca00643b7 100644 --- a/strictdoc/export/html/_static/element.css +++ b/strictdoc/export/html/_static/element.css @@ -1625,7 +1625,7 @@ sdoc-tabs.in_aside_panel sdoc-tab[active] { grid-column: 1 / -1; position: relative; color: var(--color-danger); - padding: var(--base-rhythm); + padding: var(--base-rhythm) 0; font-size: var(--font-size-sm); line-height: 1.5; } @@ -1634,10 +1634,10 @@ sdoc-tabs.in_aside_panel sdoc-tab[active] { position: relative; padding: calc(.5 * var(--base-rhythm)) var(--base-rhythm); border-radius: calc(.5 * var(--base-rhythm)); - border: 2px solid; + border: 1px solid; } -.field_issue-ribbon::before { +sdoc-node-content .field_issue-ribbon::before { position: absolute; content: ''; bottom: 100%; @@ -1653,6 +1653,7 @@ sdoc-tabs.in_aside_panel sdoc-tab[active] { content: ''; position: absolute; inset: 0; + pointer-events: none; } /* diff --git a/strictdoc/export/html/generators/document.py b/strictdoc/export/html/generators/document.py index f830097c0..2ab208788 100644 --- a/strictdoc/export/html/generators/document.py +++ b/strictdoc/export/html/generators/document.py @@ -36,7 +36,8 @@ def export( project_config=project_config, link_renderer=link_renderer, markup_renderer=markup_renderer, + jinja_environment=html_templates.jinja_environment(), git_client=git_client, standalone=standalone, ) - return view_object.render_screen(html_templates.jinja_environment()) + return view_object.render_screen() diff --git a/strictdoc/export/html/generators/document_deep_trace.py b/strictdoc/export/html/generators/document_deep_trace.py index b946fc903..0f4436aab 100644 --- a/strictdoc/export/html/generators/document_deep_trace.py +++ b/strictdoc/export/html/generators/document_deep_trace.py @@ -35,7 +35,8 @@ def export_deep( project_config=project_config, link_renderer=link_renderer, markup_renderer=markup_renderer, + jinja_environment=html_templates.jinja_environment(), git_client=git_client, standalone=False, ) - return view_object.render_screen(html_templates.jinja_environment()) + return view_object.render_screen() diff --git a/strictdoc/export/html/generators/document_pdf.py b/strictdoc/export/html/generators/document_pdf.py index 8859984ce..a3b1b689c 100644 --- a/strictdoc/export/html/generators/document_pdf.py +++ b/strictdoc/export/html/generators/document_pdf.py @@ -36,7 +36,8 @@ def export( project_config=project_config, link_renderer=link_renderer, markup_renderer=markup_renderer, + jinja_environment=html_templates.jinja_environment(), git_client=git_client, standalone=standalone, ) - return view_object.render_screen(html_templates.jinja_environment()) + return view_object.render_screen() diff --git a/strictdoc/export/html/generators/document_table.py b/strictdoc/export/html/generators/document_table.py index 814e5b109..0f5c0bf13 100644 --- a/strictdoc/export/html/generators/document_table.py +++ b/strictdoc/export/html/generators/document_table.py @@ -35,7 +35,8 @@ def export( project_config=project_config, link_renderer=link_renderer, markup_renderer=markup_renderer, + jinja_environment=html_templates.jinja_environment(), git_client=git_client, standalone=False, ) - return view_object.render_screen(html_templates.jinja_environment()) + return view_object.render_screen() diff --git a/strictdoc/export/html/generators/document_trace.py b/strictdoc/export/html/generators/document_trace.py index 7a01e8e1f..94c86daa9 100644 --- a/strictdoc/export/html/generators/document_trace.py +++ b/strictdoc/export/html/generators/document_trace.py @@ -35,7 +35,8 @@ def export( project_config=project_config, link_renderer=link_renderer, markup_renderer=markup_renderer, + jinja_environment=html_templates.jinja_environment(), git_client=git_client, standalone=False, ) - return view_object.render_screen(html_templates.jinja_environment()) + return view_object.render_screen() diff --git a/strictdoc/export/html/generators/view_objects/document_screen_view_object.py b/strictdoc/export/html/generators/view_objects/document_screen_view_object.py index 740aad597..24a750227 100644 --- a/strictdoc/export/html/generators/view_objects/document_screen_view_object.py +++ b/strictdoc/export/html/generators/view_objects/document_screen_view_object.py @@ -14,7 +14,11 @@ from strictdoc.backend.sdoc.models.document import SDocDocument from strictdoc.backend.sdoc.models.document_view import ViewElement from strictdoc.backend.sdoc.models.grammar_element import GrammarElement -from strictdoc.backend.sdoc.models.model import SDocElementIF +from strictdoc.backend.sdoc.models.model import ( + SDocDocumentIF, + SDocElementIF, + SDocNodeIF, +) from strictdoc.backend.sdoc.models.node import SDocNode, SDocNodeField from strictdoc.core.document_iterator import DocumentIterationContext from strictdoc.core.document_tree import DocumentTree @@ -48,6 +52,7 @@ def __init__( project_config: ProjectConfig, link_renderer: LinkRenderer, markup_renderer: MarkupRenderer, + jinja_environment: JinjaEnvironment, git_client: GitClient, standalone: bool, ): @@ -58,6 +63,7 @@ def __init__( self.project_config: ProjectConfig = project_config self.link_renderer: LinkRenderer = link_renderer self.markup_renderer: MarkupRenderer = markup_renderer + self.jinja_environment: JinjaEnvironment = jinja_environment self.git_client: GitClient = git_client self.standalone: bool = standalone self.document_iterator = self.traceability_index.get_document_iterator( @@ -82,39 +88,37 @@ def __init__( def has_included_document(self) -> bool: return len(self.document.included_documents) > 0 - def render_screen(self, jinja_environment: JinjaEnvironment) -> Markup: + def render_screen(self) -> Markup: if self.document_type.is_document(): if self.document.config.layout == "Website": - return jinja_environment.render_template_as_markup( + return self.jinja_environment.render_template_as_markup( "website/document/index.jinja", view_object=self ) - return jinja_environment.render_template_as_markup( + return self.jinja_environment.render_template_as_markup( "screens/document/document/index.jinja", view_object=self ) elif self.document_type.is_table(): - return jinja_environment.render_template_as_markup( + return self.jinja_environment.render_template_as_markup( "screens/document/table/index.jinja", view_object=self ) elif self.document_type.is_trace(): - return jinja_environment.render_template_as_markup( + return self.jinja_environment.render_template_as_markup( "screens/document/traceability/index.jinja", view_object=self ) elif self.document_type.is_deeptrace(): - return jinja_environment.render_template_as_markup( + return self.jinja_environment.render_template_as_markup( "screens/document/traceability_deep/index.jinja", view_object=self, ) elif self.document_type.is_pdf(): - return jinja_environment.render_template_as_markup( + return self.jinja_environment.render_template_as_markup( "screens/document/pdf/index.jinja", view_object=self ) else: raise NotImplementedError(self.document_type) # pragma: no cover - def render_updated_screen( - self, jinja_environment: JinjaEnvironment - ) -> Markup: - output = jinja_environment.render_template_as_markup( + def render_updated_screen(self) -> Markup: + output = self.jinja_environment.render_template_as_markup( "actions/" "document/" "create_requirement/" @@ -122,7 +126,7 @@ def render_updated_screen( view_object=self, ) - output += jinja_environment.render_template_as_markup( + output += self.jinja_environment.render_template_as_markup( "actions/document/_shared/stream_updated_toc.jinja.html", view_object=self, ) @@ -132,7 +136,6 @@ def render_updated_screen( def render_updated_nodes_and_toc( self, nodes: Sequence[Union[SDocDocument, SDocNode]], - jinja_environment: JinjaEnvironment, ) -> str: output: str = "" @@ -147,7 +150,7 @@ def render_updated_nodes_and_toc( template_folder = "node_content" else: raise NotImplementedError - content = jinja_environment.render_template_as_markup( + content = self.jinja_environment.render_template_as_markup( f"components/{template_folder}/index_extends_node.jinja", view_object=self, node=node_, @@ -158,7 +161,7 @@ def render_updated_nodes_and_toc( target=f"article-{node_.reserved_mid}", ) - toc_content = jinja_environment.render_template_as_markup( + toc_content = self.jinja_environment.render_template_as_markup( "screens/document/_shared/toc.jinja", view_object=self ) output += render_turbo_stream( @@ -170,9 +173,9 @@ def render_updated_nodes_and_toc( return output def render_update_document_content_with_moved_node( - self, jinja_environment: JinjaEnvironment, moved_node: Any + self, moved_node: Any ) -> Markup: - content = jinja_environment.render_template_as_markup( + content = self.jinja_environment.render_template_as_markup( "screens/document/document/frame_document_content.jinja.html", view_object=self, ) @@ -181,7 +184,7 @@ def render_update_document_content_with_moved_node( action="replace", target="frame_document_content", ) - toc_content = jinja_environment.render_template_as_markup( + toc_content = self.jinja_environment.render_template_as_markup( "actions/document/_shared/stream_updated_toc.jinja.html", view_object=self, last_moved_node_id=moved_node.reserved_mid, @@ -294,6 +297,26 @@ def render_node_field(self, node_field: SDocNodeField) -> Markup: self.document_type, node_field ) + def render_issues( + self, + node: Union[SDocNodeIF, SDocDocumentIF], + field: Optional[str] = None, + ) -> str: + issues = self.traceability_index.validation_index.get_issues( + node, field=field + ) + if issues is None: + return "" + issues_html = "" + for issue_ in issues: + issue_html = self.jinja_environment.render_template_as_markup( + "components/issue/index.jinja", + issue=issue_, + view_object=self, + ) + issues_html += issue_html + return issues_html + def get_page_title(self) -> str: return self.document_type.get_page_title() diff --git a/strictdoc/export/html/generators/view_objects/search_screen_view_object.py b/strictdoc/export/html/generators/view_objects/search_screen_view_object.py index 2b5645977..b47fb9780 100644 --- a/strictdoc/export/html/generators/view_objects/search_screen_view_object.py +++ b/strictdoc/export/html/generators/view_objects/search_screen_view_object.py @@ -11,7 +11,11 @@ from strictdoc.backend.sdoc.models.anchor import Anchor from strictdoc.backend.sdoc.models.document import SDocDocument from strictdoc.backend.sdoc.models.document_view import DocumentView -from strictdoc.backend.sdoc.models.model import SDocExtendedElementIF +from strictdoc.backend.sdoc.models.model import ( + SDocDocumentIF, + SDocExtendedElementIF, + SDocNodeIF, +) from strictdoc.backend.sdoc.models.node import SDocNode from strictdoc.core.document_tree import DocumentTree from strictdoc.core.document_tree_iterator import DocumentTreeIterator @@ -118,3 +122,13 @@ def render_local_anchor( self, node: Union[Anchor, SDocNode, SDocDocument] ) -> str: return self.link_renderer.render_local_anchor(node) + + def render_issues( + self, + node: Union[SDocNodeIF, SDocDocumentIF], # noqa: ARG002 + field: Optional[str] = None, # noqa: ARG002 + ) -> str: + """ + FIXME: It is not great that this method is called from here. + """ + return "" diff --git a/strictdoc/export/html/generators/view_objects/source_file_view_object.py b/strictdoc/export/html/generators/view_objects/source_file_view_object.py index 5fde7b24f..3ab9d7c01 100644 --- a/strictdoc/export/html/generators/view_objects/source_file_view_object.py +++ b/strictdoc/export/html/generators/view_objects/source_file_view_object.py @@ -11,6 +11,7 @@ from strictdoc.backend.sdoc.models.anchor import Anchor from strictdoc.backend.sdoc.models.document import SDocDocument from strictdoc.backend.sdoc.models.document_view import NullViewElement +from strictdoc.backend.sdoc.models.model import SDocDocumentIF, SDocNodeIF from strictdoc.backend.sdoc.models.node import SDocNode, SDocNodeField from strictdoc.backend.sdoc_source_code.models.function_range_marker import ( FunctionRangeMarker, @@ -182,6 +183,16 @@ def render_local_anchor( ) -> str: return self.link_renderer.render_local_anchor(node) + def render_issues( + self, + node: Union[SDocNodeIF, SDocDocumentIF], # noqa: ARG002 + field: Optional[str] = None, # noqa: ARG002 + ) -> str: + """ + FIXME: It is not great that this method is called from here. + """ + return "" + def get_source_file_path(self) -> str: return self.source_file.in_doctree_source_file_rel_path_posix diff --git a/strictdoc/export/html/templates/components/issue/index.jinja b/strictdoc/export/html/templates/components/issue/index.jinja index 57e3bc472..4f22afeb0 100644 --- a/strictdoc/export/html/templates/components/issue/index.jinja +++ b/strictdoc/export/html/templates/components/issue/index.jinja @@ -1,12 +1,5 @@ -{# For use: insert after field #} -{# - {% with issue_field_name = 'field_name' %} - {% include "components/issue/index.jinja" %} - {% endwith %} -#} -