Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

👌 Allow ref in neeuml to handle need parts #1222

Merged
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
32 changes: 22 additions & 10 deletions sphinx_needs/directives/needuml.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,9 @@
from sphinx_needs.directives.needflow._plantuml import make_entity_name
from sphinx_needs.filter_common import filter_needs_view
from sphinx_needs.logging import log_warning
from sphinx_needs.utils import add_doc, logger
from sphinx_needs.roles.need_part import create_need_from_part
from sphinx_needs.roles.need_ref import value_to_string
from sphinx_needs.utils import add_doc, logger, split_need_id

if TYPE_CHECKING:
from sphinxcontrib.plantuml import plantuml
Expand Down Expand Up @@ -401,24 +403,34 @@ def flow(self, need_id: str) -> str:
def ref(
self, need_id: str, option: None | str = None, text: None | str = None
) -> str:
if need_id not in self.needs:
need_id_main, need_id_part = split_need_id(need_id)

if need_id_main not in self.needs:
raise NeedumlException(
f"Jinja function ref is called with undefined need_id: '{need_id}'."
f"Jinja function ref is called with undefined need_id: '{need_id_main}'."
)
if (option and text) and (not option and not text):
raise NeedumlException(
"Jinja function ref requires exactly one entry 'option' or 'text'"
)

need_info = self.needs[need_id]
link = calculate_link(self.app, need_info, self.fromdocname)
need_info = self.needs[need_id_main]

need_uml = "[[{link} {content}]]".format(
link=link,
content=need_info.get(option, "") if option else text,
)
if need_id_part:
if need_id_part not in need_info["parts"]:
raise NeedumlException(
f"Jinja function ref is called with undefined need_id part: '{need_id}'."
)
need_info = create_need_from_part(
need_info, need_info["parts"][need_id_part]
)

return need_uml
link = calculate_link(self.app, need_info, self.fromdocname)
link_text = (
value_to_string(need_info.get(option, "")) if option else str(text or "")
).strip()

return f"[[{link}{' ' if link_text else ''}{link_text}]]"

def filter(self, filter_string: str) -> list[NeedsInfoType]:
"""
Expand Down
23 changes: 14 additions & 9 deletions sphinx_needs/roles/need_ref.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import contextlib
from collections.abc import Iterable
from typing import Any

from docutils import nodes
from sphinx.application import Sphinx
Expand Down Expand Up @@ -38,19 +39,23 @@ def transform_need_to_dict(need: NeedsInfoType) -> dict[str, str]:
dict_need = {}

for element, value in need.items():
if isinstance(value, str):
# As string are iterable, we have to handle strings first.
dict_need[element] = value
elif isinstance(value, dict):
dict_need[element] = ";".join([str(i) for i in value.items()])
elif isinstance(value, (Iterable, list, tuple)):
dict_need[element] = ";".join([str(i) for i in value])
else:
dict_need[element] = str(value)
dict_need[element] = value_to_string(value)

return dict_need


def value_to_string(value: Any) -> str:
if isinstance(value, str):
# As string are iterable, we have to handle strings first.
return value
elif isinstance(value, dict):
return ";".join([str(i) for i in value.items()])
elif isinstance(value, (Iterable, list, tuple)):
return ";".join([str(i) for i in value])

return str(value)


def process_need_ref(
app: Sphinx,
doctree: nodes.document,
Expand Down
12 changes: 11 additions & 1 deletion tests/__snapshots__/test_needuml.ambr
Original file line number Diff line number Diff line change
Expand Up @@ -1251,12 +1251,22 @@
'content': '''
DC -> Marvel: {{ref("ST_001", option="title")}}
Marvel --> DC: {{ref("ST_002", text="Different text to explain the story")}}

DC -> Marvel: {{ref("ST_001.np_id", option="id")}}
Marvel --> DC: {{ref("ST_001.np_id", option="content")}}

DC -> Marvel: {{ref("ST_001.np_id", text="Different text to explain the story 2")}}
''',
'content_calculated': '''
@startuml

DC -> Marvel: [[../index.html#ST_001 Test story]]
Marvel --> DC: [[../index.html#ST_002 Different text to explain the story]]

DC -> Marvel: [[../index.html#ST_001.np_id np_id]]
Marvel --> DC: [[../index.html#ST_001.np_id np_content]]

DC -> Marvel: [[../index.html#ST_001.np_id Different text to explain the story 2]]
@enduml

''',
Expand All @@ -1266,7 +1276,7 @@
}),
'is_arch': False,
'key': None,
'lineno': 17,
'lineno': 19,
'save': None,
'scale': '',
'target_id': 'needuml-index-0',
Expand Down
9 changes: 8 additions & 1 deletion tests/doc_test/doc_needuml_jinja_func_ref/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,12 @@ TEST DOCUMENT NEEDUML JINJA FUNCTION REF

Some content

:np:`(np_id) np_content`

.. story:: Another story
:id: ST_002

Different conftent content
Different content content

.. int:: Test needuml jinja func ref
:id: INT_001
Expand All @@ -18,3 +20,8 @@ TEST DOCUMENT NEEDUML JINJA FUNCTION REF

DC -> Marvel: {{ref("ST_001", option="title")}}
Marvel --> DC: {{ref("ST_002", text="Different text to explain the story")}}

DC -> Marvel: {{ref("ST_001.np_id", option="id")}}
Marvel --> DC: {{ref("ST_001.np_id", option="content")}}

DC -> Marvel: {{ref("ST_001.np_id", text="Different text to explain the story 2")}}
8 changes: 8 additions & 0 deletions tests/test_needuml.py
Original file line number Diff line number Diff line change
Expand Up @@ -277,6 +277,14 @@ def test_needuml_jinja_func_ref(test_app, snapshot):
assert "Marvel: [[../index.html#ST_001 Test story]]" in html
assert "DC: [[../index.html#ST_002 Different text to explain the story]]" in html

assert "Marvel: [[../index.html#ST_001.np_id np_id]]" in html
assert "DC: [[../index.html#ST_001.np_id np_content]]" in html

assert (
"Marvel: [[../index.html#ST_001.np_id Different text to explain the story 2]]"
in html
)

srcdir = Path(app.srcdir)
out_dir = srcdir / "_build"

Expand Down