Page MenuHomec4science

myxmlwriter.py
No OneTemporary

File Metadata

Created
Sat, May 4, 17:30

myxmlwriter.py

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
""" This program can be used to pretty-write a xml string to a xml file.
"""
# Copyright (C) University of Basel 2019 {{{1
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/> 1}}}
import inspect
import xml.dom.minidom as MD
import xml.etree.ElementTree as ET
import lxml.etree as LET
from datetime import datetime
from rdflib import URIRef
from os import makedirs
from os.path import sep, basename, dirname, isfile
import sys
import shutil
import warnings
sys.path.append('svgscripts')
from datatypes.page import FILE_TYPE_SVG_WORD_POSITION, FILE_TYPE_XML_MANUSCRIPT
__author__ = "Christian Steiner"
__maintainer__ = __author__
__copyright__ = 'University of Basel'
__email__ = "christian.steiner@unibas.ch"
__status__ = "Development"
__license__ = "GPL v3"
__version__ = "0.0.1"
FILE_TYPE_SVG_WORD_POSITION = FILE_TYPE_SVG_WORD_POSITION
FILE_TYPE_XML_MANUSCRIPT = FILE_TYPE_XML_MANUSCRIPT
FILE_TYPE_XML_DICT = 'xml-dictionary'
def attach_dict_to_xml_node(dictionary, xml_node):
"""Create a xml tree from a dictionary.
"""
for key in dictionary.keys():
elem_type = type(dictionary[key])
if elem_type != dict:
node = LET.SubElement(xml_node, key, attrib={'type': elem_type.__name__})
node.text = str(dictionary[key])
else:
attach_dict_to_xml_node(dictionary[key], LET.SubElement(xml_node, key))
def copy_to_bak_dir(source_file: str, bak_dir='./bak'):
"""Copy file to bakup directory.
"""
date_string = datetime.now().strftime('%Y-%m-%d_%H:%M:%S')
makedirs(bak_dir, exist_ok=True)
target_file = bak_dir + sep + basename(source_file) + '_' + date_string
shutil.copy(source_file, target_file)
def dict2xml(dictionary, target_file_name):
"""Write dict 2 xml.
"""
xml_tree = LET.ElementTree(LET.Element('root'))
attach_dict_to_xml_node(dictionary, LET.SubElement(xml_tree.getroot(), 'dict'))
write_pretty(xml_element_tree=xml_tree, file_name=target_file_name,\
script_name=inspect.currentframe().f_code.co_name, file_type=FILE_TYPE_XML_DICT)
def get_dictionary_from_node(node):
"""Return dictionary from node.
:return: dict
"""
new_dict = {}
if len(node.getchildren()) > 0:
new_dict.update({ node.tag : {} })
for child_node in node.getchildren():
new_dict.get(node.tag).update(get_dictionary_from_node(child_node))
else:
elem_cls = eval(node.get('type')) if bool(node.get('type')) else str
value = elem_cls(node.text) if bool(node.text) else None
new_dict.update({ node.tag: value })
return new_dict
def lock_xml_tree(xml_element_tree, **locker_dict):
"""Lock xml_element_tree.
"""
if xml_element_tree is not None and not test_lock(xml_element_tree, silent=True):
message = locker_dict.get('message') if bool(locker_dict.get('message')) else ''
reference_file = locker_dict.get('reference_file') if bool(locker_dict.get('reference_file')) else ''
metadata = xml_element_tree.xpath('./metadata')[0]\
if len(xml_element_tree.xpath('./metadata')) > 0\
else LET.SubElement(xml_element_tree.getroot(), 'metadata')
lock = LET.SubElement(metadata, 'lock')
LET.SubElement(lock, 'reference-file').text = reference_file
if message != '':
LET.SubElement(lock, 'message').text = message
def parse_xml_of_type(xml_source_file, file_type):
"""Return a xml_tree from xml_source_file is file is of type file_type.
"""
parser = LET.XMLParser(remove_blank_text=True)
xml_tree = LET.parse(xml_source_file, parser)
if not xml_has_type(file_type, xml_tree=xml_tree):
msg = 'File {} is not of type {}!'.format(xml_source_file, file_type)
raise Exception(msg)
return xml_tree
def test_lock(xml_element_tree=None, silent=False):
"""Test if xml_element_tree is locked and print a message.
:return: True if locked
"""
if xml_element_tree is None:
return False
if len(xml_element_tree.findall('./metadata/lock')) > 0:
reference_file = xml_element_tree.findall('./metadata/lock/reference-file')
message = xml_element_tree.findall('./metadata/lock/message')
if not silent:
warning_msg = 'File {0} is locked!'.format(xml_element_tree.docinfo.URL)
if len(reference_file) > 0:
warning_msg = warning_msg.replace('!', ' ') + 'on {0}.'.format(reference_file[0].text)
if len(message) > 0:
warning_msg = warning_msg + '\n{0}'.format(message[0].text)
warnings.warn(warning_msg)
return True
return False
def update_metadata(xml_element_tree, script_name, file_type=None):
"""Updates metadata of xml tree.
"""
if len(xml_element_tree.getroot().findall('./metadata')) > 0:
if len(xml_element_tree.getroot().find('./metadata').findall('./modifiedBy[@script="{}"]'.format(script_name))) == 0:
LET.SubElement(xml_element_tree.getroot().find('./metadata'), 'modifiedBy', attrib={'script': script_name})
xml_element_tree.getroot().find('./metadata').findall('./modifiedBy[@script="{}"]'.format(script_name))[0].text = \
datetime.now().strftime('%Y-%m-%d %H:%M:%S')
else:
metadata = LET.SubElement(xml_element_tree.getroot(), 'metadata')
if file_type is not None:
LET.SubElement(metadata, 'type').text = file_type
createdBy = LET.SubElement(metadata, 'createdBy')
LET.SubElement(createdBy, 'script').text = script_name
LET.SubElement(createdBy, 'date').text = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
def write_backup(xml_element_tree: LET.ElementTree, file_type=None, bak_dir='./bak') -> str:
"""Back up a xml_source_file.
:return: target_file_name
"""
date_string = datetime.now().strftime('%Y-%m-%d_%H:%M:%S')
makedirs(bak_dir, exist_ok=True)
target_file_name = bak_dir + sep + basename(xml_element_tree.docinfo.URL) + '_' + date_string
reference_file = xml_element_tree.docinfo.URL
write_pretty(xml_element_tree=xml_element_tree, file_name=target_file_name,\
script_name=__file__ + '({0},{1})'.format(inspect.currentframe().f_code.co_name, reference_file),\
file_type=file_type)
return target_file_name
def write_pretty(xml_string=None, xml_element_tree=None, file_name=None, script_name=None, backup=False, file_type=None, **locker_dict):
"""Writes a xml string pretty to a file.
"""
if not bool(xml_string) and not bool(xml_element_tree):
raise Exception("write_pretty needs a string or a xml.ElementTree!")
if not test_lock(xml_element_tree):
if len(locker_dict) > 0 and bool(locker_dict.get('reference_file')):
lock_xml_tree(xml_element_tree, **locker_dict)
if script_name is not None and xml_element_tree is not None:
update_metadata(xml_element_tree, script_name, file_type=file_type)
if file_name is None and xml_element_tree is not None\
and xml_element_tree.docinfo is not None and xml_element_tree.docinfo.URL is not None:
file_name = xml_element_tree.docinfo.URL
if file_name is None:
raise Exception("write_pretty needs a file_name or a xml.ElementTree with a docinfo.URL!")
if backup and xml_element_tree is not None:
write_backup(xml_element_tree, file_type=file_type)
dom = MD.parseString(xml_string) if(bool(xml_string)) else MD.parseString(ET.tostring(xml_element_tree.getroot()))
f = open(file_name, "w")
dom.writexml(f, addindent="\t", newl='\n', encoding='utf-8')
f.close()
def xml2dict(xml_source_file):
"""Create dict from xml_source_file of Type FILE_TYPE_XML_DICT.
:return: dict
"""
new_dict = {}
xml_tree = LET.parse(xml_source_file)
if xml_has_type(FILE_TYPE_XML_DICT, xml_tree=xml_tree)\
and len(xml_tree.xpath('/root/dict')) > 0:
for node in xml_tree.xpath('/root/dict')[0].getchildren():
new_dict.update(get_dictionary_from_node(node))
else:
msg = 'File {} is not of type {}!'.format(xml_source_file, FILE_TYPE_XML_DICT)
raise Exception(msg)
return new_dict
def xml_has_type(file_type, xml_source_file=None, xml_tree=None):
"""Return true if xml_source_file/xml_tree has file type == file_type.
"""
if xml_tree is None and xml_source_file is None:
return False
if xml_tree is None and isfile(xml_source_file):
xml_tree = LET.parse(xml_source_file)
if len(xml_tree.xpath('//metadata/type/text()')) < 1:
return False
return xml_tree.xpath('//metadata/type/text()')[0] == file_type

Event Timeline