Progetto:Bot/Programmi in Python per i bot/ms.py

Da Wikisource.
# -*- coding: utf-8 -*-
# text alignment program
# author : thomasv1 at gmx dot de
# author : phe at some dot where
# licence : GPL

import os, re
import difflib
import subprocess
import pickle

#funzioni dummy
def utils():
    return
def copy_File():
    return

def parseTemplate(template):
    elementi={}
    n=1
    template=template.strip()[2:-2]
    #codifica link
    x=produci_lista(template,"[[","]]",1)
    for i in x:
        key="##%d##" % (n)
        elementi[key]=i
        template=template.replace(i,key)
        n+=1
    #codifica template
    x=produci_lista(template,"{{","}}",1,"{{")
    
    for i in x:
        key="##%d##" % (n)
        elementi[key]=i
        template=template.replace(i,key)
        n+=1

    #codifica url
    x=produci_lista(template,"[","]",1)
    
    for i in x:
        key="##%d##" % (n)
        elementi[key]=i
        template=template.replace(i,key)
        n+=1
    template=template.split("|")
    #normalizzazione parametri
    n=1
    for i in range(1,len(template)):
        if not "=" in template[i]:
            template[i]=str(n)+" = "+template[i]
            n+=1
    
    
        template[i]=template[i].strip()
        for j in elementi:
            template[i]=template[i].replace(j,elementi[j])
    templateDict={}
    templateDict["nome"]=template[0].strip()
    r=re.compile(" *= *")
    for i in range(1,len(template)):
        tv=r.split(template[i], maxsplit=1)
        templateDict[tv[0].strip()]=tv[1].strip()

    return templateDict

def rebuildTemplate(td, areadati=None):
    template="{{"+td["nome"]+"\n"
    
    for d in list(td.keys())[1:]:
        if d.isdigit() and not "=" in td[d]:
            template+="| "+td[d]+"\n"
        else:
            template+="| "+ d+" = "+td[d]+"\n"
    template+="}}"
    return template

# calcola la qualità del match (da 0 a 1)
def match_page(target, source):
    s = difflib.SequenceMatcher()
    text1 = source
    text2 = target
    p = re.compile(r'[\W]+')
    text1 = p.split(text1)
    text2 = p.split(text2)
    s.set_seqs(text1,text2)
    ratio = s.ratio()
    return ratio

# elimina l'escaping dei caratteri speciali nel djvu text
def unquote_text_from_djvu(text):
    #text = text.replace('\\r', '\r')
    text = text.replace('\\n', '\n')
    text = text.replace('\\"', '"')
    text = text.replace('\\\\', '\\')
    text = text.replace('\\037', '\n')
    text = text.replace('\\035', '')
    text = text.replace('\\013', '')
    text = text.rstrip('\n')
    return text
# estrae lo strato testo in datail page, e produce la lista data dei testi 
# delle pagine (pagina 1 in data[0]);
# il file djvu deve essere scaricato nella cartella locale
def extract_djvu_text(filename):
    print("extracting text layer")

##    if type(filename) == type(''):
##        filename = filename.encode('utf-8')

    #utils.copy_file_from_url(url, filename, sha1)

    data = []
    # GTK app are very touchy
    os.environ['LANG'] = 'en_US.UTF8'
    # FIXME: check return code
##    ls = subprocess.Popen([ 'djvutxt', filename, '--detail=page'], stdout=subprocess.PIPE, close_fds = True)
##    text = ls.stdout.read()
##    ls.wait()
 #   comando="djvutxt --detail=page %s  text.txt" % (unicode(filename,"utf-8").encode("latin-1"))
    comando="djvutxt --detail=page %s  text.txt" % filename

    result=os.system(comando)
    print(comando, result)
    text=open("text.txt").read()
    for t in re.finditer(r'\((page -?\d+ -?\d+ -?\d+ -?\d+[ \n]+"(.*)"[ ]*|)\)\n', text):
        #t = unicode(t.group(1), 'utf-8', 'replace')
        t=t.group(1)
        t = re.sub('^page \d+ \d+ \d+ \d+[ \n]+"', '', t)
        t = re.sub('"[ ]*$', '', t)
        t = unquote_text_from_djvu(t)
        # proviamo ad aggirare il bug logico
##        if len(t)<150:
##            t=""
        data.append(t)

    # os.remove(filename)

    return data

def extract_pdf_text(filename):
    print("extracting text layer from pdf")
##
##    if type(filename) == type(''):
##        filename = filename.encode('utf-8')

    #utils.copy_file_from_url(url, filename, sha1)

    data = []
    # GTK app are very touchy
    os.environ['LANG'] = 'en_US.UTF8'
    # FIXME: check return code
##    ls = subprocess.Popen([ 'djvutxt', filename, '--detail=page'], stdout=subprocess.PIPE, close_fds = True)
##    text = ls.stdout.read()
##    ls.wait()
    comando='pdftotext -enc UTF-8 "%s"  text.txt' % (filename)
    result=os.system(comando)
    print(comando, result)
    text=open("text.txt").read()
##    text=unicode(text,"utf-8")
    text=text.replace(u"¬ ","")
    data=text.split(u"\x0c")

    # os.remove(filename)

    return data

# restituisce e stampa un messaggio di errore
def ret_val(error, text):
    if error:
        print("Error: %d, %s" % (error, text))
    return  { 'error' : error, 'text' : text }

E_ERROR = 1
E_OK = 0

# routine fondamentale; valuta il match ed esegue il caricamento in nsPagina, lasciate ogni speranza...
# returns result, status

# target è il testo di cui fare il match.
# cached_txt è la lista dei testi delle pagine djvu
# djvuname e number sono ovvi
# verbose è opzione debug che stampa il testo matchato.
# prefix è il nome locale di nsPagina
# step è boh, default 1
# se E_OK restituisce il testo a cui sono intercalati i codici match

def do_match(target, cached_text, djvuname, number, verbose=False, prefix="Pagina", step=1, oldText=True):
    s = difflib.SequenceMatcher()
    offset = 0
    output = ""
    is_poem = False

    try:
        last_page = cached_text[number - ((step+1)//2)]
    except:
        return ret_val(E_ERROR, "Unable to retrieve text layer for page: " + str(number))

    # ciclo pagina per pagina
    for pagenum in range(number, min(number + 1000, len(cached_text)), step):

        if pagenum - number == 10 and offset == 0:
            return ret_val(E_ERROR, "error : could not find a text layer.")
        # verifica match
        page1 = last_page
        last_page = page2 = cached_text[pagenum + (step//2)]
        
        try:
            page3=cached_text[pagenum + 2*(step//2)]
        except:
            page3=""
        try:
            page4=cached_text[pagenum + 3*(step//2)]
        except:
            page4=""
        text1 = page1+page2+page3+page4

        text2 = target[offset:offset+ int(1.5*len(text1))]
        text3 = text2    # salvo in text3 il testo originale
        if oldText:
            text2 = text2.replace("s","f") # trasformazione oldText
        
        p = re.compile(r'[\W]+', re.U) # lista sequenze caratteri
        fp = re.compile(r'([\W]+)', re.U) # lista sequenze caratteri e non caratteri
        ftext1 = fp.split(text1) # nelle due pagine djvu
        ftext2 = fp.split(text2) # nel testo target modificato
        ftext3 = fp.split(text3) # nel testo target nel testo originale

        page1 = p.split(page1) #lista parole in pagina corrente
        text1 = p.split(text1) #lista parole in pagina corrente + successiva
        text2 = p.split(text2) #lista parole blocco testo
        
        s.set_seqs(text1,text2)
        # produce triplette degli indici dei blocchi corrispondenti
        # (i, j, n), and means that a[i:i+n] == b[j:j+n]
        # l'ultima è dummy (len(a), len(b), 0). 
        mb = s.get_matching_blocks()
        if len(mb) < 2: # nessun match
            print("LEN(MB) < 2, breaking")
            break
        ccc = mb[-2]  #ultimo blocco matching; ccc[0]+ccc[2]=numero delle parole totale in text1 (pagina corrente+pagina successiva)
        # no idea what was the purpose of this
        #dummy = mb[-1]
        ratio = s.ratio()
        #print(i, ccc, ratio)

        if ratio < 0.10 and len(last_page)>300: #possibile illustrazione con breve didascalia
            print("low ratio", ratio)
            # return (page1,text1,text2)
            break
        # CICLO DEBUG

        mstr = u""
        
        overflow = False
        # elemento per elemento nell'ultimo blocco:
        for i in range(ccc[0] + ccc[2]): #parola per parola nella lista text1 (indici)
            matched = False
            for m in mb:    #gruppo matched per gruppo matched
                if i >= m[0] and i < m[0]+m[2] : # se l'indice della parola è compresa in un guppo matched...
                   matched = True
                   if i >= len(page1): #se la parola è fuori della pagina corrente
                       overflow = True
                   break
            if not overflow: # se la parola è all'interno della pagina corrente... 
                ss = ftext1[2*i] #recupero parola (0, 2....)
                if matched:
                    ss =u"\033[1;32m%s\033[0;49m"%ss
                if 2*i+1 < len(ftext1):
                    mstr = mstr + ss + ftext1[2*i+1]
        if verbose:
            # pywikibot.output(mstr)
            print(mstr)
            print("--------------------------------")


        # costruzione vera e propria del testo matched
        ftext2=ftext3[:] # riprendo il testo originale
        mstr = ""
        no_color = ""
        overflow = False
        for i in range(ccc[1]+ccc[2]):
            matched = False
            for m in mb:
                if i >= m[1] and i < m[1]+m[2] :
                   matched = True
                   if m[0]+i-m[1] >= len(page1):
                       overflow = True
                   break

            if not overflow:
                ss = ftext2[2*i]
                if matched:
                    ss =u"\033[1;31m%s\033[0;49m"%ss
                if 2*i+1 < len(ftext2): 
                    mstr = mstr + ss + ftext2[2*i+1]
                    no_color = no_color + ftext2[2*i] + ftext2[2*i+1] # a no_color viene appesa la parola + il blocco non alfabetico,
                                                                      # ma solo se overflow è falso
        if verbose:
            # pywikibot.output(mstr)
            print(mstr)
            print("====================================")
        # creazione separatore; is_poem è vero se nella pagina precedente c'era un poem non chiuso
        if is_poem:
            sep = u"\n</poem>\n==[["+prefix+":%s/%d]]==\n<poem>\n"%(djvuname,pagenum)
        else:
            sep = u"\n==[["+prefix+":%s/%d]]==\n"%(djvuname,pagenum)
        print("%s/%d" %(djvuname,pagenum))

        # Move the end of the last page to the start of the next page
        # if the end of the last page look like a paragraph start. 16 char
        # width to detect that is a guessed value.
        # ????? 
        no_color = no_color.rstrip()
        match = re.match(u"(?ms).*(\n\n.*)$", no_color)
        if match and len(match.group(1)) <= 16:
            no_color = no_color[:-len(match.group(1))]
        else:
            match = re.match(u"(?ms).*(\n\w+\W*)$", no_color)
            if match:
                no_color = no_color[:-(len(match.group(1)) - 1)]

        offset += len(no_color) # aggiornamento offset sul target per proseguire dalla prossima pagina

        if no_color and no_color[0]=='\n': # eliminazione eventuale acapo in testa a no_color
            no_color = no_color[1:]
        no_color = no_color.lstrip(' ')    # ... e di eventuali spazi
        output += sep + no_color     # ad output viene aggiunto l separatore ==[[Pagina....]]== e il contenuto di no_color  (testo matched)

        if no_color.rfind(u"<poem>") > no_color.rfind(u"</poem>"):
            is_poem = True
        elif no_color.rfind(u"<poem>") < no_color.rfind(u"</poem>"):
            is_poem = False
    # ciclo pagina per pagina terminato
    if offset != 0 and target[offset:]:
        if len(target) - offset >= 16:
            output += u"\n=== no match ===\n"
        output += target[offset:].lstrip(' ')

    if offset == 0:
        output = ""

    if output == "":
        return ret_val(E_ERROR, "text does not match")
    else:
        return ret_val(E_OK, output)

		
		
		
# It's possible to get a name collision if two different wiki have local
# file with the same name but different contents. In this case the cache will
# be ineffective but no wrong data can be used as we check its sha1.
def get_djvu(cache, mysite, djvuname, check_timestamp = False):

    print("get_djvu", repr(djvuname))

    djvuname = djvuname.replace(" ", "_")
    cache_filename = djvuname + '.dat'

    obj = cache.get(cache_filename)
    if not obj:
        print("CACHE MISS")
        filepage = copy_File.get_filepage(mysite, djvuname)
        if not filepage:
            # can occur if File: has been deleted
            return None
        try:
            url = filepage.fileUrl()
            obj = extract_djvu_text(url, djvuname, filepage.getFileSHA1Sum())
        except:
            utils.print_traceback("extract_djvu_text() fail")
            obj = None
        if obj:
            cache.set(cache_filename, obj)
        else:
            return None
    else:
        if check_timestamp:
            filepage = copy_File.get_filepage(mysite, djvuname)
            if not filepage:
                # can occur if File: has been deleted
                return None
            sha1 = filepage.getFileSHA1Sum()
            if sha1 != obj[0]:
                print("OUTDATED FILE")
                url = filepage.fileUrl()
                try:
                    obj = extract_djvu_text(url, djvuname, sha1)
                    cache.set(cache_filename, obj)
                except:
                    return None

    return obj[1]

def carica_pcl(nome_file, folder="dati/"):
    nome_file=folder+nome_file+".pcl"
    f=open(nome_file)
    contenuto=pickle.load(f)
    f.close()
    return contenuto

def salva_pcl(variabile,nome_file="dato",folder="dati/"):
    nome_file=folder+nome_file+".pcl"
    f=open(nome_file,"w")
    pickle.dump(variabile, f)
    f.close()
    print("Variabile salvata nel file "+nome_file)
    return 

def getTarget(listaCapitoli=None, titolo=None,nomeTarget="testoNs0.txt", lang="it"):
    it=bot.Site(lang,"wikisource")
    target=u""
    if listaCapitoli==None:
        if titolo!=None:
            root=bot.Page(it,titolo).get()

            root=root.replace("{{Testo","{{testo")
            listaCapitoli=produci_lista(root,"{{testo|","}}",0)

            for i in range(len(listaCapitoli)):
                if "|" in listaCapitoli[i]:
                    listaCapitoli[i]=listaCapitoli[i].split("|")[0]
                listaCapitoli[i]=titolo+listaCapitoli[i].strip()
                print(listaCapitoli[i])
        else:
            print("Errore, necessario paramentro listaCapitoli o titolo")
            return
    
            
            


    
    monnezza=[[u"{{Qualità","}}"],["{{IncludiIntestazione","}}"],["{{Conteggio pagine","}}"],["<!-- Area dati:","<!-- a qui -->"]]
    for capitolo in listaCapitoli:
        testo=bot.Page(it,capitolo).get()
        for m in monnezza:
            x=find_stringa(testo,m[0],m[1],1)
            if x!="":
                testo=testo.replace(x,"")
        target+=testo+"\n\n"
        print(capitolo)
    open(nomeTarget,"w", encoding="utf-8").write(target)
    print("Salvato target in ",nomeTarget)
    return
    
    
# utilities 
# Nuova versione, gestisce i tag annidati; x e' la parte "aspecifica" del
# tag di apertura (es: {{ cercando {{Intestazione| )
def find_stringa(stringa,idi,idf,dc=0,x=None,side="left"):
    if side=="right":
        idip=stringa.rfind(idi)
    else:
        idip=stringa.find(idi)
    idfp=stringa.find(idf,idip+len(idi))+len(idf)
    if idip>-1 and idfp>0:
        if x!=None:
            while stringa[idip:idfp].count(x)>stringa[idip:idfp].count(idf):
                if stringa[idip:idfp].count(x)>stringa[idip:idfp].count(idf):
                    idfp=stringa.find(idf,idfp)+len(idf)
                
        if dc==0:
            vvalore=stringa[idip+len(idi):idfp-len(idf)]
        else:
            vvalore=stringa[idip:idfp]
    else:
        vvalore=""
    return vvalore

def produci_lista(testo,idi,idf,dc=1,inizio=None):
    t=testo[:]
    lista=[]
    while not find_stringa(t,idi,idf,1,inizio)=="":
        el=find_stringa(t,idi,idf,1,inizio)
        t=t.replace(el,"",1)
        if dc==0:
            el=find_stringa(el,idi,idf,0,inizio)
        lista.append(el)
    return lista
        
        


def go(fileTargetName, djvuName, startPageNumber, verbose=True, fileOutputName="output.txt", oldText=False):
##    fileTargetName=os.path.join(folder,fileTargetName)
##    djvuName=os.path.join(folder,djvuName)
##    fileOutputName=os.path.join(folder,fileOutputName)
                         
    # estrazione testo djvu in lista di testi delle pagine
    if djvuName.endswith(".djvu"):
        pagine=extract_djvu_text(djvuName)
    elif djvuName.endswith(".pdf"):
        pagine=extract_pdf_text(djvuName)
    else:
        print("Errore: il file deve essere djvu o pdf")
        return
    # caricamento target
    #target=unicode(open(fileTargetName).read(), "utf-8")
    target=open(fileTargetName).read()
    # do_match(target, cached_text, djvuname, number, verbose=True, prefix="Pagina", step=1):
    result=do_match(target,pagine,djvuName,startPageNumber,verbose=verbose, oldText=oldText)
    open(fileOutputName,"w", encoding="utf-8").write(result["text"])
    print("Testo matched salvato in ",fileOutputName)