Skip to content

Commit 79862a2

Browse files
committed
feat: display node validations on the HTML screens
This is the next step towards #1229.
1 parent d48030e commit 79862a2

File tree

23 files changed

+288
-64
lines changed

23 files changed

+288
-64
lines changed

strictdoc/core/traceability_index.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
SingleValidationError,
3232
)
3333
from strictdoc.core.tree_cycle_detector import TreeCycleDetector
34+
from strictdoc.core.validation_index import ValidationIndex
3435
from strictdoc.helpers.cast import assert_cast, assert_optional_cast
3536
from strictdoc.helpers.file_modification_time import set_file_modification_time
3637
from strictdoc.helpers.mid import MID
@@ -54,7 +55,7 @@ def __init__(
5455
self._file_traceability_index: FileTraceabilityIndex = (
5556
file_traceability_index
5657
)
57-
58+
self.validation_index: ValidationIndex = ValidationIndex()
5859
self.graph_database: GraphDatabase = graph_database
5960
self.document_tree: DocumentTree = document_tree
6061
self.asset_manager: Optional[AssetManager] = None

strictdoc/core/validation_index.py

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
from typing import Dict, List, Optional, Union
2+
3+
from strictdoc.backend.sdoc.models.model import (
4+
SDocDocumentIF,
5+
SDocNodeIF,
6+
)
7+
8+
9+
class ValidationIndex:
10+
def __init__(self) -> None:
11+
self.node_issues: Dict[
12+
Union[SDocNodeIF, SDocDocumentIF], Dict[Optional[str], List[str]]
13+
] = {}
14+
15+
def add_issue(
16+
self,
17+
node: Union[SDocNodeIF, SDocDocumentIF],
18+
issue: str,
19+
field: Optional[str] = None,
20+
subject: Optional[str] = None,
21+
) -> None:
22+
self.node_issues.setdefault(node, {}).setdefault(field, []).append(
23+
issue
24+
)
25+
26+
if isinstance(node, SDocNodeIF):
27+
document_node = node.get_parent_or_including_document()
28+
self.node_issues.setdefault(document_node, {}).setdefault(
29+
None, []
30+
).append(issue)
31+
32+
print_issue = issue
33+
if subject is not None:
34+
print_issue = f"{issue} {subject}"
35+
print(f"warning: {print_issue}") # noqa: T201
36+
37+
def get_issues(
38+
self,
39+
node: Union[SDocNodeIF, SDocDocumentIF],
40+
field: Optional[str] = None,
41+
) -> Optional[List[str]]:
42+
if node not in self.node_issues:
43+
return None
44+
node_issues = self.node_issues[node]
45+
if field not in node_issues:
46+
return None
47+
issues = node_issues[field]
48+
if isinstance(node, SDocDocumentIF) and not node.document_is_included():
49+
return [f"Document has {len(issues)} issues."]
50+
return issues

strictdoc/export/html/_static/element.css

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1625,7 +1625,7 @@ sdoc-tabs.in_aside_panel sdoc-tab[active] {
16251625
grid-column: 1 / -1;
16261626
position: relative;
16271627
color: var(--color-danger);
1628-
padding: var(--base-rhythm);
1628+
padding: var(--base-rhythm) 0;
16291629
font-size: var(--font-size-sm);
16301630
line-height: 1.5;
16311631
}
@@ -1634,10 +1634,10 @@ sdoc-tabs.in_aside_panel sdoc-tab[active] {
16341634
position: relative;
16351635
padding: calc(.5 * var(--base-rhythm)) var(--base-rhythm);
16361636
border-radius: calc(.5 * var(--base-rhythm));
1637-
border: 2px solid;
1637+
border: 1px solid;
16381638
}
16391639

1640-
.field_issue-ribbon::before {
1640+
sdoc-node-content .field_issue-ribbon::before {
16411641
position: absolute;
16421642
content: '';
16431643
bottom: 100%;
@@ -1653,6 +1653,7 @@ sdoc-tabs.in_aside_panel sdoc-tab[active] {
16531653
content: '';
16541654
position: absolute;
16551655
inset: 0;
1656+
pointer-events: none;
16561657
}
16571658

16581659
/*

strictdoc/export/html/generators/document.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,8 @@ def export(
3636
project_config=project_config,
3737
link_renderer=link_renderer,
3838
markup_renderer=markup_renderer,
39+
jinja_environment=html_templates.jinja_environment(),
3940
git_client=git_client,
4041
standalone=standalone,
4142
)
42-
return view_object.render_screen(html_templates.jinja_environment())
43+
return view_object.render_screen()

strictdoc/export/html/generators/document_deep_trace.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,8 @@ def export_deep(
3535
project_config=project_config,
3636
link_renderer=link_renderer,
3737
markup_renderer=markup_renderer,
38+
jinja_environment=html_templates.jinja_environment(),
3839
git_client=git_client,
3940
standalone=False,
4041
)
41-
return view_object.render_screen(html_templates.jinja_environment())
42+
return view_object.render_screen()

strictdoc/export/html/generators/document_pdf.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,8 @@ def export(
3636
project_config=project_config,
3737
link_renderer=link_renderer,
3838
markup_renderer=markup_renderer,
39+
jinja_environment=html_templates.jinja_environment(),
3940
git_client=git_client,
4041
standalone=standalone,
4142
)
42-
return view_object.render_screen(html_templates.jinja_environment())
43+
return view_object.render_screen()

strictdoc/export/html/generators/document_table.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,8 @@ def export(
3535
project_config=project_config,
3636
link_renderer=link_renderer,
3737
markup_renderer=markup_renderer,
38+
jinja_environment=html_templates.jinja_environment(),
3839
git_client=git_client,
3940
standalone=False,
4041
)
41-
return view_object.render_screen(html_templates.jinja_environment())
42+
return view_object.render_screen()

strictdoc/export/html/generators/document_trace.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,8 @@ def export(
3535
project_config=project_config,
3636
link_renderer=link_renderer,
3737
markup_renderer=markup_renderer,
38+
jinja_environment=html_templates.jinja_environment(),
3839
git_client=git_client,
3940
standalone=False,
4041
)
41-
return view_object.render_screen(html_templates.jinja_environment())
42+
return view_object.render_screen()

strictdoc/export/html/generators/view_objects/document_screen_view_object.py

Lines changed: 42 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,11 @@
1414
from strictdoc.backend.sdoc.models.document import SDocDocument
1515
from strictdoc.backend.sdoc.models.document_view import ViewElement
1616
from strictdoc.backend.sdoc.models.grammar_element import GrammarElement
17-
from strictdoc.backend.sdoc.models.model import SDocElementIF
17+
from strictdoc.backend.sdoc.models.model import (
18+
SDocDocumentIF,
19+
SDocElementIF,
20+
SDocNodeIF,
21+
)
1822
from strictdoc.backend.sdoc.models.node import SDocNode, SDocNodeField
1923
from strictdoc.core.document_iterator import DocumentIterationContext
2024
from strictdoc.core.document_tree import DocumentTree
@@ -48,6 +52,7 @@ def __init__(
4852
project_config: ProjectConfig,
4953
link_renderer: LinkRenderer,
5054
markup_renderer: MarkupRenderer,
55+
jinja_environment: JinjaEnvironment,
5156
git_client: GitClient,
5257
standalone: bool,
5358
):
@@ -58,6 +63,7 @@ def __init__(
5863
self.project_config: ProjectConfig = project_config
5964
self.link_renderer: LinkRenderer = link_renderer
6065
self.markup_renderer: MarkupRenderer = markup_renderer
66+
self.jinja_environment: JinjaEnvironment = jinja_environment
6167
self.git_client: GitClient = git_client
6268
self.standalone: bool = standalone
6369
self.document_iterator = self.traceability_index.get_document_iterator(
@@ -82,47 +88,45 @@ def __init__(
8288
def has_included_document(self) -> bool:
8389
return len(self.document.included_documents) > 0
8490

85-
def render_screen(self, jinja_environment: JinjaEnvironment) -> Markup:
91+
def render_screen(self) -> Markup:
8692
if self.document_type.is_document():
8793
if self.document.config.layout == "Website":
88-
return jinja_environment.render_template_as_markup(
94+
return self.jinja_environment.render_template_as_markup(
8995
"website/document/index.jinja", view_object=self
9096
)
91-
return jinja_environment.render_template_as_markup(
97+
return self.jinja_environment.render_template_as_markup(
9298
"screens/document/document/index.jinja", view_object=self
9399
)
94100
elif self.document_type.is_table():
95-
return jinja_environment.render_template_as_markup(
101+
return self.jinja_environment.render_template_as_markup(
96102
"screens/document/table/index.jinja", view_object=self
97103
)
98104
elif self.document_type.is_trace():
99-
return jinja_environment.render_template_as_markup(
105+
return self.jinja_environment.render_template_as_markup(
100106
"screens/document/traceability/index.jinja", view_object=self
101107
)
102108
elif self.document_type.is_deeptrace():
103-
return jinja_environment.render_template_as_markup(
109+
return self.jinja_environment.render_template_as_markup(
104110
"screens/document/traceability_deep/index.jinja",
105111
view_object=self,
106112
)
107113
elif self.document_type.is_pdf():
108-
return jinja_environment.render_template_as_markup(
114+
return self.jinja_environment.render_template_as_markup(
109115
"screens/document/pdf/index.jinja", view_object=self
110116
)
111117
else:
112118
raise NotImplementedError(self.document_type) # pragma: no cover
113119

114-
def render_updated_screen(
115-
self, jinja_environment: JinjaEnvironment
116-
) -> Markup:
117-
output = jinja_environment.render_template_as_markup(
120+
def render_updated_screen(self) -> Markup:
121+
output = self.jinja_environment.render_template_as_markup(
118122
"actions/"
119123
"document/"
120124
"create_requirement/"
121125
"stream_created_requirement.jinja.html",
122126
view_object=self,
123127
)
124128

125-
output += jinja_environment.render_template_as_markup(
129+
output += self.jinja_environment.render_template_as_markup(
126130
"actions/document/_shared/stream_updated_toc.jinja.html",
127131
view_object=self,
128132
)
@@ -132,7 +136,6 @@ def render_updated_screen(
132136
def render_updated_nodes_and_toc(
133137
self,
134138
nodes: Sequence[Union[SDocDocument, SDocNode]],
135-
jinja_environment: JinjaEnvironment,
136139
) -> str:
137140
output: str = ""
138141

@@ -147,7 +150,7 @@ def render_updated_nodes_and_toc(
147150
template_folder = "node_content"
148151
else:
149152
raise NotImplementedError
150-
content = jinja_environment.render_template_as_markup(
153+
content = self.jinja_environment.render_template_as_markup(
151154
f"components/{template_folder}/index_extends_node.jinja",
152155
view_object=self,
153156
node=node_,
@@ -158,7 +161,7 @@ def render_updated_nodes_and_toc(
158161
target=f"article-{node_.reserved_mid}",
159162
)
160163

161-
toc_content = jinja_environment.render_template_as_markup(
164+
toc_content = self.jinja_environment.render_template_as_markup(
162165
"screens/document/_shared/toc.jinja", view_object=self
163166
)
164167
output += render_turbo_stream(
@@ -170,9 +173,9 @@ def render_updated_nodes_and_toc(
170173
return output
171174

172175
def render_update_document_content_with_moved_node(
173-
self, jinja_environment: JinjaEnvironment, moved_node: Any
176+
self, moved_node: Any
174177
) -> Markup:
175-
content = jinja_environment.render_template_as_markup(
178+
content = self.jinja_environment.render_template_as_markup(
176179
"screens/document/document/frame_document_content.jinja.html",
177180
view_object=self,
178181
)
@@ -181,7 +184,7 @@ def render_update_document_content_with_moved_node(
181184
action="replace",
182185
target="frame_document_content",
183186
)
184-
toc_content = jinja_environment.render_template_as_markup(
187+
toc_content = self.jinja_environment.render_template_as_markup(
185188
"actions/document/_shared/stream_updated_toc.jinja.html",
186189
view_object=self,
187190
last_moved_node_id=moved_node.reserved_mid,
@@ -294,6 +297,26 @@ def render_node_field(self, node_field: SDocNodeField) -> Markup:
294297
self.document_type, node_field
295298
)
296299

300+
def render_issues(
301+
self,
302+
node: Union[SDocNodeIF, SDocDocumentIF],
303+
field: Optional[str] = None,
304+
) -> str:
305+
issues = self.traceability_index.validation_index.get_issues(
306+
node, field=field
307+
)
308+
if issues is None:
309+
return ""
310+
issues_html = ""
311+
for issue_ in issues:
312+
issue_html = self.jinja_environment.render_template_as_markup(
313+
"components/issue/index.jinja",
314+
issue=issue_,
315+
view_object=self,
316+
)
317+
issues_html += issue_html
318+
return issues_html
319+
297320
def get_page_title(self) -> str:
298321
return self.document_type.get_page_title()
299322

strictdoc/export/html/generators/view_objects/source_file_view_object.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
from strictdoc.backend.sdoc.models.anchor import Anchor
1212
from strictdoc.backend.sdoc.models.document import SDocDocument
1313
from strictdoc.backend.sdoc.models.document_view import NullViewElement
14+
from strictdoc.backend.sdoc.models.model import SDocDocumentIF, SDocNodeIF
1415
from strictdoc.backend.sdoc.models.node import SDocNode, SDocNodeField
1516
from strictdoc.backend.sdoc_source_code.models.function_range_marker import (
1617
FunctionRangeMarker,
@@ -182,6 +183,16 @@ def render_local_anchor(
182183
) -> str:
183184
return self.link_renderer.render_local_anchor(node)
184185

186+
def render_issues(
187+
self,
188+
_node: Union[SDocNodeIF, SDocDocumentIF],
189+
_field: Optional[str] = None,
190+
) -> str:
191+
"""
192+
FIXME: It is not great that this method is called from here.
193+
"""
194+
return ""
195+
185196
def get_source_file_path(self) -> str:
186197
return self.source_file.in_doctree_source_file_rel_path_posix
187198

0 commit comments

Comments
 (0)