Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
46 changes: 39 additions & 7 deletions strictdoc/core/traceability_index.py
Original file line number Diff line number Diff line change
Expand Up @@ -354,9 +354,10 @@ def get_node_by_uid_weak2(self, uid: str):

def get_linkable_node_by_uid(
self, uid
) -> Union[SDocNode, SDocSection, Anchor]:
) -> Union[SDocDocument, SDocNode, SDocSection, Anchor]:
return assert_cast(
self.get_node_by_uid(uid), (SDocNode, SDocSection, Anchor)
self.get_node_by_uid(uid),
(SDocDocument, SDocNode, SDocSection, Anchor),
)

def get_node_by_uid_weak(
Expand All @@ -383,16 +384,16 @@ def get_node_by_uid_weak(

def get_linkable_node_by_uid_weak(
self, uid
) -> Union[SDocNode, SDocSection, Anchor, None]:
) -> Union[SDocDocument, SDocNode, SDocSection, Anchor, None]:
return assert_optional_cast(
self.graph_database.get_link_value_weak(
link_type=GraphLinkType.UID_TO_NODE, lhs_node=uid
),
(SDocNode, SDocSection, Anchor),
(SDocDocument, SDocNode, SDocSection, Anchor),
)

def get_incoming_links(
self, node: Union[SDocNode, SDocSection, Anchor]
self, node: Union[SDocDocument, SDocNode, SDocSection, Anchor]
) -> Optional[List[InlineLink]]:
incoming_links = self.graph_database.get_link_values_weak(
link_type=GraphLinkType.NODE_TO_INCOMING_LINKS,
Expand Down Expand Up @@ -448,6 +449,20 @@ def create_traceability_info(
source_file_rel_path, traceability_info, traceability_index
)

def create_document(self, document: SDocDocument) -> None:
assert isinstance(document, SDocDocument)
if document.reserved_uid is not None:
self.graph_database.create_link(
link_type=GraphLinkType.UID_TO_NODE,
lhs_node=document.reserved_uid,
rhs_node=document,
)
self.graph_database.create_link(
link_type=GraphLinkType.MID_TO_NODE,
lhs_node=document.reserved_mid,
rhs_node=document,
)

def create_section(self, section: SDocSection) -> None:
assert isinstance(section, SDocSection)
if section.reserved_uid is not None:
Expand All @@ -469,12 +484,14 @@ def create_inline_link(self, new_link: InlineLink):
if self.graph_database.has_link(
link_type=GraphLinkType.UID_TO_NODE, lhs_node=new_link.link
):
node_or_anchor: Union[SDocNode, SDocSection, Anchor] = assert_cast(
node_or_anchor: Union[
SDocDocument, SDocNode, SDocSection, Anchor
] = assert_cast(
self.graph_database.get_link_value(
link_type=GraphLinkType.UID_TO_NODE,
lhs_node=new_link.link,
),
(SDocNode, SDocSection, Anchor),
(SDocDocument, SDocNode, SDocSection, Anchor),
)
self.graph_database.create_link(
link_type=GraphLinkType.NODE_TO_INCOMING_LINKS,
Expand Down Expand Up @@ -767,6 +784,21 @@ def update_disconnect_two_documents_if_no_links_left(
rhs_node=document.reserved_mid,
)

def delete_document(self, document: SDocDocument) -> None:
assert isinstance(document, SDocDocument), document

self.graph_database.delete_link(
link_type=GraphLinkType.MID_TO_NODE,
lhs_node=document.reserved_mid,
rhs_node=document,
)
if document.reserved_uid is not None:
self.graph_database.delete_link(
link_type=GraphLinkType.UID_TO_NODE,
lhs_node=document.reserved_uid,
rhs_node=document,
)

def delete_section(self, section: SDocSection) -> None:
assert isinstance(section, SDocSection), section

Expand Down
11 changes: 9 additions & 2 deletions strictdoc/core/traceability_index_builder.py
Original file line number Diff line number Diff line change
Expand Up @@ -232,7 +232,9 @@ def create_from_document_tree(
),
(
GraphLinkType.UID_TO_NODE,
OneToOneDictionary(str, (SDocNode, SDocSection, Anchor)),
OneToOneDictionary(
str, (SDocDocument, SDocNode, SDocSection, Anchor)
),
),
(
GraphLinkType.UID_TO_REQUIREMENT_CONNECTIONS,
Expand Down Expand Up @@ -352,7 +354,12 @@ def create_from_document_tree(
lhs_node=document.reserved_mid,
rhs_node=document,
)
# FIXME: Register Document with UID_TO_NODE
if document.uid:
graph_database.create_link(
link_type=GraphLinkType.UID_TO_NODE,
lhs_node=document.uid,
rhs_node=document,
)

document_tags: Dict[str, int] = {}
graph_database.create_link(
Expand Down
40 changes: 34 additions & 6 deletions strictdoc/core/transforms/update_document_config.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
# mypy: disable-error-code="arg-type,no-untyped-call,no-untyped-def"
from collections import defaultdict
from typing import Dict, List
from typing import Dict, List, Optional

from strictdoc.backend.sdoc.models.document import SDocDocument
from strictdoc.backend.sdoc.models.inline_link import InlineLink
from strictdoc.core.traceability_index import (
TraceabilityIndex,
)
Expand Down Expand Up @@ -36,11 +37,6 @@ def perform(self):

# Update the document.
document.title = form_object.document_title
document.config.uid = (
form_object.document_uid
if len(form_object.document_uid) > 0
else None
)
document.config.version = (
form_object.document_version
if len(form_object.document_version) > 0
Expand All @@ -52,16 +48,48 @@ def perform(self):
else None
)

self.traceability_index.delete_document(document)

document.config.uid = (
form_object.document_uid
if len(form_object.document_uid) > 0
else None
)

self.traceability_index.create_document(document)

def validate(
self,
form_object: DocumentConfigFormObject,
document: SDocDocument,
):
errors: Dict[str, List[str]] = defaultdict(list)
assert isinstance(document, SDocDocument)

if len(form_object.document_title) == 0:
errors["TITLE"].append("Document title must not be empty.")

# Ensure that UID doesn't have any incoming links if it is going to be
# renamed or removed
existing_uid = document.reserved_uid
new_uid = form_object.document_uid
if existing_uid is not None:
if new_uid is None or existing_uid != new_uid:
existing_incoming_links: Optional[List[InlineLink]] = (
self.traceability_index.get_incoming_links(document)
)
if (
existing_incoming_links is not None
and len(existing_incoming_links) > 0
):
errors["UID"].append(
(
"Renaming a node UID when the node has "
"incoming links is not supported yet. "
"Please delete all incoming links first."
),
)

if len(errors):
raise MultipleValidationError(
"Document form has not passed validation.", errors=errors
Expand Down
24 changes: 12 additions & 12 deletions strictdoc/export/html/form_objects/requirement_form_object.py
Original file line number Diff line number Diff line change
Expand Up @@ -193,7 +193,7 @@ def __init__(
context_document_mid: str,
fields: List[RequirementFormField],
reference_fields: List[RequirementReferenceFormField],
exiting_requirement_uid: Optional[str],
existing_requirement_uid: Optional[str],
grammar: DocumentGrammar,
# FIXME: Better name
relation_types: List[str],
Expand All @@ -213,7 +213,7 @@ def __init__(
self.reference_fields: List[RequirementReferenceFormField] = (
reference_fields
)
self.exiting_requirement_uid: Optional[str] = exiting_requirement_uid
self.existing_requirement_uid: Optional[str] = existing_requirement_uid
self.grammar: DocumentGrammar = grammar
self.relation_types: List[str] = relation_types
self.basic_free_text: bool = basic_free_text
Expand All @@ -225,7 +225,7 @@ def create_from_request(
requirement_mid: str,
request_form_data: FormData,
document: SDocDocument,
exiting_requirement_uid: Optional[str],
existing_requirement_uid: Optional[str],
basic_free_text: bool,
) -> "RequirementFormObject":
request_form_data_as_list = [
Expand Down Expand Up @@ -326,7 +326,7 @@ def create_from_request(
context_document_mid=context_document_mid,
fields=form_fields,
reference_fields=form_ref_fields,
exiting_requirement_uid=exiting_requirement_uid,
existing_requirement_uid=existing_requirement_uid,
grammar=grammar,
relation_types=element.get_relation_types(),
basic_free_text=basic_free_text,
Expand Down Expand Up @@ -384,7 +384,7 @@ def create_new(
context_document_mid=context_document_mid,
fields=form_fields,
reference_fields=[],
exiting_requirement_uid=None,
existing_requirement_uid=None,
grammar=grammar,
relation_types=element.get_relation_types(),
basic_free_text=False,
Expand Down Expand Up @@ -474,7 +474,7 @@ def create_from_requirement(
context_document_mid=context_document_mid,
fields=form_fields,
reference_fields=form_refs_fields,
exiting_requirement_uid=requirement.reserved_uid,
existing_requirement_uid=requirement.reserved_uid,
grammar=grammar,
relation_types=grammar_element_relations,
basic_free_text=requirement.basic_free_text,
Expand Down Expand Up @@ -617,7 +617,7 @@ def validate(
new_node_uid_or_none = new_node_uid

if new_node_uid_or_none is not None and (
self.is_new or self.exiting_requirement_uid != new_node_uid_or_none
self.is_new or self.existing_requirement_uid != new_node_uid_or_none
):
existing_node_with_this_uid = (
traceability_index.get_node_by_uid_weak(new_node_uid_or_none)
Expand All @@ -636,10 +636,10 @@ def validate(
Ensure that UID doesn't have any incoming links if it is going to be
renamed or removed.
"""
if self.exiting_requirement_uid is not None:
if self.existing_requirement_uid is not None:
if (
new_node_uid_or_none is None
or self.exiting_requirement_uid != new_node_uid_or_none
or self.existing_requirement_uid != new_node_uid_or_none
):
existing_node: SDocNode = traceability_index.get_node_by_mid(
MID(self.requirement_mid)
Expand Down Expand Up @@ -725,8 +725,8 @@ def validate(
)

if (
self.exiting_requirement_uid is not None
and self.exiting_requirement_uid != requirement_uid
self.existing_requirement_uid is not None
and self.existing_requirement_uid != requirement_uid
):
if len(self.reference_fields) > 0:
self.add_error(
Expand All @@ -738,7 +738,7 @@ def validate(
)
requirement_connections: SDocNodeConnections = (
traceability_index.get_node_connections(
self.exiting_requirement_uid
self.existing_requirement_uid
)
)
if len(requirement_connections.children):
Expand Down
8 changes: 4 additions & 4 deletions strictdoc/server/routers/main_router.py
Original file line number Diff line number Diff line change
Expand Up @@ -870,7 +870,7 @@ async def create_requirement(request: Request):
requirement_mid=requirement_mid,
request_form_data=request_form_data,
document=document,
exiting_requirement_uid=None,
existing_requirement_uid=None,
basic_free_text=basic_free_text,
)
)
Expand Down Expand Up @@ -1090,7 +1090,7 @@ async def document__update_requirement(request: Request):
requirement_mid=requirement_mid,
request_form_data=request_form_data,
document=document,
exiting_requirement_uid=requirement.reserved_uid,
existing_requirement_uid=requirement.reserved_uid,
basic_free_text=requirement.basic_free_text,
)
)
Expand Down Expand Up @@ -1763,7 +1763,7 @@ def document__add_comment(
context_document_mid=context_document_mid,
fields=[],
reference_fields=[],
exiting_requirement_uid=None,
existing_requirement_uid=None,
grammar=grammar,
relation_types=[],
basic_free_text=False,
Expand Down Expand Up @@ -1815,7 +1815,7 @@ def document__add_relation(
context_document_mid=context_document_mid,
fields=[],
reference_fields=[],
exiting_requirement_uid=None,
existing_requirement_uid=None,
grammar=grammar,
relation_types=grammar_element_relations,
basic_free_text=False,
Expand Down
6 changes: 6 additions & 0 deletions tests/end2end/helpers/screens/document/screen_document.py
Original file line number Diff line number Diff line change
Expand Up @@ -98,3 +98,9 @@ def do_drag_first_toc_node_to_the_second(self) -> None:
raise TimeoutError(
"StrictDoc custom timeout: Moving element in the TOC"
)

def do_click_on_tree_document(self, doc_order: int = 1) -> None:
self.test_case.assert_element_not_present("//sdoc-modal", by=By.XPATH)
self.test_case.click_xpath(
f'(//*[@data-testid="tree-document-link"])[{doc_order}]'
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
[DOCUMENT]
TITLE: Document 1

[REQUIREMENT]
UID: REQ-1
TITLE: Requirement 1
STATEMENT: >>>
Modified. [LINK: DOC-2]
<<<
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
[DOCUMENT]
TITLE: Document 2
UID: DOC-2
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
[DOCUMENT]
TITLE: Document 1

[REQUIREMENT]
UID: REQ-1
TITLE: Requirement 1
STATEMENT: >>>
Nothing here yet.
<<<
Loading
Loading