Skip to content

Commit

Permalink
Merge pull request #383 from andlaus/table_row_fixes
Browse files Browse the repository at this point in the history
TableRow: bring it in line with the spec
  • Loading branch information
andlaus authored Feb 11, 2025
2 parents 90dc288 + 6891686 commit 09dbf18
Show file tree
Hide file tree
Showing 4 changed files with 176 additions and 8 deletions.
32 changes: 32 additions & 0 deletions examples/somersaultecu.py
Original file line number Diff line number Diff line change
Expand Up @@ -1366,6 +1366,14 @@ class SomersaultSID(IntEnum):
oid=None,
short_name="none",
long_name="No Flips Done Yet",
audience=None,
functional_class_refs=[],
admin_data=None,
state_transition_refs=[],
pre_condition_state_refs=[],
is_executable_raw=None,
is_mandatory_raw=None,
is_final_raw=None,
key_raw="0",
structure_ref=None,
structure_snref=None,
Expand All @@ -1382,6 +1390,14 @@ class SomersaultSID(IntEnum):
oid=None,
short_name="forward_grudging",
long_name="Forward Flips Grudgingly Done",
audience=None,
functional_class_refs=[],
admin_data=None,
state_transition_refs=[],
pre_condition_state_refs=[],
is_executable_raw=None,
is_mandatory_raw=None,
is_final_raw=None,
key_raw="3",
structure_ref=OdxLinkRef.from_id(
somersault_structures["forward_flips_grudgingly_done"].odx_id),
Expand All @@ -1400,6 +1416,14 @@ class SomersaultSID(IntEnum):
short_name="forward_happily",
long_name="Forward Flips Happily Done",
description=None,
audience=None,
functional_class_refs=[],
admin_data=None,
state_transition_refs=[],
pre_condition_state_refs=[],
is_executable_raw=None,
is_mandatory_raw=None,
is_final_raw=None,
semantic=None,
key_raw="5",
structure_ref=OdxLinkRef.from_id(
Expand All @@ -1416,6 +1440,14 @@ class SomersaultSID(IntEnum):
short_name="backward_grudging",
long_name="Backward Flips",
description=None,
audience=None,
functional_class_refs=[],
admin_data=None,
state_transition_refs=[],
pre_condition_state_refs=[],
is_executable_raw=None,
is_mandatory_raw=None,
is_final_raw=None,
semantic=None,
key_raw="10",
structure_ref=OdxLinkRef.from_id(
Expand Down
99 changes: 91 additions & 8 deletions odxtools/tablerow.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,21 @@
from typing import TYPE_CHECKING, Any, Dict, List, Optional, Tuple
from xml.etree import ElementTree

from .admindata import AdminData
from .audience import Audience
from .basicstructure import BasicStructure
from .dataobjectproperty import DataObjectProperty
from .dtcdop import DtcDop
from .element import IdentifiableElement
from .exceptions import odxassert, odxraise, odxrequire
from .functionalclass import FunctionalClass
from .nameditemlist import NamedItemList
from .odxlink import OdxDocFragment, OdxLinkDatabase, OdxLinkId, OdxLinkRef, resolve_snref
from .odxtypes import AtomicOdxType
from .odxtypes import AtomicOdxType, odxstr_to_bool
from .snrefcontext import SnRefContext
from .specialdatagroup import SpecialDataGroup
from .state import State
from .statetransition import StateTransition
from .utils import dataclass_fields_asdict

if TYPE_CHECKING:
Expand All @@ -21,18 +27,52 @@
@dataclass
class TableRow(IdentifiableElement):
"""This class represents a TABLE-ROW."""
table_ref: OdxLinkRef
key_raw: str
structure_ref: Optional[OdxLinkRef]
structure_snref: Optional[str]
table_ref: OdxLinkRef

# the referenced DOP must be a simple DOP (i.e.,
# DataObjectProperty or DtcDop, cf section 7.3.6.11 of the spec)!
# The spec mandates that either a structure or a non-complex DOP
# must be referenced here, i.e., exactly one of the four
# attributes below is not None
dop_ref: Optional[OdxLinkRef]
dop_snref: Optional[str]
structure_ref: Optional[OdxLinkRef]
structure_snref: Optional[str]

semantic: Optional[str]
sdgs: List[SpecialDataGroup]
audience: Optional[Audience]
functional_class_refs: List[OdxLinkRef]
state_transition_refs: List[OdxLinkRef]
pre_condition_state_refs: List[OdxLinkRef]
admin_data: Optional[AdminData]

is_executable_raw: Optional[bool]
semantic: Optional[str]
is_mandatory_raw: Optional[bool]
is_final_raw: Optional[bool]

@property
def functional_classes(self) -> NamedItemList[FunctionalClass]:
return self._functional_classes

@property
def state_transitions(self) -> NamedItemList[StateTransition]:
return self._state_transitions

@property
def pre_condition_states(self) -> NamedItemList[State]:
return self._pre_condition_states

@property
def is_executable(self) -> bool:
return self.is_executable_raw in (None, True)

@property
def is_mandatory(self) -> bool:
return self.is_mandatory_raw is True

@property
def is_final(self) -> bool:
return self.is_final_raw is True

def __post_init__(self) -> None:
self._structure: Optional[BasicStructure] = None
Expand Down Expand Up @@ -73,15 +113,49 @@ def tablerow_from_et(et_element: ElementTree.Element, doc_frags: List[OdxDocFrag
SpecialDataGroup.from_et(sdge, doc_frags) for sdge in et_element.iterfind("SDGS/SDG")
]

audience = None
if (audience_elem := et_element.find("AUDIENCE")) is not None:
audience = Audience.from_et(audience_elem, doc_frags)

functional_class_refs = [
odxrequire(OdxLinkRef.from_et(el, doc_frags))
for el in et_element.iterfind("FUNCT-CLASS-REFS/FUNCT-CLASS-REF")
]

state_transition_refs = [
odxrequire(OdxLinkRef.from_et(el, doc_frags))
for el in et_element.iterfind("STATE-TRANSITION-REFS/STATE-TRANSITION-REF")
]

pre_condition_state_refs = [
odxrequire(OdxLinkRef.from_et(el, doc_frags))
for el in et_element.iterfind("PRE-CONDITION-STATE-REFS/PRE-CONDITION-STATE-REF")
]

admin_data = AdminData.from_et(et_element.find("ADMIN-DATA"), doc_frags)

is_executable_raw = odxstr_to_bool(et_element.attrib.get("IS-EXECUTABLE"))
semantic = et_element.attrib.get("SEMANTIC")
is_mandatory_raw = odxstr_to_bool(et_element.attrib.get("IS-MANDATORY"))
is_final_raw = odxstr_to_bool(et_element.attrib.get("IS-FINAL"))

return TableRow(
table_ref=table_ref,
semantic=semantic,
key_raw=key_raw,
structure_ref=structure_ref,
structure_snref=structure_snref,
dop_ref=dop_ref,
dop_snref=dop_snref,
sdgs=sdgs,
audience=audience,
functional_class_refs=functional_class_refs,
state_transition_refs=state_transition_refs,
pre_condition_state_refs=pre_condition_state_refs,
admin_data=admin_data,
is_executable_raw=is_executable_raw,
semantic=semantic,
is_mandatory_raw=is_mandatory_raw,
is_final_raw=is_final_raw,
**kwargs)

def _build_odxlinks(self) -> Dict[OdxLinkId, Any]:
Expand All @@ -108,6 +182,15 @@ def _resolve_odxlinks(self, odxlinks: OdxLinkDatabase) -> None:
for sdg in self.sdgs:
sdg._resolve_odxlinks(odxlinks)

self._functional_classes = NamedItemList(
[odxlinks.resolve(fc_ref, FunctionalClass) for fc_ref in self.functional_class_refs])

self._state_transitions = NamedItemList(
[odxlinks.resolve(st_ref, StateTransition) for st_ref in self.state_transition_refs])

self._pre_condition_states = NamedItemList(
[odxlinks.resolve(pcs_ref, State) for pcs_ref in self.pre_condition_state_refs])

def _resolve_snrefs(self, context: SnRefContext) -> None:
# convert the raw key into the proper internal
# representation. note that we cannot do this earlier because
Expand Down
29 changes: 29 additions & 0 deletions odxtools/templates/macros/printTable.xml.jinja2
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@
{%- import('macros/printElementId.xml.jinja2') as peid %}
{%- import('macros/printSpecialData.xml.jinja2') as psd %}
{%- import('macros/printDescription.xml.jinja2') as pd %}
{%- import('macros/printAdminData.xml.jinja2') as pad %}
{%- import('macros/printAudience.xml.jinja2') as paud %}

{%- macro printTable(table) %}
<TABLE {{-peid.printElementIdAttribs(table)}}
Expand All @@ -31,6 +33,33 @@
<STRUCTURE-REF ID-REF="{{ table_row.structure_ref.ref_id }}" />
{%- endif %}
{{- psd.printSpecialDataGroups(table_row.sdgs)|indent(2, first=True) }}
{%- if table_row.audience is not none %}
{{ paud.printAudience(table_row.audience) | indent(2) }}
{%- endif %}
{%- if table_row.functional_class_refs %}
<FUNCT-CLASS-REFS>
{%- for fc_ref in table_row.functional_class_refs %}
<FUNCT-CLASS-REF ID-REF="{{ fc_ref.ref_id }}" />
{%- endfor %}
</FUNCT-CLASS-REFS>
{%- endif %}
{%- if table_row.state_transition_refs %}
<STATE-TRANSITION-REFS>
{%- for st_ref in table_row.state_transition_refs %}
<STATE-TRANSITION-REF ID-REF="{{ st_ref.ref_id }}" />
{%- endfor %}
</STATE-TRANSITION-REFS>
{%- endif %}
{%- if table_row.pre_condition_state_refs %}
<PRE-CONDITION-STATE-REFS>
{%- for pcs_ref in table_row.pre_condition_state_refs %}
<PRE-CONDITION-STATE-REF ID-REF="{{ pcs_ref.ref_id }}" />
{%- endfor %}
</PRE-CONDITION-STATE-REFS>
{%- endif %}
{%- if table_row.admin_data is not none %}
{{ pad.printAdminData(table_row.admin_data)|indent(2) }}
{%- endif %}
</TABLE-ROW>
{%- else %}
<TABLE-ROW-REF ID-REF="{{ table_row.ref_id }}" />
Expand Down
24 changes: 24 additions & 0 deletions tests/test_diag_data_dictionary_spec.py
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,14 @@ def test_initialization(self) -> None:
short_name="average",
long_name="Average",
description=None,
audience=None,
functional_class_refs=[],
admin_data=None,
state_transition_refs=[],
pre_condition_state_refs=[],
is_executable_raw=None,
is_mandatory_raw=None,
is_final_raw=None,
semantic=None,
dop_ref=None,
dop_snref=None,
Expand All @@ -149,6 +157,14 @@ def test_initialization(self) -> None:
short_name="good",
long_name="Good",
description=None,
audience=None,
functional_class_refs=[],
admin_data=None,
state_transition_refs=[],
pre_condition_state_refs=[],
is_executable_raw=None,
is_mandatory_raw=None,
is_final_raw=None,
semantic=None,
dop_ref=None,
dop_snref=None,
Expand All @@ -164,6 +180,14 @@ def test_initialization(self) -> None:
short_name="best",
long_name="Best",
description=None,
audience=None,
functional_class_refs=[],
admin_data=None,
state_transition_refs=[],
pre_condition_state_refs=[],
is_executable_raw=None,
is_mandatory_raw=None,
is_final_raw=None,
semantic=None,
dop_ref=None,
dop_snref=None,
Expand Down

0 comments on commit 09dbf18

Please sign in to comment.