Coś dla biura - czyli (re)generowanie raportów

W praktyce korporacyjnej bardzo częstym problemem jest sporządzanie raportu na podstawie otrzymanych danych. Problemem jest to, że dane te - niestety - nie są najczęściej przesłane nam w formacie, który by ułatwiał to zadanie i nie mamy na to żadnego wpływu.

Typowym przykładem może być sytuacja następująca. Trzeba sporządzić sprawozdanie, plan, czy coś innego w tym rodzaju. W tym celu koordynator projektu rozsyła do wszystkich komórek jakiś formularz, który jest zwykłym plikiem, na nieszczęście jeszcze w formacie M$ Worda, który komórki te mają odesłać wypełniony.

Tekst w pliku ma strukturę pytanie - odpowiedź, np:
1. Nazwa przedsięwzięcia

.......................................................

2. Podaj budżet przedsięwzięcia bla bla bla

...........................................................

3. Zasoby konieczne do realizacji

.........................................................

4. Termin rozpoczęcia

..........................................................

5. Termin zakończenia

etc, etc.

I teraz szef mówi, że trzeba z nadesłanych odpowiedzi zrobić tabelę. Jeśli w każdym pliku pytań jest kilkanaście, a plików setki albo dziesiątki, to zwykłe "kopiuj - wklej" do Excela, jako rozwiązanie problemu zdecydowanie odpada. Trzeba do tego podejść systemowo.

W moim przypadku było jeszcze gorzej, bo pliki były w różnych formatach. Część w "doc", a część w "odt", czyli format Open Document. Postanowiłem zaprząc do pracy pythona, a pierwszą rzeczą było skonwertowanie wszystkiego na format Open Document. Wrzuciłem wszystkie pliki "doc" do jednego katalogu o nazwie doc. Wynalazłem (google) taki przepis na konwersję: http://www.artofsolving.com/files/DocumentConverter.py. Wymaga on uruchomienia OpenOffice jako serwisu. Na szczęście jest to banalne, wystarczy w linii poleceń wpisać soffice -accept="socket,port=8100;urp;" (działa to pod linuksem, ale pod windowsem powinno być podobnie).
Potem wystarczy wykonanie następującego skryptu powłoki (bash):

#!/bin/bash
licznik=1
for nazwa in doc/*
do
    python DocumentConverter.py $nazwa odt/plan_$licznik.odt
    let licznik=licznik+1 
done

Mamy już wszystkie pliki w formacie odt. Zbieramy je w jednym katalogu, niech on się nazywa odt. Teraz wyciągamy z nich sam tekst bez formatowania. Pomysł na odpowiedni programik zawdzięczam tu książce "Python Receptury".
#!/usr/bin/env python
import zipfile, re
rx_stripxml = re.compile("<[^>]*?>", re.DOTALL|re.MULTILINE)
def convert_OO(filename):
    zf=zipfile.ZipFile(filename, "r")
    data = zf.read("content.xml")
    zf.close()
    data = " ".join(rx_stripxml.sub(" ", data).split())
    return data
 
if __name__=="__main__":
    import sys
    if len(sys.argv) > 1:
        for docname in sys.argv[1:]:
            print convert_OO(docname)
    else:
        print 'nalezy podac pliki do skonwersowania jako argumenty'

Żeby nie wywoływać tego programu kilkadziesiąt razy ręcznie przyda się kolejny skrypt powłoki:
#!/bin/bash
licznik=1
for nazwa in odt/*
do
    echo "<item>" >> raport.xml
    python odt2txt.py $nazwa >> raport.xml
    echo "</item>" >> raport.xml
    let licznik=licznik+1 
done

W ten sposób powstał jeden plik tekstowy, który będziemy dalej przekształcać, żeby stał się plikiem xml. Poszczególne dokumenty powinny już być zawarte w znacznikach

Teraz wystarczy pozamieniać teksty typu "1. Nazwa przedsięwzięcia" na znaczniki xml. Robimy to za pomocą programu:

# -*- coding:utf-8 -*-
#!/usr/bin/env python
import codecs, sys
zrodlo = codecs.open('raport.xml', 'r', 'utf-8')
cel = codecs.open('raport1.xml', 'w','utf-8')
zmiany={
    u'1. Nazwa przedsięwzięcia':u'<nazwa_przedsiewziecia>',
    u'2. Podaj budżet przedsięwzięcia bla bla bla':u'</nazwa_przedsiewziecia><budzet_przedsiewziecia>',
# i tak dalej aż do końca
}
for s in zrodlo:
    for zmiana in zmiany.keys():
        s = s.replace(zmiana, zmiany[zmiana])
        print(s)
    cel.write(s)
cel.close()

I to w zasadzie wszystko. Teoria jednak w tym miejscu nieco się z praktyką rozmija, bo autorzy plików mają bardzo różne pomysły, które powodują, że plik xml trzeba bedzie zapewne w kilku miejscach poprawić ręcznie. Pomoże nam w tym zapewne parser xml.
Kiedy już utworzymy poprawny plik xml, możemy z nim zrobić w zasadzie wszystko :)