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

4 years ago
# vim: fileencoding=utf-8
#
from collections import OrderedDict
from jinja2 import Environment, PackageLoader
from pprint import pprint
from copy import copy
4 years ago
try:
from lxml.etree.ElementTree import fromstring
except ImportError:
from xml.etree.ElementTree import fromstring
class FormatError(Exception):
pass
4 years ago
class BaseFormat():
FORMAT = 'none'
4 years ago
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))
4 years ago
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))
4 years ago
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.
# Сначала получаем ключ без символа действия.
4 years ago
if isinstance(key_value, tuple):
item_to_delete = ('',) + key_value[1:]
elif isinstance(key_value, str):
item_to_delete = key_value[1:]
# Удаляем соответствующий элемент, если он есть в оригинале.
4 years ago
if item_to_delete in original.keys():
del(original[item_to_delete])
4 years ago
elif key_value[0] == '-':
# Замена соответствующего элемента из original.
# Сначала получаем ключ без символа действия.
4 years ago
if isinstance(key_value, tuple):
item_to_replace = ('',) + key_value[1:]
elif isinstance(key_value, str):
item_to_replace = key_value[1:]
# Если соответствующего элемента нет в оригинале -- пропускаем.
4 years ago
if item_to_replace not in original.keys():
continue
# Если секция для замены в шаблоне пустая -- удаляем
# соответствующую секцию.
4 years ago
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
# Если обработка комментариев включена -- сохраняем
# комментарии к заменяемой секции.
4 years ago
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)
4 years ago
else:
replacement = OrderedDict()
self._join(replacement,
template[key_value],
self._join_before_in_areas)
4 years ago
# Если после наложения шаблона словарь замены оказался
# пустым -- удаляем соотвествующий элемент в оригинале.
if (replacement == OrderedDict() or
replacement.keys() == {'#'}):
del(original[item_to_replace])
else:
original[item_to_replace] = replacement
4 years ago
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])
4 years ago
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):
4 years ago
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)
4 years ago
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
4 years ago
return is_ready
def _is_match(self):
is_match, self._match = self._match, False
return is_match