Skip to content

Commit

Permalink
Merge pull request #1666 from strictdoc-project/stanislaw/requirement…
Browse files Browse the repository at this point in the history
…_to_node

 server: creating grammar elements and arbitrary nodes
  • Loading branch information
stanislaw authored Feb 25, 2024
2 parents 9d65ef9 + 3e1fb22 commit 7e9127c
Show file tree
Hide file tree
Showing 138 changed files with 2,808 additions and 478 deletions.
64 changes: 59 additions & 5 deletions strictdoc/backend/sdoc/models/document_grammar.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import sys
from collections import OrderedDict, defaultdict
from collections import OrderedDict
from typing import Dict, List, Optional, Set, Union

from strictdoc.backend.sdoc.models.type_system import (
Expand All @@ -16,6 +16,7 @@
)
from strictdoc.helpers.auto_described import auto_described
from strictdoc.helpers.cast import assert_cast
from strictdoc.helpers.mid import MID


def create_default_relations(
Expand Down Expand Up @@ -88,11 +89,47 @@ def __init__(
del fields_map["REFS"]
self.fields.remove(refs_field)

self.mid: MID = MID.create()

@staticmethod
def create_default(tag: str):
return GrammarElement(
parent=None,
tag=tag,
fields=[
GrammarElementFieldString(
parent=None,
title="UID",
human_title=None,
required="False",
),
GrammarElementFieldString(
parent=None,
title="TITLE",
human_title=None,
required="False",
),
GrammarElementFieldString(
parent=None,
title="STATEMENT",
human_title=None,
required="False",
),
],
relations=[],
)

def get_relation_types(self) -> List[str]:
return list(
map(lambda relation_: relation_.relation_type, self.relations)
)

def get_field_titles(self) -> List[str]:
return list(map(lambda field_: field_.title, self.fields))

def get_tag_lower(self) -> str:
return self.tag.lower()

def has_relation_type_role(
self, relation_type: str, relation_role: Optional[str]
):
Expand Down Expand Up @@ -136,18 +173,19 @@ def __init__(self, parent, elements: List[GrammarElement]):
self.parent = parent
self.elements: List[GrammarElement] = elements

# When elements are created by code, not by textX, it is convenient
# if their .parent is set here automatically.
for element_ in elements:
element_.parent = self

registered_elements: Set[str] = set()
elements_by_type: Dict[str, GrammarElement] = {}
fields_by_type: Dict[str, List[str]] = defaultdict(list)
for element in elements:
registered_elements.add(element.tag)
elements_by_type[element.tag] = element
for element_field in element.fields:
fields_by_type[element.tag].append(element_field.title)

self.registered_elements: Set[str] = registered_elements
self.elements_by_type: Dict[str, GrammarElement] = elements_by_type
self.fields_order_by_type: Dict[str, List[str]] = fields_by_type

self.is_default = False

Expand Down Expand Up @@ -228,6 +266,14 @@ def create_default(parent):

return grammar

def get_element_by_mid(self, element_mid: str):
for element_ in self.elements:
if element_.mid == element_mid:
return element_
raise AssertionError(
f"Could not find a grammar element with MID: {element_mid}"
)

def dump_fields(self, requirement_type) -> str:
return ", ".join(
list(
Expand All @@ -237,3 +283,11 @@ def dump_fields(self, requirement_type) -> str:
)
)
)

def update_element(
self, existing_element: GrammarElement, updated_element: GrammarElement
):
element_index = self.elements.index(existing_element)
self.elements[element_index] = updated_element
self.elements_by_type[updated_element.tag] = updated_element
self.is_default = False
5 changes: 4 additions & 1 deletion strictdoc/backend/sdoc/processor.py
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,10 @@ def process_document_config(self, document_config: DocumentConfig):

def process_document_grammar(self, document_grammar: DocumentGrammar):
for grammar_element in document_grammar.elements:
if "STATEMENT" not in grammar_element.fields_map:
if (
grammar_element.tag == "REQUIREMENT"
and "STATEMENT" not in grammar_element.fields_map
):
raise StrictDocSemanticError.grammar_missing_reserved_statement(
grammar_element,
**get_location(document_grammar),
Expand Down
8 changes: 5 additions & 3 deletions strictdoc/backend/sdoc/validations/requirement.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,9 +31,11 @@ def multi_choice_regex_match(value):
def validate_requirement(
requirement: SDocNode, document_grammar: DocumentGrammar
):
registered_fields: Set[str] = set(
document_grammar.fields_order_by_type[requirement.requirement_type]
)
grammar_element = document_grammar.elements_by_type[
requirement.requirement_type
]
registered_fields: Set[str] = set(grammar_element.get_field_titles())

for field_name in requirement.ordered_fields_lookup:
if field_name not in registered_fields and field_name != "REFS":
raise StrictDocSemanticError.unregistered_field(
Expand Down
6 changes: 5 additions & 1 deletion strictdoc/core/transforms/create_requirement.py
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,11 @@ def perform(self):
for document_ in traceability_index.document_tree.document_list:
document_.ng_needs_generation = False

requirement = SDocObjectFactory.create_requirement(parent=parent)
# FIXME: It is better to have a general create_node method because
# we are dealing with arbitrary nodes, not only Requirement.
requirement = SDocObjectFactory.create_requirement(
parent=parent, requirement_type=form_object.element_type
)

# FIXME: Leave only one method based on set_field_value().
for form_field_name, form_fields in form_object.fields.items():
Expand Down
145 changes: 32 additions & 113 deletions strictdoc/core/transforms/update_grammar.py
Original file line number Diff line number Diff line change
@@ -1,18 +1,18 @@
from collections import OrderedDict
from dataclasses import dataclass
from typing import Dict, List, Optional, Set, Tuple
from typing import Dict, List, Set

from strictdoc.backend.sdoc.models.document import SDocDocument
from strictdoc.backend.sdoc.models.document_grammar import DocumentGrammar
from strictdoc.backend.sdoc.models.node import SDocNode, SDocNodeField
from strictdoc.backend.sdoc.models.type_system import GrammarElementField
from strictdoc.backend.sdoc.models.document_grammar import (
DocumentGrammar,
GrammarElement,
)
from strictdoc.backend.sdoc.models.node import SDocNode
from strictdoc.core.traceability_index import (
TraceabilityIndex,
)
from strictdoc.export.html.form_objects.document_grammar_form_object import (
GrammarElementFormObject,
from strictdoc.export.html.form_objects.grammar_form_object import (
GrammarFormObject,
)
from strictdoc.helpers.cast import assert_cast


@dataclass
Expand All @@ -24,123 +24,42 @@ class UpdateGrammarCommand:
def __init__(
self,
*,
form_object: GrammarElementFormObject,
form_object: GrammarFormObject,
document: SDocDocument,
traceability_index: TraceabilityIndex,
):
self.form_object: GrammarElementFormObject = form_object
self.form_object: GrammarFormObject = form_object
self.document: SDocDocument = document
self.traceability_index: TraceabilityIndex = traceability_index

def perform(self) -> bool:
form_object: GrammarElementFormObject = self.form_object
form_object: GrammarFormObject = self.form_object
document: SDocDocument = self.document

grammar_fields: Dict[str, GrammarElementField] = {}
for grammar_field in document.grammar.elements[0].fields:
grammar_fields[grammar_field.mid] = grammar_field

# Prepare fields that could have been renamed by the user has just saved the form.
renamed_fields_lookup = {}
for field in form_object.fields:
if field.field_mid not in grammar_fields:
continue
existing_field = grammar_fields[field.field_mid]
if field.field_name != existing_field.title:
renamed_fields_lookup[field.field_name] = existing_field.title

# Create new grammar.
document_grammar: DocumentGrammar = (
form_object.convert_to_document_grammar()
)

# Compare if anything was changed in the new grammar.
document_grammar_field_names = document_grammar.fields_order_by_type[
"REQUIREMENT"
]
existing_document_grammar_field_names = (
document.grammar.fields_order_by_type["REQUIREMENT"]
form_element_names = map(
lambda field_: field_.field_name, form_object.fields
)
grammar_changed = (
document_grammar_field_names
!= existing_document_grammar_field_names
)
existing_requirement_element = document.grammar.elements_by_type[
"REQUIREMENT"
]
new_requirement_element = document_grammar.elements_by_type[
"REQUIREMENT"
]
grammar_changed = (
grammar_changed
or existing_requirement_element.relations
!= new_requirement_element.relations
)
if not grammar_changed:
return False

document_grammar.parent = document
document.grammar = document_grammar

document_iterator = self.traceability_index.document_iterators[document]

for node in document_iterator.all_content():
if not node.is_requirement:
continue

requirement: SDocNode = assert_cast(node, SDocNode)
requirement_field_names = list(
requirement.ordered_fields_lookup.keys()
)

# Rewrite requirement fields because some fields could have been
# renamed.
new_ordered_fields_lookup: OrderedDict[
str, List[SDocNodeField]
] = OrderedDict()

for document_grammar_field_name in document_grammar_field_names:
# We need to find a previous field name in case the field was
# renamed.
previous_field_name = renamed_fields_lookup.get(
document_grammar_field_name, document_grammar_field_name
map_existing_elements_by_name: Dict[str, GrammarElement] = {}
for grammar_element_ in document.grammar.elements:
map_existing_elements_by_name[
grammar_element_.tag
] = grammar_element_

updated_grammar_elements: List[GrammarElement] = []
for form_element_name_ in form_element_names:
if form_element_name_ in map_existing_elements_by_name:
updated_grammar_elements.append(
map_existing_elements_by_name[form_element_name_]
)

# If the field does not exist in the grammar fields anymore,
# delete the requirement field.
if previous_field_name not in requirement_field_names:
continue

previous_fields: List[
SDocNodeField
] = requirement.ordered_fields_lookup[previous_field_name]
for previous_field in previous_fields:
previous_field.field_name = document_grammar_field_name

new_ordered_fields_lookup[
document_grammar_field_name
] = previous_fields

registered_relation_types: Set[Tuple[str, Optional[str]]] = set()
for relation in existing_requirement_element.relations:
registered_relation_types.add(
(relation.relation_type, relation.relation_role)
else:
updated_grammar_elements.append(
GrammarElement.create_default(form_element_name_)
)
if "REFS" in requirement.ordered_fields_lookup:
existing_refs_field = requirement.ordered_fields_lookup["REFS"][
0
]
new_relations = []
for (
requirement_relation_
) in existing_refs_field.field_value_references:
if (
requirement_relation_.ref_type,
requirement_relation_.role,
) in registered_relation_types:
new_relations.append(requirement_relation_)
new_ordered_fields_lookup["REFS"] = [existing_refs_field]
requirement.ordered_fields_lookup = new_ordered_fields_lookup
requirement.ng_reserved_fields_cache.clear()

new_grammar = DocumentGrammar(
parent=document, elements=updated_grammar_elements
)
document.grammar = new_grammar

return True
Loading

0 comments on commit 7e9127c

Please sign in to comment.