InEnglish

Working with OpenOffice/LibreOffice Spreadsheets with Python

Monday, 22 October 2012
|
Écrit par
Grégory Soutadé

Working with OpenOffice/LibreOffice Spreadsheets with Python One improvement of OpenOffice was to introduce Python scripting beside VBA one. You can do internal or external scripting. External scripting is done via Python UNO interface, it's like CORBA objects (...). But resources on web are poor and sparse. Only two websites have a clear and complete information :
https://www.wzdftpd.net/downloads/oowall/pyUnoServerV2.py
http://stuvel.eu/ooo-python

This is a mini HOWTO you can use in your external scripts First you have to start server side OOo/LO :

libreoffice "--accept=socket,host=localhost,port=2002;urp;" --invisible

If you don't want to see OOo/LO interface, add --headless. WARNING: You need to close ALL OOo/LO instances before starting server !

Next, load a document :

def connect(port, filename): # get the uno component context from the PyUNO runtime localContext = uno.getComponentContext() # create the UnoUrlResolver resolver = localContext.ServiceManager.createInstanceWithContext( "com.sun.star.bridge.UnoUrlResolver", localContext) # connect to the running office ctx = resolver.resolve("uno:socket,host=localhost,port=" + str(port) + ";urp;StarOffice.ComponentContext") smgr = ctx.ServiceManager # get the central desktop object DESKTOP =smgr.createInstanceWithContext("com.sun.star.frame.Desktop", ctx) url = unohelper.systemPathToFileUrl( os.path.abspath(filename)) doc = DESKTOP.loadComponentFromURL(url, '_blank', 0, ()) return doc

You can get sheets inside document by creating an enumeration :

doc = connect(port, filename) sheets = doc.getSheets() sheet_enum = sheets.createEnumeration() while sheet_enum.hasMoreElements(): sheet = sheet_enum.nextElement() print sheet.getName()

Retrieve cells :

cell = sheet.getCellByPosition(col, row)

You can use following methods on cell objects : XCell

To retrieve cell type (CellContentType) :

cell.getType()

For me object (or enumeration) comparison fails, so I use string comparison :

if cell.getType().value != 'EMPTY':

cell.getValue() will return cell float value (0.0 if cell is empty or text). Most of the case you need to cast it into int value : int(cell.getValue()) or do all your code with float values !!

Be careful, sometimes cells values are formated with text but contains float/integer !! value = value_cell.getString() will return "0x45"

Now you have all basics to do a spreadsheet parser ! If you don't know how to handle an object, juste print it and look at its supportedInterfaces dictionary, OOo API doc will tells how to handle them.

Activate eSata on Sheevaplug with Debian

Monday, 08 October 2012
|
Écrit par
Grégory Soutadé

esata.png

Activate eSata on Sheevaplug with Debian I recently bought an external hard disk with an eSata interface, it was not easy to find (almost are with USB2/3, other are expensive advanced NAS), but I did. The purpose of this disk is to make backups. But, on my Sheevaplug, the main partitions (/root, /boot...) are on an USB key (Toshiba 16 GB) running Debian stable. When I plugged my new hdd it was not recognized ! Actually I first configured my sheevaplug following some tutorials (http://www.cyrius.com/debian/kirkwood/sheevaplug/ for example). It was said to set the boot variable "arcNumber" to 2097. Why ? In facts ARM SoC doesn't have peripherals discovery mode, so you need to tell which board you're running on.

After looking a bit into Debian's kernel, it seems that eSata interface is activated only if arcNumber is set to 2678 ! If I do that, original Ubuntu on NAND flash (factory installation) doesn't recognize the current SoC because arcNumber 2678 is a patch from Debian (in original installation, eSata is activated by default). The second point is that if you set the board as an eSata board, Debian will try to boot on the eSata hard disk (even if you specify different kernel root=XX values).

So what to do ? The solution is to specify your partitions not using classic /dev/sdXXX format, but using UUID numbers. They are not human readable, nevertheless they refer to an unique partition ! The first step consists in listing your partitions UUID :

ls -l /dev/disk/by-uuid/ lrwxrwxrwx 1 root root 10 Sep 27 07:34 1642ad57-77aa-494c-aa77-6998d420eb8f -> ../../sda3 lrwxrwxrwx 1 root root 10 Sep 27 07:34 198239b4-ff16-4dda-8df0-37b106005817 -> ../../sda1 lrwxrwxrwx 1 root root 10 Sep 27 07:34 2e0cd399-3839-4e4e-bc57-5e6628841bc1 -> ../../sda2 lrwxrwxrwx 1 root root 10 Sep 27 07:34 dd27350b-2522-46a6-862e-0cbc072b535f -> ../../sda4

Then, edit /etc/fstab to use UUID and not /dev/sdXXX (it's fastidious I know) After that, you need to reboot with the serial console connected and stop automatic boot (type a key) to edit uBoot configuration. We'll set arcNumber to 2678 by default.

setenv arcNumber 2678

Then edit bootargs_options (for me it's bootargs_options_usb) to set correct UUID value

setenv usb_bootargs_root "root=UUID=2e0cd399-3839-4e4e-bc57-5e6628841bc1"

Last step is to edit the global boot_cmd to set arcNumber to 2097 before booting to NAND (in my case, if USB boot fails it will try to boot on MMC then on NAND) :

setenv bootcmd 'setenv arcNumber 2678; saveenv; run usb_boot; setenv arcNumber 2097; saveenv; run bootcmd_mmc; run bootcmd_nand'

Finally save environment variables to flash and boot

saveenv boot

My final environment variables

ethact=egiga0 bootargs_root=ubi.mtd=1 root=ubi0:rootfs rootfstype=ubifs mtdpartitions=mtdparts=orion_nand:0x400000@0x100000(uImage),0x1fb00000@0x500000(rootfs) ethaddr=00:50:43:01:4C:56 bootargs_console=console=ttyS0,115200 bootargs_root_nand=ubi.mtd=1 root=ubi0:rootfs rootfstype=ubifs bootcmd_nand=setenv bootargs $(bootargs_console) $(mtdpartitions) $(bootargs_root_nand); \ nand read.e 0x00800000 0x00100000 0x00400000; bootm 0x00800000 bootargs_root_mmc=root=/dev/mmcblk0p2 rootdelay=5 bootcmd_mmc=setenv bootargs $(bootargs_console) $(bootargs_root_mmc); mmcinit;\ ext2load mmc 0:1 0x800000 /uImage; bootm 0x00800000 real_bootcmd=run bootcmd_mmc; run bootcmd_nand filesize=32D62A usb_bootargs_console=console=ttyS0,115200 usb_bootcmd_usb=usb start; ext2load usb 0:1 0x01100000 /uInitrd; ext2load usb 0:1 0x00800000 /uImage usb_boot=setenv bootargs $(usb_bootargs_console) $(usb_bootargs_root); run usb_bootcmd_usb;\ bootm 0x00800000 0x01100000 mainlineLinus=yes bustargs_root_usbroot=/dev/sda2 usb_bootargs="root=UUID=2e0cd399-3839-4e4e-bc57-5e6628841bc1" stdin=serial stdout=serial stderr=serial mainlineLinux=yes enaMonExt=no enaCpuStream=no enaWrAllo=no pexMode=RC disL2Cache=no setL2CacheWT=yes disL2Prefetch=yes enaICPref=yes enaDCPref=yes sata_dma_mode=yes netbsd_en=no vxworks_en=no bootdelay=3 disaMvPnp=no enaAutoRecovery=yes

I added a rule in fstab to mount my hdd at startup

UUID=590f30b1-7727-4d0a-a86a-2360ec0b3f88 /media/backup ext4 defaults 0 1

A simple backup script based on rsync that power down disk after backup is done.

How to load UTF8 data with python minidom ?

Wednesday, 22 August 2012
|
Écrit par
Grégory Soutadé

For the dynastie project, I need to load data encoded in UTF-8 with Python minidom XML parser. But when I wrote node.toxml('utf-8') to display the XML tree, I get this error :

UnicodeDecodeError at /generate/1

'ascii' codec can't decode byte 0xc2 in position 187: ordinal not in range(128)

In facts Python thinks that all data in XML tree are in ASCII and try to encode it into UTF-8 (or anything else you supplied). The solution is to use your own writer that will convert all non utf-8 strings in unicode string which can be then re-encoded in every format (like utf-8). This doesn't appears in Python 3 because, in Python 3, all strings are already in unicode. Add the following class to your code :

class UnicodeWriter(codecs.StreamWriter): encode = codecs.utf_8_encode def __init__(self): self.value = u'' def write(self, object): if not type(object) == unicode: self.value = self.value + unicode(object, 'utf-8') else: self.value = self.value + object return self.value def reset(self): self.value = u'' def getvalue(self): return self.value

And our node.toxml('utf-8') becomes :

writer = UnicodeWriter() node.writexml(writer) writer.getvalue().encode('utf-8')

Proxy a subdomain with nginx

Thursday, 09 August 2012
|
Écrit par
Grégory Soutadé

A lot of things has been written about nginx and Apache : proxy_pass, proxy_redirect, subdomains... I just want to publish my configuration that is in test but works. This is a response to my requirements : I want nginx to serve the subdomain blog.soutade.fr but transfer all other requests to an Apache server (soutade.fr, www.soutade.fr, indefero.soutade.fr ...).

 

First step is to install nginx. Nginx current version is 0.7.23 (thanks to debian stable). Then edit /etc/nginx/sites-available/default :

server { listen 80 default; ## listen for ipv4 server_name soutade.fr *.soutade.fr; access_log /var/log/nginx/soutade.fr.access.log; location / { proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $remote_addr; proxy_set_header Host $host; resolver localhost; proxy_pass http://$host:8000; } } server { listen 80; ## listen for ipv4 server_name blog.soutade.fr; access_log /var/log/nginx/soutade.fr.access.log; location / { root /var/www/blog; index index.html; } location = /favicon.ico { access_log off; log_not_found off; } location ~ /\. { deny all; access_log off; log_not_found off; } }

The first block tells nginx to redirect all request from soutade.fr and *.soutade.fr to a local Apache server listening on port 8000. The second block creates a special rule for blog.soutade.fr : files will be served by nginx server and no redirection will be applied. We also disable favicon.ico error log and deny serving \.* files. There is a tip in the first block : we need to set up a local DNS server ("resolver localhost" directive). If this option is not set, it will try to do another DNS request to resolve $host address. So we'll simply install bind9 and configure it. Edit /etc/bind/named.conf.local

zone "soutade.fr" { type master; file "/etc/bind/db.soutade.fr"; };

Finally edit /etc/bind/db.soutade.fr with :

$TTL 604800 @ IN SOA soutade.fr. root.soutade.fr. ( 07082012 ; Serial 604800 ; Refresh 86400 ; Retry 2419200 ; Expire 604800 ) ; Negative Cache TTL ; @ IN NS localhost. @ IN A 127.0.0.1 * IN CNAME soutade.fr. @ IN AAAA ::1

And restart bind : sudo service bind9 restart. root.soutade.fr. is the mail address of the administrator. Now everything might be ok.