<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Fusion on Zwindler's Reflection</title><link>https://blog.zwindler.fr/tags/fusion/</link><description>Recent content in Fusion on Zwindler's Reflection</description><generator>Hugo -- gohugo.io</generator><language>fr</language><copyright>Licensed under CC BY-SA 4.0</copyright><lastBuildDate>Mon, 21 Jul 2014 14:53:01 +0000</lastBuildDate><atom:link href="https://blog.zwindler.fr/tags/fusion/index.xml" rel="self" type="application/rss+xml"/><item><title>Fusionner plusieurs CSV dans un unique XLS en ligne de commande</title><link>https://blog.zwindler.fr/2014/07/21/fusionnermerge-plusieurs-csv-dans-un-unique-xls-en-ligne-de-commande/</link><pubDate>Mon, 21 Jul 2014 14:53:01 +0000</pubDate><guid>https://blog.zwindler.fr/2014/07/21/fusionnermerge-plusieurs-csv-dans-un-unique-xls-en-ligne-de-commande/</guid><description>&lt;img src="https://blog.zwindler.fr/2014/07/pythonlogo.webp" alt="Featured image of post Fusionner plusieurs CSV dans un unique XLS en ligne de commande" /&gt;&lt;p&gt;En voulant créer mes propres rapports à partir de mes bases MySQL NDO (nagios), je me suis retrouvé avec un problème : comment consolider les données de manière à avoir un rapport clair (et pas de lignes et des lignes à la mode CSV).&lt;/p&gt;
&lt;p&gt;Comme il ne s’agissait pas de valeurs chiffrées à intégrer à des graphiques, je suis parti dans l’idée de consolider mes différents rapports dans un fichier MS Excel, notamment pour pouvoir trier les données dans les onglets séparés (les rapports traitent de sujets différents que l’on ne peut regrouper dans une seule et même vue).&lt;/p&gt;
&lt;p&gt;Je me suis vite rendu compte que ce n’était pas aussi facile que cela !&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Générer des CSV à partir de MySQL&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;La première étape a donc été dans mon cas de commencer par récupérer des CSV à partir des données de MySQL. Ça, c’était assez simple dans le cas de requêtes sans UNION, il suffit d’ajouter à la fin de la requêtes les lignes suivantes :&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;INTO OUTFILE &amp;#39;output.csv&amp;#39;
FIELDS TERMINATED BY &amp;#39;,&amp;#39;
LINES TERMINATED BY &amp;#39;n&amp;#39;;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Dans le cas du UNION, la petit spécificité qu’il faut savoir est qu’on doit insérer ces 3 lignes dans le dernier bloc SELECT, et non pas tout à la fin. Toute la sélection sera néanmoins bien prise en compte.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Générer un XLS à partir d’un fichier CSV&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;J’ai trouvé plusieurs solutions en perl et en php permettant d’installer des extensions capables de gérer les fichiers CSV et les convertir en fichier Excel, mais l’extension qui m’a le plus plu est sans aucun doute l’extension pyExcelerator en Python.&lt;br&gt;
Première chose, il faut récupérer la dernière version sur pipy (&lt;a class="link" href="https://pypi.python.org/pypi/pyExcelerator" target="_blank" rel="noopener"
&gt;pyExcelerator sur pypi&lt;/a&gt;), la décompacter puis l’installer. Pour ceux qui ne se souviendraient plus :&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;tar xjf pyexcelerator-0.6.4.1.tar.bz2
python setup.py install
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Une fois le module installé, on peut faire appel à lui comme n’importe quel autre dans le shell python (ou dans les scripts).&lt;/p&gt;
&lt;p&gt;Si jamais générer &lt;strong&gt;un&lt;/strong&gt; seul fichier &lt;strong&gt;XLS&lt;/strong&gt; pour &lt;strong&gt;un&lt;/strong&gt; seul fichier &lt;strong&gt;CSV&lt;/strong&gt; vous suffit, je vous recommande alors le script disponible sur l’article suivant&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a class="link" href="http://sujitpal.blogspot.fr/2007/02/python-script-to-convert-csv-files-to.html" target="_blank" rel="noopener"
&gt;sujitpal.blogspot.fr&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;En revanche, si comme moi vous souhaitez un script qui permet dans &lt;strong&gt;un&lt;/strong&gt; même fichier &lt;strong&gt;XLS&lt;/strong&gt; de consolider &lt;strong&gt;plusieurs&lt;/strong&gt; fichiers &lt;strong&gt;CSV&lt;/strong&gt; (un par onglet) il faut aller plus loin&amp;hellip;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Générer un XLS à partir de plusieurs fichiers CSV&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&amp;hellip;mais pas beaucoup plus ! OUF !&lt;/p&gt;
&lt;p&gt;J’ai fais l’exercice pour vous et j’ai modifié le script de Sujit Pal sur son blog &lt;em&gt;Salmon Run&lt;/em&gt; pour qu’il accepte en entrée autant de fichiers CSV que vous le souhaitez (il n’y a pas une grosse différence, il ne manquait pas grand chose). Les fichiers CSV auront chacun leur propre onglet.&lt;/p&gt;
&lt;p&gt;Pour plus de facilité &lt;a class="link" href="https://github.com/zwindler/csv_to_excel" target="_blank" rel="noopener"
&gt;j’ai posté le script sur Github à cette adresse&lt;/a&gt;.&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;#!/usr/bin/python
#!/usr/bin/python
# Tool to convert CSV files (with configurable delimiter and text wrap
# character) to Excel spreadsheets.
################################################################################
# Found on http://sujitpal.blogspot.fr/2007/02/python-script-to-convert-csv-files-to.html
# Requires setup.py install of pyExcelerator found on
# https://pypi.python.org/pypi/pyExcelerator
# Modified to concat csv files in different sheets
################################################################################
import string
import sys
import getopt
import re
import os
import os.path
import csv
from pyExcelerator import *
def usage():
&amp;#39;&amp;#39;&amp;#39; Display the usage &amp;#39;&amp;#39;&amp;#39;
print &amp;#39;Usage:&amp;#39; + sys.argv[0] + &amp;#39; [OPTIONS] csvfile&amp;#39;
print &amp;#39;OPTIONS:&amp;#39;
print &amp;#39;--title|-t: If set, the first line is the title line&amp;#39;
print &amp;#39;--lines|-l n: Split output into files of n lines or less each&amp;#39;
print &amp;#39;--sep|-s c [def:,] : The character to use for field delimiter&amp;#39;
print &amp;#39;--output|o: output file name/pattern, default: output.xls&amp;#39;
print &amp;#39;--help|h: print this information&amp;#39;
sys.exit(2)
def writeExcelHeader(worksheet, titleCols):
&amp;#39;&amp;#39;&amp;#39; Write the header line into the worksheet &amp;#39;&amp;#39;&amp;#39;
cno = 0
for titleCol in titleCols:
worksheet.write(0, cno, titleCol)
cno = cno + 1
def writeExcelRow(worksheet, lno, columns):
&amp;#39;&amp;#39;&amp;#39; Write a non-header row into the worksheet &amp;#39;&amp;#39;&amp;#39;
cno = 0
for column in columns:
worksheet.write(lno, cno, column)
cno = cno + 1
def closeExcelSheet(workbook, outputFileName):
&amp;#39;&amp;#39;&amp;#39; Saves the in-memory WorkBook object into the specified file &amp;#39;&amp;#39;&amp;#39;
workbook.save(outputFileName)
def renameOutputFile(outputFileName, fno):
&amp;#39;&amp;#39;&amp;#39; Renames the output file name by appending the current file number
to it &amp;#39;&amp;#39;&amp;#39;
dirName, baseName = os.path.split(outputFileName)
rootName, extName = os.path.splitext(baseName)
backupFileBaseName = string.join([string.join([rootName, str(fno)], &amp;#39;-&amp;#39;), extName], &amp;#39;&amp;#39;)
backupFileName = os.path.join(dirName, backupFileBaseName)
try:
os.rename(outputFileName, backupFileName)
except OSError:
print &amp;#39;Error renaming output file:&amp;#39;, outputFileName, &amp;#39;to&amp;#39;, backupFileName, &amp;#39;...aborting&amp;#39;
sys.exit(-1)
def validateOpts(opts):
&amp;#39;&amp;#39;&amp;#39; Returns option values specified, or the default if none &amp;#39;&amp;#39;&amp;#39;
titlePresent = False
linesPerFile = -1
outputFileName = &amp;#39;&amp;#39;
sepChar = &amp;#39;,&amp;#39;
for option, argval in opts:
if (option in (&amp;#39;-t&amp;#39;, &amp;#39;--title&amp;#39;)):
titlePresent = True
if (option in (&amp;#39;-l&amp;#39;, &amp;#39;--lines&amp;#39;)):
linesPerFile = int(argval)
if (option in (&amp;#39;-s&amp;#39;, &amp;#39;--sep&amp;#39;)):
sepChar = argval
if (option in (&amp;#39;-o&amp;#39;, &amp;#39;--output&amp;#39;)):
outputFileName = argval
if (option in (&amp;#39;-h&amp;#39;, &amp;#39;--help&amp;#39;)):
usage()
return titlePresent, linesPerFile, sepChar, outputFileName
def main():
&amp;#39;&amp;#39;&amp;#39; Main function &amp;#39;&amp;#39;&amp;#39;
try:
opts,args = getopt.getopt(sys.argv[1:], &amp;#39;tl:s:o:h&amp;#39;, [&amp;#39;title&amp;#39;, &amp;#39;lines=&amp;#39;, &amp;#39;sep=&amp;#39;, &amp;#39;output=&amp;#39;, &amp;#39;help&amp;#39;])
except getopt.GetoptError:
usage()
if (len(args) &amp;lt; 1): usage() &amp;#39;&amp;#39;&amp;#39; Opens a reference to an Excel WorkBook and Worksheet objects &amp;#39;&amp;#39;&amp;#39; workbook = Workbook() for inputFileName in args: try: inputFile = open(inputFileName, &amp;#39;r&amp;#39;) except IOError: print &amp;#39;File not found:&amp;#39;, inputFileName, &amp;#39;...skipping&amp;#39; continue titlePresent, linesPerFile, sepChar, outputFileName = validateOpts(opts) if (outputFileName == &amp;#39;&amp;#39;): outputFileName = &amp;#39;output.xls&amp;#39; worksheet = workbook.add_sheet(os.path.basename(inputFileName)) fno = 0 lno = 0 titleCols = [] reader = csv.reader(inputFile, delimiter=sepChar) for line in reader: if (lno == 0 and titlePresent): if (len(titleCols) == 0): titleCols = line writeExcelHeader(worksheet, titleCols) else: writeExcelRow(worksheet, lno, line) lno = lno + 1 if (linesPerFile != -1 and lno &amp;gt;= linesPerFile):
closeExcelSheet(workbook, outputFileName)
renameOutputFile(outputFileName, fno)
fno = fno + 1
lno = 0
worksheet = workbook.add_sheet(os.path.basename(inputFileName))
inputFile.close()
closeExcelSheet(workbook, outputFileName)
if (fno &amp;gt; 0):
renameOutputFile(outputFileName, fno)
if __name__ == &amp;#39;__main__&amp;#39;:
main()
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Comme d’habitude, le script est fournit gracieusement, et surtout « AS-IS with no warranty whatsoever » blablabla ;-) A vos risques et périls quoi!&lt;/p&gt;
&lt;p&gt;Par rapport au script initial :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Au lieu de mettre un seul fichier en argument de la commande, on peut en spécifier&lt;/li&gt;
&lt;/ul&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;python csv_to_excel.py file1.csv #un fichier, comme avant
python csv_to_excel file1.csv file2.csv file3.csv #plusieurs fichiers, avec un onglet par .csv
python csv_to_excel file*.csv #avec des wildcard, autant d&amp;#39;onglets que de fichiers .csv trouvés
&lt;/code&gt;&lt;/pre&gt;&lt;ul&gt;
&lt;li&gt;Il n’y a pas de « nom par défaut » pour le XLS de sortie, car il était récupéré à partir du nom du fichier .csv (ici on en a plusieurs). Si on ne spécifie par le &amp;ndash;output, le fichier sera généré en tant que ouput.xls&lt;/li&gt;
&lt;li&gt;Un fichier non trouvé ne génère plus une erreur bloquante, on passe juste au suivant&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Se l’envoyer par email&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Tant qu’à faire, autant faire les choses jusqu’au bout. Je n’ai pas envie de me connecter sur un serveur ou un partage pour récupérer la dernière version du rapport, je veux la recevoir par email. Pour peut que votre serveur (avec sendmail ou équivalent) soit correctement configuré pour envoyer des emails, la commande &lt;strong&gt;uuencode&lt;/strong&gt; permet d’encoder les pièces jointes pour qu’elles puissent être envoyées par la commande mail.&lt;/p&gt;
&lt;p&gt;Installation de la commande sous CentOS/RHEL :&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;yum install sharutils
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;On rajoute en crontab un petit shell simple qui lance la commande suivante :&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;uuencode original_report.xls encoded_report.xls | mail -s &amp;#39;My Report&amp;#39; my@email.com
&lt;/code&gt;&lt;/pre&gt;</description></item></channel></rss>