PrettyPrint JavaScript

lunedì 12 novembre 2012

Importare l'estratto conto online in Homebank

Vediamo come creare uno script che permetta di importare l'estratto conto della nostra banca on line su Homebank. Lo script funziona per il formato dati esportato da Unicredit ma si può facilmente adattare per essere compatibile anche con altre banche.

Per prima cosa scarichiamo dal sito della nostra banca il csv dell'estratto conto. Nel caso di Unicredit il file di INPUT si presenta così:

Genius Card Web IT 95 J 00000 00000 00xx0000xx00 - XXXX XXXX XXXX 0000,,,
,,,
Data Operazech.,Descrizione,EUR,Caus.
02/01/2012,"PAGAMENTO SUPERMERCATO","-50,00",079
04/01/2012,"VOSTRI EMOLUMENTI DA ENTERPRISE S.p.A","2100,00",027
05/01/2012,"DISPOSIZIONE DI BONIFICO BONIFICO A ROSSI MARIO PER AFFITTO","-357,72",208



Dal sito di Homebank possiamo vedere che il formato accettato è il seguente (link):

Column list:
date ; paymode ; info ; payee ; description ; amount ; category

Di conseguenza il nostro OUTPUT dovrà avere la seguente formattazione

15-02-04;0;;;Some cash;-40,00;Bill:Withdrawal of cash
15-02-04;1;;;Internet DSL;-45,00;Inline service/Internet


Quindi per prima cosa eliminiamo dal file di input le righe di titolo ed intestazione:

02/01/2012,"PAGAMENTO SUPERMERCATO","-50,00",079
04/01/2012,"VOSTRI EMOLUMENTI DA ENTERPRISE S.p.A","2100,00",027
05/01/2012,"DISPOSIZIONE DI BONIFICO BONIFICO A ROSSI MARIO PER AFFITTO","-357,72",208


Effettuiamo un find e replace del file in input su un file temporaneo sostituendo il separatore virgola con il punto e virgola ed eliminando i doppi apici:


sed -e 's/,"/;"/g' -e 's/",/";/g' -e 's/"/\E/g' inputFile >inputFile.tmp


Ottenendo come risultato il seguente:

02/01/2012;PAGAMENTO SUPERMERCATO;-50,00;079
04/01/2012;VOSTRI EMOLUMENTI DA ENTERPRISE S.p.A;2100,00;027
05/01/2012;DISPOSIZIONE DI BONIFICO BONIFICO A ROSSI MARIO PER AFFITTO;-357,72;208 
Il passo successivo è quello di leggere il file temporaneo così generato riga per riga riordinando nel modo corretto le colonne affinchè siano compatibili con il formato di output di Homebank.

Homebank permette anche di associare ad ogni movimento un tag in modo da permettere all'utente di analizzare i dati in maniera più precisa, tuttavia questa operazione andrebbe fatta a mano per ogni singola operazione, vediamo quindi di trovare il modo di associare ad ogni riga del file di output un tag che sarà poi riconosciuto da Homebank.

Per prima cosa apriamo HomeBank, creiamo un nuovo portafoglio e dal menu Gestione->Categorie inseriamo le categorie che ci sembrano più utili in base alla tipologia dei nostri movimenti. Fatto ciò clicchiamo sul tasto 'Esporta' e dovremmo trovarci con un file di questo tipo:

1;-;Casa
2; ;Affitto
2; ;Bollette
2; ;Spese
1;-;Personali
2; ;Regali
2; ;Altre
2; ;Abbigliamento
2; ;Utilities
2; ;Spesa
1;-;Auto
2; ;Manutenzione
2; ;Benzina
2; ;Assicurazione
1;-;Viaggi
2; ;Vacanze
2; ;Hotel
2; ;Biglietti
1;+;Emolumenti
2; ;Eterprise

Che andrà trasformato a mano nel seguente:

 
Casa:Affitto;
Casa:Bollette;
Casa:Spese;
Personali:Regali;
Personali:Altre;
Personali:Abbigliamento;
Personali:Utilities;
Personali:Telefonia;
Personali:Spesa;  
Auto:Benzina;
Auto:Assicurazione;
Auto:Manutenzione;  
Viaggi:Vacanze;
Viaggi:Hotel;
Viaggi:Biglietti;
Emolumenti:Enterprise;

Adesso aggiungiamo ad ognuna di queste categorie una serie di parole chiave presenti nelle descrizioni dei movimenti che consentano di inquadrarle in quella categoria. Ad esempio i movimenti che contengono nella descrizione la parola 'AFFITTO' saranno etichettati nella categoria Casa:Affitto quindi:

Casa:Affitto; AFFITTO

I Movimenti che invece conterranno nomi di Supermercati dovranno confluire nella categoria Personale:Spesa quindi avremo:

Personali:Spesa;   PEWEX IPERCOOP COOP PAM

E così via fino ad avere parole chiavi per ogni categoria, alla fine il nostro file somiglierà a qualcosa del genere:

Casa:Affitto; AFFITTO
Casa:Bollette; GAS ENEL
Casa:Spese;  SPESE FONDOCASSA
Personali:Regali; AMAZON E-KEY
Personali:Altre;
Personali:Abbigliamento; PULL&BEAR SYSLEY BENETTON
Personali:Utilities;
Personali:Telefonia; TIM WIND VODAFONE TRE.IT
Personali:Spesa;  
Auto:Benzina; RIFORNIMENTO AGIP CARBURANTI

Auto:Assicurazione; QUIXA ALLIANCE ADMIRAL
Auto:Manutenzione;   MECCANICO GOMMISTA
Viaggi:Vacanze;
Viaggi:Hotel; B&B
Viaggi:Biglietti; ALITALIA RYANAIR

Emolumenti:Enterprise; ENTERPRISE

Salvato questo file con il nome 'categorie' siamo pronti ad effettuare il parsing del file temporaneo che avevamo generato per ottenere così il file csv da dare in pasto ad Homebank con i movimenti già categorizzati:


while IFS=';' read data desc amount caus

       do

         #Category retrivier routine

         #replace spaces of description with |

         tempDesc="$(echo $desc | sed 's/ /|/g')"

         #find categories

         category="$(egrep -w $tempDesc categorie | head -n1 | cut -d';' -f1)"

        fi

         #append readed in output file

        echo "$data;0;;;$desc;$category">>output

      done  <inputFile.tmp

la routine precedente non fa altro che leggere riga per riga splittando il contenuto di ogni singola riga del file inputFile.tmp precedentemente generato.

Per recuperare la categoria sostituisco gli spazi del campo descrizione (desc) con il carattere di OR logico (|) ponendo la stringa generata in una variabile temporanea tempDesc:

tempDesc="$(echo $desc | sed 's/ /|/g')"  


Subito dopo effettuo una ricerca sul file categorie per selezionare la prima riga nella quale è stata trovata una delle parole di tempDesc facendomi restituire la categoria, l'opzione -w fa in modo che deve essere trovata una occorrenza dell'intera parola presente nella stringa $tempDesc all'interno del file delle categorie):

category="$(egrep -w $tempDesc categorie | head -n1 | cut -d';' -f1)"


Infine scrivo sul file di output la riga così ottenuta mettendo i campi nell'ordine corretto rispetto alla formattazione richiesta da Homebank:

echo "$data;0;;;$desc;$category">>output


A questo punto possiamo anche creare uno script in modo da rendere più automatico possibile il procedimento ed eventualmente aggiungere il parsing anche di altri formati. 

Download hmbank.sh
hmbank.sh

L'uso dello script è semplicissimo, se input.csv è il nostro file con l'elenco dei movimenti scaricato dalla nostra banca e categories è il file con le categorie organizzate con le keyword così come descritto nel post, basterà dare il comando


./hmbank.sh -i input.csv -o output.csv -c categories


per generare il file output.csv che daremo in pasto ad HomeBank.



giovedì 1 novembre 2012

Gestione del Backup

In questo post vedremo come organizzare il backup in una macchina Linux. Lo scenario è quello in cui si hanno diverse directory sparse per il file system, delle quali si vuole una copia. Inoltre sulla macchina è installato il plugin di dropbox e viene richiesto di gestire la copia di alcune directory all'interno di quella appartenente a Dropbox e di gestire automaticamente il demone dropboxd per l'upload nella cloud di tali directory.

Il modo più banale per eseguire questo task è il seguente:

Preparo uno script che sincronizza le cartelle di origine e di destinazione. Utilizzo a questo scopo il comando rsync che permette il backup incrementale ottimizzando così i tempi di copia.

synch.sh:

#!/bin/bash
rsync -avrz --delete --force --copy-dirlinks --omit-dir-times /another/path/to/Ebook/  "/backup/Ebook/"
rsync -avrz --delete --force --copy-dirlinks --omit-dir-times /path/to/Musica/  "/backup/Musica/"
rsync -avrz --delete --force --copy-dirlinks --omit-dir-times /path/to/my/Documenti/  "/backup/Documenti/" 
rsync -avrz --delete --force --copy-dirlinks --omit-dir-times /path/to/my/Documenti/  "/Dropbox/Documenti/" 



Dopodichè configuro crontab* per esegure lo script e per lanciare e terminare dropbox:
./dropbox-dist/dropboxd
 killall -9 dropbox
 
* Maggiori informazioni sull'utilizzo di crontab: http://www.manpagez.com/man/5/crontab/ 

Tuttavia questa soluzione risulta scomoda nel momento in cui dobbiamo aggiungere directory al backup o in cui cambiano i percorsi delle nostre directory.

Un metodo che garantisce maggiore flessibilità è quello di creare due directory che chiameremo: toSynch e toDropbox, all'interno delle quali creeremo dei link simbolici verso le directory di cui vogliamo una copia nella directory di backup o in quella Dropbox:



 Lo script in questo modo viene notevolmente semplificato e diventa:

#!/bin/bash
rsync -avrz --delete --force --copy-dirlinks --omit-dir-times /path/toBackup/  "/backup/"
rsync -avrz --delete --force --copy-dirlinks --omit-dir-times /path/toDropbox/  "/Dropbox/"


In questo modo se vogliamo aggiungere delle nuove directory al backup o a Dropbox, o semplicemente cambiano i percorsi delle directory basterà rispettivamente aggiungere o modificare i link simbolici all'interno delle directory toBackup e toDropbox.

A questo punto sarebbe asupicabile aggiungere al nostro sistema di backup la generazione di un log delle operazioni e la gestione degli eventuali errori e relativa notifica.

Per fare ciò avremo tre diversi livelli di log:
  • /var/log/backup.log: log delle operazioni dello script;
  • /var/log/backup_err.log: log degli errori;
  • /var/tmp/backup.tmp: log temporaneo con gli output dei singoli comandi lanciati;
Gli obiettivi sono quelli di verificare che l'operazione di backup sia andata a buon fine, ricevere una notifica (preferibilmente una mail) in caso di errori, avere tutte le informazioni necessarie a risolvere gli eventuali errori.

Per raggiungere questo obiettivo avremo bisogno di uno script in grado di interpretare alcuni comandi in particolare i seguenti:

- esegui la copia di backup;
- start dropbox
- stop drobox
- controllo degli errori ed invio mail*

Il seguente script utilizza dropbox.py per lo start e lo stop di Dropbox scaricabile direttamente da: http://www.dropbox.com/download?dl=packages/dropbox.py

Inoltre si utilizza postfix per l'invio di mail tramite riga di comando: http://www.postfix.org/BASIC_CONFIGURATION_README.html

Lo script utilizza inoltre un file di configurazione nel quale vengono specificati:
  • Il path dei file di log
  • Il path delle directory di origine e destinazione del backup
  • Le informazioni relative alla mail da inviare in caso di errori 


smartbackup.sh:
#!/bin/bash
#This script synch the directory listed into 'synchList' as [FROM] [TO] string
#It also provide to start or stop dropbox based on a command line option. 
#It write the standard output to a crontab.log file into /var/log directory and a standard error into
#crontab_error.log file in the same directory. If an error occours and crontab_error.log is not empty
#send a mail to 'mailTo' address from a 'sendTo' address based on mail account configured on system.
#Required dropbox.py on dropbox home path. dropbox.py are available on: http://www.dropboxwiki.com/Using_Dropbox_CLI

############################################## VARIABLES ##############################################
set -e
trap trapcatcher INT TERM EXIT 

PROGNAME=$(basename $0)
USAGESTR="Usage: $PROGNAME [-s] [-d] [OPTION] [-v]"

source /etc/smartbackup.conf

START="start"
STOP="stop"
DEBUG=0
MAIL=0
SYNCHRONIZE=0
DROPBOXSTART=0
DROPBOXSTOP=0

function trapcatcher
{
#   ----------------------------------------------------------------
#   Function managing fatal program error or unhandled signal
#   ----------------------------------------------------------------
    echo $name $(date) " GRAVE: Some unhandled errors occours. " >> $log
    echo "${PROGNAME} (ERROR): ${1:-"Unknown Error"}" 1>&2
    exit 1;
}

function error_exit
{

#   ----------------------------------------------------------------
#   Function for exit due to handled program error
#       Accepts 1 argument:
#           string containing descriptive error message
#   ----------------------------------------------------------------

        
    echo "${PROGNAME} (ERROR): ${1:-"Unknown Error"}" 1>&2
    exit 0
}

function USAGE ()
{

#   ----------------------------------------------------------------
#   Function for explain the proper command usage
#   ----------------------------------------------------------------


    echo ""
    echo $USAGESTR
    echo ""
    echo "OPTIONS:"
    echo "    -s  start synchronize"
    echo "    -d  [OPTION] start or stop dropbox option can be start or stop"
    echo "    -v  print version"
    echo "    -m  send mail if errors occours"
    echo "    -D  debug mode on"    
    echo "    -h  print this help"
    echo ""
    echo "EXAMPLE:"
    echo "    `basename $0` -s -d start -v -m"
    echo ""
    #exit $E_OPTERROR    # Exit and explain usage, if no argument(s) given.
}

function synchronize() {

#   ----------------------------------------------------------------
#   Function for exec the rsynch command to dir list
#   ----------------------------------------------------------------
    
    notify-send Backup "Start Synchronizing Script"
    echo $name $(date) " Start synch procedure..." >> $log

    #Cicla gli array ed esegue un rsync per ognuno di essi
    for syncStr in ${syncList[@]}
    do
        #IFS=$IFS_OLD    #Restore old separator    
        arr=$(echo $syncStr | tr ";" "  ")
        echo $name $(date) " Synchronized (from to) ${arr[0]} ${arr[1]}" >> $log
    rsync -avrz --delete --force --copy-dirlinks --omit-dir-times ${arr[0]}  ${arr[1]} >>$tempFile 2>> $errorLog        
        
    done
    
    # --------------------------------------------------------------------
    # Insert here other operation to do for synchronize
    # --------------------------------------------------------------------
   

    echo $name $(date) " Synch procedure succesfully end" >> $log
    notify-send Backup "End Synchronizing"
}

function manageDropbox() {

    if [ $DROPBOXSTART -eq 1 ];
    then
        echo $name $(date) " Run Dropbox..." >> $log
        exec $DROPBOX_HOME./dropboxd & 2>> $errorLog
    elif [ $DROPBOXSTOP -eq 1  ];
    then
        python2.7 $DROPBOX_PY_PATH stop 2>> $errorLog
        echo $name $(date) " Stopped dropbox" >> $log
    fi
    
}

function sendMail() {
    #Se sono presenti degli errori esiste e non è vuoto 
    # il file crontab_error che quindi invio per mail
    if [ -s $errorLog ];
    then
           echo $name $(date) " Some errors occours. See crontab_error.log. An email was send. " >> $log
           #Send email        
           echo "$mailBody" | mail -s "$mailSubject" -a "$errorLog" -r "$mailTo" "$mailFrom" 2>> $errorLog
        
    else
        #deleting temp file
        echo $name $(date) " Deleting temp file: $tempFile" >> $log
        rm $tempFile 2>>$errorLog        
           echo $name $(date) " Backup successfully end. " >> $log
        
    fi

}

#   ----------------------------------------------------------------
#   Start Script 
#   ----------------------------------------------------------------

# Parse command line options.
while getopts svDhmd: OPT; do
    case "$OPT" in
        s)
            SYNCHRONIZE=1
            ;;
        d)
            manageDropbox $OPTARG
        if [ $OPTARG = $START ];
        then
            DROPBOXSTART=1
        elif [ $OPTARG = $STOP ];
        then
            DROPBOXSTOP=1
        else 
        echo USAGE 1>&2
        error_exit "$LINENO: Invalid -d Option: only start and stop permitted"
        fi
            ;;
    m)
            MAIL=1
            ;;
    D)
        DEBUG=1
            #logError="/dev/stderr" #Dangerous
        #log="/dev/stdout"    #Dangerous
            ;;
    v)
            echo "$name v.$version"    
            ;;
    h)
        USAGE
            ;;
        \?)
            # getopts issues an error message
            USAGE 1>&2
        error_exit "$LINENO: Invalid Option "
            ;;
    esac
done

# Remove the switches we parsed above.
# shift `expr $OPTIND - 1`

# We want at least one non-option argument. 
# Remove this block if you don't need it.
if [ $# -eq 0 ]; then
    USAGE 1>&2
    error_exit "$LINENO: At least one non-option argument is needed "
fi

#   ----------------------------------------------------------------
#   Based on the current active flag execute the proper command in the
#   right order
#   ----------------------------------------------------------------

if [ $SYNCHRONIZE -eq 1 ];
then synchronize
fi

if [[ $DROPBOXSTART -eq 1 || $DROPBOXSTOP -eq 1 ]];
then 
    manageDropbox
fi

if [ $MAIL -eq 1 ];
then
    sendMail
fi


#Reset trap setting
trap - INT TERM EXIT



smartbackup.conf:
#Configuration file for smartbackup.sh

#Name and version script
name="smartbackup"
version="1.4"
#Notify mail to
mailTo="mail@yourAccount.com" 
#Notify mail from
mailFrom="mail@yourAccount.com"
#Subject
mailSubject="BACKUP ERROR NOTIFY"
#Body
mailBody="This mail contains the system backup error log. Remember to clean crontab_error.log"

#Temp file
tempFile="/var/tmp/smartbackup.tmp"
#Error log path
errorLog="/var/log/crontab_error.log"
#Common logging file for this backup
log='/var/log/crontab.log'
#Dropbox home path
DROPBOX_HOME="/home/yournickname/programs/dropbox/"
#dropbox.py home path
DROPBOX_PY_PATH="/home/yournickname/dropbox.py"

#TO SYNCHRONIZE
syncList[0]="/path/toBackup/;/media/yourMedia/backup/"
syncList[1]="/path/toSynch/;/media/Dropbox/"