You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

315 lines
13 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

# vim: fileencoding=utf-8
#
from collections import OrderedDict
from jinja2 import Environment, PackageLoader
from pprint import pprint
from copy import copy
try:
from lxml.etree.ElementTree import fromstring
except ImportError:
from xml.etree.ElementTree import fromstring
class FormatError(Exception):
pass
class BaseFormat():
FORMAT = 'none'
def __init__(self, processing_methods):
self._processing_methods = processing_methods
self._document_dictionary = OrderedDict()
self._item_to_add = OrderedDict()
self.TEMPLATES_DIRECTORY = 'templates'
self._fatal_error_flag = False
self._ready_to_update = False
self._match = False
self._need_finish = False
self._comments_processing = False
self._join_before = False
self._join_before_in_areas = False
# для отладки.
self._line_timer = 0
def _lines_to_dictionary(self, document_lines):
# print('Lines processing...')
for line in document_lines:
# print(self._line_timer, '\t', line)
for processing_method in self._processing_methods:
processing_method(line)
if self._fatal_error_flag:
# Действия если файл невозможно разобрать.
self._document_dictionary = OrderedDict()
raise FormatError('line {} is not correct.'.
format(self._line_timer))
if self._is_match():
if self._is_ready_to_update():
self._document_dictionary.update(self._item_to_add)
break
else:
# Действия если не удалось разобрать строку.
self._document_dictionary = OrderedDict()
raise FormatError('line {} is not correct.'.
format(self._line_timer))
self._line_timer += 1
if self._need_finish:
self._finish_method()
def _parse_xml_to_dictionary(self, xml_document_text):
root = fromstring(xml_document_text)
self._document_dictionary = self._processing_methods[root.tag](root)
def print_dictionary(self):
pprint(self._document_dictionary)
def join_template(self, template):
self._join(self._document_dictionary,
template._document_dictionary,
self._join_before)
def _get_list_of_logic_lines(self, text):
list_of_lines = []
lines_to_join = []
for line in text.splitlines():
line = line.strip()
if line == '':
continue
if not line.endswith("\\"):
lines_to_join.append(line)
joined_line = "".join(lines_to_join)
list_of_lines.append(joined_line)
lines_to_join = []
else:
lines_to_join.append(line[:-1])
return list_of_lines
def _join(self, original, template, join_before):
if template == OrderedDict():
return
if join_before:
forwarded_items = OrderedDict()
for key_value in template:
if key_value[0] == '!':
# Удаление соответствующего элемента из original.
# Сначала получаем ключ без символа действия.
if isinstance(key_value, tuple):
item_to_delete = ('',) + key_value[1:]
elif isinstance(key_value, str):
item_to_delete = key_value[1:]
# Удаляем соответствующий элемент, если он есть в оригинале.
if item_to_delete in original.keys():
del(original[item_to_delete])
elif key_value[0] == '-':
# Замена соответствующего элемента из original.
# Сначала получаем ключ без символа действия.
if isinstance(key_value, tuple):
item_to_replace = ('',) + key_value[1:]
elif isinstance(key_value, str):
item_to_replace = key_value[1:]
# Если соответствующего элемента нет в оригинале -- пропускаем.
if item_to_replace not in original.keys():
continue
# Если секция для замены в шаблоне пустая -- удаляем
# соответствующую секцию.
if isinstance(template[key_value], dict) and\
template[key_value] == OrderedDict():
original.pop(item_to_replace)
continue
# Если символ замены стоит перед параметром, а не перед
# секцией -- просто заменяем значение параметра.
if not isinstance(template[key_value], dict):
original[item_to_replace] = template[key_value]
continue
# Если обработка комментариев включена -- сохраняем
# комментарии к заменяемой секции.
if self._comments_processing:
if '#' in original[item_to_replace]:
replacement = OrderedDict({'#':
original[item_to_replace]
['#']}
)
# накладываем словарь шаблона на пустой словарь, чтобы
# выполнить все управляющие элементы, которые
# могут туда попасть.
self._join(replacement,
template[key_value],
self._join_before_in_areas)
else:
replacement = OrderedDict()
self._join(replacement,
template[key_value],
self._join_before_in_areas)
# Если после наложения шаблона словарь замены оказался
# пустым -- удаляем соотвествующий элемент в оригинале.
if (replacement == OrderedDict() or
replacement.keys() == {'#'}):
del(original[item_to_replace])
else:
original[item_to_replace] = replacement
else:
original[item_to_replace] = OrderedDict()
self._join(original[item_to_replace],
template[key_value],
self._join_before_in_areas)
if (original[item_to_replace] == OrderedDict() or
original[item_to_replace].keys() == {'#'}):
del(original[item_to_replace])
elif key_value not in original.keys():
if isinstance(template[key_value], dict):
dictionary_to_add = OrderedDict()
self._join(dictionary_to_add,
template[key_value],
self._join_before_in_areas)
if dictionary_to_add != OrderedDict():
if not join_before:
original[key_value] = dictionary_to_add
else:
forwarded_items[key_value] = dictionary_to_add
else:
if not join_before:
original[key_value] = template[key_value]
else:
forwarded_items[key_value] = template[key_value]
else:
if isinstance(original[key_value], dict) and \
isinstance(template[key_value], dict):
self._join(original[key_value],
template[key_value],
self._join_before_in_areas)
else:
if self._comments_processing:
original[key_value][-1] = template[key_value][-1]
else:
original[key_value] = template[key_value]
if join_before:
for key_value in reversed(forwarded_items.keys()):
original[key_value] = forwarded_items[key_value]
original.move_to_end(key_value, last=False)
def make_template(self, template):
full_diff, set_to_check = self.compare_dictionaries(
self._document_dictionary,
template._document_dictionary
)
template_object = copy(self)
template_object._document_dictionary = full_diff
return template_object
def compare_dictionaries(self, dict_1, dict_2):
to_remove_dictionary = OrderedDict()
to_add_dictionary = OrderedDict()
to_replace_dictionary = OrderedDict()
unchanged_set = set()
to_remove = dict_1.keys() - dict_2.keys()
if '#' in to_remove:
to_remove.remove('#')
for key in dict_1:
if key in to_remove:
if isinstance(key, tuple):
new_key = ('!', *key[1:])
else:
new_key = '!{}'.format(key)
if isinstance(dict_1[key], dict):
to_remove_dictionary.update({new_key: dict_1[key]})
else:
if self._comments_processing:
to_remove_dictionary.update({new_key:
[dict_1[key][-1]]})
else:
to_remove_dictionary.update({new_key: dict_1[key]})
to_add = dict_2.keys() - dict_1.keys()
if '#' in to_add:
to_add.remove('#')
for key in dict_2:
if key in to_add:
if isinstance(dict_2[key], dict):
section = dict_2[key].copy()
if '#' in section:
section.remove('#')
to_add_dictionary.update({key: section})
else:
if self._comments_processing:
to_add_dictionary.update({key: [dict_2[key][-1]]})
else:
to_add_dictionary.update({key: dict_2[key]})
intersect = dict_1.keys() & dict_2.keys()
for key in intersect:
if (isinstance(dict_1[key], dict) and
isinstance(dict_2[key], dict) and
dict_1[key] != dict_2[key]):
diff, set_to_check = self.compare_dictionaries(dict_1[key],
dict_2[key])
if set_to_check:
to_add_dictionary.update({key: diff})
else:
if isinstance(key, tuple):
new_key = ('-', *key[1:])
else:
new_key = '-{}'.format(key)
to_replace_dictionary.update({new_key:
dict_2[key]})
elif dict_1[key] != dict_2[key]:
if self._comments_processing:
to_add_dictionary.update({key: [dict_2[key][-1]]})
else:
to_add_dictionary.update({key: dict_2[key]})
else:
unchanged_set.add(key)
full_diff = OrderedDict()
full_diff.update(**to_remove_dictionary,
**to_replace_dictionary,
**to_add_dictionary)
return full_diff, unchanged_set
@property
def document_text(self):
file_loader = PackageLoader('calculate.templates.format',
self.TEMPLATES_DIRECTORY)
formats_environment = Environment(loader=file_loader,
trim_blocks=True,
lstrip_blocks=True)
formats_environment.globals.update(zip=zip)
formats_environment.add_extension('jinja2.ext.do')
template = formats_environment.get_template(self.FORMAT)
document_text = template.render(
document_dictionary=self._document_dictionary
)
return document_text
def _finish_method(self):
pass
def _is_ready_to_update(self):
is_ready, self._ready_to_update = self._ready_to_update, False
return is_ready
def _is_match(self):
is_match, self._match = self._match, False
return is_match