Skip to content

Commit

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

export/html: DIFF: match requirements without UID, with the same title/statement/rationale
  • Loading branch information
stanislaw authored Dec 4, 2023
2 parents 4b5b278 + f80ef26 commit fe24926
Show file tree
Hide file tree
Showing 5 changed files with 119 additions and 31 deletions.
13 changes: 9 additions & 4 deletions strictdoc/export/html/templates/screens/git/form.jinja
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,9 @@
<input
type="text"
{% if left_revision is not none %}
value={{ left_revision }}
value="{{ left_revision }}"
{% else %}
value="73dd8f411f864382349e951c51d3c4ce1b90fd28"
value=""
{% endif %}
placeholder="Enter the LHS revision here"
id="left_revision"
Expand All @@ -19,14 +19,19 @@
<input
type="text"
{% if right_revision is not none %}
value={{ right_revision }}
value="{{ right_revision }}"
{% else %}
value="HEAD+"
value=""
{% endif %}
placeholder="Enter the RHS revision here"
id="right_revision"
name="right_revision"
/>

</form>

{% if error_message is not none %}
{{ error_message }}
{% endif %}

</sdoc-form>
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,13 @@
{{ requirement.reserved_title }}
</span>
{%- endif -%}
{% if requirement_modified and requirement.reserved_uid is not none and requirement.reserved_uid|length > 0 %}
{%- with uid=requirement.reserved_uid -%}
{%- include "screens/git/sync/button.jinja" -%}
{%- endwith -%}
{% if requirement_modified %}
{% set requirement_token = change_stats.find_requirement_token(requirement) %}
{% if requirement_token is not none %}
{%- with uid=requirement_token -%}
{%- include "screens/git/sync/button.jinja" -%}
{%- endwith -%}
{%- endif -%}
{% endif %}
</summary>

Expand Down
2 changes: 1 addition & 1 deletion strictdoc/git/git_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,7 @@ def check_revision(self, revision: str):
)
if result.returncode == 0:
return result.stdout.strip()
raise LookupError(f"Non-existing revision: {revision}")
raise LookupError(f"Non-existing revision: {revision}.")

def commit_all(self, message: str):
result = subprocess.run(
Expand Down
68 changes: 64 additions & 4 deletions strictdoc/git/project_diff_analyzer.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
from strictdoc.helpers.cast import assert_cast
from strictdoc.helpers.diff import get_colored_diff_string, similar
from strictdoc.helpers.md5 import get_md5
from strictdoc.helpers.mid import MID


def calculate_similarity(lhs: Requirement, rhs: Requirement) -> float:
Expand Down Expand Up @@ -84,7 +85,7 @@ def contains_requirement_field(
self, requirement: Requirement, field_name: str, field_value: str
):
assert isinstance(field_value, str)
other_requirement: Optional[Requirement] = self._find_requirement(
other_requirement: Optional[Requirement] = self.find_requirement(
requirement
)
if other_requirement is None:
Expand Down Expand Up @@ -214,7 +215,7 @@ def get_diffed_requirement_field(
assert isinstance(field_value, str)
assert side in ("left", "right")

other_requirement: Optional[Requirement] = self._find_requirement(
other_requirement: Optional[Requirement] = self.find_requirement(
requirement
)
if (
Expand Down Expand Up @@ -254,7 +255,7 @@ def contains_requirement_relations(
relation_uid: str,
relation_role: Optional[str],
):
other_requirement: Optional[Requirement] = self._find_requirement(
other_requirement: Optional[Requirement] = self.find_requirement(
requirement
)
if other_requirement is None:
Expand All @@ -271,7 +272,7 @@ def contains_requirement_relations(
return True
return False

def _find_requirement(
def find_requirement(
self, requirement: Requirement
) -> Optional[Requirement]:
if requirement in self.cache_requirement_to_requirement:
Expand Down Expand Up @@ -323,6 +324,65 @@ def _find_requirement(
return other_requirement


@dataclass
class ChangeStats:
map_requirements_to_tokens: Dict[Requirement, str] = field(
default_factory=dict
)

def find_requirement_token(self, requirement: Requirement) -> Optional[str]:
return self.map_requirements_to_tokens.get(requirement)

@staticmethod
def create_from_two_indexes(
lhs_index: TraceabilityIndex,
rhs_index: TraceabilityIndex,
lhs_stats: ProjectTreeDiffStats,
rhs_stats: ProjectTreeDiffStats,
):
stats = ChangeStats()

ChangeStats._iterate_one_index(lhs_index, rhs_stats, stats)
ChangeStats._iterate_one_index(rhs_index, lhs_stats, stats)

return stats

@staticmethod
def _iterate_one_index(
index: TraceabilityIndex,
stats: ProjectTreeDiffStats,
change_stats: "ChangeStats",
):
for document in index.document_tree.document_list:
document_iterator = DocumentCachingIterator(document)

for node in document_iterator.all_content():
if isinstance(node, Requirement):
# FIXME: Is this 100% valid?
if node in change_stats.map_requirements_to_tokens:
continue

requirement: Requirement = assert_cast(node, Requirement)
other_requirement_or_none: Optional[
Requirement
] = stats.find_requirement(requirement)
if other_requirement_or_none is None:
continue

other_requirement: Requirement = other_requirement_or_none

requirement_token: str = MID.create().get_string_value()

change_stats.map_requirements_to_tokens[
requirement
] = requirement_token
change_stats.map_requirements_to_tokens[
other_requirement
] = requirement_token

return stats


class ProjectDiffAnalyzer:
@staticmethod
def analyze_document_tree(
Expand Down
56 changes: 38 additions & 18 deletions strictdoc/server/routers/other_router.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
from strictdoc.export.html.renderers.link_renderer import LinkRenderer
from strictdoc.git.git_client import GitClient
from strictdoc.git.project_diff_analyzer import (
ChangeStats,
ProjectDiffAnalyzer,
ProjectTreeDiffStats,
)
Expand Down Expand Up @@ -44,35 +45,46 @@ def get_git_diff(
)
left_revision_resolved = None
right_revision_resolved = None
results = False

if left_revision is not None:
assert right_revision is not None

results = False
error_message: Optional[str] = None

if (
left_revision is not None
and len(left_revision) > 0
and right_revision is not None
and len(right_revision) > 0
):
git_client = GitClient(".")
try:
if left_revision != "HEAD+":
left_revision_resolved = git_client.check_revision(
left_revision
)
else:
return HTMLResponse(
content=(
"Left revision argument 'HEAD+' is not supported. "
"'HEAD+' can only be used as a right revision argument."
),
status_code=412,
raise LookupError(
"Left revision argument 'HEAD+' is not supported. "
"'HEAD+' can only be used as a right revision argument."
)

if right_revision != "HEAD+":
if right_revision == "HEAD+":
right_revision_resolved = "HEAD+"
else:
right_revision_resolved = git_client.check_revision(
right_revision
)
else:
right_revision_resolved = "HEAD+"

results = True
except LookupError as exception_:
return HTMLResponse(content=exception_.args[0], status_code=404)
results = True
error_message = exception_.args[0]
elif (left_revision is not None and len(left_revision) > 0) or (
right_revision is not None and len(right_revision) > 0
):
error_message = "Valid Git revisions must be provided."
else:
# In the case when both revisions are empty, we load the starting
# diff page.
pass

template = html_templates.jinja_environment().get_template(
"screens/git/index.jinja"
Expand All @@ -81,6 +93,7 @@ def get_git_diff(
link_renderer = LinkRenderer(
root_path="", static_path=project_config.dir_for_sdoc_assets
)

if not results:
output = template.render(
project_config=project_config,
Expand All @@ -90,10 +103,12 @@ def get_git_diff(
strictdoc_version=__version__,
link_renderer=link_renderer,
results=False,
left_revision=None,
right_revision=None,
left_revision=left_revision,
right_revision=right_revision,
error_message=error_message,
)
return HTMLResponse(content=output, status_code=200)
status_code = 200 if error_message is None else 422
return HTMLResponse(content=output, status_code=status_code)

assert left_revision_resolved is not None
assert right_revision_resolved is not None
Expand Down Expand Up @@ -149,6 +164,9 @@ def get_git_diff(
rhs_stats: ProjectTreeDiffStats = (
ProjectDiffAnalyzer.analyze_document_tree(traceability_index_rhs)
)
change_stats: ChangeStats = ChangeStats.create_from_two_indexes(
traceability_index_lhs, traceability_index_rhs, lhs_stats, rhs_stats
)

documents_iterator_lhs = DocumentTreeIterator(
traceability_index_lhs.document_tree
Expand All @@ -166,6 +184,7 @@ def get_git_diff(
right_revision=right_revision,
lhs_stats=lhs_stats,
rhs_stats=rhs_stats,
change_stats=change_stats,
traceability_index_lhs=traceability_index_lhs,
traceability_index_rhs=traceability_index_rhs,
link_renderer=link_renderer,
Expand All @@ -174,6 +193,7 @@ def get_git_diff(
standalone=False,
strictdoc_version=__version__,
results=True,
error_message=None,
)
return HTMLResponse(
content=output,
Expand Down

0 comments on commit fe24926

Please sign in to comment.