def writeRSTInto(self, sourcepath, destpath, srclanguage, title, maintoctree_maxdepth): """ RST.writeRSTInto sourcepath : source path (e.g. '~/projects/phokaia/') destpath : destination path srclanguage : SourceLanguage object title : (str) maintoctree_maxdepth : (int) """ self.errors.debug("(RST.writeRSTInto()) sourcepath={0}; destpath={1}; " \ "srclanguage={2}; title={3}; " \ "maintoctree_maxdepth={4}".format(sourcepath, destpath, srclanguage, title, maintoctree_maxdepth)) self.errors.debug("(RST.writeRSTInto()) logotherasdata entries : "+\ str(tuple(self.logotherasdata.keys()))) title_width = string_width(title) #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ # Is <destpath> valid ? if not os.path.exists(destpath): msg = "(ERR011) (RST.writeRSTInto) Missing directory : '{0}'" self.errors.error(msg.format(destpath)) return if not os.path.isdir(destpath): msg = "(ERR010) (RST.writeRSTInto) '{0}' is not a directory" self.errors.error(msg.format(destpath)) return #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ # adding the default files : if logotheras.options.OPTIONS["rst::template directory"] is None: self.addTemplateFilesIntoRSTDir(sourcepath = os.path.join("logotheras", "rst", "templates"), destpath = destpath) else: self.addTemplateFilesIntoRSTDir(sourcepath = \ logotheras.options.OPTIONS["rst::template directory"], destpath = destpath) #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ # let's add the audio files from sourcepath/source/audio : audio_sourcepath = os.path.join(sourcepath, "source", "audio") # e.g. phokaia/source/audio audio_destpath = os.path.join(destpath, "build", "audio") # e.g. tmp/phokaia/build/audio # if the source path doesn't exist, no audio file. if os.path.exists(audio_sourcepath) == True: shutil.copytree( src = audio_sourcepath, dst = audio_destpath ) #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ # writing the data : if logotheras.options.OPTIONS["rst::share out RST files"] is None: #~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~ # everything is packed into one file : indexfile_name = "index.rst" with open( os.path.join(destpath, indexfile_name), "w" ) as indexfile: indexfile.write( RST.header ) indexfile.write(NEWLINE) indexfile.write("="*title_width + NEWLINE) indexfile.write(title + NEWLINE) indexfile.write("="*title_width + NEWLINE) indexfile.write(NEWLINE) indexfile.write(".. toctree::" + NEWLINE) indexfile.write(" :maxdepth: {0}{1}".format(maintoctree_maxdepth, NEWLINE)) indexfile.write(NEWLINE) num_article = 1 for articledata in self.logotherasdata.getArticles(in_alphabetical_order = True): self.writeArticle( articledata = articledata, destpath = destpath, upperfile = indexfile, num_article = num_article) num_article += 1 elif logotheras.options.OPTIONS["rst::share out RST files"].startswith('a'): #~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~ # several outputfiles : all the words begin with the same letters length = len(logotheras.options.OPTIONS["rst::share out RST files"]) indexfile_name = "index.rst" with open( os.path.join(destpath, indexfile_name), "w" ) as indexfile: indexfile.write( RST.header ) indexfile.write(NEWLINE) indexfile.write("="*title_width + NEWLINE) indexfile.write(title + NEWLINE) indexfile.write("="*title_width + NEWLINE) indexfile.write(NEWLINE) indexfile.write(".. toctree::" + NEWLINE) indexfile.write(" :maxdepth: {0}{1}".format(maintoctree_maxdepth, NEWLINE)) indexfile.write(NEWLINE) num_article = 1 for (datafile_number, (articles, initial)) in enumerate(self.logotherasdata.getArticlesGroupedBy( method="initial", length=length, srclanguage=srclanguage)): # name of this new file ? datafile_name = "gro_{0:0>6}".format(datafile_number) complete_datafile_name = os.path.join( destpath, datafile_name + ".rst" ) # adding this file to index.rst : indexfile.write( " " + datafile_name + NEWLINE ) with open(complete_datafile_name, 'w') as datafile: self.errors.info("writing data into a new file : " + datafile.name) # <initial> -> <_initial> # # We modify <initial> by removing any diacritic or putting the # initial(s) in upper case : _initial = srclanguage.set_to_its_most_visual_form(initial) # title : either "(W) word" either "(W) word1 ... word2" if len(articles) == 0: datafile_title = "(no article to be written)" elif len(articles) == 1: datafile_title = str(_initial) + \ " (" + articles[0] + ")" else: datafile_title = str(_initial) + \ " ("+ articles[0] + " … " + articles[-1] + ")" datafile_title_width = string_width(datafile_title) datafile.write(RST.header) datafile.write(NEWLINE) datafile.write("="*datafile_title_width + NEWLINE) datafile.write(datafile_title + NEWLINE) datafile.write("="*datafile_title_width + NEWLINE) datafile.write(NEWLINE) datafile.write(".. toctree::" + NEWLINE) datafile.write(" :maxdepth: {0}{1}".format(maintoctree_maxdepth, NEWLINE)) datafile.write(NEWLINE) for article_name in articles: self.writeArticle( articledata = self.logotherasdata[article_name], destpath = destpath, upperfile = datafile, num_article = num_article) num_article += 1 else : #~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~ # several outputfiles : each outputfile contains the same number of articles length = int(logotheras.options.OPTIONS["rst::share out RST files"]) indexfile_name = "index.rst" with open( os.path.join(destpath, indexfile_name), "w" ) as indexfile: indexfile.write( RST.header ) indexfile.write(NEWLINE) indexfile.write("="*title_width + NEWLINE) indexfile.write(title + NEWLINE) indexfile.write("="*title_width + NEWLINE) indexfile.write(NEWLINE) indexfile.write(".. toctree::" + NEWLINE) indexfile.write(" :maxdepth: {0}{1}".format(maintoctree_maxdepth, NEWLINE)) indexfile.write(NEWLINE) num_article = 1 for (datafile_number, articles) in enumerate(self.logotherasdata.getArticlesGroupedBy( method="articles' number", length=length, srclanguage=srclanguage)): # name of this new file ? datafile_name = "gro_{0:0>6}".format(datafile_number) complete_datafile_name = os.path.join( destpath, datafile_name + ".rst" ) # adding this file to index.rst : indexfile.write( " " + datafile_name + NEWLINE ) with open(complete_datafile_name, 'w') as datafile: self.errors.info("writing data into a new file : " + datafile.name) # title : either "word" either "word1 ... word2" if len(articles) == 1: datafile_title = articles[0] else: datafile_title = articles[0] + " … " + articles[-1] datafile.write(RST.header) datafile.write(NEWLINE) datafile.write("="*datafile_title_width + NEWLINE) datafile.write(datafile_title + NEWLINE) datafile.write("="*datafile_title_width + NEWLINE) datafile.write(NEWLINE) datafile.write(".. toctree::" + NEWLINE) datafile.write(" :maxdepth: {0}{1}".format(maintoctree_maxdepth, NEWLINE)) datafile.write(NEWLINE) for article_name in articles: self.writeArticle( articledata = self.logotherasdata[article_name], destpath = destpath, upperfile = datafile, num_article = num_article) num_article += 1
def writeArticle( self, articledata, destpath, upperfile, num_article): """ RST.writeArticle Add the article <articledata> into <destpath>. PARAMETERS : o articledata : ArticleData object o destpath : (str) o upperfile : (file descriptor of index.rst) o num_article : (int) NO RETURN VALUE """ debug_msg = "(RST.writeArticle) article's name='{0}'; fullname='{1}'; " \ "destpath='{2}'; upperfile='{3}' " self.errors.debug(debug_msg.format(articledata.headerdata.artiname, articledata.headerdata.fullname, destpath, upperfile)) #....................................................................... # normal case : we write the article # Buffer where the lines to be written are stored : buff = [] # This function uses MD5 to compute the hash of the article's name. # # e.g., articlefile_name = "art_778b6ba126e285e" # # Previously, hash() built-in function was used, but this function # doesn't give the same results EVEN ON THE SAME PLATFORM. # md5_hash = hashlib.md5(articledata.headerdata.artiname.encode()).hexdigest() articlefile_name = "art_"+md5_hash # e.g., complete_articlefile_name = "path/art778b6ba126e285e.rst" complete_articlefile_name = os.path.join( destpath, articlefile_name + ".rst" ) # new article in the upperfile, index.rst or what so ever : upperfile.write(" {0}{1}".format(articlefile_name, NEWLINE)) # do we need to build a morphological grid ? build_morphogrid = False # do the function need to build a morphological grid ? morpho_pos = 0 # (int) line in <buff> of the "morphologie" entry morphogrid_name = "" # (str) infgrid = None # InflectionGrid object # we initialize <morphogrid_data> to get all the informations # required to fill such a grid. # # see HeaderData.get_morphogridline_data() to get more informations about # the format of <morphogrid_data> which is basically a dict={str:str}. morphogrid_data = articledata.headerdata.get_morphogridline_data() if morphogrid_data is not None: # yes, we have to build a morphological grid : morphogrid_name = morphogrid_data['name'] morphogrid_flection = morphogrid_data['flection'] if morphogrid_name not in MORPHOGRIDS: build_morphogrid = False self.errors.error("(ERR016) " \ "unknown morphogrid's name '{0}'".format(morphogrid_name)) else: build_morphogrid = True #. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . # let's build <infgrid> : infgrid = InflectionGrid( *MORPHOGRIDS[morphogrid_name].combinations, keywords_order = MORPHOGRIDS[morphogrid_name].keywords_order, default_values = MORPHOGRIDS[morphogrid_name].default_values) #. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . # filling <infgrid> with computed values thanks to the Inflectiones library : if logotheras.options.OPTIONS["rst::compute morphogrids' content"]: if morphogrid_flection not in INFLECTIONES: # error : unknown inflection msg = "(ERR015)" \ "unknown inflection '{0}'; " \ "known inflections : {1}" self.errors.error(msg.format(morphogrid_flection, INFLECTIONES.keys())) else: info_msg = "calling INFLECTIONES['{0}'] for artiname='{1}'." self.errors.info(info_msg.format(morphogrid_flection, articledata.headerdata.artiname)) # we fill the dict <characters_output> from the options : # # every option begininng with "rst::CO::" is copied # into <characters_output> but the first characters # (i.e. 'rst::CO::') are cut : # "rst::CO::xxx" -> "xxx" # characters_output = dict() for key, value in logotheras.options.OPTIONS.items(): if key.startswith("rst::CO::"): _key = key[len("rst::CO::"):] characters_output[ _key ] = value # calling the Inflectiones library : (infgrid, error_msg) = \ INFLECTIONES[morphogrid_flection].inflect_from_logotheras_entry( morphogrid_data = morphogrid_data, fullname = articledata.headerdata.fullname, infgrid_source = infgrid, ortho_separator = \ logotheras.options.OPTIONS["rst::inflectiones::ortho separator"], phone_separator = \ logotheras.options.OPTIONS["rst::inflectiones::phone separator"], characters_output = characters_output, formatstring = \ logotheras.options.OPTIONS["rst::inflectiones::format string"]) # the call to INFLECTIONES[].inflect_from_logotheras_entry() returned # an empty result : an error occurred. if infgrid is None: build_morphogrid = False msg = "(ERR014) INFLECTIONES['{0}'] " \ "for '{1}' was unable to return a result. " \ "Error message = '{2}'." self.errors.error(msg.format(morphogrid_flection, articledata.headerdata.fullname, error_msg)) #~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~ # technical header without any data : for line in RST.header.split("\n"): buff.append( line ) #~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~ # true header : article's names buff.append("") article_names = articledata.headerdata.artiname if articledata.headerdata.fullname: article_names += " ({0})".format(articledata.headerdata.fullname) if articledata.headerdata.sortingname and \ logotheras.options.OPTIONS["textdbdict::display sortingname"]: article_names += " [{0}]".format(articledata.headerdata.sortingname) if articledata.headerdata.articlescategory: article_names += " : {0}".format(articledata.headerdata.articlescategory) article_names_width = string_width(article_names) buff.append("=" * article_names_width) buff.append(article_names) buff.append("=" * article_names_width) buff.append("") #~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~ # add the audio link if such a link has been defined in the header : if "audiorecord" in articledata.headerdata.informations: audiofile = os.path.join("audio", "article", "header", articledata.headerdata.informations["audiorecord"]) audiofile_fullpath = os.path.join(destpath, "build", audiofile) if not os.path.exists(audiofile_fullpath): error_msg = "(ERR013) " \ "(RST.writeArticle) article's name='{0}'; " \ "fullname='{1}';" \ " audio file name as it appears in the header : '{2}'; " \ " full audio file name : '{3}' ." self.errors.error( error_msg.format(articledata.headerdata.artiname, articledata.headerdata.fullname, articledata.headerdata.informations["audiorecord"], audiofile_fullpath )) else: buff.append("→ `(enregistrement audio) <{0}>`_".format(audiofile)) #~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~ # entries' loop : for entryname in articledata.bodydata: entrydata = articledata.bodydata[entryname] debug_msg = "RST.writeArticle:: entryname={0} entrydata.entry_to_be_duplicated={1}" self.errors.debug(debug_msg.format(entryname, entrydata.entry_to_be_duplicated,)) #~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ # adding entrydata.title to <buff> : title = self.getHLevelRST(entrydata.hlevel) + " " if entrydata.title is None: # no title : title = "" else: # we add the selection detected by self.getHLevelRST() to <title> : title += entrydata.title title_width = string_width(title) buff.append("") buff.append("-" * title_width) buff.append(title) buff.append("-" * title_width) buff.append("") #~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ # adding entrydata.text to <buff> : for line in entrydata.text: buff.append(self.str2rststr(context="standard", src=line, errorcontext="line in an entrydata object="+str(entrydata))) #~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ # adding the extracts in entrydata to <buff> : buff.extend( self.fillBuffWithExtracts( entrydata ) ) #~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ # initializing <morpho_pos> : position just before the morphology part of the entries if build_morphogrid and entrydata.title == 'morphologie': morpho_pos = len(buff) #~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ # filling <infgrid> with the values read in the dictionary : #~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ # if build_morphogrid is False, nothing to do. # if entrydata.entry_to_be_duplicated is None we have something like that : # # (2.1) indicatif # (2.1.1) indicatif présent # (2.1.1.3) indicatif.présent.3S <---- # (2.1.1.3.1) ▣ἐστι(ν)▣ : indicatif.présent.3S # # "indicatif.présent.3S" is ok but there's no symbol before and after a morphological # form, so there's nothing to do. if build_morphogrid and \ entrydata.entry_to_be_duplicated: # we translate <entrydata.title> : # # E.g. "(2.1.1) indicatif.présent" (French keywords) # becomes : # "(2.1.1) indicative.present" (English keywords required by the # Infgrid library) # entrydata_title = self.translateGrammaticalKeyWords(entrydata.title) # we search the data in <entrydata_title> : search_result = infgrid.searchSectionsInAString( srcstring = entrydata_title ) # we expect only one result, i.e. only one substring matching an InflectionGrid # description as in "active.indicative.imperfect.1S : j'aimais". if len(search_result) != 1: error_msg = "(ERR012) (RST.writeArticle) " \ "article's name='{0}'; fullname='{1}';" \ " searching a substring in '{2}' > '{3}' : " \ "no substring linked to an InflectionGrid adress " \ "or more than one substring found. " \ "Either a keyword in '{2}' is unknown or not translated " \ "either a non default keyword is missing in '{3}'. " \ "search_result={4}; " \ "infgrid.keywords_order={5}; " \ "infgrid.default_values={6}; " \ "infgrid.gridsv.keys()={7}" self.errors.error( error_msg.format(articledata.headerdata.artiname, articledata.headerdata.fullname, entrydata.title, entrydata_title, search_result, infgrid.keywords_order, infgrid.default_values, sorted(infgrid.gridsv.keys()))) else: #substring = search_result[0][0] #substring_pos0 = search_result[0][1] resdict = search_result[0][2] # <_value> = formated version of <entrydata.entry_to_be_duplicated> fmtstring = logotheras.options.OPTIONS["rst::grammatical form::format string"] _value = fmtstring.replace("ORTHO", entrydata.entry_to_be_duplicated) # filling <infgrid> : infgrid.fillData(adress = resdict, value = _value, overwrite_the_data = True) #~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~ if infgrid is not None: res_template = infgrid.applyToATemplate( template = MORPHOGRIDS[morphogrid_name].strtemplate, unknown_value = "(...)") # we fill some fields : # the generic title : res_template = res_template.replace("__CAPTIONTITLE__", "") # some classes : res_template = res_template.replace("__CLASS__", "morphotable") res_template = res_template.replace("__TABLECLASSTITLE__", "morphotable-title") res_template = res_template.replace("__TABLEBORDER__", "1") res_morpho = [] res_morpho.append( "" ) res_morpho.append( ".. raw:: html" ) res_morpho.append( "" ) res_morpho.append( " <embed>" ) res_morpho.append( "" ) for line in res_template.split("\n"): res_morpho.append( " " + line ) res_morpho.append( "" ) res_morpho.append( " </embed>") # adding the morphology grid just before the line number #<morpholo_pos> : for line in res_morpho[::-1]: buff.insert(morpho_pos, line) # filling the <hermaia> object : for stradress in infgrid.gridsv: # <infgrid.gridsv[stradress]> follows the # "rst::grammatical form::format string" format, # by example "<b>ORTHO</b>". # # So we drop every HTML tags (see @DOC0002) if infgrid.gridsv[stradress] is not None: form = re.sub('<[^<]+?>', '', infgrid.gridsv[stradress]) self.hermaia.add_to_database( form = form, sourceword = articledata.headerdata.artiname, inflection = morphogrid_data['flection'], context = stradress ) #~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~.~ # new article written in a new file : with open( complete_articlefile_name, "w" ) as articlefile: for line in buff: articlefile.write( line + NEWLINE )