Skip to content

Commit

Permalink
Add domain specific info to result (#9)
Browse files Browse the repository at this point in the history
Signed-off-by: patrickpa <[email protected]>
  • Loading branch information
patrickpa authored Jun 12, 2024
1 parent 75db6fc commit 2ffbfd5
Show file tree
Hide file tree
Showing 5 changed files with 294 additions and 15 deletions.
42 changes: 32 additions & 10 deletions qc_baselib/models/result.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,11 @@
# with this file, You can obtain one at https://mozilla.org/MPL/2.0/.
import enum

from typing import List, Any, Set, Dict

from typing import List, Any, Set
from pydantic import model_validator
from pydantic_xml import BaseXmlModel, attr, element
from pydantic_xml import BaseXmlModel, attr, element, computed_element
from lxml import etree

from .common import ParamType, IssueSeverity

Expand All @@ -21,9 +23,9 @@ class XMLLocationType(BaseXmlModel, tag="XMLLocation"):


class InertialLocationType(BaseXmlModel, tag="InertialLocation"):
x: float
y: float
z: float
x: float = attr(name="x")
y: float = attr(name="y")
z: float = attr(name="z")


class FileLocationType(BaseXmlModel, tag="FileLocation"):
Expand All @@ -32,16 +34,18 @@ class FileLocationType(BaseXmlModel, tag="FileLocation"):
file_type: str = attr(name="fileType")


class LocationType(BaseXmlModel, tag="Location"):
class LocationType(BaseXmlModel, tag="Locations"):
file_location: List[FileLocationType] = []
xml_location: List[XMLLocationType] = []
road_location: List[InertialLocationType] = []
inertial_location: List[InertialLocationType] = []
description: str = attr(name="description")

@model_validator(mode="after")
def check_at_least_one_element(self) -> Any:
if (
len(self.file_location) + len(self.xml_location) + len(self.road_location)
len(self.file_location)
+ len(self.xml_location)
+ len(self.inertial_location)
< 1
):
raise ValueError(
Expand Down Expand Up @@ -148,7 +152,18 @@ def check_any_empty(self) -> Any:
return self


class IssueType(BaseXmlModel, tag="Issue"):
class DomainSpecificInfoType(
BaseXmlModel,
tag="DomainSpecificInfo",
):
name: str = attr(name="name")


class IssueType(
BaseXmlModel,
tag="Issue",
arbitrary_types_allowed=True,
):
locations: List[LocationType] = []
issue_id: int = attr(name="issueId")
description: str = attr(name="description")
Expand All @@ -158,6 +173,13 @@ class IssueType(BaseXmlModel, tag="Issue"):
default="",
pattern=r"^((\w+(\.\w+)+)):(([a-z]+)):(([0-9]+(\.[0-9]+)+)):((([a-z][\w_]*)\.)*)([a-z][\w_]*)$",
)
# Raw is defined here to enable parsing of "any" XML tag inside the domain
# specific information. It is linked to the DomainSpecificInfoType model
# to enforce the attributes.
# Linked Issue: https://github.com/dapper91/pydantic-xml/issues/100
domain_specific_info: List[etree._Element] = element(
tag="DomainSpecificInfo", default=[], excluded=True
)


class MetadataType(BaseXmlModel, tag="Metadata"):
Expand All @@ -176,7 +198,7 @@ class CheckerType(BaseXmlModel, tag="Checker", validate_assignment=True):
addressed_rule: List[RuleType] = []
issues: List[IssueType] = []
metadata: List[MetadataType] = []
status: StatusType = attr(name="status", default="")
status: StatusType = attr(name="status", default=None)
checker_id: str = attr(name="checkerId")
description: str = attr(name="description")
summary: str = attr(name="summary")
Expand Down
89 changes: 89 additions & 0 deletions qc_baselib/result.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,22 @@
# Public License, v. 2.0. If a copy of the MPL was not distributed
# with this file, You can obtain one at https://mozilla.org/MPL/2.0/.

from copy import deepcopy
from dataclasses import dataclass
from typing import Union, List, Dict, Any
from lxml import etree
from .models import IssueSeverity, result

REPORT_OUTPUT_FORMAT = "xqar"
DEFAULT_REPORT_VERSION = "0.0.1"


@dataclass
class DomainSpecificInfoContent:
name: str
content: List[etree._Element]


class IDManager:
_id = -1

Expand Down Expand Up @@ -87,6 +96,7 @@ def write_to_file(self, xml_output_file_path: str) -> None:
xml_declaration=True,
standalone=False,
encoding="UTF-8",
skip_empty=True,
)
report_xml_file.write(xml_text)

Expand Down Expand Up @@ -286,6 +296,57 @@ def add_xml_location(
result.LocationType(xml_location=[xml_location], description=description)
)

def add_inertial_location(
self,
checker_bundle_name: str,
checker_id: str,
issue_id: int,
x: float,
y: float,
z: float,
description: str,
) -> None:
inertial_location = result.InertialLocationType(x=x, y=y, z=z)

bundle = self._get_checker_bundle(checker_bundle_name=checker_bundle_name)

checker = self._get_checker(bundle=bundle, checker_id=checker_id)
issue = self._get_issue(checker=checker, issue_id=issue_id)

issue.locations.append(
result.LocationType(
inertial_location=[inertial_location], description=description
)
)

def add_domain_specific_info(
self,
checker_bundle_name: str,
checker_id: str,
issue_id: int,
domain_specific_info_name: str,
xml_info: List[etree._Element],
) -> None:
"""
Adds named domain specific information.
The domain specific information contains a name and a list of relevant
xml elements.
"""
bundle = self._get_checker_bundle(checker_bundle_name=checker_bundle_name)
checker = self._get_checker(bundle=bundle, checker_id=checker_id)
issue = self._get_issue(checker=checker, issue_id=issue_id)

domain_specific_tag = etree.Element(
"DomainSpecificInfo", attrib={"name": domain_specific_info_name}
)

for xml_info_element in xml_info:
xml_info_to_append = deepcopy(xml_info_element)
domain_specific_tag.append(xml_info_to_append)

issue.domain_specific_info.append(domain_specific_tag)

def set_checker_status(
self, checker_bundle_name: str, checker_id: str, status: result.StatusType
) -> None:
Expand Down Expand Up @@ -379,3 +440,31 @@ def get_issue_count(self) -> int:
)

return issue_count

def get_domain_specific_info(
self,
checker_bundle_name: str,
checker_id: str,
issue_id: int,
) -> List[DomainSpecificInfoContent]:
"""
Returns a list of named domain specific information.
Each domain specific information content contains a name and a list of
relevant xml elements.
"""
bundle = self._get_checker_bundle(checker_bundle_name=checker_bundle_name)
checker = self._get_checker(bundle=bundle, checker_id=checker_id)
issue = self._get_issue(checker=checker, issue_id=issue_id)

domain_specific_list = []

for domain_specific_info in issue.domain_specific_info:
attrib = domain_specific_info.attrib
info_dict = DomainSpecificInfoContent(
name=attrib["name"], content=domain_specific_info.getchildren()
)

domain_specific_list.append(info_dict)

return domain_specific_list
20 changes: 19 additions & 1 deletion tests/data/demo_checker_bundle_extended.xqar
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,25 @@
</Checker>
<Checker checkerId="exampleIssueRuleChecker" description="This is a description of checker with issue and the involved ruleUID" status="completed" summary="">
<AddressedRule ruleUID="test.com:qc:1.0.0:qwerty.qwerty"/>
<Issue description="This is an information from the demo usecase" issueId="2" level="1" ruleUID="test.com:qc:1.0.0:qwerty.qwerty"/>
<Issue description="This is an information from the demo usecase" issueId="2" level="1" ruleUID="test.com:qc:1.0.0:qwerty.qwerty">
<Locations description="inertial position">
<InertialLocation x="1.000000" y="2.000000" z="3.000000"/>
</Locations>
<DomainSpecificInfo name="test_domain_1">
<RoadLocation b="5.4" c="0.0" id="aa" />
<RoadLocation b="5.4" c="0.0" id="aa" />
<TestTagFor>
<InternalElementA a="1.0"/>
<InternalElementA a="1.0"/>
<InternalElementNested a="1.0">
<NestedElement/>
</InternalElementNested>
</TestTagFor>
</DomainSpecificInfo>
<DomainSpecificInfo name="test_domain_2">
<RoadLocation b="5.4" c="0.0" id="aa" />
</DomainSpecificInfo>
</Issue>
</Checker>
<Checker checkerId="exampleSkippedChecker" description="This is a description of checker with skipped status" status="skipped" summary="Skipped execution"/>
</CheckerBundle>
Expand Down
11 changes: 7 additions & 4 deletions tests/data/result_test_output.xqar
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,15 @@
<Checker status="completed" checkerId="TestChecker" description="Test checker" summary="Executed evaluation">
<AddressedRule ruleUID="test.com:qc:1.0.0:qwerty.qwerty"/>
<Issue issueId="0" description="Issue found at odr" level="3" ruleUID="test.com:qc:1.0.0:qwerty.qwerty">
<Location description="Location for issue">
<Locations description="Location for issue">
<FileLocation column="0" row="1" fileType="odr"/>
</Location>
<Location description="Location for issue">
</Locations>
<Locations description="Location for issue">
<XMLLocation xpath="/foo/test/path"/>
</Location>
</Locations>
<Locations description="Location for issue">
<InertialLocation x="1.0" y="2.0" z="3.0"/>
</Locations>
</Issue>
</Checker>
</CheckerBundle>
Expand Down
Loading

0 comments on commit 2ffbfd5

Please sign in to comment.