Skip to content

Commit

Permalink
Merge pull request strictdoc-project#1488 from strictdoc-project/stan…
Browse files Browse the repository at this point in the history
…islaw/requirements

project_statistics: calculate sections without any free text, add relevant query
  • Loading branch information
stanislaw authored Nov 29, 2023
2 parents 7146af4 + d335427 commit 64b4e10
Show file tree
Hide file tree
Showing 7 changed files with 52 additions and 0 deletions.
2 changes: 2 additions & 0 deletions docs/strictdoc_04_release_notes.sdoc
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ TITLE: Unreleased work
The requirement-to-source traceability feature was extended to support linking requirements to the RST files.

One more input scenario was handled for the Create Document workflow. When a project config has ``include_doc_paths`` or ``exclude_doc_paths`` search path filters specified, and an input document path contradicts to the provided filters, a validation message is shown.

The Project Statistics screen was extended with the "Sections without any text" metric. Now it is possible to visualize which sections are still missing any introduction or description (free text).
[/FREETEXT]

[/SECTION]
Expand Down
6 changes: 6 additions & 0 deletions strictdoc/core/query_engine/grammar.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,8 @@
|
NodeContainsExpression
|
NodeContainsAnyFreeTextExpression
|
NodeIsRequirementExpression
|
NodeIsSectionExpression
Expand Down Expand Up @@ -66,6 +68,10 @@
'node.contains("' string = /[A-Za-z0-9]+/ '")'
;
NodeContainsAnyFreeTextExpression:
_ = 'node.contains_any_text'
;
NodeHasParentRequirementsExpression:
_ = 'node.has_parent_requirements'
;
Expand Down
16 changes: 16 additions & 0 deletions strictdoc/core/query_engine/query_object.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,11 @@ def __init__(self, parent, _):
self.parent = parent


class NodeContainsAnyFreeTextExpression:
def __init__(self, parent, _):
self.parent = parent


class NodeHasChildRequirementsExpression:
def __init__(self, parent, _):
self.parent = parent
Expand Down Expand Up @@ -133,6 +138,8 @@ def _evaluate(self, node, expression) -> bool:
return self._evaluate_not_equal(node, expression)
if isinstance(expression, NodeContainsExpression):
return self._evaluate_node_contains(node, expression)
if isinstance(expression, NodeContainsAnyFreeTextExpression):
return self._evaluate_node_contains_any_text(node)
if isinstance(expression, NodeHasParentRequirementsExpression):
return self._evaluate_node_has_parent_requirements(node)
if isinstance(expression, NodeHasChildRequirementsExpression):
Expand Down Expand Up @@ -261,3 +268,12 @@ def _evaluate_node_contains(
return True
return False
raise NotImplementedError

def _evaluate_node_contains_any_text(self, node):
if not isinstance(node, Section):
raise TypeError(
f"node.contains_any_text can be only called on "
f"Section objects, got: {node.__class__.__name__}. To fix "
f"the error, prepend your query with node.is_section."
)
return len(node.free_texts) > 0
2 changes: 2 additions & 0 deletions strictdoc/core/query_engine/query_reader.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
AndExpression,
EqualExpression,
InExpression,
NodeContainsAnyFreeTextExpression,
NodeContainsExpression,
NodeFieldExpression,
NodeHasChildRequirementsExpression,
Expand All @@ -26,6 +27,7 @@
EqualExpression,
InExpression,
NodeContainsExpression,
NodeContainsAnyFreeTextExpression,
NodeFieldExpression,
NodeHasChildRequirementsExpression,
NodeHasParentRequirementsExpression,
Expand Down
8 changes: 8 additions & 0 deletions strictdoc/export/html/generators/project_statistics.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

from strictdoc import __version__
from strictdoc.backend.sdoc.models.requirement import Requirement
from strictdoc.backend.sdoc.models.section import Section
from strictdoc.core.document_iterator import DocumentCachingIterator
from strictdoc.core.document_tree_iterator import DocumentTreeIterator
from strictdoc.core.project_config import ProjectConfig
Expand All @@ -28,6 +29,9 @@ class DocumentTreeStats: # pylint: disable=too-many-instance-attributes
total_tbc: int = 0
git_commit_hash: Optional[str] = None

# Section
sections_without_free_text: int = 0

# UID
requirements_no_uid: int = 0
requirements_no_links: int = 0
Expand Down Expand Up @@ -72,6 +76,10 @@ def export(
for document in traceability_index.document_tree.document_list:
document_iterator = DocumentCachingIterator(document)
for node in document_iterator.all_content():
if isinstance(node, Section):
if len(node.free_texts) == 0:
document_tree_stats.sections_without_free_text += 1

if isinstance(node, Requirement):
requirement: Requirement = assert_cast(node, Requirement)
document_tree_stats.total_requirements += 1
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,12 @@
"Key":"Total documents",
"Value": document_tree_stats.total_documents,
},
{"Section":"Sections"},
{
"Key":"Sections without any text",
"Link": link_renderer.render_url('search?q=(node.is_section and not node.contains_any_text)'),
"Value": document_tree_stats.sections_without_free_text,
},
{"Section":"Requirements"},
{
"Key":"Total requirements",
Expand Down
12 changes: 12 additions & 0 deletions tests/unit/strictdoc/core/query_engine/test_query_reader.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
from strictdoc.core.query_engine.query_object import (
EqualExpression,
InExpression,
NodeContainsAnyFreeTextExpression,
NodeFieldExpression,
NodeHasParentRequirementsExpression,
NodeIsRequirementExpression,
Expand Down Expand Up @@ -175,3 +176,14 @@ def test_90_not_expression():
assert isinstance(query_object, Query)
assert isinstance(query_object.root_expression, NotExpression)
assert isinstance(query_object.root_expression.expression, EqualExpression)


def test_95_contains_any_free_text():
query = """\
node.contains_any_text\
"""
query_object = QueryReader.read(query)
assert isinstance(query_object, Query)
assert isinstance(
query_object.root_expression, NodeContainsAnyFreeTextExpression
)

0 comments on commit 64b4e10

Please sign in to comment.