class htmltree(object): """ Helper class for generating html documents """ m2h = markdown2html() def __init__(self, tag_name='div', cls='', *args, **kwargs): self.attrib = {'class': cls} if cls else {} self.tag_name = tag_name self.root = ET.Element(tag_name, self.attrib) self.counter = 0 self.roots = [self.root] def tag(self, tag_name, value='', attrib={}, no_escape=True, **kwargs): """ Method will create and append element with tag based on tag_name value :param tag_name: tag name :param value: tag value :param attrib: optional attribute dict :param no_escape: whether to escape value :return: element """ attrib_copy = {} if 'cls' in kwargs: attrib_copy['class'] = kwargs.pop('cls') attrib_copy.update(attrib) attrib_copy.update(kwargs) element = ET.Element(tag_name, attrib_copy) element.text = cgi.escape(value) if not no_escape else value self.current().append(element) return element def tagc(self, tag_name, cls='', value=''): if cls: return self.tag(tag_name, value, {'class': cls}) return self.tag(tag_name, value) def current(self): """ :return: current top """ return self.roots[self.counter] def add(self, element): """ :return: appends element to top """ return self.current().append(element) def item_list_title(self, item_field, add_link=False, add_id=True, text=None): """ Method creates and appends header with level If subtitle is given href to title will consist of subtitle :param title: main header title :param subtitle: optional subtitle :param level: h level :param hide_subtitle: hide subtitle section :return: """ if add_link: with self.open('a', attrib={'href': '#' + item_field.href_id}): self.item_list_title(item_field, add_link=False, add_id=add_id, text=text) else: attrib = {'id': item_field.href_id} if add_id else {} with self.open('h3', attrib=attrib): self.span(text or item_field.get('name', 'key', 'href_name')) return self def main_section_title(self, item, attrib={}, **kwargs): """ Method creates level 2 header also with "on the side" href with icon to this href :type item: ist.nodes.TypeSelection or ist.nodes.TypeRecord or ist.nodes.TypeAbstract """ # with self.open('span', '', { 'class': 'pull-right side-anchor' }): # href_attrib = { 'href': '#' + item.href_id } # href_attrib.update({ 'title': 'Permalink to this section' }) # with self.open('a', '', href_attrib): # self.span(' ', { 'class': 'glyphicon glyphicon-link', 'aria-hidden': 'true' }) with self.open('h2'): self.tag('a', item.href_name, attrib={'href': '#' + item.href_id}) # self.tag('h2', item.href_name, attrib, **kwargs) def mark_as_obsolete(self, element): """ :type item: ist.nodes.TypeSelection or ist.nodes.TypeRecord or ist.nodes.TypeAbstract """ with self.open('div', cls='obsolete'): self.tag('p', 'Obsolete', cls='obsolete-title') self.description(element.attributes.obsolete) def add_clear(self): self.div('', cls='clear') def h2(self, value='', attrib={}, **kwargs): """ Method creates level 3 header :param value: header title :param attrib: optional attribute :return: element """ # attrib.update(self.generate_id(value)) self.tag('h2', value, attrib, **kwargs) def h3(self, value='', attrib={}, **kwargs): """ Method creates level 3 header :param value: header title :param attrib: optional attribute :return: element """ return self.tag('h3', value, attrib, **kwargs) def h4(self, value='', attrib={}, **kwargs): """ Method creates level 4 header :param value: header title :param attrib: optional attribute :return: element """ return self.tag('h4', value, attrib, **kwargs) def h5(self, value='', attrib={}, **kwargs): """ Method creates level 5 header :param value: header title :param attrib: optional attribute :return: element """ return self.tag('h5', value, attrib, **kwargs) def h6(self, value='', attrib={}, **kwargs): """ Method creates level 6 header :param value: header title :param attrib: optional attribute :return: element """ return self.tag('h6', value, attrib, **kwargs) def ul(self, value='', attrib={}, **kwargs): """ Method creates ul element :param value: ul optional text :param attrib: optional attribute :return: element """ return self.tag('ul', value, attrib, **kwargs) def ol(self, value='', attrib={}, **kwargs): """ Method creates ol element :param value: ol optional text :param attrib: optional attribute :return: element """ return self.tag('ol', value, attrib, **kwargs) def span(self, value='', attrib={}, **kwargs): """ Method creates span element :param value: span optional text :param attrib: optional attribute :return: element """ return self.tag('span', value, attrib, **kwargs) def spanc(self, cls, value=''): return self.tagc('span', cls, value) def info(self, value='', attrib={"class": 'info'}, **kwargs): """ Method creates info element :param value: span optional text :param attrib: optional attribute, default has class of 'leading-text' :return: element """ return self.tag('span', value, attrib, **kwargs) def div(self, value='', attrib={}, **kwargs): """ Method creates div element :param value: div optional text :param attrib: optional attribute :return: element """ return self.tag('div', value, attrib, **kwargs) def bold(self, value='', attrib={}, **kwargs): """ Method creates bold element :param value: bold optional text :param attrib: optional attribute :return: element """ return self.tag('strong', value, attrib, **kwargs) def italic(self, value='', attrib={}, **kwargs): """ Method creates em element :param value: em optional text :param attrib: optional attribute :return: element """ return self.tag('em', value, attrib, **kwargs) def li(self, value='', attrib={}, **kwargs): """ Method creates li element :param value: li optional text :param attrib: optional attribute :return: element """ return self.tag('li', value, attrib, **kwargs) def link(self, target, text='', ns=''): """ Method creates link element based on given attributes :param target: machine link name :param text: link title :param ns: namespace for link """ return self.tag('a', text if text else target, attrib=self.generate_href(target, ns)) def link_to_main(self, item, text=None): """ :type item: ist.nodes.TypeSelection or ist.nodes.TypeRecord or ist.nodes.TypeAbstract """ # return self.tag('a', item.name + '(' + item.id + ')', self.generate_href(item.id)) return self.tag('a', text or item.href_name, {'href': '#' + item.href_id}) def open(self, tag_name, value='', attrib={}, **kwargs): """ Method opens current element, shifts current top. When adding new elements, current top is this newly created :param tag_name: tag name :param value: optional text :param attrib: optional attribs :return: self """ element = self.tag(tag_name, value, attrib, **kwargs) self.roots.append(element) return self def openc(self, tag_name, cls=''): """ Method opens current element, shifts current top. When adding new elements, current top is this newly created :param tag_name: tag name :param cls: class name :return: self """ if cls: element = self.tag(tag_name, '', {"class": cls}) else: element = self.tag(tag_name, '') self.roots.append(element) return self def description(self, value): """ method will append description element to top element has predefined class :param value: :return: """ # self.tag('span', 'DESCRIPTION', attrib={ 'class': 'desc-title' }) if not value: return self.tag('div', 'no description provided', cls='description no-description') value = self.m2h.parse(value, reduce_to_tree=True) value.attrib['class'] = 'description' self.add(value) # return self.tag('div', value, { 'class': 'description' }, no_escape=True) def __enter__(self): """ Enter the runtime context related to this object. :return: """ self.counter += 1 return self def __exit__(self, exception_type, exception_value, traceback): """ Exit the runtime context related to this object. :param exception_type: :param exception_value: :param traceback: :return: """ # add debug info if exception_type: return False self.counter -= 1 self.roots.pop() return self def dump(self): """ Debug html dump :return: """ return ET.tostring(self.root, method='html') def __repr__(self): return '<htmltree object>' def style(self, location): """ Method add css link style :param location: css file location relative to server :return: element """ self.tag('link', '', rel='stylesheet', type='text/css', media='screen', href=location) def script(self, location): """ Method add css link script :param location: css file location relative to server :return: element """ self.tag('script', '', attrib={}, type='text/javascript', src=location) def id(self, id): """ Method sets id to root element :param id: id value """ self.root.attrib['id'] = id @staticmethod def secure(value): """ Method for securing input value :param value: value to be secured :return: safe value consisting of numbers and alphabet chars and _ char """ if value: value = re.sub(r'\W+', '-', value) value = re.sub(r'-$', '', value) return value return '' @staticmethod def chain_values(value, sub_value=''): """ Method for chaining given values Used in href and id creation :param value: main value :param sub_value: optional sub value :return: secured chained value: "values-subvalue" or "value" in lowercase """ chain = value if not sub_value else sub_value + '-' + value return htmltree.secure(chain).lower()
class texlist(list): """ Helper class for creating latex document """ m2h = markdown2html() def __init__(self, name=''): super(list, self).__init__() self.name = name self.counter = 1 def tag(self, field_name, *values): """ Method adds \name{value1}{value2} :param field_name: :param values: :return: self """ self.slash(field_name) for value in values: self.add_s(value) return self def KeyItem(self, name='', description=''): """ Method add KeyItem tag :param name: :param description: :return: """ self.slash('KeyItem') if name: self.add_s(name) if description: with self: self.add_description_field(description) return self def add(self, value=''): """ Method add value surrounded with braces :param value: :return: """ self.append("{" + str(value) + "}") return self def add_s(self, value=''): ''' Add field with value escaped ''' self.append("{" + self.name_mode(value) + "}") return self def add_d(self, value=''): ''' Add field with value underscores replaced by dashed ''' self.append("{" + self.name_mode(value) + "}") return self def open(self): """ Add open brace """ self.append('{') return self def close(self): """ Add close brace """ self.append('}') return self def hyperB(self, value, ns='IT::'): """ Add HyperB element :param value: id :param ns: optional namespace :return: self """ if __debug__: self.tag('hyperB', self.name_mode((ns if ns.endswith('::') else ns + '::') + value)) self.add(self.plain_mode((ns if ns.endswith('::') else ns + '::') + value)) else: self.tag('hyperB', self.name_mode((ns if ns.endswith('::') else ns + '::') + value)) self.add(self.plain_mode(value)) return self def slash(self, value=''): """ Add \value :param value: :return: self """ self.append('\\') if value: self.append(value) return self def Alink(self, url, ns="IT::", text=None): """ Method adds Alink section :param url: Alink url :param ns: optional namespace :param text: optional text (otherwise url is used) :return: self """ ns = str(ns) url = str(url) ns = ns if ns.endswith('::') else ns + '::' ns = ns if url.find('::') == -1 else '' if __debug__: self.tag('Alink', self.name_mode(ns + url)) self.add(self.plain_mode(ns + (url if text is None else text))) else: self.tag('Alink', self.name_mode(ns + url)) self.add(self.plain_mode(url if text is None else text)) return self def AddDoc(self, value): """ Adds \AddDoc{value} :return: self """ self.slash('AddDoc') self.add(self.plain_mode(value)) def textlangle(self, value, namespace='\\it '): """ Add < value > with optional italic :param value: :param namespace: :return: """ self.slash('textlangle') self.add(self.plain_mode(namespace + str(value) + ' ').lower()) self.slash('textrangle') return self def newline(self): """ Adds newline :return: self """ self.append('\n') return self def element(self): """ Resets counter :return: """ self.counter = 0 return self def open_element(self, name): """ Opens current element by name \begin{name} :param name: element name :return: self """ self.tag('begin', name) return self def close_element(self, name): """ Closes current element by name \end{name} :param name: element name :return: self """ self.tag('end', name) return self def __enter__(self): """ Enter the runtime context related to this object. :return: """ if self.counter == 0: self.open_element(self.name) else: self.open() self.counter += 1 return self def __exit__(self, exception_type, exception_value, tb): """ Exit the runtime context related to this object. :param exception_type: :param exception_value: :param tb: :return: """ # add debug info if exception_type: return False self.counter -= 1 if self.counter == 0: self.close_element(self.name) else: self.close() return self def add_description_field(self, value): """ Adds complex description field :param value: string value with markdown support :return: self """ self.add(self.description(value)) def description(self, value): """ Creates complex description field :param value: string value with markdown support :return: self """ # return self.escape (value.strip ().replace ('\n', '\\\\')) html = self.m2h.parse(str(value), True) latex = Html2Latex(html) result = latex.to_latex() desc_result = list() for r in result: if r.startswith('{$') and r.endswith('$}'): desc_result.append(self.equation_mode(r)) else: desc_result.append(self.plain_mode(r)) return ''.join(desc_result) @staticmethod def equation_mode(value): """ Method will ensure that value will be valid equation in latex :type value: str or list :param value: value tu be secured :return: """ return value if type(value) is str else ''.join(value) @staticmethod def plain_mode(value): """ Method will ensure that value will be valid text in latex no equation :type value: str or list :param value: value tu be secured :return: """ value = value if type(value) is str else ''.join(value) value = value \ .replace('_', '\\_') \ .replace("'", "'") \ .replace('->', '$\\rightarrow$') \ .replace('<-', '$\\leftarrow$') \ .replace('\n\n', '\n') return str(value) @staticmethod def name_mode(value): """ Method will ensure that value will be valid name in latex This method will replace all characters except a-z A-Z 0-9 and - :type value: str or list :param value: value tu be secured :return: """ value = value if type(value) is str else ''.join(value) return re.sub('[^a-zA-Z0-9-]', '', value)
class TexList(list): """ Helper class for creating latex document """ special_chars = [ ['''\\''', r'{\textbackslash}'], [r'{', r'{\{}'], [r'}', r'{\}}'], [r'%', r'{\%}'], [r'$', r'{\$}'], [r'#', r'{\#}'], [r'&', r'{\&}'], [r'_', r'{\_}'], [r'^', r'{\^{}}'], [r'|', r'{\textbar}'], [r'>', r'{\textgreater}'], [r'<', r'{\textless}'], [r'->', r'{\rightarrow}'], [r'<-', r'{\leftarrow}'], ] m2h = markdown2html() _OPEN = '{' _CLOSE = '}' _SLASH = '\\' _IT = 'it' _SPACE = ' ' _BEGIN = 'begin' _END = 'end' _NEWLINE = '\n' _TAB = '\t' _TEXTLANGLE = 'textlangle' _TEXTRANGLE = 'textrangle' """Will create well-formatted latex file but result will be INVALID, for debug purposes only""" PRETTY_FORMAT = False def __init__(self): super(TexList, self).__init__() self.levels = {} self.l = 0 self.open_name = '' self.append('') def add(self, value, t=None): if t is None: self.append(self._OPEN + str(value) + self._CLOSE) else: self.append(self._OPEN + str(t(value)) + self._CLOSE) def comment(self, value): if not self.PRETTY_FORMAT: return self.append(" % " + str(value)) def to_string(self, fix_newlines=True): if not fix_newlines: return ''.join(self) tmp_list = list() nl = False for x in self: if x == '': continue if x == self._NEWLINE: if nl: tmp_list.append(x) nl = False else: tmp_list.append(x) if str(x).strip(): nl = True return ''.join(tmp_list) def _function_call(self, args, mode=None, func=None): if func: self.slash(func) # secure arguments args = args if type(args) is list else [args] if mode is None: mode = [None] * len(args) mode = mode if type(mode) is list else [mode] for i in range(len(args)): self.add(str(args[i]), mode[i]) def slash(self, value=''): """ Add \value """ if value: self.append(self._SLASH + str(value)) else: self.append(self._SLASH) def macro_alink(self, item, text=None): """ Adds \Alink{item.href_id}{item.href_name}, will create href :type item: ist.nodes.TypeSelection or ist.nodes.TypeRecord or ist.nodes.TypeAbstract or Unicode """ self._function_call(func='Alink', args=[item.href_id, text or item.href_name], mode=[self.TYPE_NONE, self.TYPE_PLAIN]) def macro_alink_(self, url, text): t = TexList() t._function_call(func='Alink', args=[url, text or text], mode=[self.TYPE_NAME, self.TYPE_PLAIN]) self.append(str(t)) def macro_hyper_b(self, item, text=None): """ Adds \hyperB{item.href_id}{item.href_name}, will register href :type item: Parsable or ist.nodes.TypeSelection or ist.nodes.TypeRecord or ist.nodes.TypeAbstract """ self._function_call(func='hyperB', args=[item.href_id, text or item.href_name], mode=[self.TYPE_NONE, self.TYPE_PLAIN]) def macro_add_doc(self, item): """ Adds \hyperB{item.href_id}{item.href_name}, will register href :type item: Parsable or ist.nodes.TypeSelection or ist.nodes.TypeRecord or ist.nodes.TypeAbstract """ self._function_call(func='AddDoc', args=[item.href_name], mode=[self.TYPE_PLAIN]) def macro_text_lr_angle(self, value, mode=None, italic=True): self.slash(self._TEXTLANGLE) with self: self.append(self._SPACE) self._function_call(func=self._IT if italic else None, args=value, mode=mode if mode else self.TYPE_PLAIN) self.append(self._SPACE) self.slash(self._TEXTRANGLE) def begin(self, name): self._newline() self.slash(self._BEGIN) self.add(name) self._newline() def end(self, name): self._newline() self.slash(self._END) self.add(name) self._newline() def __enter__(self): """ Enter the runtime context related to this object. """ if self.open_name: self.levels[self.l] = self.open_name self.l += 1 self.slash(self.open_name) self.open_name = '' else: self.levels[self.l] = False self.l += 1 self.append(self._OPEN) return self def __exit__(self, exception_type, exception_value, tb): """ Exit the runtime context related to this object. """ if exception_type: return False self.l -= 1 if not self.levels[self.l]: self.append(self._CLOSE) return self def _newline(self): if not self.PRETTY_FORMAT: return if self[-1] != self._NEWLINE: self.append(self._NEWLINE) def _tab(self, n=1): if not self.PRETTY_FORMAT: return self.append(self._TAB * n) @classmethod def description(cls, value): if not value.strip(): return '' html = TexList.m2h.parse2latex(str(value)) latex = Html2Latex(html) result = latex.to_latex() return ''.join(result) @classmethod def equation_mode(cls, value): """ Method will ensure that value will be valid equation in latex :type value: str or list :param value: value tu be secured :return: """ return value if type(value) is str else ''.join(value) @classmethod def plain_mode(cls, value): """ Method will ensure that value will be valid text in latex no equation :type value: str or list :param value: value tu be secured :return: """ value = value if type(value) is str else ''.join(value) value = cls.prepare_plain(value) return cls.finish_plain(value) @classmethod def prepare_plain(cls, value): # replace all characters with placeholders # since some characters contains other non allowed chars for i in range(len(cls.special_chars)): value = value.replace(cls.special_chars[i][0], '(-(-{}-)-)'.format(i)) return value @classmethod def finish_plain(cls, value): # replace placeholders with escaped characters for i in range(len(cls.special_chars)): value = value.replace('(-(-{}-)-)'.format(i), cls.special_chars[i][1]) return str(value) @classmethod def name_mode(cls, value): """ Method will ensure that value will be valid name in latex This method will replace all characters except a-z A-Z 0-9 and - :type value: str or list :param value: value tu be secured :return: """ value = value if type(value) is str else ''.join(value) return re.sub('[^a-zA-Z0-9-]+', '-', value) @classmethod def auto_mode(cls, values): result = list() for value in values: if value.startswith('{$') and value.endswith('$}'): result.append(TexList.TYPE_EQ(value)) elif value.startswith('\Alink'): result.append(value) else: result.append(TexList.TYPE_PLAIN(value)) return ''.join(result).rstrip('\\') @classmethod def none_mode(cls, values): if type(values) is str: return values return ''.join(values) def __str__(self): return ''.join(self) TYPE_NAME = name_mode TYPE_EQ = equation_mode TYPE_PLAIN = plain_mode TYPE_AUTO = auto_mode TYPE_NONE = none_mode def item_open(self, name): self.open_name = name return self
class texlist(list): """ Helper class for creating latex document """ m2h = markdown2html() def __init__(self, name=''): super(list, self).__init__() self.name = name self.counter = 1 def tag(self, field_name, *values): """ Method adds \name{value1}{value2} :param field_name: :param values: :return: self """ self.slash(field_name) for value in values: self.add_s(value) return self def KeyItem(self, name='', description=''): """ Method add KeyItem tag :param name: :param description: :return: """ self.slash('KeyItem') if name: self.add_s(name) if description: with self: self.add_description_field(description) return self def add(self, value=''): """ Method add value surrounded with braces :param value: :return: """ self.append("{" + value + "}") return self def add_s(self, value=''): ''' Add field with value escaped ''' self.append("{" + self.escape(value) + "}") return self def add_d(self, value=''): ''' Add field with value underscores replaced by dashed ''' self.append("{" + self.secure(value) + "}") return self def open(self): """ Add open brace """ self.append('{') return self def close(self): """ Add close brace """ self.append('}') return self def hyperB(self, value, ns='IT::'): """ Add HyperB element :param value: id :param ns: optional namespace :return: self """ if __debug__: self.tag( 'hyperB', self.secure((ns if ns.endswith('::') else ns + '::') + value)) self.add( self.escape((ns if ns.endswith('::') else ns + '::') + value)) else: self.tag( 'hyperB', self.secure((ns if ns.endswith('::') else ns + '::') + value)) self.add(self.escape(value)) return self def slash(self, value=''): """ Add \value :param value: :return: self """ self.append('\\') if value: self.append(value) return self def Alink(self, url, ns="IT::", text=None): """ Method adds Alink section :param url: Alink url :param ns: optional namespace :param text: optional text (otherwise url is used) :return: self """ ns = ns if ns.endswith('::') else ns + '::' ns = ns if url.find('::') == -1 else '' if __debug__: self.tag('Alink', self.secure(ns + url)) self.add(self.escape(ns + (url if text is None else text))) else: self.tag('Alink', self.secure(ns + url)) self.add(self.escape(url if text is None else text)) return self def AddDoc(self, value): """ Add \AddDoc{value} :return: self """ self.slash('AddDoc', self.escape(value)) def textlangle(self, value, namespace='\\it '): """ Add < value > with optional italic :param value: :param namespace: :return: """ self.slash('textlangle') self.add(self.escape(namespace + value + ' ').lower()) self.slash('textrangle') return self def newline(self): """ Adds newline :return: self """ self.append('\n') return self def element(self): """ Resets counter :return: """ self.counter = 0 return self def open_element(self, name): """ Opens current element by name \begin{name} :param name: element name :return: self """ self.tag('begin', name) return self def close_element(self, name): """ Closes current element by name \end{name} :param name: element name :return: self """ self.tag('end', name) return self def __enter__(self): """ Enter the runtime context related to this object. :return: """ if self.counter == 0: self.open_element(self.name) else: self.open() self.counter += 1 return self def __exit__(self, exception_type, exception_value, traceback): """ Exit the runtime context related to this object. :param exception_type: :param exception_value: :param traceback: :return: """ self.counter -= 1 if self.counter == 0: self.close_element(self.name) else: self.close() return self def add_description_field(self, value): """ Adds complex description field :param value: string value with markdown support :return: self """ self.add(self.description(value)) def description(self, value): """ Creates complex description field :param value: string value with markdown support :return: self """ # return self.escape (value.strip ().replace ('\n', '\\\\')) html = self.m2h.parse('' + value + '', True) latex = Html2Latex(html) result = latex.to_latex() result = self.escape(''.join(result)) return result return self def secure(self, value): """ Method secures given value :param value: value to be secured :return: secured value """ return value \ .replace('_', '-') \ .replace('>', '') \ .replace('<', '') def escape(self, value): """ Method escapes given value :param value: value to be escaped :return: escaped value """ value = re.sub(r'\$ElementData', r'\$ElementData', value) value = value \ .replace('_', '\\_') \ .replace('->', '$\\rightarrow$') \ .replace('<-', '$\\leftarrow$') \ .replace('\n\n', '\n') return value