Skip to content

Commit acb81ed

Browse files
authored
Merge pull request #2391 from strictdoc-project/stanislaw/new_section_included_documents
UI: Edit Grammar Element: support updating composite nodes and HUMAN_TITLE fields
2 parents dd4c71e + fbdec7a commit acb81ed

File tree

13 files changed

+336
-36
lines changed

13 files changed

+336
-36
lines changed

docs/strictdoc_04_release_notes.sdoc

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,14 +45,32 @@ STATEMENT: >>>
4545
This document maintains a record of all changes to StrictDoc since November 2023. It serves as a user-friendly version of the changelog, complementing the automatically generated, commit-by-commit changelog available as GitHub releases: `StrictDoc Releases <https://github.com/strictdoc-project/strictdoc/releases>`_.
4646
<<<
4747

48+
[[SECTION]]
49+
MID: a5ab8dd5bc4045e99299eff729e430e6
50+
TITLE: Unreleased
51+
52+
[TEXT]
53+
MID: 6d0bb97e7ef74fc1ae93cf2a91480e6a
54+
STATEMENT: >>>
55+
This release includes the following enhancements and bugfixes.
56+
57+
**Fix for updating HUMAN_TITLE in Edit Grammar Element UI.**
58+
59+
This change removes a previously implemented "clever" optimization that skipped saving a grammar element if none of its fields appeared to be modified. Over time, this check became outdated due to the introduction of new grammar element features such as HUMAN_TITLE, PROPERTIES/IS_COMPOSITE, PROPERTIES/VIEW_STYLE, and others. As a result, the issue manifested as a no-op where updates, such as changing a HUMAN_TITLE, were not saved to disk after submitting the form.
60+
61+
Thanks to @zheylmun for reporting this issue.
62+
<<<
63+
64+
[[/SECTION]]
65+
4866
[[SECTION]]
4967
MID: 447ab1fa1d38453ab46e0002e6eb852d
5068
TITLE: 0.10.0 (2025-07-20)
5169

5270
[TEXT]
5371
MID: c536381cea6842f0b77c1aa5ac942ce0
5472
STATEMENT: >>>
55-
This release includes the following enhancements.
73+
This release includes the following enhancements and bugfixes.
5674

5775
**1) A small improvement to the behavior of the LEVEL/AUTO_LEVEL fields.**
5876

strictdoc/core/transforms/update_grammar_element.py

Lines changed: 1 addition & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ def __init__(
2929
self.document: SDocDocument = document
3030
self.traceability_index: TraceabilityIndex = traceability_index
3131

32-
def perform(self) -> bool:
32+
def perform(self) -> None:
3333
form_object: GrammarElementFormObject = self.form_object
3434
document: SDocDocument = self.document
3535
existing_element: GrammarElement = document.grammar.get_element_by_mid(
@@ -61,23 +61,9 @@ def perform(self) -> bool:
6161
#
6262
document_grammar_field_names = updated_element.get_field_titles()
6363

64-
existing_document_grammar_field_names = (
65-
existing_element.get_field_titles()
66-
)
67-
grammar_changed = (
68-
document_grammar_field_names
69-
!= existing_document_grammar_field_names
70-
)
7164
existing_requirement_element = document.grammar.elements_by_type[
7265
existing_element.tag
7366
]
74-
grammar_changed = (
75-
grammar_changed
76-
or existing_requirement_element.relations
77-
!= updated_element.relations
78-
)
79-
if not grammar_changed:
80-
return False
8167

8268
document.grammar.update_element(existing_element, updated_element)
8369

@@ -138,5 +124,3 @@ def perform(self) -> bool:
138124
new_relations.append(requirement_relation_)
139125
requirement.relations = new_relations
140126
requirement.ordered_fields_lookup = new_ordered_fields_lookup
141-
142-
return True

strictdoc/export/html/form_objects/grammar_element_form_object.py

Lines changed: 68 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,12 @@ def get_input_field_name(self) -> str:
8080
def get_input_field_human_title(self) -> str:
8181
return f"document_grammar_field[{self.field_mid}][field_human_title]"
8282

83+
def get_input_field_required(self) -> str:
84+
return f"document_grammar_field[{self.field_mid}][field_required]"
85+
86+
def get_input_field_required_value(self) -> str:
87+
return "true" if self.field_required else "false"
88+
8389

8490
@auto_described
8591
class GrammarFormRelation:
@@ -110,6 +116,9 @@ def __init__(
110116
document_mid: str,
111117
element_mid: str,
112118
element_name: str,
119+
is_composite: Optional[bool],
120+
prefix: Optional[str],
121+
view_style: Optional[str],
113122
fields: List[GrammarFormField],
114123
relations: List[GrammarFormRelation],
115124
project_config: ProjectConfig,
@@ -120,6 +129,9 @@ def __init__(
120129
self.document_mid = document_mid
121130
self.element_mid: str = element_mid
122131
self.element_name: str = element_name
132+
self.is_composite: Optional[bool] = is_composite
133+
self.prefix: Optional[str] = prefix
134+
self.view_style: Optional[str] = view_style
123135
self.fields: List[GrammarFormField] = fields
124136
self.relations: List[GrammarFormRelation] = relations
125137
self.project_config: ProjectConfig = project_config
@@ -145,6 +157,10 @@ def create_from_request(
145157

146158
element_mid = request_form_dict["element_mid"]
147159

160+
element_is_composite = request_form_dict["is_composite"]
161+
element_prefix = request_form_dict["prefix"]
162+
element_view_style = request_form_dict["view_style"]
163+
148164
#
149165
# Grammar fields.
150166
#
@@ -158,11 +174,14 @@ def create_from_request(
158174
field_human_title = field_human_title.strip()
159175
if len(field_human_title) == 0:
160176
field_human_title = None
177+
field_required_string = field_dict.get("field_required")
178+
field_required = field_required_string == "true"
179+
161180
form_object_field = GrammarFormField(
162181
field_mid=field_mid,
163182
field_name=field_name,
164183
field_human_title=field_human_title,
165-
field_required=False,
184+
field_required=field_required,
166185
reserved=is_reserved_field(field_name),
167186
)
168187
form_object_fields.append(form_object_field)
@@ -194,6 +213,15 @@ def create_from_request(
194213
document_mid=document.reserved_mid,
195214
element_mid=element_mid,
196215
element_name=element.tag,
216+
is_composite=(
217+
True
218+
if element_is_composite == "true"
219+
else False
220+
if element_is_composite == "false"
221+
else None
222+
),
223+
prefix=element_prefix,
224+
view_style=element_view_style,
197225
fields=form_object_fields,
198226
relations=form_object_relations,
199227
project_config=project_config,
@@ -236,6 +264,9 @@ def create_from_document(
236264
document_mid=document.reserved_mid,
237265
element_mid=element_mid,
238266
element_name=element.tag,
267+
is_composite=element.property_is_composite,
268+
prefix=element.property_prefix,
269+
view_style=element.property_view_style,
239270
fields=grammar_form_fields,
240271
relations=grammar_form_relations,
241272
project_config=project_config,
@@ -270,7 +301,10 @@ def validate(self) -> bool:
270301
else:
271302
fields_so_far.add(field.field_name)
272303

273-
if len(self.relations) == 0 and self.element_name != "TEXT":
304+
if len(self.relations) == 0 and self.element_name not in (
305+
"TEXT",
306+
"SECTION",
307+
):
274308
self.add_error(
275309
"Relations_Row",
276310
(
@@ -362,13 +396,19 @@ def convert_to_grammar_element(
362396
raise NotImplementedError(relation)
363397

364398
existing_element = existing_grammar.get_element_by_mid(self.element_mid)
399+
365400
requirement_element = GrammarElement(
366401
parent=None,
367402
tag=existing_element.tag,
368-
# FIXME: MERGE NODES.
369-
property_is_composite="",
370-
property_prefix="",
371-
property_view_style="",
403+
property_is_composite=(
404+
"True"
405+
if self.is_composite == True
406+
else "False"
407+
if self.is_composite == False
408+
else ""
409+
),
410+
property_prefix=self.prefix or "",
411+
property_view_style=self.view_style or "",
372412
fields=grammar_fields,
373413
relations=relation_fields,
374414
)
@@ -462,3 +502,25 @@ def render_close_form() -> str:
462502
action="update",
463503
target="modal",
464504
)
505+
506+
def get_input_field_is_composite(self) -> str:
507+
return "is_composite"
508+
509+
def get_input_field_is_composite_value(self) -> str:
510+
if self.is_composite is not None:
511+
if self.is_composite == True:
512+
return "true"
513+
return "false"
514+
return ""
515+
516+
def get_input_field_prefix(self) -> str:
517+
return "prefix"
518+
519+
def get_input_field_prefix_value(self) -> str:
520+
return self.prefix or ""
521+
522+
def get_input_field_view_style(self) -> str:
523+
return "view_style"
524+
525+
def get_input_field_view_style_value(self) -> str:
526+
return self.view_style or ""

strictdoc/export/html/templates/components/grammar_form_element/index.jinja

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,25 @@
2323
<input type="hidden" id="document_mid" name="document_mid" value="{{ form_object.document_mid }}"/>
2424
<input type="hidden" id="element_mid" name="element_mid" value="{{ form_object.element_mid }}"/>
2525

26+
{# FIXME: This will become a proper checkbox field eventually. #}
27+
<input
28+
type="hidden"
29+
value="{{ form_object.get_input_field_is_composite_value() }}"
30+
name="{{ form_object.get_input_field_is_composite() }}"
31+
/>
32+
{# FIXME: This will become a proper checkbox field eventually. #}
33+
<input
34+
type="hidden"
35+
value="{{ form_object.get_input_field_prefix_value() }}"
36+
name="{{ form_object.get_input_field_prefix() }}"
37+
/>
38+
{# FIXME: This will become a proper checkbox field eventually. #}
39+
<input
40+
type="hidden"
41+
value="{{ form_object.get_input_field_view_style_value() }}"
42+
name="{{ form_object.get_input_field_view_style() }}"
43+
/>
44+
2645
{# Fields TAB #}
2746
<sdoc-tab-content id="Fields" active>
2847
<sdoc-form-descr>

strictdoc/export/html/templates/components/grammar_form_element/row_with_custom_field/index.jinja

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,13 @@
3737
{%- endif -%}
3838
>
3939

40+
{# FIXME: This will become a proper checkbox field eventually. #}
41+
<input
42+
type="hidden"
43+
value="{{ form_object.field.get_input_field_required_value() }}"
44+
name="{{ form_object.field.get_input_field_required() }}"
45+
/>
46+
4047
{%- set placeholder_name = form_object.field.field_name|default('custom field name', true) %}
4148
{# field_placeholder = "Enter "~placeholder_name~" here...", #}
4249
{%- with

strictdoc/export/html/templates/components/grammar_form_element/row_with_reserved_field/index.jinja

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,13 @@
2323
{%- endif -%}
2424
>
2525

26+
{# FIXME: This will become a proper checkbox field eventually. #}
27+
<input
28+
type="hidden"
29+
value="{{ form_object.field.get_input_field_required_value() }}"
30+
name="{{ form_object.field.get_input_field_required() }}"
31+
/>
32+
2633
{%- with
2734
field_class_name = "monospace",
2835
field_editable = false,

strictdoc/server/routers/main_router.py

Lines changed: 7 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -2377,18 +2377,7 @@ async def document__save_grammar_element(request: Request) -> Response:
23772377
document=document,
23782378
traceability_index=export_action.traceability_index,
23792379
)
2380-
grammar_changed = update_grammar_action.perform()
2381-
2382-
# If the grammar has not changed, do nothing and save the edit form.
2383-
if not grammar_changed:
2384-
output = form_object.render_close_form()
2385-
return HTMLResponse(
2386-
content=output,
2387-
status_code=200,
2388-
headers={
2389-
"Content-Type": "text/vnd.turbo-stream.html",
2390-
},
2391-
)
2380+
update_grammar_action.perform()
23922381

23932382
# Re-generate the document's SDOC.
23942383
SDWriter(project_config).write_to_file(document)
@@ -2448,6 +2437,9 @@ def document__add_grammar_field(document_mid: str) -> Response:
24482437
document_mid=document_mid,
24492438
element_mid="NOT_RELEVANT",
24502439
element_name="NOT_RELEVANT",
2440+
is_composite=None, # Not used in this limited partial template.
2441+
prefix=None, # Not used in this limited partial template.
2442+
view_style=None, # Not used in this limited partial template.
24512443
fields=[], # Not used in this limited partial template.
24522444
relations=[], # Not used in this limited partial template.
24532445
project_config=project_config,
@@ -2469,6 +2461,9 @@ def document__add_grammar_relation(document_mid: str) -> Response:
24692461
document_mid=document_mid,
24702462
element_mid="NOT_RELEVANT",
24712463
element_name="NOT_RELEVANT",
2464+
is_composite=None, # Not used in this limited partial template.
2465+
prefix=None, # Not used in this limited partial template.
2466+
view_style=None, # Not used in this limited partial template.
24722467
fields=[], # Not used in this limited partial template.
24732468
relations=[], # Not used in this limited partial template.
24742469
project_config=project_config,
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,18 @@ TITLE: Document 1
33

44
[GRAMMAR]
55
ELEMENTS:
6+
- TAG: SECTION
7+
PROPERTIES:
8+
IS_COMPOSITE: True
9+
PREFIX: SECT-
10+
VIEW_STYLE: Narrative
11+
FIELDS:
12+
- TITLE: UID
13+
TYPE: String
14+
REQUIRED: False
15+
- TITLE: TITLE
16+
TYPE: String
17+
REQUIRED: False
618
- TAG: TEXT
719
FIELDS:
820
- TITLE: UID
@@ -28,9 +40,14 @@ ELEMENTS:
2840
RELATIONS:
2941
- TYPE: Parent
3042

43+
[[SECTION]]
44+
TITLE: Section example
45+
3146
[REQUIREMENT]
3247
TITLE: Requirement title
3348
STATEMENT: >>>
3449
Requirement statement.
3550
<<<
3651
CUSTOM_FIELD: Value 1
52+
53+
[[/SECTION]]
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,18 @@ TITLE: Document 1
33

44
[GRAMMAR]
55
ELEMENTS:
6+
- TAG: SECTION
7+
PROPERTIES:
8+
IS_COMPOSITE: True
9+
PREFIX: SECT-
10+
VIEW_STYLE: Narrative
11+
FIELDS:
12+
- TITLE: UID
13+
TYPE: String
14+
REQUIRED: False
15+
- TITLE: TITLE
16+
TYPE: String
17+
REQUIRED: False
618
- TAG: TEXT
719
FIELDS:
820
- TITLE: UID
@@ -28,9 +40,14 @@ ELEMENTS:
2840
RELATIONS:
2941
- TYPE: Parent
3042

43+
[[SECTION]]
44+
TITLE: Section example
45+
3146
[REQUIREMENT]
3247
TITLE: Requirement title
3348
STATEMENT: >>>
3449
Requirement statement.
3550
<<<
3651
CUSTOM_FIELD: Value 1
52+
53+
[[/SECTION]]

0 commit comments

Comments
 (0)