From ab366754dfc0c184a39f4847674fed8f039cb647 Mon Sep 17 00:00:00 2001 From: Dirk Alders Date: Sun, 26 Jan 2020 16:11:50 +0100 Subject: [PATCH] Initial reqif implementation --- __init__.py | 67 +++++++++ templates/template.reqif | 185 +++++++++++++++++++++++ xml_parser.py | 311 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 563 insertions(+) create mode 100644 __init__.py create mode 100755 templates/template.reqif create mode 100644 xml_parser.py diff --git a/__init__.py b/__init__.py new file mode 100644 index 0000000..0165c4e --- /dev/null +++ b/__init__.py @@ -0,0 +1,67 @@ +import os +from reqif import xml_parser + + +class reqif_dict(dict): + KEY_TITLE = 'titel' + KEY_COMMENT = 'comment' + KEY_ITEM_DICT = 'item_dict' + KEY_UID_LIST_SORTED = 'uid_list_sorted' + + def __init__(self, filename, specification_key, specification_identifier): + dict.__init__(self) + self.rd = reqif(filename) + self[self.KEY_TITLE] = self.rd.get_title() + self[self.KEY_COMMENT] = self.rd.get_comment() + self[self.KEY_UID_LIST_SORTED] = [] + for specification in self.rd.get_specifications(): + if specification.get(specification_key) == specification_identifier: + sw_spec = specification + break + # + self[self.KEY_ITEM_DICT] = {} + for obj_id in sw_spec.children: + item = self.rd.get_spec_object(obj_id) + req_id = item.get('system_uid') + self[self.KEY_ITEM_DICT][req_id] = item + if req_id is not None: + self[self.KEY_UID_LIST_SORTED].append(req_id) + + +class reqif(object): + def __init__(self, filename): + self._data = xml_parser.parse(filename) + + def get_comment(self): + return self._data._comment + + def get_data_types(self): + return self._data._datatypes + + def get_relations_by_id(self, obj_id): + rv = dict() + rv['incomming'] = list() + rv['outgoing'] = list() + for relation in self._data._relations: + if obj_id == relation[xml_parser.KEY_SOURCE]: + rv['outgoing'].append(relation) + elif obj_id == relation[xml_parser.KEY_TARGET]: + rv['incomming'].append(relation) + return rv + + def get_specifications(self): + return self._data._specifications + + def get_specification_by_id(self, obj_id): + for specification in self._data._specifications: + if obj_id == specification[xml_parser.KEY_SYSTEM_UID]: + return specification + + def get_spec_object(self, obj_id): + return self._data._spec_objects[obj_id] + + def get_spec_types(self): + return self._data._spec_types + + def get_title(self): + return self._data._title diff --git a/templates/template.reqif b/templates/template.reqif new file mode 100755 index 0000000..b770f24 --- /dev/null +++ b/templates/template.reqif @@ -0,0 +1,185 @@ + + + + + Comment + 2019-05-14T09:16:28.691+02:00 + ProR (http://pror.org) + 1.0 + ProR (http://pror.org) + Title + + + + + + + + + + + + + + + + _MR7eM3YYEem_kd-7nxt1sg + + + + + + + + + _jk-t8HYYEem_kd-7nxt1sg + + + + + _rk_RcHYYEem_kd-7nxt1sg + + + + + + + + + _MR7eM3YYEem_kd-7nxt1sg + + + + + _dTWX0HYYEem_kd-7nxt1sg + + + + + _rk_RcHYYEem_kd-7nxt1sg + + + + + _rk_RcHYYEem_kd-7nxt1sg + + + + + _rk_RcHYYEem_kd-7nxt1sg + + + + + + + + + + + _7VrjAHYYEem_kd-7nxt1sg + + + + + _4-K5EHYYEem_kd-7nxt1sg + + + + + + + _LiZCMHYZEem_kd-7nxt1sg + + + + + _MR7eNXYYEem_kd-7nxt1sg + + + + + _uC2ToHYbEem_kd-7nxt1sg + + + + + _jIvPIHYeEem_kd-7nxt1sg + + + + + _pAz1wHYeEem_kd-7nxt1sg + + + + + _MR7eNHYYEem_kd-7nxt1sg + + + + + + + + + _MR7eN3YYEem_kd-7nxt1sg + + + + + _MR7eNnYYEem_kd-7nxt1sg + + + + + _MR7eOHYYEem_kd-7nxt1sg + + + + + _XzMFcHYZEem_kd-7nxt1sg + + + + + + + + + + + + + + + + + + + + + + + + + + ID + Heading + + + + + + + + + + + + + + + + + diff --git a/xml_parser.py b/xml_parser.py new file mode 100644 index 0000000..fb80534 --- /dev/null +++ b/xml_parser.py @@ -0,0 +1,311 @@ +import xml.etree.ElementTree as ET + + +KEY_DESCRIPTION = 'description' +"""Description (short) of an element""" +KEY_DETAILED_DESCRIPTION = 'detailed_description' +"""Description (short) of an element""" +KEY_ITEM_ID = 'id' +"""The human readable item id""" +KEY_LAST_CHANGE = 'last_change' +"""Date of the last change""" +KEY_SOURCE = 'source' +"""The source of a relation""" +KEY_SPEC_DATA = 'data' +"""A dict holding the data definition (key is the unique id of the data definition). Note this is not the datatype, but a reference to the datatype""" +KEY_START_EXECUTION_FROM_LEVEL = 'start_execution_from_level' +"""The level, of an spec object from where on the test should be executed""" +KEY_SYSTEM_TYPE_UID = 'system_type_uid' +"""The unique id of the corresponding type""" +KEY_SYSTEM_UID = 'system_uid' +"""A unique id""" +KEY_TARGET = 'target' +"""The target of a relation""" +KEY_XML_TAG = 'xml_tag' +"""The tag of the original xml object""" + +PREFIX = '{http://www.omg.org/spec/ReqIF/20110401/reqif.xsd}' +TAG_CHILDREN = '%sCHILDREN' % PREFIX +TAG_DEFINITION = '%sDEFINITION' % PREFIX +TAG_OBJECT = '%sOBJECT' % PREFIX +TAG_OBJECT_REF = '%sSPEC-OBJECT-REF' % PREFIX +TAG_PROPERTIES = '%sPROPERTIES' % PREFIX +TAG_SOURCE = '%sSOURCE' % PREFIX +TAG_SPECIFIED_VALUES = '%sSPECIFIED-VALUES' % PREFIX +TAG_SPEC_ATTRIBUTES = '%sSPEC-ATTRIBUTES' % PREFIX +TAG_TARGET = '%sTARGET' % PREFIX +TAG_THE_VALUE = 'THE-VALUE' +TAG_TYPE = '%sTYPE' % PREFIX +TAG_VALUES = '%sVALUES' % PREFIX + + +class base_object(dict): + ATTRIB_KEY_DESC = 'DESC' + ATTRIB_KEY_IDENTIFIER = 'IDENTIFIER' + ATTRIB_KEY_LONG_NAME = 'LONG-NAME' + ATTRIB_KEY_LAST_CHANGE = 'LAST-CHANGE' + ATTRIB_DATA = {KEY_DESCRIPTION: ATTRIB_KEY_LONG_NAME, + KEY_DETAILED_DESCRIPTION: ATTRIB_KEY_DESC, + KEY_LAST_CHANGE: ATTRIB_KEY_LAST_CHANGE, + KEY_SYSTEM_UID: ATTRIB_KEY_IDENTIFIER} + + def __init__(self, xml_object): + dict.__init__(self) + for key in self.ATTRIB_DATA: + data = xml_object.attrib.get(self.ATTRIB_DATA[key]) + if data is not None: + self[key] = data + self[KEY_XML_TAG] = xml_object.tag + type_obj = xml_object.find(TAG_TYPE) + if type_obj is not None: + self[KEY_SYSTEM_TYPE_UID] = type_obj[0].text + + def __str__(self): + keys = self.keys() + keys.sort() + rv = '' + for key in keys: + rv += '%s: %s\n' % (key, self[key]) + return rv + + +class datatype(base_object): + ATTRIB_KEY_KEY = 'KEY' + + def __init__(self, xml_datatype_object): + base_object.__init__(self, xml_datatype_object) + self.enmu_dict = dict() + if 'ENUMERATION' in self[KEY_XML_TAG]: + for child in xml_datatype_object.find(TAG_SPECIFIED_VALUES): + key = child.attrib[self.ATTRIB_KEY_IDENTIFIER] + value = None + if child.find(TAG_PROPERTIES) is not None: + try: + value = int(child.find(TAG_PROPERTIES)[0].attrib.get(self.ATTRIB_KEY_KEY)) + except ValueError: + value = child.find(TAG_PROPERTIES)[0].attrib.get(self.ATTRIB_KEY_KEY) + value = value or child.attrib[self.ATTRIB_KEY_LONG_NAME] + self.enmu_dict[key] = value + + def xml_obj_to_value(self, xml_obj): + if 'STRING' in self[KEY_XML_TAG]: + data = xml_obj.attrib.get(TAG_THE_VALUE) + return data + elif 'INTEGER' in self[KEY_XML_TAG]: + data = xml_obj.attrib.get(TAG_THE_VALUE) + return int(data) + elif 'BOOLEAN' in self[KEY_XML_TAG]: + data = xml_obj.attrib.get(TAG_THE_VALUE) + if data.lower() == 'false': + return False + else: + return True + elif 'ENUMERATION' in self[KEY_XML_TAG]: + if xml_obj.find(TAG_VALUES) is not None: + return self.enmu_dict.get(xml_obj.find(TAG_VALUES)[0].text) + + +class datatype_dict(dict): + def __init__(self, xml_datatypes_object): + dict.__init__(self) + for child in xml_datatypes_object: + dt = datatype(child) + self[dt[KEY_SYSTEM_UID]] = dt + + def __str__(self): + rv = '' + for key in self: + rv += str(self[key]) + '\n' + return rv + + +class spec_type(base_object): + def __init__(self, xml_spec_type_object): + base_object.__init__(self, xml_spec_type_object) + self[KEY_SPEC_DATA] = dict() + for child in xml_spec_type_object.find(TAG_SPEC_ATTRIBUTES): + a = base_object(child) + self[KEY_SPEC_DATA][a[KEY_SYSTEM_UID]] = a + + +class spec_types_dict(dict): + def __init__(self, xml_spec_types_object): + dict.__init__(self) + for child in xml_spec_types_object: + st = spec_type(child) + self[st[KEY_SYSTEM_UID]] = st + + def __str__(self): + rv = '' + for key in self: + rv += str(self[key]) + '\n' + return rv + + +class spec_object(base_object): + def __init__(self, xml_object, spec_types, data_types): + base_object.__init__(self, xml_object) + try: + for child in xml_object.find(TAG_VALUES): + attr_id = child.find(TAG_DEFINITION)[0].text + stype = spec_types[self[KEY_SYSTEM_TYPE_UID]] + if attr_id in stype[KEY_SPEC_DATA]: + spec_attr = stype[KEY_SPEC_DATA][attr_id] + data_type = data_types[spec_attr[KEY_SYSTEM_TYPE_UID]] + self[spec_attr[KEY_DESCRIPTION]] = data_type.xml_obj_to_value(child) + except TypeError: + pass + + +class spec_objects(dict): + def __init__(self, xml_object, spec_types, data_types): + dict.__init__(self) + for child in xml_object: + so = spec_object(child, spec_types, data_types) + self[so[KEY_SYSTEM_UID]] = so + + def __str__(self): + rv = '' + for key in self: + rv += str(self[key]) + '\n' + return rv + + +class spec_relation(spec_object): + def __init__(self, xml_object, spec_types, data_types): + spec_object.__init__(self, xml_object, spec_types, data_types) + self[KEY_SOURCE] = xml_object.find(TAG_SOURCE).find(TAG_OBJECT_REF).text + self[KEY_TARGET] = xml_object.find(TAG_TARGET).find(TAG_OBJECT_REF).text + + +class spec_relations(list): + def __init__(self, xml_object, spec_types, data_types): + list.__init__(self) + for child in xml_object: + self.append(spec_relation(child, spec_types, data_types)) + + def __str__(self): + rv = '' + for key in self: + rv += str(self[key]) + '\n' + return rv + + +class id_tree(str): + def __init__(self, system_uid): + str.__init__(system_uid) + self.children = list() + + def append(self, child): + self.children.append(child) + + def get_flat_id_tree(self): + """ + This method returns an ordered but flat list of all id_tree elements. + """ + obj_list = list() + for child in self.children: + obj_list.append(child) + obj_list.extend(child.get_flat_id_tree()) + return obj_list + + def executed_or_has_executed_children(self, item_type_id, execution_level, get_spec_object_method, default_execution_level): + spec_obj = get_spec_object_method(str(self)) + if spec_obj[KEY_SYSTEM_TYPE_UID] == item_type_id: + if execution_level >= spec_obj.get(KEY_START_EXECUTION_FROM_LEVEL, default_execution_level): + return True + for child in self.children: + if child.executed_or_has_executed_children(item_type_id, execution_level, get_spec_object_method, default_execution_level): + return True + return False + + +class specification(spec_object): + def __init__(self, xml_object, spec_types, data_types, spec_objects): + spec_object.__init__(self, xml_object, spec_types, data_types) + self._spec_objects = spec_objects + self.children = list() + self._add_hirarchy_objects(xml_object.find(TAG_CHILDREN), self.children) + + def _add_hirarchy_objects(self, children, parent_list): + for child in children: + child_id = child.find('%s/%s' % (TAG_OBJECT, TAG_OBJECT_REF)).text + child_tree_obj = id_tree(child_id) + parent_list.append(child_tree_obj) + next_children = child.find(TAG_CHILDREN) + if next_children is not None: + self._add_hirarchy_objects(next_children, child_tree_obj.children) + + def get_system_uid_list_by_type(self, type_id): + """ + This method returns an ordered but flat list of all items of this specification with the specified type. + """ + obj_list = list() + for child in self.children: + if self._spec_objects[child][KEY_SYSTEM_TYPE_UID] == type_id: + obj_list.append(str(child)) + for item_id in child.get_flat_id_tree(): + if self._spec_objects[item_id][KEY_SYSTEM_TYPE_UID] == type_id: + obj_list.append(str(item_id)) + return obj_list + + +class specifications(list): + def __init__(self, xml_object, spec_types, data_types, spec_objects): + list.__init__(self) + for child in xml_object: + self.append(specification(child, spec_types, data_types, spec_objects)) + + def __str__(self): + rv = '' + for i in self: + rv += str(i) + '\n' + return rv + + +class parse(object): + TAG_HEADER_MAIN = '%sTHE-HEADER' % PREFIX + TAG_HEADER_REQIF = '%sREQ-IF-HEADER' % PREFIX + TAG_TITLE = '%sTITLE' % PREFIX + TAG_COMMENT = '%sCOMMENT' % PREFIX + + TAG_CORE_CONTENT = '%sCORE-CONTENT' % PREFIX + TAG_REQ_IF_CONTENT = '%sREQ-IF-CONTENT' % PREFIX + TAG_DATATYPES = '%sDATATYPES' % PREFIX + TAG_SPEC_RELATIONS = '%sSPEC-RELATIONS' % PREFIX + TAG_SPEC_TYPES = '%sSPEC-TYPES' % PREFIX + TAG_SPEC_OBJECTS = '%sSPEC-OBJECTS' % PREFIX + TAG_SPECIFICATION = '%sSPECIFICATIONS' % PREFIX + + def __init__(self, filename): + tree = ET.parse(filename) + req_if = tree.getroot() + + self._title = req_if.find('./%s/%s/%s' % (self.TAG_HEADER_MAIN, self.TAG_HEADER_REQIF, self.TAG_TITLE)).text + self._comment = req_if.find('./%s/%s/%s' % (self.TAG_HEADER_MAIN, self.TAG_HEADER_REQIF, self.TAG_COMMENT)).text + + xml_obj = req_if.find('./%s/%s/%s' % (self.TAG_CORE_CONTENT, self.TAG_REQ_IF_CONTENT, self.TAG_DATATYPES)) + self._datatypes = datatype_dict(xml_obj) + xml_obj = req_if.find('./%s/%s/%s' % (self.TAG_CORE_CONTENT, self.TAG_REQ_IF_CONTENT, self.TAG_SPEC_TYPES)) + self._spec_types = spec_types_dict(xml_obj) + xml_obj = req_if.find('./%s/%s/%s' % (self.TAG_CORE_CONTENT, self.TAG_REQ_IF_CONTENT, self.TAG_SPEC_OBJECTS)) + self._spec_objects = spec_objects(xml_obj, self._spec_types, self._datatypes) + xml_obj = req_if.find('./%s/%s/%s' % (self.TAG_CORE_CONTENT, self.TAG_REQ_IF_CONTENT, self.TAG_SPEC_RELATIONS)) + try: + self._relations = spec_relations(xml_obj, self._spec_types, self._datatypes) + except TypeError: + self._relations = [] + xml_obj = req_if.find('./%s/%s/%s' % (self.TAG_CORE_CONTENT, self.TAG_REQ_IF_CONTENT, self.TAG_SPECIFICATION)) + self._specifications = specifications(xml_obj, self._spec_types, self._datatypes, self._spec_objects) + + def __str__(self): + rv = 'Title: %s' % self._title + rv += 'Comment: %s' % self._comment + rv += 'DATATYPES:\n----------\n' + rv += str(self._datatypes) + rv += str(self._spec_types) + rv += '\n\n\nSPEC_OBJECTS:\n-------------\n' + rv += str(self._spec_objects) + rv += '\n\n\nSPECIFICATIONS:\n---------------\n' + rv += str(self._specifications) + return rv