# -*- coding: utf-8 -*- # 2015 Calculate Ltd. http://www.calculate-linux.org # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. from lxml import etree as ET from copy import deepcopy def display_xml(xml): print(xml_to_str(xml)) def xml_to_str(xml): return str(ET.tostring(xml, pretty_print=True), encoding="UTF-8") def str_to_xml_doc(text): return ET.XML(bytes(bytearray(text, encoding="UTF-8"))) #backwards compat class xpath(): @staticmethod def Evaluate(xpath, xml): f = ET.XPath(xpath) return f(xml) #can't add methods to Cython lib. #have to do this the ugly way def firstChild(element): if(element.text): return element if(len(element) == 0): return None return element[0] def insertBefore(elem, new_child, ref_child): child_parent = new_child.getparent() if child_parent is not None: child_parent.remove(new_child) if(ref_child is None): elem.append(new_child) ref_child.addprevious(new_child) return new_child class xmlShare(): """Общий класс для объектов XML, наследуем """ def _createElement(self, doc, tag, text="", attributes=None): """Создание нового XML элемента""" if not isinstance(attributes, dict): attributes = {} element = ET.Element(tag, attributes) if text: element.text = text return element class xmlNode(xmlShare): """Класс для создания нод без аттрибутов """ def __init__(self): self.node = None def createNode(self, doc, tag, text=""): """Создает XML элемент без аттрибутов""" self.node = self._createElement(doc, tag, text) return self.node def getNode(self): return self.node def toxml(self, pretty_print=True): if(self.node is not None): return ET.tostring(self.node, encoding="UTF-8", pretty_print=pretty_print) class xmlCaption(): """Класс XML заголовок """ def __init__(self): # Заголовок области XML нода self.caption = False def createCaption(self, doc, name, quotes, action=None): """Создание заголовка области""" tmpNode = xmlNode() self.caption = tmpNode.createNode(doc, "caption") nameNode = tmpNode.createNode(doc, "name", name) self.caption.append(nameNode) if action: actNode = tmpNode.createNode(doc, "action", action) self.caption.append(actNode) for q in quotes: quoteNode = tmpNode.createNode(doc, "quote", q) self.caption.append(quoteNode) return self.caption def getCaption(self): """Выдает XML ноду заголовка области""" return self.caption class xmlField(xmlShare): """Класс для работы с XML полем """ def __init__(self): # XML нода поле self.field = None def createField(self, doc, typeField, quotes, name="", values=(), action=None): """Cоздание XML ноды поле""" self.field = self._createElement(doc, "field", "", {"type": typeField}) if name: nameNode = self._createElement(doc, "name", name) self.field.append(nameNode) for v in values: valueNode = self._createElement(doc, "value", v) self.field.append(valueNode) if action: actNode = self._createElement(doc, "action", action) self.field.append(actNode) for q in quotes: quoteNode = self._createElement(doc, "quote", q) self.field.append(quoteNode) return self.field def toxml(self, pretty_print=True): if(self.field is not None): return ET.tostring(self.field, encoding="UTF-8", pretty_print=pretty_print) class xmlFields(): """Класс, в котором находится список ХМL нод field """ def __init__(self): self.fields = [] def appendField(self, field): """Добавить XML ноду field""" self.fields.append(field) return self.fields def getFields(self): """Выдать список XML нод""" return self.fields class xmlArea(): """Класс для работы с XML областью """ def __init__(self): # Область self.area = None def createArea(self, doc, xmlCaption, xmlFields): """Создание XML области""" tmpNode = xmlNode() self.area = tmpNode.createNode(doc, "area") if xmlCaption and xmlCaption.getCaption() is not None: self.area.append(xmlCaption.getCaption()) if xmlFields: fields = xmlFields.getFields() for field in fields: self.area.append(field) return self.area class xmlDoc(): """Класс для работы с XML документом """ def __init__(self): # документ self.doc = None # главная нода self.root = None # тело документа self.body = None # Заголовок области - временный (в реальности один объект заголовок) self.tmpCaption = None # Поля - временные (в реальности один объект поля) self.tmpFields = None # Разделитель областей - по умолчанию перевод строки "\n" self.sepAreas = None # Разделитель разделенных списков - по умолчанию перевод строки "\n" # self.sepSplitFields = False def toxml(self, pretty_print=True): if(self.doc is not None): return ET.tostring(self.doc, encoding="UTF-8", pretty_print=pretty_print) def createDoc(self, typeDoc, version): """Создание нового документа новый документ""" docTxt = ('' '{version}' '{type_doc}' ''.format(version=version, type_doc=typeDoc)) # self.doc = minidom.parseString(docTxt) # self.root = self.doc.documentElement # self.body = xpath.Evaluate('child::body', self.root)[0] self.doc = str_to_xml_doc(docTxt) self.root = self.doc self.body = xpath.Evaluate('child::body', self.root)[0] # установка разделителя областей self.sepAreas = self.createField("br", [], "", [], False, False) # установка разделителя областей разделенных списков # self.sepSplitFields = self.createField("br",[],"",[],False,False) return self.doc def addField(self, field): """Добавляет поле во временный список Из этого списка в дальнейшем формируется XML область """ if not self.tmpFields: self.tmpFields = xmlFields() self.tmpFields.appendField(field) def createCaption(self, name, quotes, action=False): """Cоздает заголовок области Помещает заголовок в временный артибут Используется при создании области """ self.tmpCaption = xmlCaption() return self.tmpCaption.createCaption(self.doc, name, quotes, action) def createField(self, typeField, quotes=(), name="", values=(), action=False, addTmpField=True): """Cоздает поле Если установлена переменнная addTmpField добавляет поле во временный список """ fieldObj = xmlField() field = fieldObj.createField(self.doc, typeField, quotes, name, values, action) if addTmpField: self.addField(field) return field def clearTmpFields(self): """Очищает временный список""" self.tmpFields = False def createArea(self): """Cоздает область Область создается на основании временного атрибута и временного списка """ areaObj = xmlArea() area = areaObj.createArea(self.doc, self.tmpCaption, self.tmpFields) self.clearTmpCaptionAndFields() return area def clearTmpCaptionAndFields(self): """Очищает временный аттрибут и временный список""" self.tmpCaption = False self.tmpFields = False def getNodeRoot(self): """Выдает корневую ноду""" return self.root def getNodeBody(self): """Выдает ноду body""" return self.body def setActionField(self, xmlField, actionTxt): """Устанавливает свойство action для XML поля""" xmlActions = xpath.Evaluate('child::action', xmlField) if xmlActions and firstChild(xmlActions[0]) is not None: firstChild(xmlActions[0]).text = actionTxt else: nodeObj = xmlNode() newNode = nodeObj.createNode(self.doc, "action", actionTxt) xmlField.append(newNode) def setActionArea(self, xmlArea, actionTxt): """Устанавливает свойство action для XML области""" xmlActions = xpath.Evaluate('child::caption/action', xmlArea) xmlCaptions = xpath.Evaluate('child::caption', xmlArea) if xmlActions and firstChild(xmlActions[0]) is not None: firstChild(xmlActions[0]).text = actionTxt else: if xmlCaptions: nodeObj = xmlNode() newNode = nodeObj.createNode(self.doc, "action", actionTxt) xmlCaptions[0].append(newNode) def joinField(self, xmlArea, xmlNewField): """Объединяет XML ноду область и XML ноду поле""" newNameField = self.getNameField(xmlNewField) if not newNameField or not newNameField.strip(): return False fieldsOldComp = xpath.Evaluate("child::field[child::name='%s']" \ % newNameField, xmlArea) # Если поле не найдено добавляем его typeNewField = self.getTypeField(xmlNewField) if not fieldsOldComp and typeNewField != "seplist": if self.getActionField(xmlNewField) != "drop": self.setActionField(xmlNewField, "append") xmlArea.append(xmlNewField) return True newFieldsAction = self.getActionField(xmlNewField) newValues = self.getFieldValues(xmlNewField) flagCompare = True for nodeFieldOld in fieldsOldComp: if newFieldsAction == "drop": if nodeFieldOld.getnext() is not None and \ self.getTypeField( nodeFieldOld.getnext()) == "br": xmlArea.remove(nodeFieldOld.getnext()) elif nodeFieldOld.getprevious() is not None and \ self.getTypeField( nodeFieldOld.getprevious()) == "br": xmlArea.remove(nodeFieldOld.getprevious()) xmlArea.remove(nodeFieldOld) continue oldValues = self.getFieldValues(nodeFieldOld) # Сравнение значений переменной шаблона и файла if set(newValues) != set(oldValues): flagCompare = False if self.getActionField(xmlNewField) == "drop": return True appSplLst = [] insSplLst = [] if typeNewField == "seplist": if fieldsOldComp: xmlOldField = fieldsOldComp[-1] else: xmlOldField = None seplistNewXML = self.getSepListToField(xmlNewField) if seplistNewXML: for nodeSeplist in seplistNewXML: if self.getActionField(nodeSeplist) != "drop": if newFieldsAction == "join": flagCompareSeplist = False newValues = self.getFieldValues(nodeSeplist) for nodeFieldOld in fieldsOldComp: oldValues = self.getFieldValues(nodeFieldOld) for newValue in newValues: if newValue in oldValues: flagCompareSeplist = True break if not flagCompareSeplist: nextNode = xmlOldField.getnext() newInsNode = deepcopy(nodeSeplist) self.setActionField(newInsNode, "append") if nextNode is not None: appSplLst.append((newInsNode, nextNode, "insert")) else: appSplLst.append((newInsNode, False, "append")) else: newInsNode = deepcopy(nodeSeplist) if self.getActionField(newInsNode) == "join": self.setActionField(newInsNode, "append") if xmlOldField is not None: insSplLst.append((newInsNode, xmlOldField, "insert")) else: insSplLst.append((newInsNode, False, "append")) # xmlArea.insertBefore(\ # nodeSeplist.cloneNode(True), # xmlOldField) parentNode = nodeSeplist.getparent() parentNode.remove(nodeSeplist) insNodesRepl = [] for newNode, nxtNode, app in insSplLst: flagCompareSeplist = False newValues = self.getFieldValues(newNode) for nodeRepl, nxtNode_, app_ in insNodesRepl: nxtNode, app = nxtNode_, app_ oldValues = self.getFieldValues(nodeRepl) for newValue in newValues: if newValue in oldValues: flagCompareSeplist = True break if not flagCompareSeplist: if xmlOldField is not None: insNodesRepl.append((newNode, nxtNode, app)) for newNode, nxtNode, app in insNodesRepl: if app == "insert": insertBefore(xmlArea, newNode, nxtNode) elif app == "append": xmlArea.append(newNode) if xmlOldField is not None: parentNode = xmlOldField.getparent() if parentNode is not None and newFieldsAction != "join": parentNode.remove(xmlOldField) for newNode, nxtNode, app in appSplLst: if app == "insert": insertBefore(xmlArea, newNode, nxtNode) elif app == "append": xmlArea.append(newNode) if not flagCompare and typeNewField != "seplist": # Устанавливаем action=replace self.setActionField(xmlNewField, "replace") # Если параметры поля не сходятся заменяем поле xmlArea.replace(fieldsOldComp[-1], deepcopy(xmlNewField)) if newFieldsAction == "join": fieldsOldRemove = [] else: fieldsOldRemove = fieldsOldComp[:-1] for nodeFieldOld in fieldsOldRemove: actionOldNode = self.getActionField(nodeFieldOld) if actionOldNode == "insert" or actionOldNode == "append": pass else: if nodeFieldOld.getnext() is not None and \ self.getTypeField( nodeFieldOld.getnext()) == "br": xmlArea.remove(nodeFieldOld.getnext()) xmlArea.remove(nodeFieldOld) return True def getSepListToField(self, xmlField): """Выдает элементы распределенного массива Область предок поля, в этой области ищутся элементы распределенного массива """ nameField = self.getNameField(xmlField) if not nameField: return [] parentNode = xmlField.getparent() if parentNode is not None: fieldsVal = xpath.Evaluate( "child::field[attribute::type='seplist'][child::name='%s'] " \ % nameField, parentNode) return fieldsVal else: return [] def removeComment(self, xmlArea): """Удаляет комментарии в XML области""" fieldNodes = xpath.Evaluate('descendant::field', xmlArea) for fieldNode in fieldNodes: if "type" in fieldNode.keys(): if fieldNode.get("type") == "comment" or \ fieldNode.get("type") == "br": parentNode = fieldNode.getparent() parentNode.remove(fieldNode) else: if self.getActionField(fieldNode) == "drop": pass elif self.getActionField(fieldNode) == "join": pass else: self.setActionField(fieldNode, "append") def joinBody(self, baseBody, newBody): """Объединяет две области Body""" newFields = xpath.Evaluate('child::field', newBody) xmlNewAreas = xpath.Evaluate('child::area', newBody) for i, xmlNewArea in enumerate(xmlNewAreas): self.joinArea(baseBody, xmlNewArea) joinNewFields = xpath.Evaluate("child::field[child::action='join']", newBody) self.addNewFielsOldArea(newFields, joinNewFields, baseBody) def getRemoveNodeSepList(self, removeNodesDict, baseNode, xmNewlField): """Находит элементы разделенного списка Параметры: removeNodesDict - Cловарь удаляемых полей разделенного списка формируется программой baseNode - Нода в которой идет поиск xmNewlField - Нода field которая проверяется на принадлежность к разделенному списку """ flagNewNodeSeplist = False if self.getTypeField(xmNewlField) == "seplist": flagNewNodeSeplist = True nameNewField = self.getNameField(xmNewlField) if nameNewField: if nameNewField in removeNodesDict: return removeNodesDict[nameNewField] else: oldFields = xpath.Evaluate('child::field', baseNode) removeNodes = [] lenOldFields = len(oldFields) for i in range(lenOldFields): oldNode = oldFields[i] flagSep = self.getTypeField(oldNode) == "seplist" if flagNewNodeSeplist: flagSep = True if flagSep and \ nameNewField == self.getNameField(oldNode): removeNodes.append(oldNode) if i + 1 < lenOldFields: nextNode = oldFields[i + 1] if self.getTypeField(nextNode) == "br": removeNodes.append(nextNode) removeNodesDict[nameNewField] = removeNodes return removeNodes def addNewFielsOldArea(self, newFields, joinNewFields, xmlOldArea): """Добавляет новые XML поля в область шаблона""" removeNodesDict = {} notRemoveNodesDict = {} for notRemNode in joinNewFields: nameField = self.getNameField(notRemNode) if nameField not in notRemoveNodesDict: notRemoveNodesDict[nameField] = [] notRemoveNodesDict[nameField].append(notRemNode) else: notRemoveNodesDict[nameField].append(notRemNode) notSepListField = [] sepListField = [] for nField in newFields: if self.getRemoveNodeSepList(removeNodesDict, xmlOldArea, nField): sepListField.append(nField) else: if self.getNameField(nField): notSepListField.append(nField) for name in notRemoveNodesDict: if name in removeNodesDict: removeNodesDict[name] = [] for removeNodes in removeNodesDict.values(): if removeNodes: if self.getTypeField(removeNodes[-1]) == "seplist": removeNodes = removeNodes[:-1] else: removeNodes = removeNodes[:-2] for removeNode in removeNodes: xmlOldArea.remove(removeNode) for node in sepListField: node.set("type", "seplist") if not (self.getActionField(node) == "join" or self.getActionField(node) == "drop"): self.setActionField(node, "insert") self.joinField(xmlOldArea, node) for node in notSepListField: if self.getTypeField(node) == "seplist": self.setActionField(node, "append") xmlOldArea.append(node) else: self.joinField(xmlOldArea, node) def insertBeforeSepAreas(self, xmlArea): """Добавляет разделитель областей перед каждой областью""" if self.sepAreas is None: return False areaNodes = xpath.Evaluate('descendant::area', xmlArea) for areaNode in areaNodes: prevNode = areaNode.getprevious() if prevNode is not None: parentNode = areaNode.getparent() insertBefore(parentNode, deepcopy(self.sepAreas), areaNode) return True def getAreaFields(self, nameArea, xmlArea, allVars=False): """По имени области выводит названия и значения всех переменных поиск ведется только в 1-х потомках области xmlArea на выход словарь переменных {имя:значение} """ namesAreaCompareAll = xpath.Evaluate( "child::area/child::caption[child::name='%s']" % nameArea, xmlArea) if not namesAreaCompareAll: return False dictVar = {} for namesAreaCompare in namesAreaCompareAll: fields = xpath.Evaluate("child::field/child::name", namesAreaCompare.getparent()) for fieldName in fields: nodeField = fieldName.getparent() fieldValue = xpath.Evaluate("child::value", nodeField) name = firstChild(fieldName).text value = "" if fieldValue and firstChild(fieldValue[0]) is not None: value = firstChild(fieldValue[0]).text dictVar[name] = value if not allVars: break return dictVar def getAreaFieldValues(self, nameArea, nameField, xmlArea): """По имени области и имени переменной выводит значениe переменной поиск ведется только в 1-х потомках области xmlArea """ namesAreaComare = xpath.Evaluate( "child::area/child::caption[child::name='%s']" % nameArea, xmlArea) fieldsVal = False for areaComp in namesAreaComare: fieldsVal = xpath.Evaluate( "child::field[child::name='%s'] " % nameField, areaComp.getparent()) if fieldsVal: break if not fieldsVal: return False fieldValue = xpath.Evaluate("child::value", fieldsVal[0]) if not fieldValue: return False if firstChild(fieldValue[0]) is not None: return firstChild(fieldValue[0]).text else: return "" def getAllAreas(self): """Выдает все области""" return xpath.Evaluate('descendant::area', self.body) def getArea(self, nameArea, xmlArea): """По имени области находит области (первый потомок xmlArea)""" namesAreaComare = xpath.Evaluate( "child::area/child::caption[child::name='%s']" % nameArea, xmlArea) return [x.getparent() for x in namesAreaComare] def joinArea(self, baseNode, xmlNewArea): """Объединяет область c областью Body (xmlNewArea c baseNode)""" def appendArea(baseNode, xmlNewArea): fieldsRemove = xpath.Evaluate( "descendant::field[child::action='drop']", xmlNewArea) for rmNode in fieldsRemove: parentNode = rmNode.getparent() parentNode.remove(rmNode) captionAreasRemove = xpath.Evaluate( "descendant::area/child::caption[child::action='drop']", xmlNewArea) for rmNodeCapt in captionAreasRemove: rmNode = rmNodeCapt.getparent() parentNode = rmNode.getparent() parentNode.remove(rmNode) self.setActionArea(xmlNewArea, "append") # Добавляем разделитель областей во вложенные области areaNodes = xpath.Evaluate('descendant::area', xmlNewArea) for areaNode in areaNodes: self.setActionArea(areaNode, "append") parentNode = areaNode.getparent() insertBefore(parentNode, deepcopy(self.sepAreas), areaNode) baseNode.append(xmlNewArea) # Добавляем разделитель областей insertBefore(baseNode, deepcopy(self.sepAreas), xmlNewArea) nodesNames = xpath.Evaluate('child::area/caption/name', baseNode) nodesNewArea = xpath.Evaluate('child::caption/name', xmlNewArea) if not nodesNames: # Добавляем область if nodesNewArea: newAreaAction = self.getActionArea(xmlNewArea) if not (newAreaAction == "drop" or newAreaAction == "replace"): appendArea(baseNode, xmlNewArea) return True if not nodesNames or not nodesNewArea: return False nameArea = "" if firstChild(nodesNewArea[0]) is not None: nameArea = firstChild(nodesNewArea[0]).text.strip() flagFindArea = False newAreaAction = None baseNodes = [] for oName in nodesNames: newAreaAction = self.getActionArea(xmlNewArea) oArea = oName.getparent().getparent() oNameTxt = "" if firstChild(oName) is not None: oNameTxt = firstChild(oName).text if nameArea == oNameTxt: flagFindArea = True # При использовании удаления if newAreaAction == "drop": prevNode = oName.getparent().getparent().getprevious() removePrevNodes = [] while prevNode is not None and self.getTypeField(prevNode) == "br": removePrevNodes.append(prevNode) prevNode = prevNode.getprevious() for removeNode in removePrevNodes: baseNode.remove(removeNode) baseNode.remove(oName.getparent().getparent()) continue elif newAreaAction == "replace": oldAreaNode = oName.getparent().getparent() newAreaCaption = xpath.Evaluate('child::caption', xmlNewArea)[0] oldAreaCaption = xpath.Evaluate('child::caption', oldAreaNode)[0] if newAreaCaption and oldAreaCaption: #its was "replace(old, new)" in legacy code, even though #the func takes (new, old). Mistake, or on purpose? #xmlNewArea.replaceChild(oldAreaCaption, newAreaCaption) xmlNewArea.replace(newAreaCaption, oldAreaCaption) self.setActionArea(xmlNewArea, "replace") baseNode.replace(oldAreaNode, xmlNewArea) continue baseNodes.append(oName.getparent().getparent()) newFields = xpath.Evaluate('child::field', xmlNewArea) joinNewFields = xpath.Evaluate( "child::field[child::action='join']", xmlNewArea) self.addNewFielsOldArea(newFields, joinNewFields, oArea) if not flagFindArea: # Добавляем область if not (newAreaAction == "drop" or newAreaAction == "replace"): appendArea(baseNode, xmlNewArea) else: tmpXmlNewAreas = xpath.Evaluate('child::area', xmlNewArea) for na in tmpXmlNewAreas: for bn in baseNodes: self.joinArea(bn, na) return True def joinDoc(self, xmlNewDoc): """Объединяет два документа""" newRootNode = xmlNewDoc.getroottree().getroot() newBodyNode = xpath.Evaluate('child::body', newRootNode)[0] # newImportBodyNode = self.doc.importNode(newBodyNode, True) newImportBodyNode = deepcopy(newBodyNode) # Перед объединение области с документом # удаляем комментарии self.removeComment(newImportBodyNode) self.joinBody(self.body, newImportBodyNode) # расставляем BR self.insertBRtoBody(self.body) def getQuoteField(self, xmlField): """Выдает текст из поля""" xmlQuotes = xpath.Evaluate('child::quote', xmlField) br = "" if (xmlField.get("type") == "br"): br = "\n" if xmlQuotes: field = xmlQuotes[0] if firstChild(field) is not None: return firstChild(field).text + br return "" + br def getFieldsArea(self, xmlArea): """Выдает потомков XML области""" xmlFields = [] childNodes = list(xmlArea) for node in childNodes: # if node.nodeType == node.ELEMENT_NODE: if node.tag == "area" or node.tag == "field": xmlFields.append(node) return xmlFields def getTypeField(self, xmlField): """Выдает тип поля""" return xmlField.get("type") def getNameField(self, xmlField): """Выдает имя поля""" xmlNameFields = xpath.Evaluate('child::name', xmlField) if xmlNameFields and firstChild(xmlNameFields[0]) is not None: return firstChild(xmlNameFields[0]).text else: return False def getNameArea(self, xmlArea): """Выдает имя области""" xmlNameAreas = xpath.Evaluate('child::caption/name', xmlArea) if xmlNameAreas and firstChild(xmlNameAreas[0]) is not None: return firstChild(xmlNameAreas[0]).text else: return False def xmlToText(self, xmlAreas, text): """Преобразует список XML областей в текст""" def getQuotesArea(xmlArea): quotes = [] xmlQuotes = xpath.Evaluate('child::caption/quote', xmlArea) for node in xmlQuotes: if firstChild(node) is not None: quotes.append(firstChild(node).text) if len(quotes) == 0: quotes.append("") quotes.append("") elif len(quotes) == 1: quotes.append("") return quotes for i in xmlAreas: if i.tag == "area": quotesI = getQuotesArea(i) startAreaI = quotesI[0] endAreaI = quotesI[1] text.append(startAreaI) xmlFieldsI = self.getFieldsArea(i) for f in xmlFieldsI: if f.tag == "area": quotesF = getQuotesArea(f) startAreaF = quotesF[0] endAreaF = quotesF[1] text.append(startAreaF) xmlFieldsF = self.getFieldsArea(f) self.xmlToText(xmlFieldsF, text) text.append(endAreaF) else: fieldF = self.getQuoteField(f) text.append(fieldF) text.append(endAreaI) else: fieldI = self.getQuoteField(i) text.append(fieldI) def getActionField(self, xmlField): """Выдает свойство action XML поля""" xmlActions = xpath.Evaluate('child::action', xmlField) if xmlActions and firstChild(xmlActions[0]) is not None: return firstChild(xmlActions[0]).text else: return False def getFieldValues(self, xmlField): """Выдает значения XML поля в виде массива""" vals = [] xmlValues = xpath.Evaluate('child::value', xmlField) if xmlValues: for node in xmlValues: if firstChild(node) is not None: vals.append(firstChild(node).text) return vals def getActionArea(self, xmlArea): """Выдает свойство action XML области""" xmlActions = xpath.Evaluate('child::caption/action', xmlArea) if xmlActions and firstChild(xmlActions[0]) is not None: return firstChild(xmlActions[0]).text else: return False def delActionNodeArea(self, xmlArea): """Удаляет свойство action XML области""" xmlActions = xpath.Evaluate('child::caption/action', xmlArea) if xmlActions and firstChild(xmlActions[0]) is not None: parentNode = xmlActions[0].getparent() parentNode.remove(xmlActions[0]) return True else: return False def delActionNodeField(self, xmlField): """Удаляет свойство action XML поля""" xmlActions = xpath.Evaluate('child::action', xmlField) if xmlActions and firstChild(xmlActions[0]) is not None: parentNode = xmlActions[0].getparent() parentNode.remove(xmlActions[0]) return True else: return False # Создает распределенные списки def postParserListSeplist(self, xmlArea): """Создает распределенные списки из элементов области""" # Потомки childNodes = self.getFieldsArea(xmlArea) # содержит списки нод полей с одинаковыми именами в одной области fieldsSeplist = {} for node in childNodes: if node.tag == "area": self.postParserListSeplist(node) else: fieldName = False xmlFieldNameNodes = xpath.Evaluate('child::name', node) if xmlFieldNameNodes and firstChild(xmlFieldNameNodes[0]) is not None: fieldName = firstChild(xmlFieldNameNodes[0]).text if fieldName: if fieldName in fieldsSeplist: fieldsSeplist[fieldName].append(node) else: fieldsSeplist[fieldName] = [] fieldsSeplist[fieldName].append(node) for listNodes in fieldsSeplist.values(): if len(listNodes) > 1: for node in listNodes: node.set("type", "seplist") def insertBRtoBody(self, xmlArea): """Добавляет необходимые переводы строк """ # Потомки childNodes = self.getFieldsArea(xmlArea) # нода BR fieldXMLBr = self.createField("br", [], "", [], False, False) # Предыдущая нода lastNode = None lenChildNodes = len(childNodes) for i in range(lenChildNodes): node = childNodes[i] lastTmpNode = node # Нода area if node.tag == "area": if self.getActionArea(node) == "append" or \ self.getActionArea(node) == "join": self.delActionNodeArea(node) if lastNode is not None and lastNode.get("type") == "br" or \ lastNode is not None and lastNode.get("type") == "comment": indNext = i + 1 if indNext == lenChildNodes: xmlArea.append(deepcopy(fieldXMLBr)) else: nextNode = childNodes[indNext] lastTmpNode = insertBefore(xmlArea, deepcopy(fieldXMLBr), nextNode) else: insertBefore(xmlArea, deepcopy(fieldXMLBr), node) self.insertBRtoBody(node) # Нода field else: if self.getActionField(node) == "append" or \ self.getActionField(node) == "join": self.delActionNodeField(node) if lastNode is not None and lastNode.get("type") == "br" or \ lastNode is not None and lastNode.get("type") == "comment": indNext = i + 1 if indNext == lenChildNodes: xmlArea.append(deepcopy(fieldXMLBr)) else: nextNode = childNodes[indNext] lastTmpNode = insertBefore(xmlArea, deepcopy(fieldXMLBr), nextNode) else: insertBefore(xmlArea, deepcopy(fieldXMLBr), node) lastNode = lastTmpNode def postParserList(self): """Находит подходящие XML области и делаем из них поля-массивы""" xmlAreas = xpath.Evaluate('descendant::area', self.body) for xmlArea in xmlAreas: flagListXml = True fieldValues = [] xmlFields = xpath.Evaluate('child::field', xmlArea) if not xmlFields: flagListXml = False lenXmlFields = len(xmlFields) lenBrArea = 0 for xmlField in xmlFields: xmlNames = xpath.Evaluate('child::name', xmlField) xmlVals = xpath.Evaluate('child::value', xmlField) # if "type" in xmlField.keys() and \ # xmlField.get("type") == "br": if xmlField.get("type") == "br": lenBrArea += 1 continue if not xmlNames and not xmlVals: flagListXml = False break if xmlNames and firstChild(xmlNames[0]) is not None and \ firstChild(xmlNames[0]).text: flagListXml = False break if not (xmlVals and firstChild(xmlVals[0]) is not None and firstChild(xmlVals[0]).text): flagListXml = False break else: fieldValues.append(firstChild(xmlVals[0]).text) if lenXmlFields == lenBrArea: flagListXml = False if flagListXml: nameNode = xpath.Evaluate('child::caption/name', xmlArea)[0] fieldName = "" if firstChild(nameNode) is not None: fieldName = firstChild(nameNode).text listArea = [] self.xmlToText([xmlArea], listArea) fieldQuote = "".join(listArea) fieldXMLBr = False if fieldQuote and fieldQuote[-1] == "\n": fieldQuote = fieldQuote[:-1] fieldXMLBr = self.createField("br", [], "", [], False, False) fieldXML = self.createField("list", [fieldQuote], fieldName, fieldValues, False, False) areaAction = self.getActionArea(xmlArea) if areaAction: self.setActionField(fieldXML, areaAction) parentNode = xmlArea.getparent() insertBefore(parentNode, fieldXML, xmlArea) if fieldXMLBr: insertBefore(parentNode, fieldXMLBr, xmlArea) parentNode.remove(xmlArea)