WordPress e Excel: connessi grazie a Python e le API XML-RPC

5 agosto 2015

Grazie alle API XML-RPC, possiamo far dialogare uno script Python con WordPress e fare cose veramente interessanti come modificare il CMS a partire da file Excel. È il caso di un cliente che doveva aggiornare il suo catalogo on-line periodicamente partendo da alcuni fogli Excel generati dal suo gestionale interno. Tali aggiornamenti, in media bimestrali o trimestrali, comprendono:

  • inserimento di nuovi prodotti;
  • rimozioni di prodotti non più a catalogo;
  • modifica di dati relativi al prodotto e/o ai suoi accessori;
  • categorizzazione dei prodotti;
  • spostamento di prodotti da una categoria all’altra;
  • inserimento di nuove categorie o rimozione di categorie non più utilizzate;
  • definizione di varianti di prodotto;
  • assegnazione dei prodotti a più target di utilizzo (un martello interessa sia ad un muratore che ad un falegname);
  • assegnazione di manuali tecnici e/o brochure commerciali ad uno o più prodotti e/o varianti;
  • gestione dell’ordinamento dei prodotti (desunto dall’ordinamento all’interno del foglio Excel).

Vista la mole di dati da aggiornare e l’impossibilità di rilevare quali modifiche siano state apportate ai fogli excel periodicamente forniti, è stato proposto al cliente che ogni aggiornamento consistesse in una rimozione di tutta la gerarchia delle categorie/prodotti e nella successiva ricostruzione ex-novo partendo dai dati contenuti nei fogli Excel, a patto che tale ‘ricostruzione’ del catalogo non richiedesse più di trenta minuti di “disservizio” ad ogni aggiornamento.

L’esiguità del tempo concesso per l’aggiornamento ha ovviamente escluso la possibilità di inserimento, modifica o rimozione manuale dei dati e dei file allegati e ha portato a trovare una soluzione programmatica alla necessità. La scelta del linguaggio utilizzato per la realizzazione dello script di importazione dei dati è ricaduta su Python per la concisione del codice, la vasta gamma di librerie disponibili e l’elevata capacità di maneggiare strutture dati complesse con efficienza sintattica.

Quelli che seguono sono esempi di codice prelevati direttamente dallo script di importazione ed editati in modo da renderli il più semplice possibile da studiare o riutilizzare.

Lettura dei fogli di Excel

Per affrontare la fase della lettura dei fogli di Excel si è fatto ricorso alla libreria xlrd. Il foglio Excel contiene numerose colonne, tuttavia noi siamo interessati solamente ad alcune, necessario inoltre gestire l’eventualità che alcune delle colonne siano state spostate e/o scambiate di posto con altre; infine, l’organizzazione del foglio Excel prevede che il valore di una colonna (“PRODOTTO”) abbia il medesimo valore per tutti gli articoli che compongono il prodotto (una sorta di distinta base).

#!/usr/bin/env python2
# coding: utf-8

from decimal import Decimal
from pprint import pprint
from xlrd import open_workbook
import sys

#
# dizionario contenente la posizione di ogni colonna all'interno del file excel
# i nomi delle colonne sono definiti nella prima riga del foglio excel e devono
# essere identici ai nomi qui riportati. Eventuali colonne aggiuntive saranno
# semplicemente ignorate
#
XL_COLUMNS = {
    'PRODOTTO': -1,
    'DES PRODOTTO': -1,
    'TIPO': -1,
    'ARTICOLO': -1,
    'DES ITA': -1,
    'DES ENG': -1,
    'QTY': -1,
    'EUR ARTICOLO': -1
}

# --

def stringa( cell ):
"""
Ritorna il contenuto di una cella come stringa
"""
if cell.ctype == 2:
    return "%d" % cell.value
else:
    return cell.value.strip()

# --

def intero( cell ):
"""
Ritorna il contenuto di una cella come intero
Ritorna None se la cella non è numerica
"""
if cell.ctype == 2:
    return int( cell.value )
else:
    return None

# --

def decimale( cell ):
"""
Ritorna il contenuto di una cella come valore con 2 cifre decimali
Ritorna None se la cella non è numerica
"""
if cell.ctype == 2:
    return Decimal( str( cell.value ) ).quantize(Decimal('1.00'))
else:
    return None

# --

if __name__ == '__main__':
#
# recupero posizioni colonne
#
xl = open_workbook('listino.xls')

sheet = xl.sheet_by_index(0)

for col in range( sheet.ncols):
XL_COLUMNS[ stringa( sheet.cell(0,col) ) ] = col

if -1 in XL_COLUMNS.values():
    print "Intestazioni colonne mancanti alla riga 1:"
for k,v in XL_COLUMNS.items():
if v == -1:
    print k
sys.exit( 1 )

#
# recupero gerarchia prodotti/dotazioni/accessori/opzioni/servizi
#
products = {}
valid_article_codes = []

for row in range(1, sheet.nrows):
product_code = stringa( sheet.cell(row, XL_COLUMNS['PRODOTTO']) ).upper()
article_code = stringa( sheet.cell(row, XL_COLUMNS['ARTICOLO']) ).upper()
article_type = stringa( sheet.cell(row, XL_COLUMNS['TIPO']) )
descr_italian = stringa( sheet.cell(row, XL_COLUMNS['DES ITA']) )
descr_english = stringa( sheet.cell(row, XL_COLUMNS['DES ENG']) )
qty = intero( sheet.cell(row, XL_COLUMNS['QTY']) )
price = decimale( sheet.cell(row, XL_COLUMNS['EUR ARTICOLO']) )

valid_article_codes.append( article_code )

#
#
#
if not product_code and not article_type:
continue

if product_code not in products:
products[ product_code ] = {
    'accessories': [],
    'options': [],
    'services': [],
    'versions': [],
    'dotations': [],
    'targets': [],
    'ordinamento': row,
}

if product_code == article_code: # Prodotto Principale
products[ product_code ]['descr_italian'] = descr_italian
products[ product_code ]['descr_english'] = descr_english
products[ product_code ]['price'] = price

elif article_type == 'A': # Accessorio
products[ product_code ]['accessories'].append({
    'article_code': article_code,
    'descr_italian': descr_italian,
    'descr_english': descr_english,
    'price': price,
})

elif article_type == 'D': # Dotazione
if 'codice sconto' not in descr_italian.lower():
products[ product_code ]['dotations'].append({
    'article_code': article_code,
    'descr_italian': descr_italian,
    'descr_english': descr_english,
    'qty': qty,
})

elif article_type == 'O': # Opzione
products[ product_code ]['options'].append({
    'article_code': article_code,
    'descr_italian': descr_italian,
    'descr_english': descr_english,
    'price': price,
})

elif article_type == 'S': # Servizio
products[ product_code ]['services'].append({
    'article_code': article_code,
    'descr_italian': descr_italian,
    'descr_english': descr_english,
    'price': price,
})

elif article_type == 'V': # Versione
products[ product_code ]['versions'].append({
    'article_code': article_code,
    'descr_italian': descr_italian,
    'descr_english': descr_english,
    'price': price,
})

#
# stampa la struttura dati ottenuta dal file excel
#

pprint( products )

Fatto questo, il prossimo passaggio sarà quello relativo al recupero delle informazioni relative ai media da associare come per esempio documenti PDF e immagini.

Se vuoi aggiornamenti su WordPress e Excel: connessi grazie a Python e le API XML-RPC inserisci la tua e-mail nel box qui sotto:
 
X
Se vuoi aggiornamenti su WordPress e Excel: connessi grazie a Python e le API XML-RPC

inserisci la tua e-mail nel box qui sotto:

Ho letto e acconsento l'informativa sulla privacy

Acconsento al trattamento di cui al punto 3 dell'informativa sulla privacy