Skip to content

Commit

Permalink
Track UID_TO_NODE for SDocDocument to resolve LINK to DOCUMENT
Browse files Browse the repository at this point in the history
We can now use [LINK: DOC] to link to whole documents, where DOC is

 [DOCUMENT]
 TITLE: Document
 UID: DOC

in the same or another sdoc.
  • Loading branch information
haxtibal committed Nov 6, 2024
1 parent 5ac32b9 commit 1a3b51e
Show file tree
Hide file tree
Showing 11 changed files with 128 additions and 10 deletions.
17 changes: 10 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 @@ -469,12 +470,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
11 changes: 9 additions & 2 deletions strictdoc/core/traceability_index_builder.py
Original file line number Diff line number Diff line change
Expand Up @@ -258,7 +258,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 @@ -378,7 +380,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
34 changes: 33 additions & 1 deletion strictdoc/core/transforms/update_document_config.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,12 @@
# mypy: disable-error-code="arg-type,no-untyped-call,no-untyped-def"
from collections import defaultdict
from typing import Dict, List

# neue imports
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.backend.sdoc.models.node import SDocNode
from strictdoc.core.traceability_index import (
TraceabilityIndex,
)
Expand All @@ -12,6 +16,7 @@
from strictdoc.export.html.form_objects.document_config_form_object import (
DocumentConfigFormObject,
)
from strictdoc.helpers.mid import MID


class UpdateDocumentConfigTransform:
Expand Down Expand Up @@ -59,9 +64,36 @@ def validate(
):
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_node: SDocNode = self.traceability_index.get_node_by_mid(
MID(form_object.document_mid)
)
existing_uid = existing_node.reserved_uid
if existing_uid is not None:
if (
form_object.document_uid is None
or existing_uid != form_object.document_uid
):
existing_incoming_links: Optional[List[InlineLink]] = (
self.traceability_index.get_incoming_links(existing_node)
)
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
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
[DOCUMENT]
TITLE: Document 1
UID: DOC-1

[REQUIREMENT]
UID: REQ-2
TITLE: Foo Bar
STATEMENT: >>>
Read [LINK: DOC-1] then [LINK: DOC-2].
<<<
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
[DOCUMENT]
TITLE: Document 2
UID: DOC-2

[TEXT]
STATEMENT: >>>
Read [LINK: DOC-2] then [LINK: DOC-1].
<<<
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
RUN: %strictdoc export %S --output-dir Output | filecheck %s --dump-input=fail
CHECK: Published: Document 1

RUN: %cat %S/Output/html/%THIS_TEST_FOLDER/input1.html | filecheck %s --dump-input=fail --check-prefix CHECK-HTML-DOC1

CHECK-HTML-DOC1: Read <a href="../11_node_LINK_references_a_document/input1.html#_TOP">🔗&nbsp;Document 1</a> then <a href="../11_node_LINK_references_a_document/input2.html#_TOP">🔗&nbsp;Document 2</a>.

RUN: %cat %S/Output/html/%THIS_TEST_FOLDER/input2.html | filecheck %s --dump-input=fail --check-prefix CHECK-HTML-DOC2

CHECK-HTML-DOC2: Read <a href="../11_node_LINK_references_a_document/input2.html#_TOP">🔗&nbsp;Document 2</a> then <a href="../11_node_LINK_references_a_document/input1.html#_TOP">🔗&nbsp;Document 1</a>.
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
.. _DOC-1:

Document 1
$$$$$$$$$$

.. _REQ-2:

Foo Bar
=======

.. list-table::
:align: left
:header-rows: 0

* - **UID:**
- REQ-2

Read :ref:`Document 1 <DOC-1>` then :ref:`Document 2 <DOC-2>`.
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
.. _DOC-2:

Document 2
$$$$$$$$$$

Read :ref:`Document 2 <DOC-2>` then :ref:`Document 1 <DOC-1>`.
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
[DOCUMENT]
TITLE: Document 1
UID: DOC-1

[REQUIREMENT]
UID: REQ-2
TITLE: Foo Bar
STATEMENT: >>>
Read [LINK: DOC-1] then [LINK: DOC-2].
<<<
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
[DOCUMENT]
TITLE: Document 2
UID: DOC-2

[TEXT]
STATEMENT: >>>
Read [LINK: DOC-2] then [LINK: DOC-1].
<<<
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
RUN: %strictdoc export %S --formats=rst --output-dir Output

RUN: %check_exists --file "%S/Output/rst/input1.rst"

RUN: %diff "%S/Output/rst/input1.rst" "%S/expected/input1.rst"
RUN: %diff "%S/Output/rst/input2.rst" "%S/expected/input2.rst"

0 comments on commit 1a3b51e

Please sign in to comment.