ODT
e PDF
.
I dati da includere nel report vengono estratti dal database mediante delle query.PDF
è inoltre possibile assemblare in un unico file "PDF" più report e file.
ODT
(testo, tabelle, immagini ...).
Una porzione di report può anche non definire un modello; utile nel caso si voglia definire una query di dati da utilizzare nelle porzioni successive.
Una porzione di report può anche non definire una query; in tal caso il modello verrà applicato una sola volta.DTL
; un linguaggio specifico per la elaborarazione dei modelli (template processing), molto versatile e diffuso in vari ambienti tra cui anche Perl e Dojo Toolkit, entrambi usati in MasonSQL.
Il generatore dei report utilizza la libreria Perl DTL::Fast
che implementa parte delle specifiche del linguaggio DTL.
Rispetto alla implementazione ufficiale ci sono alcune incompatibilità ed estensioni che citiamo solo per chi fa un uso sofisticato del linguaggio.
In DTL
si utilizzano i caratteri {{
e }}
per delimitare il testo da interpretare.var
si scriverà nel modello o nella query {{ var }}
.
Le variabili in DTL
possono anche essere raggruppate gerarchicamente, usando hash e array.{{ myhash.mykey }}
dove myhash
è il nome della hash e mykey
è il nome della chiave.
DTL
.
I parametri vengono inseriti in una hash di nome ARGV
(ARGument Variables) che sarà visibile in tutti i modelli e query.
Ad esempio se si chiama il generatore con i seguenti parametri param1=100 MyPAR='my string data'
esse saranno reperibili in DTL
come {{ ARGV.param1 }}
e {{ ARGV.MyPAR }}
query
nel linguaggio SQL
con la quale reperire i dati nel database.
Nella query
possiamo inserire dei costrutti DTL
che verranno elaborati prima di ogni esecuzione per estrarre la tabella dei dati.
Per ogni riga dei dati ricavati verrà generata la porzione usando il modello (se presente), rendendo visibili i dati delle colonne come variabili DTL
.
I dati della riga saranno visibili in DTL
come hash; ad esempio, se la porzione ha nome HEAD
i dati della riga saranno del tipo {{ HEAD.var }}
.
In caso di campi di tipo JSON
il contenuto del campo verrà convertito in hash
o array
e sarà possibile fare riferimento ai dati sottostanti come ad esempio {{ HEAD.myhash.myfield }}
{{ HEAD.myarray[5] }}
L'hash in DTL
avrà un ulteriore valore ROW_NUMBER che esprime il numero di riga del recordset, contando da 1. Per esempio il tag {{ HEAD.ROW_NUMBER }}
conterrà il numero di riga corrente.
La hash così definita sarà anche visibile nelle elaborazioni successive di query e modelli di porzioni allo stesso livello o superiore.
SQL
in formato bytea
.
Un'immagine presente in una porzione sarà sostituita solo quando il suo nome contiene un tag DTL
. Il formato del nome sarà: #<porzione_nome>.<immagine_nome_del_campo>
. Le immagini senza tag DTL
verranno ignorate.
Le immagini dei modelli verranno sostituite con le immagini appropriate dal database.
Il formato dei nomi delle immagini aggiunge al report, saranno: <porzione_nome>_<immagine_nome>_<numero_di_riga>.<suffix>
Il tipo di immagine MIME verrà rilevato e aggiunto gli attributi di immagine.[Config]
è disponibile il form [ODT Reports]
con il quale è possibile definire i report fornendo i modelli usati per l'assemblaggio e definendo le porzioni con le relative query di estrazione dei dati da stampare.
Nel form sono presenti due pulsanti (Test ODT
e Test PDF
) con i quali vengono generati e poi scaricati i report nei formati ODT
o PDF
, utilizzando i parametri indicati nel campo Test parameters
.ODT
non include eventuali porzioni di tipo PDF
(file e report allegati) che verranno ignorati.
I file dei modelli vengono allegati alla tabella dei report (public.odt_reports
) e si possono caricare dal form. Ogni report ha un archivio separato dei file, eventualmente organizzato in cartelle sottostanti.termine | descrizione |
---|---|
modello |
generico documento contenente le indicazioni di struttura o di grafica in cui si inseriscono le informazioni mediante riferimenti a dati esterni |
modello di stampa |
file nel formato ODT utilizzato come base per la costruzione del documento |
sezione |
parte di un documento ODT contrassegnata da un nome |
porzione |
porzione del report, generata utilizzando una sezione, una tabella o un documento ODT |
report |
documento nel formato ODT |
DTL |
Django Template Language; usato per inserire nei modelli e nelle query i dati |
dati |
I dati possono essere forniti al generatore di report come parametri (a riga di comando) o come risultato delle query |
query |
istruzione nel formato SQL per estrarre i dati dal database. La query può contenere costrutti nel formato DTL |
nome tabella | descrizione |
---|---|
public.odt_reports |
elenco dei report |
public.odt_report_portions |
elenco delle porzioni dei report |
nome campo | descrizione |
---|---|
id |
chiave primaria |
name |
nome del report |
description |
descrizione breve del report |
comment |
commento/descrizione lunga del report |
cmd_parameters |
campo usato dal configuratore per passare i parametri al generatore per le stampe di prova. Il formato è del tipo "param1=number param2='my string' " |
nome campo | descrizione |
---|---|
id |
chiave primaria |
ord |
ordine di elaborazione |
id_odt_report |
riferimento alla chiave primaria del report |
name |
nome della porzione |
id_father |
chiave primaria della porzione padre |
type |
tipo di porzione |
file_name |
nome del file del modello o del file dove è presente l'oggetto usato come modello (indicare anche il percorso se il file è stato salvato in una cartella dell'archivio del report) |
obj_ref |
nome di riferimento dell'oggetto usato come modello |
query |
query SQL |
public.odt_report_portions
si determinano le porzioni da elaborare in base al seguente schema che stabilisce i valori da attribuire ai campi type
, file_name
e obj_ref
:
type![]() |
descrizione della porzione | file_name | obj_ref |
---|---|---|---|
base_report |
modello di documento da utilizzare come base | nome del file ODT |
|
odt_file |
file in formato ODT |
nome del file ODT |
|
odt_section |
sezione odt | nome del file ODT da cui copiare la sezione |
nome della sezione |
odt_table |
tabella ODT |
nome del file ODT da cui copiare la sezione |
nome della tabella |
pdf_file |
file in formato PDF , da allegare |
nome del file PDF |
|
pdf_report |
report da allegare, in formato PDF |
nome del report |
file_name
e obj_ref
possono contenere costrutti DTL
, così e possibile rendere parametrici le sorgenti dalle quali caricare modelli e allegati.
La query della porzione viene valutata per prima; poi si valutano i campi file_name
e obj_ref
per ogni riga restituita dalla query. In tal modo sarà possibile cambiare il modello, riga per riga.
ODT
da utilizzare come base per la generazione del report.DTL
, utile per elaborare l'intestazione di pagina e il piè di pagina ma anche eventuali altri oggetti del documento di base.ODT
(odt_section, odt_table e odt_file) in quanto è utilizzata come contenitore per esse.PDF
(pdf_file e pdf_report) .ODT
gli stili applicati saranno quelli definiti nel documento di base, qui definito.
ODT
presente nel file, identificata con il suo nome.odt_file
non è definito, la sezione verrà letta dal file del modello base_report
.ODT
presente nel file, identificata con il suo nome.odt_file
non è definito, la sezione verrà letta dal file del modello base_report
.ODT
.DTL
verrà applicata al contenuto.PDF
ed allegato.
ODT
generati con Libreoffice soffrono di un bug che provoca la proliferazione di elementi di tipo SPAN
anche se non includono differenti attributi di formattazione, rispetto al testo adiacente.
Il problema è qui documentato:
Il seguente esempio di codice XML mostra il problema, che impedisce al parser di interpretare correttamente i costrutti DTL
:
<style:style style:name="T5" style:family="text"><style:text-properties officeooo:rsid="00260f63"/></style:style> . . . . <text:p text:style-name="P9">{{ <text:span text:style-name="T5">Ordini</text:span>.stato_ordine }}</text:p>Prima di applicare il parser
DTL
è quindi necessario verificare che non ci siano elementi di tipo text:span
con style contenente soltanto proprietà officeooo:rsid
che andranno eliminate prima di procedere con il parsing.
demo.employees
, demo.orders
e demo.commissions
Se il report viene richiamato con il parametro ID_EMPLOYEE verrà stampato un solo impiegato con quella chiave primaria.
Dobbiamo quindi definire due query: SELECT id, name, surname, FROM demo.employees WHERE {% if not defined ARGV.ID_EMPLOYEE %}true{% endif %} or id = '{{ ARGV.ID_EMPLOYEE }}'::integer;Limitiamo l'elenco nel caso sia definita la variabile
ARGV.ID_EMPLOYEE
(variabile fornita a riga di comando) al solo impiegato che ha il campo id
uguale alla variabile.
SELECT price, type, percentage as perc FROM demo.orders INNER JOIN demo.commissions ON commissions.id = orders.commission WHERE orders.employee = {{ EB.id }};Limitiamo l'elenco utilizzando la variabile
EB.id
che corrisponde al campo id
(chiave primaria dell'impiegato) della prima query, in quanto chiameremo con il nome EB
(Employee body) la porzione del report corrispondente alla prima query.
Ipotizzando di aver definito un report di esempio nella tabella public.odt_reports
con questi valori:
id | name | description | comment | cmd_parameters |
---|---|---|---|---|
23 | demoreport | example of report | no comment | ID_EMPLOYEE=3 |
my_templates.odt
con i modelli, la tabella delle porzioni assumerà questi valori:
id | ord | id_odt_report | name | id_father | type | file_name | obj_ref | query |
---|---|---|---|---|---|---|---|---|
18 | 1 | 23 | base_report | my_templates.odt | ||||
19 | 2 | 23 | odt_section | employees_header{% defined ARGV.ID_EMPLOYEE %}_SINGLE{% endif %} | ||||
14 | 3 | 23 | EB | odt_section | employees_body | SELECT id, name, surname, FROM demo.employees WHERE {% if not defined ARGV.ID_EMPLOYEE %}true{% endif %} or id = '{{ ARGV.ID_EMPLOYEE }}'::integer; |
||
21 | 4 | 23 | 14 | odt_section | orders_header | |||
22 | 5 | 23 | OB | 14 | odt_section | orders_body | SELECT price, type, percentage as perc FROM demo.orders INNER JOIN demo.commissions ON commissions.id = orders.commission WHERE orders.employee = {{ EB.id }}; |
|
23 | 6 | 23 | 14 | odt_section | orders_footer | |||
24 | 7 | 23 | odt_section | employees_footer |
employees_header
:
================================================= List orders divided per employee =================================================Sezione
employees_header_SINGLE
:
================================================= Orders of an employee =================================================Sezione
employees_body
Employee {{ EB.name }} {{ EB.surname }}Sezione
orders_header
+---------------+--------------+----------------+ | price | % commission | type |Sezione
orders_body
+---------------+--------------+----------------+ | {% sprintf "%13.2f" OB.price %} | {% sprintf "%12.2f" OB.perc %} | {% sprintf "%13s" OB.type %} |Sezione
orders_footer
+---------------+--------------+----------------+ [{% sprintf '%3i' EB.id %}]Sezione
employees_footer
=================================================Per ottenere un report simile al seguente, nell'ipotesi di chiamare il generatore con il parametro
ID_EMPLOYEE=3
:
================================================= Orders of an employee ================================================= Employee Ted Danson +---------------+--------------+----------------+ | price | % commission | type | +---------------+--------------+----------------+ | 12.50 | 12% | Breads | +---------------+--------------+----------------+ | 230.00 | 20% | Drinks | +---------------+--------------+----------------+ | 345.33 | 10% | Prepared | +---------------+--------------+----------------+ [ 3] =================================================