Skip to content

Commit b27d808

Browse files
authored
Merge pull request #2235 from thseiler/feature/2229-add-metadata
export/html2pdf: #2229 additional metadata for the front page
2 parents 087f328 + 35e1db7 commit b27d808

File tree

11 files changed

+197
-9
lines changed

11 files changed

+197
-9
lines changed

docs/strictdoc_01_user_guide.sdoc

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -812,6 +812,35 @@ Default is ``True``.
812812

813813
[/SECTION]
814814

815+
[SECTION]
816+
MID: 726b6a1a2f854e82a7670cb8480b887e
817+
TITLE: Additional Metadata
818+
819+
[TEXT]
820+
MID: f86d45b7ad1a48ec919b43a695ca9126
821+
STATEMENT: >>>
822+
StrictDoc allows additional metadata to be added in the [DOCUMENT] element. The ``METADATA:`` field accepts any number of entries, each consisting of a key-value pair on a separate line. Keys must begin with a letter and end with a colon, but are not otherwise constrained.
823+
824+
.. code-block:: text
825+
826+
[DOCUMENT]
827+
TITLE: Hello world
828+
OPTIONS:
829+
REQUIREMENT_IN_TOC: True
830+
METADATA:
831+
KEY: Value
832+
...
833+
834+
The additional metadata is also included on the front page of the exported PDF. This is useful for displaying document-specific information, such as the author's name or project identifiers.
835+
836+
.. note ::
837+
838+
The front page can be further customised using the ``html2pdf_template``
839+
directive in strictoc.toml
840+
<<<
841+
842+
[/SECTION]
843+
815844
[/SECTION]
816845

817846
[SECTION]
@@ -3516,7 +3545,7 @@ To activate the HTML2PDF screen in the web interface, add/edit the ``strictdoc.t
35163545
"HTML2PDF"
35173546
]
35183547

3519-
This feature is not enabled by default because the implementation has not been completed yet. The underlying JavaScript library is being improved with respect to how the SDoc HTML content is split between pages, in particular the splitting of HTML ``<table>`` tags is being worked out. One feature which is still missing is the ability to generate user-specific front pages with custom meta information.
3548+
This feature is not enabled by default because the implementation has not been completed yet. The underlying JavaScript library is being improved with respect to how the SDoc HTML content is split between pages, in particular the splitting of HTML ``<table>`` tags is being worked out. One feature which is still missing is the ability to edit custom front page metadata through the web UI.
35203549
<<<
35213550

35223551
[/SECTION]

strictdoc/backend/sdoc/grammar/grammar.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,7 @@
8383
)?
8484
(' DEFAULT_VIEW: ' default_view = SingleLineString '\n')?
8585
)?
86+
('METADATA:' '\n' custom_metadata = DocumentCustomMetadata)?
8687
;
8788
8889
DocumentView[noskipws]:
@@ -133,6 +134,17 @@
133134
LayoutChoice[noskipws]:
134135
'Default' | 'Website'
135136
;
137+
138+
DocumentCustomMetadata[noskipws]:
139+
(entries+=DocumentCustomMetadataKeyValuePair)*
140+
;
141+
142+
DocumentCustomMetadataKeyValuePair[noskipws]:
143+
' ' key=DocumentCustomMetadataKey ': ' value=SingleLineString '\n'
144+
;
145+
146+
DocumentCustomMetadataKey: /[a-zA-Z_][a-zA-Z0-9_-]*/;
147+
136148
"""
137149

138150
SECTION_GRAMMAR = rf"""

strictdoc/backend/sdoc/models/constants.py

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
11
from strictdoc.backend.sdoc.models.anchor import Anchor
22
from strictdoc.backend.sdoc.models.document import SDocDocument
3-
from strictdoc.backend.sdoc.models.document_config import DocumentConfig
3+
from strictdoc.backend.sdoc.models.document_config import (
4+
DocumentConfig,
5+
DocumentCustomMetadata,
6+
DocumentCustomMetadataKeyValuePair,
7+
)
48
from strictdoc.backend.sdoc.models.document_from_file import DocumentFromFile
59
from strictdoc.backend.sdoc.models.document_grammar import (
610
DocumentGrammar,
@@ -68,6 +72,8 @@
6872

6973
DOCUMENT_MODELS = [
7074
DocumentConfig,
75+
DocumentCustomMetadata,
76+
DocumentCustomMetadataKeyValuePair,
7177
SDocDocument,
7278
DocumentView,
7379
ViewElement,

strictdoc/backend/sdoc/models/document_config.py

Lines changed: 52 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,38 @@
1-
# mypy: disable-error-code="no-untyped-def"
2-
from typing import Optional
1+
from typing import Dict, List, Optional
32

43
from strictdoc.helpers.auto_described import auto_described
54

65

6+
@auto_described
7+
class DocumentCustomMetadataKeyValuePair:
8+
def __init__(
9+
self,
10+
*,
11+
parent: Optional["DocumentCustomMetadata"] = None,
12+
key: Optional[str],
13+
value: Optional[str],
14+
) -> None:
15+
_ = parent
16+
self.key = key
17+
self.value = value
18+
19+
20+
@auto_described
21+
class DocumentCustomMetadata:
22+
def __init__(
23+
self,
24+
*,
25+
parent: Optional["DocumentConfig"] = None,
26+
entries: Optional[List[DocumentCustomMetadataKeyValuePair]],
27+
) -> None:
28+
_ = parent
29+
self.entries = entries
30+
31+
732
@auto_described
833
class DocumentConfig:
934
@staticmethod
10-
def default_config(document) -> "DocumentConfig":
35+
def default_config(document) -> "DocumentConfig": # type: ignore[no-untyped-def]
1136
return DocumentConfig(
1237
parent=document,
1338
version=None,
@@ -23,9 +48,10 @@ def default_config(document) -> "DocumentConfig":
2348
requirement_style=None,
2449
requirement_in_toc=None,
2550
default_view=None,
51+
custom_metadata=None,
2652
)
2753

28-
def __init__(
54+
def __init__( # type: ignore[no-untyped-def]
2955
self,
3056
*,
3157
parent,
@@ -42,6 +68,7 @@ def __init__(
4268
requirement_style: Optional[str],
4369
requirement_in_toc: Optional[str],
4470
default_view: Optional[str],
71+
custom_metadata: Optional[DocumentCustomMetadata],
4572
) -> None:
4673
self.parent = parent
4774
self.version: Optional[str] = version
@@ -84,6 +111,8 @@ def __init__(
84111
self.ng_line_start: int = 0
85112
self.ng_col_start: int = 0
86113

114+
self.custom_metadata: Optional[DocumentCustomMetadata] = custom_metadata
115+
87116
def get_markup(self) -> str:
88117
if self.markup is None:
89118
return "RST"
@@ -113,7 +142,7 @@ def get_requirement_prefix(self) -> str:
113142
return self.requirement_prefix
114143
return "REQ-"
115144

116-
def has_meta(self):
145+
def has_meta(self) -> bool:
117146
# TODO: When OPTIONS are not provided to a document, the self.number and
118147
# self.version are both None. Otherwise, they become empty strings "".
119148
# This issue might deserve a bug report to TextX.
@@ -124,3 +153,21 @@ def has_meta(self):
124153
self.classification is not None and len(self.classification) > 0
125154
)
126155
)
156+
157+
def has_custom_meta(self) -> bool:
158+
return (
159+
self.custom_metadata is not None
160+
and self.custom_metadata.entries is not None
161+
)
162+
163+
def get_custom_meta(self) -> Optional[Dict[str, str]]:
164+
if (
165+
self.custom_metadata is not None
166+
and self.custom_metadata.entries is not None
167+
):
168+
return {
169+
entry.key: entry.value
170+
for entry in self.custom_metadata.entries
171+
if entry.key is not None and entry.value is not None
172+
}
173+
return {}

strictdoc/backend/sdoc/writer.py

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -175,6 +175,24 @@ def write_with_fragments(
175175
output += default_view
176176
output += "\n"
177177

178+
custom_metadata = document_config.custom_metadata
179+
if custom_metadata is not None:
180+
output += "METADATA:"
181+
output += "\n"
182+
183+
for keyvalue_pair in custom_metadata.entries:
184+
if (
185+
keyvalue_pair.key is not None
186+
and keyvalue_pair.value is not None
187+
):
188+
output += (
189+
" "
190+
+ keyvalue_pair.key
191+
+ ": "
192+
+ keyvalue_pair.value
193+
)
194+
output += "\n"
195+
178196
document_view = document.view
179197
assert len(document_view.views) > 0
180198
if not isinstance(document_view.views[0], DefaultViewElement):

strictdoc/export/html/templates/components/node_field/document_meta/index.jinja

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
1-
{%- if view_object.document.config.has_meta() -%}
1+
{%- set document_config = view_object.document.config -%}
2+
3+
{%- if document_config.has_meta() or document_config.has_custom_meta() -%}
24
<sdoc-meta>
5+
{% if document_config.has_custom_meta %}
36
{%- if view_object.document.config.uid -%}
47
<sdoc-meta-label data-testid="document-config-uid-label">UID:</sdoc-meta-label>
58
<sdoc-meta-field data-testid="document-config-uid-field">
@@ -44,5 +47,16 @@
4447
</sdoc-meta-field>
4548
{%- endif -%}
4649

50+
{% set custom_metadata = document_config.get_custom_meta() %}
51+
{% for key, value in custom_metadata.items() %}
52+
<sdoc-meta-label data-testid="document-config-version-label">{{key}}:</sdoc-meta-label>
53+
<sdoc-meta-field data-testid="document-config-version-field">
54+
{%- with field_content = value %}
55+
<sdoc-autogen>{{ field_content }}</sdoc-autogen>
56+
{%- endwith -%}
57+
</sdoc-meta-field>
58+
{% endfor %}
59+
{% endif %}
60+
4761
</sdoc-meta>
4862
{%- endif -%}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,11 @@
11
[DOCUMENT]
22
TITLE: Empty Document
3+
VERSION: Git commit: @GIT_VERSION, Git branch: @GIT_BRANCH
4+
DATE: @GIT_COMMIT_DATETIME
5+
CLASSIFICATION: Confidential
6+
OPTIONS:
7+
REQUIREMENT_STYLE: Inline
8+
METADATA:
9+
AUTHOR: James T. Kirk
10+
CHECKED-BY: Chuck Norris
11+
APPROVED-BY: Wile E. Coyote

tests/end2end/screens/pdf/view_pdf_document_custom_template/html2pdf_template/index.jinja

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,8 @@ Notes:
4848
{{ view_object.document.title }}</h1>
4949
</div>
5050
<div class="html2pdf-frontpage-grid-bottom">
51-
{%- if view_object.document.config.has_meta() -%}
51+
{%- set document_config = view_object.document.config -%}
52+
{%- if document_config.has_meta() or document_config.has_custom_meta() -%}
5253
<sdoc-meta>
5354
{%- if view_object.document.config.uid -%}
5455
<sdoc-meta-label data-testid="document-config-uid-label">UID:</sdoc-meta-label>
@@ -97,6 +98,24 @@ Notes:
9798
{%- endwith -%}
9899
</sdoc-meta-field>
99100
{%- endif -%}
101+
102+
{% if document_config.has_custom_meta %}
103+
{% set custom_metadata = document_config.get_custom_meta() %}
104+
{% for key, value in custom_metadata.items() %}
105+
<sdoc-meta-label data-testid="document-config-version-label">{{key}}:</sdoc-meta-label>
106+
107+
{# We make the custom metadata fields higher with a min-height style directive.
108+
We do this to support the review/release workflows at ACME laboratories inc.,
109+
which involve placing "electronic signatures" on the front page of the PDF.
110+
#}
111+
<sdoc-meta-field data-testid="document-config-version-field" style="min-height: 60px;">
112+
{%- with field_content = value %}
113+
<sdoc-autogen>{{ field_content }}</sdoc-autogen>
114+
{%- endwith -%}
115+
</sdoc-meta-field>
116+
{% endfor %}
117+
{% endif %}
118+
100119
</sdoc-meta>
101120
{%- endif -%}
102121
</div>

tests/end2end/screens/pdf/view_pdf_document_custom_template/test_case.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,4 +36,12 @@ def test(self):
3636
screen_pdf.assert_on_pdf_document()
3737
screen_pdf.assert_not_empty_view()
3838

39+
#
40+
# Check that text from the custom template is there
41+
#
3942
screen_pdf.assert_text("ACME Laboratories Inc.")
43+
44+
#
45+
# Check that metadata from the document is there
46+
#
47+
screen_pdf.assert_text("Wile E. Coyote")

tests/unit/helpers/document_builder.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,7 @@ def _create_empty_document(
105105
requirement_style=None,
106106
requirement_in_toc=None,
107107
default_view=None,
108+
custom_metadata=None,
108109
)
109110
section_contents = []
110111
document = SDocDocument(

tests/unit/strictdoc/backend/sdoc/test_dsl_passthrough.py

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -678,6 +678,31 @@ def test_073_document_config_requirement_prefix(default_project_config):
678678
assert input_sdoc == output
679679

680680

681+
def test_074_document_config_metadata(default_project_config):
682+
input_sdoc = """
683+
[DOCUMENT]
684+
TITLE: Test Doc
685+
UID: SDOC-01
686+
VERSION: 0.0.1
687+
METADATA:
688+
AUTHOR: James T. Kirk
689+
CHECKED-BY: Chuck Norris
690+
APPROVED-BY: Wile E. Coyote
691+
692+
[REQUIREMENT]
693+
""".lstrip()
694+
695+
reader = SDReader()
696+
697+
document = reader.read(input_sdoc)
698+
assert isinstance(document, SDocDocument)
699+
700+
writer = SDWriter(default_project_config)
701+
output = writer.write(document)
702+
703+
assert input_sdoc == output
704+
705+
681706
def test_090_document_config_all_fields(default_project_config):
682707
input_sdoc = """
683708
[DOCUMENT]

0 commit comments

Comments
 (0)