diff --git a/texla.py b/texla.py index 6565f3e..b4ef673 100644 --- a/texla.py +++ b/texla.py @@ -1,65 +1,66 @@ from log import * import json import yaml from texla.Parser import Parser from texla.Renderers.MediaWikiRenderer import MediaWikiRenderer import texla.PageTree.Exporter as exporter from texla.Exceptions.TexlaExceptions import * +from texla.Reporter import Reporter def execute_texla_mediawiki(config): p = Parser(config) a = open(config['input_path'], 'r').read() try: tree = p.parse(a) except (PreparserError, ParserError) as err: err.print_error() exit() f = open(config['output_path'] + '.tree', 'w') json_tree = tree.to_json(0) n_blocks = tree.n_blocks() logging.info('PARSED %i Blocks', n_blocks) f.write(json_tree) logging.info('\033[0;34m############### STARTING RENDERING ###############\033[0m') + #creating Reporter + reporter = Reporter(p.tree_explorer) #rendering - rend = MediaWikiRenderer(config) + rend = MediaWikiRenderer(config, reporter) rend.start_rendering(tree) o = open(config['output_path'] + '.json', 'w') o.write(json.dumps(rend.tree.get_tree_json(), indent=3)) - p = open(config['output_path'] + '.debug', 'w') - p.write(json.dumps(rend.used_tags, indent=2)) #print page tree before POST-PROCESSING logging.info('PageTree:\n'+rend.tree.get_tree_debug()) #collpasing logging.info('\033[0;34m############### STARTING POST-PROCESSING ###############\033[0m') tree = rend.tree tree.collapse_tree(config['collapse_content_level'], config['collapse_pages_level']) #printing tree after POST-PROCESSING logging.info('PageTree:\n'+rend.tree.get_tree_debug()) oc = open(config['output_path'] + '-coll.json', 'w') oc.write(json.dumps(rend.tree.get_tree_json(), indent=3)) logging.info('######## STARTING EXPORTING ########') if config['create_index']: tree.create_indexes(config["export_book_page"]) exporter.exportPages(tree.pages, config['output_path'] + '.mw', config['export_format']) if config['export_single_pages']: exporter.export_singlePages(tree.pages, config['output_path'] + '_pages', config['export_format']) if config['export_pages_tree']: exporter.export_pages_tree(tree.pages.values(), config['output_path'] + "_pages") - + reporter.print_report(console=True) logging.info('Finished') if __name__ == '__main__': #reading JSON configs config = yaml.load(open('configs.yaml','r')) #loading localized keywords config['keywords'] = yaml.load(open('i18n.yaml','r'))[config['lang']] #executing process for alla renderers if config['renderer'] == 'mediawiki': execute_texla_mediawiki(config) diff --git a/texla/Parser/Blocks/DefaultBlock.py b/texla/Parser/Blocks/DefaultBlock.py index cd15d97..4163304 100644 --- a/texla/Parser/Blocks/DefaultBlock.py +++ b/texla/Parser/Blocks/DefaultBlock.py @@ -1,50 +1,51 @@ '''Default Block''' import logging from .Utilities import * from .Block import Block class DefaultBlock(Block): ''' This Block is used when the parser doesn't find a proper parser_hook to call for a matched env or command''' @staticmethod def parse_env(parser ,tex, parent_block, params): #getting the name of env if 'env' in params: env_name = params['env'] else: env_name = 'no_env' if 'star' in params: env_name = env_name + '*' if params['star'] else env_name #default block is created logging.debug('DefaultBlock.parse_env @ %s:',tex[:5]+'...') block = DefaultBlock(tex, env_name, parent_block) #We cannot look inside tex, we don't know #what to parser. #we return the block return block @staticmethod def parse_cmd(parser ,tex, parent_block, params): cmd = params['cmd'] cmd = cmd + '*' if params['star'] else cmd #the options has to be matched from the tex match = CommandParser.get_command_options(tex) #match is (options string, left tex ptex = '\\'+cmd+match[0] logging.debug('DefaultBlock.parse_cmd @ %s:',ptex) #default block is created block = DefaultBlock(ptex, cmd, parent_block) #we return the block and the left tex to parse return (block, match[1]) def __init__(self, tex, block_name, parent_block): - super().__init__('default-'+block_name, tex, parent_block) + super().__init__('default', tex, parent_block) #the tex is added also as attribute + self.type = block_name self.attributes['content'] = tex parser_hooks = { 'default_env' : DefaultBlock.parse_env, 'default_cmd' : DefaultBlock.parse_cmd, } diff --git a/texla/Parser/TreeExplorer.py b/texla/Parser/TreeExplorer.py index f582588..2a829d2 100644 --- a/texla/Parser/TreeExplorer.py +++ b/texla/Parser/TreeExplorer.py @@ -1,110 +1,120 @@ import logging class TreeExplorer: """ The TreeExplorer class is an utility to navigate and extract information from the tree of parsed blocks. For example it is useful to extract the tree of the parents of a block for debugging reasons. It is useful also in rendering to localize blocks inside the document. """ def __init__(self, root_block): """ The constructor needs a root_block to begin the tree""" self.root_block = root_block self.blocks = {'@': root_block} + self.block_names = {} #registering blocks by id self.register_blocks(root_block.ch_blocks) @staticmethod def create_tree_from_children(block): #first of all we need the root_block current = block while True: if current.parent_block is None: root_block = current break current = current.parent_block #now we can return a new TreeExplorer #constructed from the found root. return TreeExplorer(root_block) def register_blocks(self, blocks): """This methods reads all the blocks tree from the root_block and created a dictionary with id:block""" for block in blocks: self.blocks[block.id] = block if block.N_chblocks > 0: self.register_blocks(block.ch_blocks) def update_blocks_register(self): """This methods update the blocks' ids register recalling register_blocks with the root_block""" self.register_blocks(self.root_block.ch_blocks) def get_parents_list(self, block): """This method returns the list of the parent blocks of the requested block """ if isinstance(block, str): block = self.blocks[block] parents = [] current = block while True: if current == self.root_block: break parents.append(current.parent_block) current = current.parent_block parents.reverse() return parents def get_parents_list_ids(self, block): parents = self.get_parents_list(block) return [x.id for x in parents] def get_block(self, blockid): return self.blocks.get(blockid) def print_tree(self, block, filter_list=None): """This methods prints a beautified tree starting from block parameter and his children. If filter_list is present only the block with the id in the list are printed. It returns a list of output strings""" output = [] if filter_list is None or block.id in filter_list: lstr = ". "* (block.tree_depth+1) output.append(lstr+ ". "+ " "+"_"*40 ) output.append(lstr+ "#"+"---"+ ">|ID : {}".format(block.id)) output.append(lstr+ ". "+ " |block_name : {}". format(block.block_name)) output.append(lstr+ ". "+ " |attributes: ") for at,attr in block.attributes.items(): output.append(lstr+ ". " + " | - "+ "{} : {}". format(at, attr)) output.append(lstr+ ". ."+"\u203E"*40+"\n") output = "\n".join(output) #iterating on the block children for bl in block.ch_blocks: output += self.print_tree(bl, filter_list) return output def print_tree_to_blocks(self, blocks): """This methods print the tree of parents of the list of blocks passed as parameter. First of all it gets all the parents ids and then prints the tree using the list as filter.""" fl = [] for bl in blocks: fl+= self.get_parents_list_ids(bl) if isinstance(bl, str): fl.append(bl) else: fl.append(bl.id) return self.print_tree(self.root_block, filter_list=fl) def print_tree_to_block(self, block): return self.print_tree_to_blocks([block]) def print_all_tree(self): - return self.print_tree(self.root_block) \ No newline at end of file + return self.print_tree(self.root_block) + + def register_block_names(self): + """This function registers the block_names, creating + a dictionary with blocks groups by type""" + self.block_names.clear() + for bl in self.blocks.values(): + if not bl in self.block_names: + self.block_names[bl.block_name] = [] + self.block_names[bl.block_name].append(bl) \ No newline at end of file diff --git a/texla/Renderers/MediaWikiRenderer.py b/texla/Renderers/MediaWikiRenderer.py index 6f82745..78c219c 100644 --- a/texla/Renderers/MediaWikiRenderer.py +++ b/texla/Renderers/MediaWikiRenderer.py @@ -1,579 +1,579 @@ import logging from .Renderer import Renderer from ..PageTree.PageTree import * class MediaWikiRenderer(Renderer): - def __init__(self, configs): - super().__init__() + def __init__(self, configs, reporter): + super().__init__(reporter) self.configs = configs self.doc_title = configs['doc_title'] #saving the hooks self.render_hooks = { #root 'root-block': self.r_document, 'default': self.default, #text 'par': self.r_par, 'newpage': self.r_newpage, 'newline': self.r_newline, '\\': self.r_newline, 'text': self.r_text, 'clearpage': self.r_newpage, 'cleardoublepage': self.r_newpage, #formatting 'emph': self.r_textit, 'textbf': self.r_textbf, 'textit': self.r_textit, 'textsc': self.r_textsc, 'textsuperscript': self.r_superscript, 'textsubscript': self.r_subscript, 'underline': self.r_underline, 'uline': self.r_underline, '%': self.r_special_character, '&': self.r_special_character, '$': self.r_special_character, '{': self.r_special_character, '}': self.r_special_character, '#': self.r_special_character, '_': self.r_special_character, 'dots': self.r_dots, 'ldots': self.r_dots, 'flushright': self.r_flushright, 'flushleft': self.r_flushleft, 'center': self.r_center, 'centerline': self.r_center, 'abstract': self.r_abstract, 'linebreak': self.r_break, 'pagebreak': self.r_break, 'nolinebreak': self.r_break, 'nopagebreak': self.r_break, 'verbatim': self.r_verbatim, 'verb': self.r_verb, #spaces 'vspace': self.r_vspace, 'mandatory_space': self.r_mandatory_space, #theorems 'theorem' : self.r_theorem, 'proof' : self.r_proof, #sectioning 'part': self.sectioning, 'chapter': self.sectioning, 'section': self.sectioning, 'subsection': self.sectioning, 'subsubsection': self.sectioning, 'paragraph': self.sectioning, 'subparagraph': self.sectioning, #math 'displaymath': self.r_display_math, 'inlinemath': self.r_inline_math, 'ensuremath': self.r_inline_math, 'equation': self.r_display_math, 'eqnarray': self.r_align, 'multline': self.r_align, 'align': self.r_align, 'alignat': self.r_align, 'gather': self.r_gather, #lists 'itemize': self.r_itemize, 'enumerate': self.r_enumerate, 'description': self.r_description, #quotes 'quotation': self.r_quotes, 'quote': self.r_quotes, 'verse': self.r_verse, 'footnote': self.r_footnote, #labels 'label': self.r_label, 'ref': self.r_ref, 'vref': self.r_ref, 'pageref': self.r_ref, 'eqref': self.r_ref, #accents "accented_letter": self.r_accented_letter, #figures "figure": self.r_figure } #register plugins self.register_plugins(configs["plugins"]) #tree object self.tree = PageTree(configs) #parameter for list formatting self.list_level = '' #parameters for theorem handling self.in_theorem = False self.theorem_number = 0 self.th_numbering = {} ######################################## #STARTING POINT def start_rendering(self, root_block): """starting rendering from root-block""" #start rendering of base class super(MediaWikiRenderer, self).start_rendering(root_block) self.render_block(root_block) #after rendering self.tree.after_render() #end rendering of base class super(MediaWikiRenderer, self).end_rendering() ####### ROOT BLOCK def r_document(self, block): #we trigger the rendering of content text = self.render_children_blocks(block) #text is the tex outside sections self.tree.addText(text) #returning the text to respect the interface return text ######################################## #DEFAULT def default(self, block): #we don't print anything return '' ######################################### #TEXT def r_text(self, block): text = block.attributes['text'] # The following replace happens as ~ is the latex symbol # for unbreakable space return text.replace("~", " ") def r_newline(self, block): return '\n' def r_newpage(self, block): return '\n\n' def r_par(self, block): return '\n\n' ######################################### #SECTIONING def sectioning(self, block): title = block.attributes['title'] section_name = block.attributes['section_name'] #remove the \n insiede title title = re.sub('\\n*', '', title) #creation of the new page self.tree.createPage(title, section_name) #content processing text = self.render_children_blocks(block) #adding text to current page self.tree.addText(text) #exiting the section self.tree.exitPage() return '' ######################################### #MATH def r_display_math(self, block): s = block.attributes['content'] #rendering labels self.render_blocks(block.labels) return '' + s + '' def r_inline_math(self, block): s = block.attributes['content'] #rendering labels self.render_blocks(block.labels) return '' + s + '' def r_align(self, block): s = block.attributes['content'] #rendering labels self.render_blocks(block.labels) return '\\begin{align}' +\ s + '\end{align}' def r_gather(self, block): s = block.attributes['content'] output = [] for eq in s.split("\\\\"): eq = eq.replace("\n","").strip() output.append('' +\ eq + '') #rendering labels self.render_blocks(block.labels) return '\n'.join(output) ######################################### #LABELS and refs def r_label(self, block): label = block.attributes['label'] self.tree.addLabel(label) return '' def r_ref(self, block): ref = block.attributes['ref'] #saving ref in Babel of PageTree self.tree.addReference(ref) return "{{ref@"+ ref+ "}}" ######################################### #FIGURE def r_figure(self, block): captions = block.get_children("caption") includegraphics = block.get_children("includegraphics") s = "[[File:" if len(includegraphics): inc = includegraphics[0] s += inc.attributes["img_name"] else: return "" if len(block.get_children("centering")): s += "|" + self.configs["keywords"]["center"] if len(captions): cap = captions[0] s += "|" + cap.attributes["caption"] s += "]]" return s; ######################################### #FORMATTING def r_special_character(self, block): return block.attributes['character'] def r_dots(self, block): return '...' def r_textbf(self, block): s = [] s.append("\'\'\'") s.append(self.render_children_blocks(block)) s.append("\'\'\'") return ''.join(s) def r_textit(self, block): s = [] s.append("\'\'") s.append(self.render_children_blocks(block)) s.append("\'\'") return ''.join(s) def r_textsc(self, block): return self.render_children_blocks(block).upper() def r_superscript(self, block): s = [] s.append('') s.append(self.render_children_blocks(block)) s.append('') return ''.join(s) def r_subscript(self, block): s = [] s.append('') s.append(self.render_children_blocks(block)) s.append('') return ''.join(s) def r_underline(self, block): s = [] s.append('{{Sottolineato|') s.append(self.render_children_blocks(block)) s.append('}}') return ''.join(s) def r_abstract(self, block): s = [] s.append('{{Abstract|') s.append(self.render_children_blocks(block)) s.append('}}') return ''.join(s) def r_break(self, block): return '' def r_vspace(self,block): return '\n\n' def r_mandatory_space(self,block): return ' ' def r_verbatim(self, block): return '
' + block.attributes['content'] +'
' def r_verb(self, block): return '' + block.attributes['content'] +'' ######################################### #ALIGNMENT def r_center(self, block): s = [] s.append('{{Center|') s.append(self.render_children_blocks(block)) s.append('}}') return ''.join(s) def r_flushleft(self, block): s = [] s.append('{{Flushleft|') s.append(self.render_children_blocks(block)) s.append('}}') return ''.join(s) def r_flushright(self, block): s = [] s.append('{{Flushright|') s.append(self.render_children_blocks(block)) s.append('}}') return ''.join(s) ######################################### #LISTS def r_itemize(self, block): self.list_level += '*' s = ['\n'] for item in block.ch_blocks: s.append(self.list_level) s.append(self.render_children_blocks(item).strip()) s.append("\n") self.list_level = self.list_level[:-1] return ''.join(s) def r_enumerate(self, block): self.list_level += '#' s = ['\n'] for item in block.ch_blocks: s.append(self.list_level) s.append(self.render_children_blocks(item).strip()) s.append("\n") self.list_level = self.list_level[:-1] return ''.join(s) def r_description(self, block): s = ['\n'] for item in block.ch_blocks: s.append(';') s.append(item.attributes['word']) s.append(':') s.append(self.render_children_blocks(item)) s.append("\n") return ''.join(s) ######################################### #QUOTES def r_quotes(self, block): s = [] s.append('
') s.append(self.render_children_blocks(block)) s.append('
') return ''.join(s) def r_verse(self, block): s = [] s.append('
') s.append('\n'.join(self.render_children_blocks(block).split('//'))) s.append('
') return ''.join(s) def r_footnote(self, block): s = [] s.append("") s.append(self.render_children_blocks(block)) s.append("") return ''.join(s) ######################################### #Theorems def r_theorem(self, block): #the label in theorems is not working for now th_definition = block.attributes['definition'] th_title = '' if block.attributes['title'] != None: th_title +=" "+ block.attributes['title'] s = [] #adding the theorem to the tree self.theorem_number += 1 self.tree.addTheorem(str(self.theorem_number), th_definition) #checking if the Environment template is used environ = False if self.configs['lang'] =='it': if th_definition.lower() == 'teorema': #adding content to page through a template s.append("\n{{InizioTeorema|titolo=" + \ th_title+"|number={{thnum@"+ str(self.theorem_number)+"}}"+\ "|anchor={{thanchor@"+ str(self.theorem_number) +"}}}}") s.append(self.render_children_blocks(block)) s.append("{{FineTeorema}}\n") elif th_definition.lower() == 'definizione': s.append("\n{{InizioDefinizione|titolo=" + \ th_title+"|number={{thnum@"+ str(self.theorem_number)+"}}"+\ "|anchor={{thanchor@"+ str(self.theorem_number) +"}}}}") s.append(self.render_children_blocks(block)) s.append("{{FineDefinizione}}\n") elif th_definition.lower() == 'proposizione': s.append("\n{{InizioProposizione|titolo=" + \ th_title+"|number={{thnum@"+ str(self.theorem_number)+"}}"+\ "|anchor={{thanchor@"+ str(self.theorem_number) +"}}}}") s.append(self.render_children_blocks(block)) s.append("{{FineProposizione}}\n") elif th_definition.lower() == 'lemma': s.append("\n{{InizioLemma|title=" + \ th_title+"|number={{thnum@"+ str(self.theorem_number)+"}}"+\ "|anchor={{thanchor@"+ str(self.theorem_number) +"}}}}") s.append(self.render_children_blocks(block)) s.append("{{FineLemma}}\n") elif th_definition.lower() == 'corollario': s.append("\n{{InizioCorollario|titolo=" + \ th_title+"|number={{thnum@"+ str(self.theorem_number)+"}}"+\ "|anchor={{thanchor@"+ str(self.theorem_number) +"}}}}") s.append(self.render_children_blocks(block)) s.append("{{FineCorollario}}\n") elif th_definition.lower()[:-2] == 'eserciz': s.append("\n{{InizioEsercizio|titolo=" + \ th_title+"|number={{thnum@"+ str(self.theorem_number)+"}}"+\ "|anchor={{thanchor@"+ str(self.theorem_number) +"}}}}") s.append(self.render_children_blocks(block)) s.append("{{FineEsercizio}}\n") elif th_definition.lower()[:-1] == 'osservazion': s.append("\n{{InizioOsservazione|titolo=" + \ th_title+"|number={{thnum@"+ str(self.theorem_number)+"}}"+\ "|anchor={{thanchor@"+ str(self.theorem_number) +"}}}}") s.append(self.render_children_blocks(block)) s.append("{{FineOsservazione}}\n") elif th_definition.lower()[:-2] == 'esemp': s.append("\n{{InizioEsempio|titolo=" + \ th_title+"|number={{thnum@"+ str(self.theorem_number)+"}}"+\ "|anchor={{thanchor@"+ str(self.theorem_number) +"}}}}") s.append(self.render_children_blocks(block)) s.append("{{FineEsempio}}\n") elif th_definition.lower() == 'dimostrazione': s.append("\n{{InizioDimostrazione|titolo=" + \ th_title+"|number={{thnum@"+ str(self.theorem_number)+"}}"+\ "|anchor={{thanchor@"+ str(self.theorem_number) +"}}}}") s.append(self.render_children_blocks(block)) s.append("{{FineDimostrazione}}\n") else: s.append("\n{{Environment|name="+ th_definition + \ "|title=" + th_title +\ "|content=") s.append(self.render_children_blocks(block)) s.append("}}\n") elif self.configs['lang'] =='en': if th_definition.lower() == 'theorem': #adding content to page through a template s.append("\n{{BeginTheorem|title=" + \ th_title+"|number={{thnum@"+ str(self.theorem_number)+"}}"+\ "|anchor={{thanchor@"+ str(self.theorem_number) +"}}}}") s.append(self.render_children_blocks(block)) s.append("{{EndTheorem}}\n") elif th_definition.lower() == 'definition': s.append("\n{{BeginDefinition|title=" + \ th_title+"|number={{thnum@"+ str(self.theorem_number)+"}}"+\ "|anchor={{thanchor@"+ str(self.theorem_number) +"}}}}") s.append(self.render_children_blocks(block)) s.append("{{EndDefinition}}\n") elif th_definition.lower() == 'proposition': s.append("\n{{BeginProposition|title=" + \ th_title+"|number={{thnum@"+ str(self.theorem_number)+"}}"+\ "|anchor={{thanchor@"+ str(self.theorem_number) +"}}}}") s.append(self.render_children_blocks(block)) s.append("{{EndProposition}}\n") elif th_definition.lower() == 'lemma': s.append("\n{{BeginLemma|title=" + \ th_title+"|number={{thnum@"+ str(self.theorem_number)+"}}"+\ "|anchor={{thanchor@"+ str(self.theorem_number) +"}}}}") s.append(self.render_children_blocks(block)) s.append("{{EndLemma}}\n") elif th_definition.lower() == 'corollary': s.append("\n{{BeginCorollary|title=" + \ th_title+"|number={{thnum@"+ str(self.theorem_number)+"}}"+\ "|anchor={{thanchor@"+ str(self.theorem_number) +"}}}}") s.append(self.render_children_blocks(block)) s.append("{{EndCorollary}}\n") elif th_definition.lower() == 'exercise': s.append("\n{{BeginExercise|title=" + \ th_title+"|number={{thnum@"+ str(self.theorem_number)+"}}"+\ "|anchor={{thanchor@"+ str(self.theorem_number) +"}}}}") s.append(self.render_children_blocks(block)) s.append("{{EndExercise}}\n") elif th_definition.lower() == 'observation': s.append("\n{{BeginObservation|title=" + \ th_title+"|number={{thnum@"+ str(self.theorem_number)+"}}"+\ "|anchor={{thanchor@"+ str(self.theorem_number) +"}}}}") s.append(self.render_children_blocks(block)) s.append("{{EndObservation}}\n") elif th_definition.lower() == 'remark': s.append("\n{{BeginRemark|title=" + \ th_title+"|number={{thnum@"+ str(self.theorem_number)+"}}"+\ "|anchor={{thanchor@"+ str(self.theorem_number) +"}}}}") s.append(self.render_children_blocks(block)) s.append("{{EndRemark}}\n") elif th_definition.lower() == 'example': s.append("\n{{BeginExample|title=" + \ th_title+"|number={{thnum@"+ str(self.theorem_number)+"}}"+\ "|anchor={{thanchor@"+ str(self.theorem_number) +"}}}}") s.append(self.render_children_blocks(block)) s.append("{{EndExample}}\n") elif th_definition.lower() == 'demonstration': s.append("\n{{BeginDemonstration|title=" + \ th_title+"|number={{thnum@"+ str(self.theorem_number)+"}}"+\ "|anchor={{thanchor@"+ str(self.theorem_number) +"}}}}") s.append(self.render_children_blocks(block)) s.append("{{EndDemonstration}}\n") else: s.append("\n{{Environment|name="+ th_definition + \ "|title=" + th_title +\ "|content=") s.append(self.render_children_blocks(block)) s.append("}}\n") #exit from theorem ambient self.tree.exitTheorem() return '\n'.join(s) def r_proof(self, block): s=[] if self.configs['lang'] == 'it': if block.title !=None: s.append('\n{{InizioDimostrazione|titolo='+\ block.attributes['title']+ "}}") s.append(self.render_children_blocks(block)) s.append("{{FineDimostrazione}}\n") else: s.append('\n{{InizioDimostrazione}}') s.append(self.render_children_blocks(block)) s.append("{{FineDimostrazione}}\n") elif self.configs['lang'] == 'en': if block.title !=None: s.append('\n{{BeginProof|title='+\ block.attributes['title']+"}}") s.append(self.render_children_blocks(block)) s.append("{{EndProof}}\n") else: s.append('\n{{BeginProof}}') s.append(self.render_children_blocks(block)) s.append("{{EndProof}}\n") return '\n'.join(s) ######################################### #ACCENTED letters def r_accented_letter(self, block): if block.attributes["accent_type"] == '"' \ and block.attributes["letter"] == "a": return "รค" if block.attributes["accent_type"] in ["'","`"]: return block.attributes["letter"]+\ block.attributes["accent_type"] else: return block.attributes["letter"] diff --git a/texla/Renderers/Renderer.py b/texla/Renderers/Renderer.py index 1d56299..1cf982e 100644 --- a/texla/Renderers/Renderer.py +++ b/texla/Renderers/Renderer.py @@ -1,185 +1,179 @@ from ..Parser import Blocks from ..Parser.TreeExplorer import TreeExplorer import logging import importlib class Renderer(): - def __init__(self): + def __init__(self, reporter): + self.reporter = reporter #hooks implemented directly by the Renderer class. self.render_hooks = {} #plugins hooks self.pre_render_hooks = {} self.post_render_hooks = {} self.start_hooks = [] self.end_hooks = [] self.loaded_plugins = {} - self.used_tags = {} self.tree_explorer = None def register_plugins(self, plugins): for plugin in plugins: module = importlib.import_module("..plugins"+'.'+ plugin, __name__) if hasattr(module, "plugin_render_hooks"): self.loaded_plugins[plugin] = module self.register_render_plugin_hooks(module.plugin_render_hooks) logging.info("Renderer.register_plugins "\ "@ Loaded plugin: {}".format(plugin)) logging.debug("Plugin {} render hooks: {}".format( plugin, list(module.plugin_render_hooks.keys()))) if hasattr(module, "plugin_lifecycle_hooks"): self.register_lifecyle_plugin_hooks(module.plugin_lifecycle_hooks) logging.info("Plugin {} lifecycle hooks: {}".format( plugin, list(module.plugin_lifecycle_hooks.keys()))) def register_render_plugin_hooks(self, hooks): '''This function registers the hooks for renderer plugins. The plugins can define hooks for pre and post render actions. The pre hook receives the block before the rendering and can only return the block itself, modified. The post hook receive the block and the text from the renderer: it has to return the final text only. The keyword ALL creates a hooks for all the blocks. Note that it is always called after all the other hooks.''' for bl in hooks: if "pre" in hooks[bl]: self.register_pre_renderer_hook(bl, hooks[bl]["pre"]) if "post" in hooks[bl]: self.register_post_renderer_hook(bl, hooks[bl]["post"]) #checking ALL keyword if "ALL" in hooks: if "pre" in hooks["ALL"]: self.register_pre_renderer_hook(bl, hooks["ALL"]["pre"]) if "post" in hooks["ALL"]: self.register_post_renderer_hook(bl, hooks["ALL"]["post"]) def register_lifecyle_plugin_hooks(self, hooks): ''' This function registers the hooks for the renderer lifecycle. Plugins can register hooks for the start and end actions. The start hook is called with the root_block of the chain. The end hook is called without arguments. These hooks must be used only to signal the actions to the plugins.''' if "start" in hooks: self.register_start_hook(hooks["start"]) if "end" in hooks: self.register_end_hook(hooks["end"]) def register_pre_renderer_hook(self, block, hook): if block not in self.pre_render_hooks: self.pre_render_hooks[block] = [] self.pre_render_hooks[block].append(hook) def register_post_renderer_hook(self, block, hook): if block not in self.post_render_hooks: self.post_render_hooks[block] = [] self.post_render_hooks[block].append(hook) def register_start_hook(self, hook): self.start_hooks.append(hook) def register_end_hook(self, hook): self.end_hooks.append(hook) def start_rendering(self, root_block): '''This function create a TreeExplorer instance and passes it to the plugin that has the variable needs_tree_explorer=True. Then it starts the plugins''' self.tree_explorer = TreeExplorer(root_block) #passing the tree_explorer for pl in self.loaded_plugins.values(): if hasattr(pl, "needs_tree_explorer"): if pl.needs_tree_explorer: logging.debug("Renderer @ Inserting "\ "TreeExplorer into plugin {}".format(pl)) pl.tree_explorer = self.tree_explorer #starting the plugins for hook in self.start_hooks: hook(root_block) def end_rendering(self): #ending plugins for hook in self.end_hooks: hook() def render_children_blocks(self, block, collapse=True): '''This is one of the most important funciont of the rendering process. This function takes all the children blocks of a block and get they rendering output. If collapsed=True it returns a unique string, otherwise it returns a list of tuples with[(block_name, output)] ''' output = [] for bl in block.ch_blocks: #it's not necessary checking for renderer_hook #because default hook is mandatory output.append((bl.block_name, self.render_block(bl))) logging.debug('Render.ch_blocks @ %s', output) if collapse: return ''.join([x[1] for x in output]) else: return output def render_block(self, bl): '''This function calls the right render_hook for the block. If there isn't an hook it calld the default, that is mandatory''' output = "" ######### pre hooks ############ #hooks executed in the order of inserction #They receive the block and they can only modify the block object if bl.block_name in self.pre_render_hooks: for prehook in self.pre_render_hooks[bl.block_name]: #calling prehook with the block prehook(bl) #calling after the others the ALL hooks if "ALL" in self.pre_render_hooks: for prehook in self.pre_render_hooks["ALL"]: #calling prehook with the block prehook(bl) ######## rendering ############# if bl.block_name in self.render_hooks: - self.used_tag('ok | ' + bl.block_name) logging.debug('Render @ block: ' + bl.block_name) output = self.render_hooks[bl.block_name](bl) else: #default hook is mandatory - self.used_tag('default | ' + bl.block_name) logging.debug('Render @ block: default@' + bl.block_name) + #reporting to the Reporter + if bl.block_name != "default": + self.reporter.add_not_rendered_block(bl) output = self.render_hooks['default'](bl) ######## post hooks ########### #hooks executed in the order of inserction. #They receive the block and text. They have to return the #output text, that is passed to the next posthook if bl.block_name in self.post_render_hooks: for posthook in self.post_render_hooks[bl.block_name]: #calling posthook with the block and output output = posthook(bl, output) #calling ALL hooks after the others if "ALL" in self.post_render_hooks: for posthook in self.post_render_hooks["ALL"]: #calling posthook with the block and output output = posthook(bl, output) #final output return output def render_blocks(self, bls, collapse=False): '''This function renderes a list of blocks. It's the same as render_children_blocks but with a generic list''' output = [] for bl in bls: output.append((bl.block_name,self.render_block(bl))) if collapse: return ''.join([x[1] for x in output]) else: return output - - #Utils for debug - def used_tag(self, tag): - if tag in self.used_tags: - self.used_tags[tag] += 1 - else: - self.used_tags[tag] = 1 diff --git a/texla/Reporter.py b/texla/Reporter.py new file mode 100644 index 0000000..4984a78 --- /dev/null +++ b/texla/Reporter.py @@ -0,0 +1,38 @@ +from .Parser.TreeExplorer import TreeExplorer + +class Reporter: + + def __init__(self, tree): + self.tree_explorer = tree + #registering the block_names + self.tree_explorer.register_block_names() + self.not_parsed_blocks = self.tree_explorer.block_names["default"] + self.not_rendered_blocks = [] + self.not_parsed_types = {} + self.not_rendered_types = {} + for bl in self.not_parsed_blocks: + if bl.type not in self.not_parsed_types: + self.not_parsed_types[bl.type] = [] + self.not_parsed_types[bl.type].append(bl) + + + def add_not_rendered_block(self, block): + """This method saves a block that is not rendered by the Renderer.""" + self.not_rendered_blocks.append(block) + if not block.block_name in self.not_rendered_types: + self.not_rendered_types[block.block_name] = [] + self.not_rendered_types[block.block_name].append(block) + + def print_report(self, console=True): + s = ["-----------------------\nTeXLa Report\n-----------------------"] + s.append("\n- NOT PARSED blocks:") + for bl, v in self.not_parsed_types.items(): + s.append("\t- {} : {}".format(bl, len(v))) + s.append("\n- NOT RENDERED blocks:") + for bl, v in self.not_rendered_types.items(): + s.append("\t- {} : {}".format(bl, len(v))) + text= "\n".join(s) + if console: + print(text) + with open("sandbox/texla_report.txt",'w') as file: + file.write(text)