Robitex's Blog

Ideas in the web

Archivi Mensili: dicembre 2010

Leggere la directory corrente con lualatex


Certi compiti difficili…

In LaTeX si sa, certi compiti sono particolarmente difficili, altri, forse, impossibili. Uno di questi è inserire nel documento la directory corrente. Non che sia indispensabile, anzi, direi che leggere il path del file nel documento solo perché si pensa che un giorno possa essere utile per ritrovare il file, penso sia poco elegante. Comunque…

Risolvere con lualatex

Compilare un sorgente LaTeX con lualatex comporta solo lievi modifiche, e la possibilità di far eseguire codice Lua attraverso la nuova primitiva \directlua{}.
Di per se Lua non offre sofisticate librerie per l’accesso al disco, così occorrerebbe installare la Lua File system Library installabile in Ubuntu con il relativo pacchetto nel repository universe chiamato liblua5.1-filesystem0, ed in Windows con il programma LuaRocks.
Tuttavia LuaTeX viene compilata assieme alla libreria Lua Filesystem, così è possibile utilizzarla senza che si debba installare esplicitamente.

Le funzioni della libreria lfs sono riportate nel manuale disponibile on-line su questo sito, e sono tutte memorizzate nella tabella lfs. Per i nostri scopi utilizzeremo la funzione currentdir(), che restituisce il path della directory corrente, sempre che non ci siano errori.

Riporto il codice di un sorgente LaTeX da compilare con lualatex che conterrà il percorso dello stesso file sorgente, ma che funziona solo con Linux come spiegherò fra poco.

% funziona per Linux
\documentclass{minimal}

\newcommand{\curdir}{%
   \directlua{tex.sprint(lfs.currentdir())}
}

\begin{document}
\curdir
\end{document}

Il post non vuole altro che segnalare quanto compiti assai complicati e dispendiosi in termini di tempo per trovare una soluzione, siano semplici ed eleganti con lualatex, che si basa sul nuovo motore di composizione ancora in fase di sviluppo LuaTeX.

Ed è per questo che è stata riportata la soluzione per Linux, perché quella per Windows è leggermente più complicata: occorre sostituire nel percorso del file il carattere backslash perché se il percorso della directory corrente entra così com’è nel flusso di token, verrà interpretata come una serie di comando che produrranno un errore.

Nei file system di tipo Unix, il carattere che separa i nomi è la slash e non ci sono quindi problemi. In Window se per esempio il path della directory di lavoro è “C:\Documenti” corrisponderà ai caratteri C: seguiti dalla macro \Documenti, un bel inconveniente.

Ecco il codice per Windows (e che funziona perfettamente anche in Linux perché il path rimarrà immutato). Nel codice dovendo inserire il carattere di escape del motore di composizione se ne evita l’effetto utilizzando la macro primitiva string.

% funziona anche in Windows
\documentclass{minimal}

\newcommand{\curdir}{%
    \directlua{
    require "lfs"
    local cd = string.gsub(lfs.currentdir(),
              [[\string\]],
	      [[\string\textbackslash ]])
   tex.sprint(cd)}
}

\begin{document}
\curdir
\end{document}

Rovesciare il punto di vista

Il comando \curdir così come l’abbiamo definito ha un difetto di efficienza: ogni volta che lo eseguiamo, con la libreria lfs determina la directory corrente, ovvero qualcosa che in realtà conoscevamo dalla prima chiamata alla macro.
Se per esempio nel documento il percorso del file viene inserito a piè di pagina, avremo tante chiamate a lfs.currentdir() tante quante sono le pagine stesse, ma se rovesciassimo il punto di vista sfruttando proprio il problema del backslash?

Nel seguente codice, completo per la compilazione con lualatex, si esegue la funzione per determinare il path e poi si inserisce nel flusso dei token che saranno “digeriti” dal motore tipografico, la definizione di una macro che espande semplicemente al path stesso.
In questo modo, la funzione di libreria viene chiamata solo una volta, mentre nel documento l’autore si troverà una macro standard, espandibile ed efficiente.
Per prima cosa, ho racchiuso il codice Lua in un blocco do … end, per eliminare dalla memoria qualsiasi variabile, poi ho memorizzato per comodità in una variabile stringa chiamata bs il carattere di backslash.
La definizione di una macro che espande al path è: \def\curdir{valore del path}, così è sufficiente chiamare la funzione tex.sprint() con l’equivalente del comando, utilizzando l’operatore di concatenazione delle stringhe di Lua sintatticamente definito da due punti .. .

\documentclass{minimal}

\directlua{
    do
       require "lfs"
       local bs = "\string\\"
       local cd = string.gsub(lfs.currentdir(),
              bs,
	      bs.."textbackslash ")

       tex.sprint(bs.."def"..bs.."curdir{"..cd.."}")
    end
}

\begin{document}

\texttt{\curdir}

\emph{\curdir}

\curdir

\curdir

\curdir

\end{document}

Saluti ed Auguri di Buon Natale e Felice Anno nuovo!!!

Ottenere il PDF di un post di WordPress


Sarebbe utile se…

…ci fosse una maniera semplice e veloce per mettere a disposizione degli utenti del blog il file dell’articolo anche in formato PDF pronto per la stampa!

Che dire, con Lua questo ed altro!

Doverosa premessa

Il problema di convertire il testo tra formati diversi dovrebbe essere già risolto con i tool esistenti. In realtà non è proprio così semplice perché i due formati possono differire semanticamente.

Per esempio se il formato di partenza è html, e si vuol produrre il PDF con LaTeX per assicurarsi la massima qualità tipografica, allora alcune strutture possono non avere un diretto equivalente. Se poi nel sorgente html vi sono alcuni tag propri di WordPress come quelli per il codice che funzionano con Javascript le cose sono di difficile generalizzazione.

Codice

Con i tag specifici di WordPress e con le specifiche che di solito utilizzo, ho risolto scrivendo uno script in Lua che legge il file html, converte i tag in costrutti LaTeX, crea il file sorgente su disco ed infine lo compila con pdfLaTeX, con tanto di sommario cliccabile.

Uniche note di spiegazione che do del codice è l’uso massiccio della funzione string.gsub(), spesso in modo semplice, alcune volte invece con l’uso di funzioni anonime di sottoformattazione come il caso delle formule matematiche (renderizzate da WordPress con LaTeX stesso), e nel caso di inserimento di immagini.

#!/usr/bin/lua

-- 2010 12 15
-- trasforma un file di WordPress dal formato html
-- al formato pdf attraverso LaTeX
--
-----------------------
-- functions section --
-----------------------
local function readFile( fn )
    local f = assert(io.open(fn , "r"))
    return f:read("*all")
end

local function getFilename( s )
   if not string.find(s, "%.") then
       return s
   else
      return string.match(s, "(.*)%.[^%.]-$")
   end
end

local function html2latex( s )
     local result = string.gsub(s, "%[sourcecode%]", "\\begin{lstlisting}")
     result = string.gsub(result, "%[/sourcecode%]", "\\end{lstlisting}")
     result = string.gsub(result, "<h4>(.-)</h4>", "\\subsection{%1}")
     result = string.gsub(result, "<h3>(.-)</h3>", "\\section{%1}")
     result = string.gsub(result, "<h2>(.-)</h2>", "\\section{%1}")
     result = string.gsub(result, "<strong>(.-)</strong>",
                 function (s)
                     return "\\textbf{" .. string.gsub(s, "_" , "\\_") .."}"
                 end
                 )
     result = string.gsub(result, "<em>(.-)</em>", "\\emph{%1}")
     result = string.gsub(result, "%$latex%s+(.-)%$",
                function (s)
                    local s1 = string.match(s,"^\\displaystyle(.*)")
                    if s1 then
                        return "\\[\n" .. s1 .."\n\\]"
                    else
                        return "\\(" .. s .. "\\)"
                    end
                end
               )
     
     -- url substitution     
     result = string.gsub(result,
                          "<a%s+href%s*=%s*[\'\"](.-)[\'\"]>(.-)</a>",
                          "\\href{%1}{%2}")
     result = string.gsub(result,
              "%[caption%s+.-caption%s*=%s*[\'\"](.-)[\'\"].-src%s*=%s*[\'\"](.-)[\'\"].-%/caption%]",
              function (cp,uurrll) return "\\begin{figure}\n\\centering\n"..
                                          "\\includegraphics{" .. string.match(uurrll, "[^/]-$") .. }\n" ..
                                          "\\caption{"..cp.."}\n\\end{figure}\n" end
                                          )
     return result
end

-- create the LaTeX source file
local function makeSource( fn , s )
    -- apertura file
    local f = assert(io.open( fn  .. ".tex", "w"))
    -- scrittura dati
   f:write(s)
   -- chiusura del file
   f:close()
end
-- compile file by name without extension
local function makePDF( fn )
      print("Start pdfLaTeX twin runs...")
      os.execute("pdflatex " .. fn)
       os.execute("pdflatex " .. fn)
       os.remove(fn..".log")
       os.remove(fn..".aux")
       print("End")
end
------------------------------
-- constant strings section --
------------------------------

local preambleDoc = [[
\documentclass[a4paper,10pt]{article}

\usepackage[utf8]{inputenc}
\usepackage[italian]{babel}
\usepackage[T1]{fontenc}
\usepackage[margin=18mm]{geometry}
\usepackage{indentfirst}
\usepackage{microtype}

\usepackage{listings}
\usepackage{xcolor}
\usepackage{inconsolata}
\usepackage{graphicx}
\usepackage[colorlinks=true,linkcolor=blue]{hyperref}

\lstset{%
   backgroundcolor=\color{yellow!20},
   basicstyle=\small\ttfamily,
   framexleftmargin=5pt
}
]]

local openingDoc = [[
% opening
\title{}
\author{The Blogger\\blogger dot email dot mailbox at ecc dot com}
]]

local startDoc = [[
\begin{document}

\maketitle

\begin{abstract}
Articolo dal blog di WordPress
\end{abstract}

\vfill
\tableofcontents
\vfill

\newpage
]]

local endDoc = [[
\end{document}
]]

--------------------------
-- main execution chunk --
--------------------------
local filename = arg[1]

if not filename then
    print("Errore: file d'ingresso non specificato come argomento")
    os.exit()
end

local fname = getFilename(filename)

local contentLaTeX = {}
contentLaTeX[#contentLaTeX+1] = preambleDoc
contentLaTeX[#contentLaTeX+1] = openingDoc
contentLaTeX[#contentLaTeX+1] = startDoc
contentLaTeX[#contentLaTeX+1] = html2latex( readFile( filename ))
contentLaTeX[#contentLaTeX+1] = endDoc

makeSource( fname , table.concat(contentLaTeX) )
makePDF( fname )

-- end of file

Come è ovvio il sorgente LaTeX viene mantenuto nella stessa directory del file html per consentire di effettuare degli aggiustamenti di finezza che solo gli utenti LaTeX sanno percepire 🙂

Bè le ciambelle non sempre vengono col buco… e per applicare lo script a questo stesso articolo ho dovuto intervenire manualmente perché il codice sostituisce anche i tag nel sorgente Lua (cosa da non fare evidentemente).

Poiché capiterà solo per questo post, non correggerò lo script lasciando a voi di proporre la giusta patch.
Scarica questo post nel formato PDF ottenuto con lo script descritto in questo stesso post.
Ciao ed alla prossima.

Assegnare le coordinate x y o z ai nodi in Nolian


Nolian scripting series, episode 2

Prima che finisca questo video che spesso mi accompagna nel pensare a mio padre, e prima che venga Natale, vi posto una versione evoluta del codice della puntata precedente alias episode 1.

Un paio di modifiche…

Per prima cosa ho ampliato la funzione al caso in cui l’utente non specifichi la coordinata da applicare, desumendola da un nodo campione selezionato dall’utente stesso.

Possiamo quindi lanciare il comando az(), cliccare su un nodo che si trova già alla quota desiderata (coordinata z), e selezionare i nodi destinazione. Oppure possiamo specificare il valore di zeta, sempre tra parentesi tonde, e applicare la modifica selezionando direttamente i nodi sul modello.

La seconda modifica è a livello implementativo per la generalizzazione della funzionalità: ho scritto una metafunzione che ho chiamato assegnaCoordNodi che accetta come primo argomento una stringa relativa al nome dell’asse coordinato “x”, “y” o “z”. In questo modo è immediato scrivere le funzioni utente ax(), ay(), ed az(), tutte con la stessa sintassi e con lo stesso comportamento.

Adesso potete assegnare ad un qualsiasi gruppo di nodi del modello in Nolian una coordinata x, y o z. Ecco il codice:

-- libreria delle funzioni personali in Nolian
--
-- Copyright (c) 2010 Roberto Giacomelli
-- email: giaconet dot mailbox at gmail dot com
--
-- 2010/12/15
-- released under the term of https://robitex.wordpress.com/legalese

-- meta function
-- Assegna ai nodi selezionati una stessa coordinata
function assegnaCoordNodi( coord , val )
   if not coord then
       print ("Errore di libreria: asse coordinato non specificato.")
       return
   elseif not(coord ~= "x" or  coord ~= "y" or coord ~= "z") then
       print ("Errore di libreria: asse "..coord .." non riconosciuto.")
       print("Deve essere o 'x' o 'y' o 'z'")
       return
   end
   
   if not val then
      print("Selezionare il nodo con la "..coord.." desiderata...")
      _sel.doselect( true )
      local nodobase = _sel.nodes()
      _sel.clear()
      if #nodobase > 1 then
         print ("Selezionati più nodi, riprova.")
         return
      else
         val = _n.get(nodobase[1])[coord]
      end
   end
   
   print("Selezionare nodi da cambiare con " .. coord .."="..val .." ...")
   
   _sel.doselect( true )
   nodi = _sel.nodes()
   _sel.clear()
   
   local pointAux = {}
   for i, nodeIndex in ipairs( nodi ) do
       pointAux[coord] = val
       _n.set(nodeIndex, pointAux)
   end
   
   print( #nodi .." nodi modificati con coord. " .. coord .. "=" ..val)
end


function ax(x)
   assegnaCoordNodi("x", x)
end

function ay(y)
   assegnaCoordNodi("y", y)
end

function az(z)
   assegnaCoordNodi("z", z)
end

Un uso con variabili

Per come è costruita Lua, nella console (nella finestra di comando dello scripting interno di Nolian che appare con CTRL + T), abbiamo a disposizione un ambiente interattivo completo. Ciò significa che possiamo settare variabili ed utilizzare espressioni numeriche come argomento delle funzioni ax, ay, az. Per esempio questa sessione in console è valida:

> z1 = 145.5
> z2 = 258.5
> az ( (z1+z2)/2 )
> az (2/3*(z1+z2))

Le variabili rimangono disponibili fino all’uscita da Nolian. Possiamo perfino pre-caricare valori costanti inserendoli nel file mylib.lua, file dove abbiamo deciso di custodire i nostri script. Sempre più fantastico vero?

So,
Dedicated to my father.

Assegnare coordinate ai nodi in Nolian


Nolian scripting series, episode 1

Scarica l’articolo in PDF per la stampa

Benvenuti,
da qualche versione a questa parte Nolian permette l’esecuzione di comandi personalizzati. Questa funzionalità è chiamata dalla Softing scripting interno.
Questo post è il primo di una spero lunga e proficua serie dedicata a potenziare le nostre capacità di modellazione agli elementi finiti in Nolian. Lasciate un commento per qualsiasi cosa.

Il primo problema che affrontiamo è questo: assegnare una stessa coordinata ad un gruppo di nodi. Ci dilungheremo solo per questa volta sulle questioni pratiche per poi risolvere brillantemente ciò che con le funzioni standard risulta noioso.

Questioni implementative

Con il roboante titolo di questo paragrafo, intendo spiegare come far eseguire un nostro comando con riferimento alla versione EWS32 di Nolian.
La prima e piacevole cosa da sapere è che il motore di scripting interno funziona con Lua. La bellissima notizia è motivata dal fatto che Lua è un linguaggio elegante e potente, e con esso è possibile non solo gestire ogni aspetto del nostro modello ma anche ideare comandi con una sintassi flessibile, in puro stile hacker.

The Nolian scripting Console

The Nolian scripting Console

1 – Avviamo dunque una sessione di Nolian e premiamo la combinazione di tasti CTRL + T per avviare la console di scripting.
La console offre una riga di comando ed un pannello testuale per visualizzare l’output (vedi screenshot). D’ora in poi quando diremo console ci riferiremo ad essa.

2 – Operazione matematica
Al prompt della console che non è altro che un ambiente di comando Lua, per prima cosa eseguiamo un calcolo digitando:

> 2*math.pi*100

Il risultato è prontamente stampato nella finestra di output.

3 – Creazione di una prima funzione
Metti che non ci ricordiamo l’area di una barra d’acciaio, allora al prompt digitiamo riga dopo riga il seguente codice:

> function area( d )
>> return math.pi * d^2 / 4
>> end
> area (1.4)
> area(1.2)
> area(1.6)

Da questo momento fino a che non viene definitivamente chiusa la sessione di Nolian, la funzione area() sarà a disposizione.

Il nostro primo vero script

Creiamo una nuova directory nella cartella d’installazione di Nolian chiamata lua, dove sistemeremo i file di testo dei nostri script, quindi aprite un editor (vi consiglio l’ottimo e gratuito SciTE), scrivete il codice print(“Ciao, questo è il mio primo script!”) e salvate il file con il nome primo.lua nella directory appena creata.
Eseguite lo script dando in console il comando dofile(“lua/primo.lua”).

Caricando in questo modo un file, tutte le funzioni in esso contenute vengono compilate e caricate nell’ambiente di scripting interno. Useremo questa tecnica per scrivere un file di nome mylib.lua, nella solita directory lua, caricando il quale avremo a disposizione le nostre funzioni personalizzate.

Ai nodi un ordinata

La funzione per assegnare un dato valore per la coordinata z dei nodi selezionati, si chiamerà az (assegna zeta). La selezione di un gruppo di nodi si implementa con la funzione _sel.doselect() memorizzata nella tabella _sel.
Poi dovremo gestire i nodi selezionati tramite un array contenente gli indici degli stessi:

_sel.doselect( true )
nodi = _sel.nodes()
_sel.clear()

Per ogni nodo dovremo poi modificare la coordinata zeta con la funzione _n.set(nodeIndex, pointAux) che si aspetta come primo argomento l’indice del nodo e come secondo argomento un punto, ovvero una tabella (l’unica struttura dati messa a disposizione da Lua), contenente la chiave z settata al valore voluto.

Queste funzioni, che cominciano un po’ discutibilmente con il carattere di trattino basso, sono quelle che ci permettono di interagire con il modello attualmente caricato nella sessione di Nolian.
Ecco il codice completo della funzione:

-- libreria delle funzioni personali in Nolian
--
-- Copyright (c) 2010 Roberto Giacomelli
-- email: giaconet dot mailbox at gmail dot com
--
-- 2010/12/15
-- released under the term of https://robitex.wordpress.com/legalese

-- Assegna ai nodi selezionati una stessa coordinata zeta
function az( z )
   if not z then
      print"Coordinata non specificata!"
      return
   end

   _sel.doselect( true )
   nodi = _sel.nodes()
   _sel.clear()
   
   local pointAux = {}
   for i, nodeIndex in ipairs( nodi ) do
       pointAux.z = z
       _n.set(nodeIndex, pointAux)
   end
end

Il suo uso è semplice: carichiamo la funzione memorizzata nel file individuato precedentemente, con dofile(“lua/mylib.lua”) in console. Poi diamo il comando seguente ed eseguiamo la selezione. I nodi prenderanno la quota specificata.

> az( 1203 )

Conclusioni

Uno script Lua non è altro che un file di testo. Caricando nostre librerie, possiamo accedere a funzioni che abbreviano senza errori e senza fatica l’inserimento dei dati del modello in Nolian.

Bibliografia

Acquistate il PiL e aiuterete lo sviluppo di Lua!!! Pure utile è la guida in linea di Nolian alla sezione dedicata.
The End.

Gli editor che utilizzo


Mi sono accorto che sul pc dell’ufficio ho installato nel tempo un numero consistente di editor, ovvero di quei programmi che permettono di editare file di testo in cui, sostanzialmente, ogni byte corrisponde ad un carattere secondo una codifica.

I file di testo sono essenziali in molte situazioni, dai file di configurazione di sistema, ai file sorgenti dei programmi, dalle pagine web (il formato html è un formato testuale), allo scambio dei dati tra sistemi diversi (database e backup in particolare).

E vediamoli questi editor

SciTE: uno dei migliori è SciTE, Scintilla Text Editor, ed il momento in cui lo installai per la prima volta si perde nella notte dei tempi.

Notepad++: adatto per scrivere codice praticamente in qualsiasi linguaggio di programmazione esistente

PSPad: editor micidiale per leggere file difficili, PSPad è veramente un duro.

LeD: quest’editor aiuta nella stesura di sorgenti LaTeX. Per ora lo tengo ma non lo uso praticamente più a favore di editor più snelli e forse più stabili.

TeXMaker: se devo comporre un sorgente LaTeX d’impegno intermedio uso TeXMaker, o il suo fratellastro TeXMakerX. Sono due coltellini svizzeri giusti per compiti medi.

TeXWorks: progettato per abbassare la soglia d’ingresso al mondo TeX, questo editor è utilissimo nella correzione finale delle bozze di documenti importanti redatti con LaTeX oppure con ConTeXt. Consente infatti la ricerca diretta ed inversa tra pdf e sorgente, rendendo immediate le modifiche di finezza.

Insomma, sul mio sistema Windows sono installati ben sette diversi editor, mentre sulla mia Linux box?

Be’ Linux, come tutti gli altri sistemi derivati da Unix, è la patria degli editor. Magari contare quelli che uso su Ubuntu sarà oggetto di un altro post e non mancherebbero le sorprese…

Alla prossima. Ciao.

Reinventare l’inventario


Articolo in formato PDF per la stampa

In cosa consiste un inventario?

Moltissimi esercizi commerciali alla fine dell’anno redigono l’inventario del magazzino. Si tratta di un elenco di oggetti dei quali se ne riportano le quantità rilevate ed il costo di acquisto dal fornitore.
Normalmente il report dell’inventario viene strutturato per reparti o per fornitori, oppure ancora per classi di articolo, riportando i relativi totali.
In generale un lavoro piuttosto noioso se non è supportato da software opportuni.

Una soluzione con il software libero

Nella maggior parte dei casi non si dispone di programmi di reporting, così ci si arrangia con un foglio di calcolo come OpenOffice.org Calc, utilizzando funzioni di subtotale per ottenere le somme parziali.
Per evitare errori e noiosi interventi manuali, in puro stile hacker, scriveremo un programma che costruisce un file e lo compila con LaTeX per produrre il documento relativo all’inventario nel formato pdf.

Inventario con Lua più LaTeX

Il programma elaborerà i dati in un formato di testo puro e LaTeX assumerà il ruolo di motore di reporting.
I programmi necessari alla soluzione presentata sono disponibili per un gran numero di sistemi operativi, solo che dovrete pensarci da soli ad installarli.

Ho scelto di scrivere il programma in Lua (versione 5.1), perché è un linguaggio di scripting che non necessita di compilazione, semplice ma potente, nato proprio con l’obbiettivo di svolgere elaborazioni di data description.
Per produrre il PDF ho scelto LaTeX perché elabora file di puro testo per fornire un output tipograficamente perfetto 🙂
Occorre quindi che sia Lua 5.1 sia una distribuzione TeX (per esempio TeX Live) siano installati sul vostro sistema.

Formato dei dati d’inventario

Le caratteristiche degli articoli del nostro magazzino sono peculiari. Per fissare le idee stabiliremo la seguente serie di campi abbastanza comuni tanto da consentire facilmente estensioni:

  • codice: identificativo univoco articolo
  • descrizione: breve testo descrittivo
  • fornitore: codice univoco del fornitore
  • reparto: codice univoco del reparto
  • costo: costo di acquisto di un pezzo
  • quantità: pezzi rilevati a magazzino

Caliamo direttamente questa struttura in un formato auto-descrittivo definito da un lista di elementi item simili al seguente, dove i dati sono racchiusi tra parentesi graffe e dove l’identazione ed i ritorni a capo sono opzionali (nella tastiera italiana le graffe si ottengono con le combinazioni Alt Gr + Shift + [ e Alt Gr + Shift + ] ):

item{code="C123",
     descr="NOME MODELLO - Classe",
     supplier="MYSUP",
     department="MYDEP",
     cost=123.45,
     qty=1,
     }

Come è evidente nell’esempio, i valori testuali sono racchiusi tra doppi apici mentre i valori numerici sono rappresentati con il punto decimale (non possiamo usare la virgola, simbolo già previsto dalla sintassi per la separazione dei campi (a proposito l’ultima virgola è opzionale)).
Possiamo anche inserire liberamente caratteri spazio, tranne che all’interno dei valori di tipo testo perché ovviamente diventano parte del valore stesso, così i due codici “C123″ e ” C123″ o “C 123” individuano due diversi articoli!

Nel formato key=value le parole chiavi sono sempre le stesse, così un blocco di dati potrebbe apparire come segue:

item{code="W1234", descr="W1234 - Quaderno", supplier="LOL", department="CRT", cost=   12, qty= 8}
item{code="Z4321", descr="Z4321 - Penna",    supplier="LOL", department="CRT", cost= 5.20, qty= 1}
item{code="Z5521", descr="Z5521 - Gomma",    supplier="LOL", department="CRT", cost= 2.80, qty=10}
item{code="Q0021", descr="Q0021 - Matita",   supplier="LOL", department="CRT", cost= 1.20, qty=80}
item{code="Q9921", descr="Q9921 - Penna",    supplier="OLO", department="CRT", cost=15.20, qty= 8}
...

Questo miscuglio di formato e sintassi è già interessante di per se e, considerando che le parentesi tonde che racchiudono gli argomenti di una funzione in Lua possono essere omesse se l’argomento è un unica tabella (od anche una stringa letterale), diventa anche codice Lua perfettamente eseguibile…

Le tabelle di Lua

In Lua esiste solo una struttura dati complessa con cui si implementano gli array, le liste e le strutture ad albero, chiamata tabella. Il linguaggio prevede che per creare un oggetto tabella si debba usare un costruttore che nella forma più semplice è {}.
Ciascun elemento di una tabella viene restituito specificando la chiave tra parentesi quadre od in dot notation come nel seguente esempio:

-- nb: il doppio trattino indica un commento
-- per prima cosa creiamo una nuova tabella e ne assegnamo
-- il riferimento ad una variabile locale
local mytab = {}

mytab["primo"] = 1
mytab["secondo"] = 2

-- dot notation:
print(mytab.primo)   --> stampa 1

mytab.terzo = mytab.primo + mytab.secondo

print(mytab.terzo)   --> stampa 3

local sec = "secondo"
print(mytab[sec])    --> stampa 2

Introdotte anche solo così velocemente le tabelle, si può intravedere il carattere del linguaggio Lua, semplice essenziale ma anche piuttosto elegante e potente.
Il formato dati ideato precedentemente rappresenta in Lua una serie di chiamate alla funzione item alla quale è passato come argomento il record dei dati dell’articolo d’inventario sotto forma di tabella.

Esercizio

Per prendere confidenza con le tabelle di Lua scriviamo una versione della funzione item perché restituisca il totale di magazzino in termini di costo, quantità e numero di articoli.

Intanto alcune note pratiche: apriamo il nostro editor di testo preferito (per Windows per esempio il Blocco Note), ed incolliamo nella relativa finestra i dati di esempio precedenti o quelli generati da un vostro foglio di calcolo. Salviamo il file con il nome di “inv.txt”.
Creiamo poi un secondo file chiamato “count.lua” in cui avremo inserito il codice seguente:

-- tabella codici articoli
local codeart = {}

-- variabili di conteggio
local totalcost = 0
local totalqty = 0
local numart = 0

function item(record)
    -- memorizzo il codice articolo in tabella
    -- in questo modo conteggio i codici
    -- non le chiamate ad item
    if not codeart[record.code] then
       codeart[record.code] = true
       numart = numart + 1
    end

    totalcost = totalcost + record.cost*record.qty
    totalqty = totalqty + record.qty
end

dofile("inv.txt")

print("Totali articoli: "..numart)
print("Costo totale: "..totalcost)
print("Quantita totale pezzi: "..totalqty)

A questo punto per stampare il conteggio d’inventario lanciamo da console il comando:

lua count.lua

Costruire l’albero ovvero mettere tabella dentro tabella

Ammettiamo che il magazzino sia suddiviso in reparti e che a sua volta i reparti siano suddivisi per fornitore, a cui apparterranno i relativi articoli. Questa struttura è un albero, e se rammentate come sono organizzati i vostri file nel disco rigido ne avrete un secondo esempio assai famigliare.
Bene, scriveremo la nostra funzione item in modo che costruisca l’albero corrispondente all’inventario di magazzino utilizzando una tabella che conterrà le tabelle dei reparti ciascuno contenente le tabelle dei fornitori, che ancora conterranno le tabelle degli articoli, traducendo esattamente la struttura con cui abbiamo deciso di organizzare il magazzino.

Presa confidenza con la struttura a livelli il codice non è ne difficile ne lungo:

-- nuovo oggetto tabella d'inventario
local store = {}

-- definizione della funzione item:
-- main data processing function
function item(record)
    -- livello principale: reparti
    -- creo per comodità una variabile che contiene l'ID
    -- del reparto
    local dep = record.department

    -- creo la tabella relativa al reparto se ancora non esiste
    if not store[dep] then
        store[dep] = {}
    end

    -- livello fornitore
    -- variabile di comodo che contiene il codice del fornitore
    local sup = record.supplier
    -- var di comodo per il riferimento alla tabella di reparto
    local tabdep = store[dep]

    -- se non esiste la tabella fornitore la creo
    if not tabdep[sup] then
        tabdep[sup] = {}
    end

    -- livello articolo
    -- vars di comodo alla tabella fornitore ed al codice articolo
    local tabsup = tabdep[sup]
    local codeart = record.code

    -- se il codice esiste aggiorniamo per quantità
    -- altrimenti creo nuova tabella articolo dove memorizzo
    -- la descrizione, il costo e la quantità
    if tabsup[codeart] then
        tabsup[codeart].qty = tabsup[codeart].qty + record.qty
    else
        tabsup[codeart] = {}
        local tabart = tabsup[codeart]
        tabart.descr = record.descr
        tabart.cost = record.cost
        tabart.qty = record.qty
    end
end

Raccolta dati

I vantaggi della struttura ad albero creata dalla funzione item sono due: oltre a classificare per livelli gli articoli è possibile inserire lo stesso articolo con successive chiamate alla funzione. Durante la raccolta dati infatti capita che pezzi diversi dello stesso articolo vengano registrati in tempi diversi.

La raccolta dati può essere effettuata connettendo un lettore di codici a barre ad un notebook e gestendo i dati con un foglio elettronico o meglio con un database. In questo modo l’inventario procede molto velocemente e senza fatica, a condizione che sugli articoli sia presente il codice a barre, tramite un etichetta per esempio, e che siano già stati inseriti i dati corrispondenti ai codici.

Visitare l’albero

La seconda parte del problema è generare il report. Occorre infatti visitare l’albero per trarne i totali dei vari livelli e produrre con essi il file sorgente LaTeX.
Per ragioni di tempo, terremo semplice il codice lasciando irrisolti alcuni problemi come un migliore utilizzo della potenza compositiva di LaTeX e l’ordinamento in ordine alfabetico delle categorie e classi del nostro magazzino.
Useremo per semplicità l’ambiente verbatim con impaginazione su due colonne, trascrivendo per ciascun articolo la descrizione, la quantità, il costo, ed il valore totale.

Chiamiamo renderTree la funzione Lua che visita l’albero iterativamente e scrive le righe in una tabella che, per motivi di performance, verrà trascritta nel file sorgente tutta in una volta. Useremo l’iteratore pairs per leggere i valori nelle tabelle dei vari livelli, iteratore che restituisce due argomenti la chiave ed il suo valore nella tabella indicata.

-- tabella per memorizzare le righe di output
local invLines = {}

-- larghezze in caratteri delle singole colonne
local descrCol = 25
local qtyCol = 8
local costCol = 10

-- formatta un intero
local function formatInt(n)
   local nn = string.format("%d",n)
   return string.rep(" ", qtyCol-#nn) .. nn
end

-- formatta un importo
local function formatDec( x )
   local xx = string.gsub(string.format("%.2f", x),"%." , ",")
   return string.rep(" ", costCol-#xx) .. xx
end

-- funzione di comodo per aggiungere elementi riga
local function addRow( s )
   invLines[#invLines+1] = s
end

-- funzione di formattazione riga articolo
-- .. è l'operatore di concatenazione stringhe
local function addItem(d, q, c)
   -- d descrizione
   -- q quantità
   -- c costo di un unico pezzo

   if #d > descrCol then
      d = string.sub(d,1,descrCol)
   else
      d = d .. string.rep(" ", descrCol-#d)
   end

   addRow(d.." "..
         formatInt(q).." "..
	 formatDec(c).." "..
	 formatDec(q*c)
	 )
end

-- funzione di formattazione riga totale
local function addTot( q, c )
    local d = "Tot." .. string.rep(" ", descrCol-4)
    addRow(d .. " " ..
           formatInt(q) .. " " ..
	   string.rep(" ", costCol+1)..
	   formatDec(c)
	   )
end

-- funzione principale creazione "vista"
local function renderTree()
   -- contatori generali
   local totstoreCost = 0
   local totstoreQty = 0

   local rule = string.rep("-",descrCol+qtyCol+2*costCol+3)

   -- ciclo principale per ciascun reparto
   for keydep, dep in pairs(store) do
      -- scriviamo l'intestazione di reparto
      addRow(rule)
      addRow("Reparto: " .. keydep)
      addRow(rule)
      addRow("")

      -- contatori di reparto
      local totdepCost = 0
      local totdepQty = 0

      -- ciclo per ogni fornitore del reparto
      for keysup, sup in pairs( dep ) do
          -- scriviamo una riga d'intestazione per il fornitore
          addRow("Fornitore :" .. keysup)
	  addRow(rule)

          -- contatori del fornitore
          local totsupCost = 0
          local totsupQty = 0

          -- ciclo sugli articoli del fornitore
          for keycode, code in pairs( sup ) do
	      addItem(code.descr, code.qty, code.cost)
              totsupCost = totsupCost + code.qty*code.cost
              totsupQty = totsupQty + code.qty
          end
          -- chiudo il fornitore scrivendone i totali
          addRow(rule)
          addTot(totsupQty, totsupCost)
          addRow(rule)
	  addRow("")

          -- aggiorno i totali di reparto
          totdepCost = totdepCost + totsupCost
          totdepQty = totdepQty + totsupQty
      end
      -- chiudo il reparto scrivendone i totali
      addRow(rule)
      addRow("Fine reparto")
      addTot(totdepQty, totdepCost)
      addRow(rule)
      addRow("")

      -- aggiorno i totali d'inventario
      totstoreCost = totstoreCost + totdepCost
      totstoreQty = totstoreQty + totdepQty
   end

   -- scrivo i totali di magazzino
   addRow(rule)
   addTot(totstoreQty, totstoreCost)
   addRow(rule)
end

Creare il file sorgente

Per questo basterà creare delle stringhe opportune che contengano le necessarie istruzioni LaTeX di preambolo e di chiusura racchiudendo il testo tra doppie parentesi quadre:

local docPreamble = [[
%
% this file was created by makeinv.lua script
% Copyright (c) 2010 Roberto Giacomelli
%

\documentclass[a4paper,11pt]{report}

\usepackage[utf8]{inputenc}
\usepackage[T1]{fontenc}
\usepackage[italian]{babel}

\usepackage[margin=1.6cm,bottom=2.5cm]{geometry}
\usepackage{inconsolata}

%
%
%
\begin{document}
\twocolumn
\scriptsize
\sffamily

\begin{verbatim}]]

local docEnd = [[
\end{verbatim}
\end{document}
]]

In questo modo il sorgente LaTeX è incastonato nel sorgente dello script. Naturalmente esiste la possibilità di prelevare il codice necessario da file esterni in modo da rendere il tutto più facilmente gestibile.
Poi sarà possibile costruire il file chiamato inventary.tex con il seguente codice:

local filename = "inventary"

-- create the LaTeX source file
local function makesource( fn )
	-- apertura file
	local f = assert(io.open( fn  .. ".tex", "w"))

	-- data assembly
	local s = table.concat(invLines,"/n")

	-- scrittura dati
	f:write(s)

	-- chiusura del file
	f:close()
end

Compilare il file con LaTeX

Certo, occorre una libreria in grado di interagire con il sistema operativo ed in Lua questa libreria si chiama os. Ecco il codice:

local function makepdf( fn )
       print("Start pdfLaTeX run...")
       os.execute("pdflatex " .. fn)
       os.remove(fn..".log")
       os.remove(fn..".aux")
       print("End")
end

Codice finale

Il codice finale si occupa di lanciare le funzioni per l’esecuzione del lavoro, eccolo:

-- execute all the item functions in the source datafile
dofile("inv.txt")

-- setup the start of the source LaTeX file
addRow(docPreamble)

-- make the pdf file of inventary
renderTree()

-- setup the end of the source LaTeX file
addRow(docEnd)

makesource( filename )
makepdf( filename )

-- end of script file

Lo script per intero

Assemblando le varie funzioni scritte il codice completo dello script è il seguente:

-- makeinv.lua
-- a script to make an invetary
-- Copyright (c) 2010 Roberto Giacomelli
-- see the page robitex.wordpress.com/legalese/ for licence details
-- enjoy

-- nuovo oggetto tabella d'inventario
local store = {}

-- definizione della funzione item:
-- main data processing function
function item(record)
    -- livello principale: reparti
    -- creo per comodità una variabile che contiene l'ID
    -- del reparto
    local dep = record.department

    -- creo la tabella relativa al reparto se ancora non esiste
    if not store[dep] then
        store[dep] = {}
    end

    -- livello fornitore
    -- variabile di comodo che contiene il codice del fornitore
    local sup = record.supplier
    -- var di comodo per il riferimento alla tabella di reparto
    local tabdep = store[dep]

    -- se non esiste la tabella fornitore la creo
    if not tabdep[sup] then
        tabdep[sup] = {}
    end

    -- livello articolo
    -- vars di comodo alla tabella fornitore ed al codice articolo
    local tabsup = tabdep[sup]
    local codeart = record.code

    -- se il codice esiste aggiorniamo per quantità
    -- altrimenti creo nuova tabella articolo dove memorizzo
    -- la descrizione, il costo e la quantità
    if tabsup[codeart] then
        tabsup[codeart].qty = tabsup[codeart].qty + record.qty
    else
        tabsup[codeart] = {}
        local tabart = tabsup[codeart]
        tabart.descr = record.descr
        tabart.cost = record.cost
        tabart.qty = record.qty
    end
end

-- tabella per memorizzare le righe di output
local invLines = {}

-- larghezze in caratteri delle singole colonne
local descrCol = 25
local qtyCol = 8
local costCol = 10

-- formatta un intero
local function formatInt(n)
   local nn = string.format("%d",n)
   return string.rep(" ", qtyCol-#nn) .. nn
end

-- formatta un importo
local function formatDec( x )
   local xx = string.gsub(string.format("%.2f", x),"%." , ",")
   return string.rep(" ", costCol-#xx) .. xx
end

-- funzione di comodo per aggiungere elementi riga
local function addRow( s )
   invLines[#invLines+1] = s
end

-- funzione di formattazione riga articolo
-- .. è l'operatore di concatenazione stringhe
local function addItem(d, q, c)
   -- d descrizione
   -- q quantità
   -- c costo di un unico pezzo

   if #d > descrCol then
      d = string.sub(d,1,descrCol)
   else
      d = d .. string.rep(" ", descrCol-#d)
   end

   addRow(d.." "..
         formatInt(q).." "..
	 formatDec(c).." "..
	 formatDec(q*c)
	 )
end

-- funzione di formattazione riga totale
local function addTot( q, c )
    local d = "Tot." .. string.rep(" ", descrCol-4)
    addRow(d .. " " ..
           formatInt(q) .. " " ..
	   string.rep(" ", costCol+1) ..
	   formatDec(c)
	   )
end

-- funzione principale creazione "vista"
local function renderTree()
   -- contatori generali
   local totstoreCost = 0
   local totstoreQty = 0

   local rule = string.rep("-",descrCol+qtyCol+2*costCol+3)

   -- ciclo principale per ciascun reparto
   for keydep, dep in pairs(store) do
      -- scriviamo l'intestazione di reparto
      addRow(rule)
      addRow("Reparto: " .. keydep)
      addRow(rule)
      addRow("")

      -- contatori di reparto
      local totdepCost = 0
      local totdepQty = 0

      -- ciclo per ogni fornitore del reparto
      for keysup, sup in pairs( dep ) do
          -- scriviamo una riga d'intestazione per il fornitore
          addRow("Fornitore: " .. keysup)
	  addRow(rule)

          -- contatori del fornitore
          local totsupCost = 0
          local totsupQty = 0

          -- ciclo sugli articoli del fornitore
          for keycode, code in pairs( sup ) do
	      addItem(code.descr, code.qty, code.cost)
              totsupCost = totsupCost + code.qty*code.cost
              totsupQty = totsupQty + code.qty
          end
          -- chiudo il fornitore scrivendone i totali
          addRow(rule)
          addTot(totsupQty, totsupCost)
          addRow(rule)
	  addRow("")

          -- aggiorno i totali di reparto
          totdepCost = totdepCost + totsupCost
          totdepQty = totdepQty + totsupQty
      end
      -- chiudo il reparto scrivendone i totali
      addRow(rule)
      addRow("Fine reparto")
      addTot(totdepQty, totdepCost)
      addRow(rule)
      addRow("")

      -- aggiorno i totali d'inventario
      totstoreCost = totstoreCost + totdepCost
      totstoreQty = totstoreQty + totdepQty
   end

   -- scrivo i totali di magazzino
   addRow(rule)
   addTot(totstoreQty, totstoreCost)
   addRow(rule)
end

local docPreamble = [[
%
% this file was created by makeinv.lua script
% Copyright (c) 2010 Roberto Giacomelli
%

\documentclass[a4paper,11pt]{report}

\usepackage[utf8]{inputenc}
\usepackage[T1]{fontenc}
\usepackage[italian]{babel}

\usepackage[margin=1.6cm,bottom=2.5cm]{geometry}
\usepackage{inconsolata}

%
%
%
\begin{document}
\twocolumn
\scriptsize
\sffamily

\begin{verbatim}
]]

local docEnd = [[
\end{verbatim}
\end{document}
]]

local filename = "inventary"

-- create the LaTeX source file
local function makesource( fn )
	-- apertura file
	local f = assert(io.open( fn  .. ".tex", "w"))

	-- data assembly
	local s = table.concat(invLines,"\n")

	-- scrittura dati
	f:write(s)

	-- chiusura del file
	f:close()
end

local function makepdf( fn )
       print("Start pdfLaTeX run...")
       os.execute("pdflatex " .. fn)
       os.remove(fn..".log")
       os.remove(fn..".aux")
       print("End")
end

-- execute all the item functions in the source datafile
dofile("inv.txt")

-- setup the start of the source LaTeX file
addRow(docPreamble)

-- make the pdf file of inventary
renderTree()

-- setup the end of the source LaTeX file
addRow(docEnd)

makesource( filename )
makepdf( filename )

-- end of script file

Applicazione

The final PDF document of example inventary

The final PDF document of example inventary

Scaricate il seguente file di prova chiamato appunto inv.pdf contenente 5000 articoli casuali. Purtroppo WordPress per ragioni di sicurezza non consente di fare l’upload di file con estensione txt per cui dovrete fare un passaggio in più copiando ed incollando i dati dal file in formato PDF (generato con SciTE).

Salvate lo script precedente in un file chiamato makeinv.lua (eventualmente assegnategli preventivamente i permessi di esecuzione), ed eseguitelo con il comando:

lua makeinv.lua

Otterrete questo file PDF in meno di un decimo di secondo!!!

Conclusioni

Lo script è veloce ed indipendente dal sistema operativo: legge un file di testo puro contenente i dati, li assembla nella struttura che corrisponde all’organizzazione reale del nostro magazzino e restituisce un file pronto per essere compilato da LaTeX.

Generando il sorgente LaTeX lo script può essere definito come un componente metaLaTeX. Tutti i dati sono testuali quindi trasparenti e facili da personalizzare. LaTeX poi produce un risultato tipografico eccellente.

L’idea si applica alla costruzione di report di dati complessi come un database cinematografico od un bilancio aziendale, oppure ad inventari ancora più complessi per esempio per una catena di negozi.

Bibliografia

Testo consigliato in assoluto per la programmazione in Lua è il PiL, scritto in maniera brillante dall’Autore stesso del linguaggio Roberto Ierusalimschy, pertanto non deve mancare nella nostra libreria.

Auguri

Tempo d’inventario, tempo di Natale e fine d’anno dunque…
Auguri a tutti!!

La Regione Puglia e Microsoft


Il fatto che la Regione Puglia consideri strategica l’innovazione tecnologica nell’ambito della Pubblica Amministrazione è molto importante. Evoluti servizi on-line possono notevolmente migliorare i servizi e la nascita di nuovi ambiti di sviluppo, nuovi progetti di partecipazione alle decisioni per esempio.

Proprio l’enorme importanza dell’argomento, si riflette direttamente nell’importanza degli strumenti informatici. Il software diviene il mezzo principale con cui innovare, dagli aspetti gestionali a quelli di comunicazione.

Dunque quali sono le caratteristiche del software richieste da questo progetto?

Il software ed i formati dei dati devono poter costituire una risorsa per gli utenti (cittadini, amministrazioni pubbliche, studenti, lavoratori), in grado di:

evolversi: stiamo quindi costruendo non più un solo punto di vista tecnico sui sistemi informativi ma anche una nuova visione biotecnica, dove gli organismi informatici vivono in un ambiente di rete che richiede loro nuove connessioni di scambio dati e nuove forme di elaborazione.

essere indipendente: le decisioni di progetto del sistema devono poter essere condivise, discusse, migliorate. Per garantire questo il codice deve poter essere liberamente consultato per capire esattamente come funziona. In altre parole, deve essere possibile dare uno sguardo approfondito del lavoro di sviluppo per poterne controllare in caso sia necessario, ogni particolare. L’indipendenza significa sostanzialmente sviluppo aperto.

utilizzare standard aperti: il formato dei dati è essenziale per la conservazione e lo scambio dei dati stessi, e questo per l’essenza stessa degli oggetti informatici, un aspetto quindi profondamente tecnico e di primaria importanza, per cui è essenziale rendere il formato indipendente da qualsiasi software specifico.

Chi deve costruire un sistema bioinformatico simile?

pluralità dei centri di sviluppo: lo sviluppo e la messa a punto (ingegnerizzazione) di un sistema per la pubblica amministrazione, deve selezionare il maggior numero delle migliori idee possibili. Caratteristica fondamentale è quindi la pluralità dei centri dello sviluppo software. Altrimenti si perderebbero inutilmente risorse di eccellenza che viceversa devono essere valorizzate.

Quanto costerà mantenerlo?

La cifra più piccola possibile: lo sviluppo software costa molto, ma aprendo a molteplici contributi volontari, costa assai meno.

Come può essere esportato anche in altre aree geografiche?

licenza libera: un software rilasciato sotto licenza libera può essere rapidamente riutilizzato in altri centri geograficamente lontani, anzi, ampliandosi il numero degli utenti, si ampliano sia i contributi allo sviluppo sia l’esperienza d’uso, e si riducono ulteriormente i costi.

Come fare in modo che sia messa in moto l’evoluzione dei componenti?

continuità e comunità: per garantire la continuità dello sviluppo e mettere in pratica gli obbiettivi propri di un arcipelago software in muta evoluzione, il centro di sviluppo ovviamente deve funzionare nel tempo. Inoltre continue sollecitazioni riguardanti nuove funzionalità o miglioramenti di quelle esistenti devono poter provenire dagli utenti, liberi di vedere i propri suggerimenti realizzarsi in una discussione aperta.

Conclusioni

Le aziende, e specie le multinazionali, impostano l’azione produttiva mirando direttamente o indirettamente ad ampliare i propri risultati economici e di mercato. Certo, il futuro potrebbe portare a nuovi ed inaspettati equilibri visto che si rendono urgenti una serie di profonde modifiche nel modo di funzionare della società umana.
Non sono contrario quindi ad accordi tra pubblica amministrazione ed aziende per portare avanti l’innovazione, purché quello che si fa abbia le caratteristiche imprescindibili che ho tentato di analizzare in questo post.
E probabilmente le associazioni e gli utenti del software libero non hanno tutti i torti a criticare la recente intesa tra Regione Puglia e Microsoft sull’argomento (consultate la Deliberazione n. 2571 del 23/11/2010).

Spero di aver dato anch’io un contributo ragionato alla discussione.
Vi segnalo anche l’interessante articolo sul sito della Stampa del professor Angelo Raffaele Meo.

Ciao ed alla prossima.

%d blogger hanno fatto clic su Mi Piace per questo: