martedì 28 novembre 2017

Python: lavorare con i file Excel.

Con Python è possibile, tramite la libreria openpyxl, lavorare sui file Excel.

Nel mio caso il problema proposto era il seguente: in una condivisione di rete si vogliono salvare dei file excel che contengono delle "Action List": le righe contengono delle attività con delle date di scadenza. Si vuole inviare, in modo automatico, delle mail quando l'attività non è stata completata e la data di scadenza viene superata.

Tramite le macro VBA in Excel si possono testare le date e inviare mail, ma per poterle attivare si dovrebbe aprire il file.

Invece, tramite Python, posso creare un programma che legga i files e poi schedularlo in modo venga eseguito ad intervalli regolari.


Per spiegarmi meglio questo potrebbe essere un esempio di Action List:


In ogni file, inoltre, c'è un foglio che contiene gli indirizzi mail delle persone coinvolte:


Noi abbiamo creato un modello con alcune macro/formattazioni condizionali e la persona si sceglie tramite un elenco a tendina.

Installazione libreria:

pip install openpyxl
Collecting openpyxl
  Downloading openpyxl-2.4.8.tar.gz (156kB)
    100% |¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦| 163kB 1.6MB/s
Collecting jdcal (from openpyxl)
  Downloading jdcal-1.3.tar.gz
Collecting et_xmlfile (from openpyxl)
  Downloading et_xmlfile-1.0.1.tar.gz
Installing collected packages: jdcal, et-xmlfile, openpyxl
  Running setup.py install for jdcal ... done
  Running setup.py install for et-xmlfile ... done
  Running setup.py install for openpyxl ... done
Successfully installed et-xmlfile-1.0.1 jdcal-1.3 openpyxl-2.4.8

Esempi di utilizzo di alcune funzioni presenti nella libreria.

NOTA: non metto il sorgente completo poichè è troppo personalizzato sulla nostra realtà: percorsi, nomi files, persone, copia per conoscenza, etc.. Eventualmente, se volete qualche dettaglio in più o non vi funziona qualcosa, scrivetemi senza problemi.

Apertura del file excel

    #apro il file Excel
    try:
        book = openpyxl.load_workbook(nome_file_excel, data_only=True)
    except:
        <gestione errori>
        return      

Nota: di default, se una cella contiene una formula, il sistema ritorna la formula contenuta e non il valore che assume; con "data_only=True" si imposta di ritornare il valore della formula.

Assegnazione di uno specifico foglio di lavoro all'interno del fine Excel

    #Assegna foglio General
    try:
        sheet_gen = book["General"]
    except:
        <gestione errori>
        return

Legge il contenuto di una singola cella per esempio A2

    cellaA2 = sheet['A2'].value

Legge il contenuto di un range di celle (A1-F200)

    cells = sheet['A1': 'F200']

    # ciclo principale
    for nriga in range(0, 200):

        #Salvo descrizione principale del punto
        if cells[nriga][0].value == "Description":

Per salvare il fatto che ho inviato una mail di sollecito ho fatto in modo, per ogni file elaborato, di creare un file di testo (con lo stesso nome file del fine Excel) dove registro l'invio mail. In questo modo posso gestire l'invio di nuovi solleciti, per esempio, dopo 7 giorni.

Infine ho creato un file Excel riepilogativo, con all'interno 2 fogli:
  1. Lista di tutte le attività, con date di scadenza, persone, nome file, etc..
  2. Lista di tutti i solleciti inviati.
con alcune features:
  • prima riga di intestazione campi con blocco riquadri attivo e filtro attivo.
  • larghezza colonne preimpostata.
  • eventuali date già scadute sono colorate di rosso.
  • link attivo al file in modo che sia sufficiente un click per aprirlo.
esempio del file:


variabili globali usate:

# Variabili per file statistiche
FILE_STAT  = r"<path>\Statistiche.xlsx" # File con statistiche
lista_action = []
lista_mails  = []

#------------------------------------------------------------------------------
# scrivi_file_statistiche : Scrive file con statistiche
#------------------------------------------------------------------------------
# input : nulla
# return: nulla
#------------------------------------------------------------------------------
def scrivi_file_statistiche():

    global lista_action
    global lista_mails

    wb_stat = Workbook()

    # Creazione foglio con tutte le attività
    ws_stat = wb_stat.active

    # Aggiungo Prima riga con intestazione
    ws_stat.append(["Nome", "Attività", "Punto", "Data", "Compl.", "Title of meeting", "File"])
    
    conta_action = len(lista_action)
    conta_righe  = conta_action + 1
        
    area = "A1:G" + str(conta_righe)

    for i in range(0, conta_action):
        ws_stat.append([lista_action[i][0], lista_action[i][1], str(lista_action[i][2]), lista_action[i][3], lista_action[i][4], lista_action[i][5], lista_action[i][6] ])

    tab = Table(displayName="Table1", ref = area)

    # Imposto larghezza colonne
    ws_stat.column_dimensions["A"].width = 20.0 # persona
    ws_stat.column_dimensions["B"].width = 60.0 # descrizione act
    ws_stat.column_dimensions["D"].width = 20.0 # data act
    ws_stat.column_dimensions["F"].width = 50.0 # descrizione progetto
    ws_stat.column_dimensions["G"].width = 60.0 # File

    # Imposto blocco riquadri
    c = ws_stat['B2']
    ws_stat.freeze_panes = c

    # Creo nuovo stile per la prima riga
    stile_firt_row = NamedStyle(name="stile_firt_row")
    stile_firt_row.font = Font(italic=True, bold=True)
    stile_firt_row.fill = PatternFill(start_color='FF00FF00' , end_color='FF00FF00', fill_type = "solid")
    wb_stat.add_named_style(stile_firt_row)

    # Applico stile alle cella della prima riga
    ws_stat['A1'].style = 'stile_firt_row'
    ws_stat['B1'].style = 'stile_firt_row'
    ws_stat['C1'].style = 'stile_firt_row'
    ws_stat['D1'].style = 'stile_firt_row'
    ws_stat['E1'].style = 'stile_firt_row'
    ws_stat['F1'].style = 'stile_firt_row'
    ws_stat['G1'].style = 'stile_firt_row'

    # Costruisco link al file e coloro di rosso le date già trascorse
    oggi = datetime.datetime.now()
    i = 0
    for row in ws_stat.iter_rows():

        i += 1

        # Salto la riga di intestazione
        if i == 1:
            continue

        # link al file
        link = row[6].value 

        # Assegno Hyperlink alla cella nella colonna 2
        ws_stat.cell(row=i, column=2).hyperlink = link

        # Uso testo colore blu
        ws_stat.cell(row=i, column=2).font = Font(color=colors.BLUE)

        # Salto righe con attività completata
        if row[4].value:
            if row[4].value == 100:
                continue

        # Se la data è già trascorsa coloro di rosso la cella con la data
        if str(row[3].value) < str(oggi):
            row[3].fill = PatternFill(start_color='00FF0000' , end_color='00FF0000', fill_type = "solid")

    ws_stat.add_table(tab)

    # Creazione foglio con tutte le mail inviate
    ws_mail = wb_stat.create_sheet("Mail Sent") 

    # Aggiungo Prima riga con intestazione
    ws_mail.append(["Nome", "Attività", "Punto", "Data", "Data mail", "Title of meeting", "Indirizzo", "File"])
    
    conta_mails = len(lista_mails)
    conta_righe  = conta_mails + 1
        
    area = "A1:H" + str(conta_righe)

    for i in range(0, conta_mails):
        ws_mail.append([lista_mails[i][0], lista_mails[i][1], str(lista_mails[i][2]), lista_mails[i][3], lista_mails[i][4], lista_mails[i][5], lista_mails[i][6], lista_mails[i][7] ])

    tab1 = Table(displayName="Table_mail", ref = area)

    # Imposto larghezza colonne
    ws_mail.column_dimensions["A"].width = 20.0 # persona
    ws_mail.column_dimensions["B"].width = 60.0 # descrizione act
    ws_mail.column_dimensions["D"].width = 20.0 # data act
    ws_mail.column_dimensions["E"].width = 20.0 # data mail
    ws_mail.column_dimensions["F"].width = 50.0 # descrizione progetto
    ws_mail.column_dimensions["G"].width = 25.0 # indirizzo
    ws_mail.column_dimensions["H"].width = 60.0 # File

    # Imposto blocco riquadri
    c = ws_mail['B2']
    ws_mail.freeze_panes = c

    # Applico stile alle cella della prima riga
    ws_mail['A1'].style = 'stile_firt_row'
    ws_mail['B1'].style = 'stile_firt_row'
    ws_mail['C1'].style = 'stile_firt_row'
    ws_mail['D1'].style = 'stile_firt_row'
    ws_mail['E1'].style = 'stile_firt_row'
    ws_mail['F1'].style = 'stile_firt_row'
    ws_mail['G1'].style = 'stile_firt_row'
    ws_mail['H1'].style = 'stile_firt_row'

    # Costruisco link al file 
    i = 0
    for row in ws_mail.iter_rows():

        i += 1

        # Salto la riga di intestazione
        if i == 1:
            continue

        # Costruisco link al file
        if row[7].value:

            link = row[7].value

            # Assegno Hyperlink alla cella nella colonna 2
            ws_mail.cell(row=i, column=2).hyperlink = link

            # Uso testo colore blu
            ws_mail.cell(row=i, column=2).font = Font(color=colors.BLUE)

    ws_mail.add_table(tab1)

    # Salvo file Excel
    try:
        wb_stat.save(FILE_STAT)
    except Exception as errore:
         <gestione errori>

Per trovare la lista di tutti i file con una determinata estensione presenti in una cartella ho utilizzato la funzione spiegata in questo post.

Nessun commento:

Posta un commento