Dagens patch

Det blir mycket RDF i lagen.nu 1.5. Och det blir mycket användande av RDFLib. Efter att ha hittat finfina instruktioner om hur man får biblioteket – inklusive SPARQL-parser – att snurra under windows har jag gett mig på min pet peeve i n3-serialiseringskoden, nämligen dess ovana att definera egna anonyma namespaceprefix. Normalt serialiserar den nämligen n3-formatet något i stil med såhär:

@prefix _8: http://lagen.nu/.
@prefix _9: http://lagen.nu/1962:700#.
@prefix dct: http://dublincore.org/documents/dcmi-terms/.
@prefix rinfo: http://rinfo.lagrummet.se/taxo/2007/09/rinfo/pub#.

_8:NJA_2005_s_878 dct:identifier "NJA 2005 s. 878 (NJA 2005:95)";
rinfo:lagrum _9:K29P7;
rinfo:rattsfallshanvisning _8:NJA_1996_s_63,
_8:NJA_2000_s_421;

Men nu blir det det oändligt mycket mer läsbara:

@prefix dct: http://dublincore.org/documents/dcmi-terms/.
@prefix rinfo: http://rinfo.lagrummet.se/taxo/2007/09/rinfo/pub#.

 <http://lagen.nu/NJA_2005_s_878> dct:identifier "NJA 2005 s. 878 (NJA 2005:95)";
     rinfo:lagrum <http://lagen.nu/1962:700#K29P7>;
     rinfo:rattsfallshanvisning <http://lagen.nu/NJA_1996_s_63>,
         <http://lagen.nu/NJA_2000_s_421>;

Här är patchen:

C:\Users\staffan\tmp\rdflib-2.4.0\rdflib>diff -u syntax\NamespaceManager.py~ syntax\NamespaceManager.py
--- syntax\NamespaceManager.py~ 2007-04-04 22:05:32.000000000 +0200
+++ syntax\NamespaceManager.py  2008-06-14 21:36:32.606307200 +0200
@@ -59,8 +59,7 @@
namespace = URIRef(namespace)
prefix = self.store.prefix(namespace)
if prefix is None:
-                prefix = "_%s" % len(list(self.store.namespaces()))
-                self.bind(prefix, namespace)
+                raise Exception("Prefix for %s not bound" % namespace)
self.__cache[uri] = (prefix, namespace, name)
return self.__cache[uri]

Den som förstår RDFLib bättre kan säkert få till samma effekt utan att patcha källkoden genom att subklassa NamespaceManager och trycka in den i kedjan någonstans, men mina försök till det misslyckades.

Lägesrapport

Liten uppdatering om vad jag håller på med:

Träning: Har börjat komma igång så smått efter kalmar, målsättningen är fyra lättare pass i veckan (ett simpass, två löppass och ett långpass på cykeln) med mycket fokus på bra teknik.

För löpningen innebär det att öka stegfrekvensen till ca 180 steg i minuten (Jag har några podrunner-mixar med lämplig BPM i lurarna för att hålla takten vilket hjälper mycket – synd bara att musiken är så trist) och se till att landa på fotsulan (inte hälen) med foten under kroppen (inte framför). 180 steg i minuten är vansinnigt mycket fortare än de kanske 150-160 jag brukar springa med, men jag kan redan känna att det här är mindre slitigt för lederna och mer slitigt för flåset.

För simningen innebär det att lära mig växelvis andning, dvs andas var tredje simtag på omväxlande höger och vänster sida. Tidigare har jag andats varannat simtag, alltid på höger sida, och det är lite svårt att få in så mycket luft i lungorna den korta sekund munnen är ovan vattenlinjen att det räcker för tre simtag. Idag skedde dock någon form av litet genombrott och jag kunde köra 400 m oavbrutet i en lugn rytm utan att syret tog slut. Försöken att lära mig voltvändning är dock än så länge fruktlösa.

För cyklingen innebär det att få till ett bra rundtramp, dvs att utnyttja benens alla muskler till att inte bara trycka ner pedalen (mellan klockan 2-5 om man tänker sig vevpartiet som en urtavla) utan även dra den bakåt (5-7), uppåt (7-11) och slutligen trycka framåt (11-2). Det övar jag främst genom att klicka ur ena skon ur pedalen och dra runt cykeln enbart med andra foten under några minuter (det är sjukt tungt!). Det svåra är att få jämnt tryck och hastighet så att inga ”döda punkter” finns på hela varvet. Jag försöker även öka kadensen – det rekommenderas att man trampar 90 varv / minut. Jag har ingen kadensmätare så jag har ingen aning om vad jag ligger på, men förmodligen alldeles för lågt.

Jobb: På måndag slutar min semester från min huvudsyssla som amanuens på IRI (vars webbplats för övrigt numera är uppfräshad, XHTML-validerad, allmänt semantisk och mikroformatbeströsslad). Hösten lär bjuda på en hel del löpande administrationsarbete, men också arrangemang av konferenser och liknande evenemang. Boka den 29 november för konferensen ”Den rättsliga informationsförsörjningen: Säkerhetskrav?” redan nu!

Men jag har ett gäng sidouppdrag av varierande omfattning. Under sommaren har jag jobbat lite med det offentliga rättsinformationssystemet (som jag även var aktiv inom under förra hösten) med att skriva lite skön pythonkod som genererar RDF och XHTML2-versioner av de (konsoliderade) författningar som ingår i SFS. Lite samma som gamla lagen.nu-koden, men den här gången med en kodkvalité som jag faktiskt inte behöver skämmas över. Bland annat har jag därigenom lärt mig att använda Genshi för XML-generering medelst templates – inte alls dumt. Koden kommer förhoppningsvis snart göras tillgänglig under någon lämpligt öppen licens, tillsammans med resten av rättsinfoprojektets kodbas.

Det finns några ytterligare saker i pipen, bland annat medförfattande av en lite mer akademisk artikel om ett spännande ämne, en permanent post som krönikör i en större branschtidning, och en föreläsning på en av juridiska programmets specialkurser. Mer detaljer kommer när det börjar närma sig slutförandet. I övrigt försöker jag hålla nere på extraknäcken så att jag kan fokusera på…

Studier: Har tyvärr varit lite eftersatta under våren. Nu är det bot och bättring som gäller. Skatterätt (som jag inte direkt sett fram emot, men däremot alla mina kompisar som vill göra smarta avdrag) och förvaltningsrätt (vilket ska bli märkligt kul) är det som gäller under året. Jag ska även tenta av de två kurser jag har släpande (fastighetsrätt och processrätt). Banne mig.

Sen är det bara teoretiska terminen (med ffa rättshistoria och allmän rättslära), specialkursterminen (där jag ska försöka begränsa mig till två kurser från ett betydligt större smörgåsbord) och examensarbetet (där jag kommer på ett nytt ämne i veckan som jag vill skriva om) kvar innan jag blir jur kand. Nu är det bara hemvägen (mindre än hälften) kvar, två år. Det är ju ING-EN-TING!

Unicode och python

UnicodeDecodeError: 'ascii' codec can't decode byte 0x84 in position 1: ordinal not in range(128)

Om du programmerat python känner du säkert igen ovanstående felmeddelande. Roten av problemet är att python har två strängtyper; den ursprungliga str som innehåller en singlebytesträng utan någon vidare information om vilken teckenkodning som använts, och unicode, som är en fullständig unicodesträngtyp.

Det faktum att båda dessa typer finns, fungerar och ser nästan likadana ut gör att en pythonprogrammerare måste vara på tårna hela tiden för att vara säker på vad för sorts data han bollar med — något som tar bort mycket av fördelarna med dynamismen i språket. Annars blir det lätt så att kod som verkar fungerande helt plötsligt krashar om någon stoppar in ett åttabitarstecken nånstans där ingen hade tänkt på att kunde göra det. Och om man inte är disciplinerad är det lätt att man snärjer in sig i en röra av unicode(…) och foo.encode(’iso-8859-1’). [1]

För lagen.nu 2.0-koden försöker jag använda unicode rakt igenom, så mycket som möjligt, och behandla str som om det vore en bytearray. Det går …sådär. ElementTree ger alltid ifrån sig unicode och man mår bäst om man bara stoppar in unicode i den (även om det går att stoppa in str, men det är inte att rekommendera att använda något med höga bitten satt). BeautifulSoup är unicode-only från och med version 3, vilket är ett stort steg framåt.

Django är dock ett sorgebarn i sammanhanget. Gränssnitten både utåt, mot webbläsaren och innåt, mot databasen, är str, dessutom utf-8-kodat (om man inte ändrat DEFAULT_CHARSET, vilket dock inte är en egentlig lösning), något som är det sämsta av bägge världar. Jag menar, len av en sträng som innehåller ’räksmörgås’ ska vara 10, aldrig 13, men kolla vad len(u’räksmörgås’.encode(’utf-8’)) är.

Vad värre är, det går inte att stoppa in unicode överallt heller. De modeller man skapar behöver få sina strängvärden som utf-8-kodade str, annars får man 'Warning: Data truncated for column 'subject' at row 1' och frågetecken i databasen. Åtminstone mot min MySQL-databas, som internt lagrar i utf-8 (tror jag). Är det likadant vad gäller Postgres och SQLite? Vem vet?

Det går väl att lära sig leva med. Sätt upp en barriär mellan django-land och min egen kod, och se till att all str-data kodas om till unicode, och vice versa. Det kanske ordnas innan 1.0, men det är mycket att göra.

Django är dessvärre inte ensam i detta — igår tillbringade jag alldeles för lång tid på att försöka använda email-modulen med unicodesträngar, men den modulen är inte unicodesäker. Simpleparse klarar inte heller unicodeindata. Det finns säkert fler exempel.

Just unicodehantering[2] är ett av de få områdena där jag saknar COM/CLR/Java-världen. Jag menar, multibytetecken funkade smärtfritt i VB6 för typ en miljon år sedan.

På temat unicode saknade jag igår några bra teststrängar för att testa unicodedata med lite exotiskare tecken (allt över U+0255, typ) — utländska motsvarigheter till Räksmörgås, typ. Jag hittade inget direkt, men klistrade ihop något baserat på wikipedias förstasida:

Unicode/UTF-8-test

Som en added bonus innehåller den en hel del BiDi-tester med vad jag tror är korrekt rendering. Jag hade ingen aning om att det var ett så komplicerat ämne, men nu har jag lärt mig en ny HTML-tag. Det är inte varje dag!


[1] Så ser lagen.nu 1.0-koden ut. Ibland blir jag påmind om en kompis som förklarade hur han programmerar C: ”klagar kompilatorn så sätter jag dit en ’*’, om inte det hjälper byter jag ut den mot ett ’&'”

[2] Och till en mindre del debugging, även om det har ändrats sedan jag upptäckte Wing IDE

Jack of all trades, master of none

För att bygga lagen.nu 2.0 kommer det krävas ett rätt stort lass med teknologier. Jag har väl ungefär samlat ihop alla pusselbitar och känner mig ungefär som när man storhandlat inför en riktigt ambitiös middagsbjudning — alla råvaror och alla verktyg finns på plats, nu gäller det bara att hantera dem. Lite om vad som ingår:

XML: Lagen.nu 1.0 använde XML en hel del bakom kulisserna, men XML-formaten var odokumenterade, ad-hoc’iga och i största allmänhet inte så väldigt XMLiga (inga namespaces, data som borde varit i noder lagrades i attribut och vice versa, inget utnyttjande av befintliga standarder som RDF och XLink). Det här ska på något sätt bli bättre i 2.0 — det finns en hel del internationella projekt kring hur juridisk information ska representeras i XML, men de flesta känns som kommittéprodukter med mycket snack och lite spec — det känns som XML inbjuder till sånt, i högre grad än andra komponenter i stacken nedan. Metalex verkar dock lovande.

XSLT: För att få ut de semantiskt korrekta XML-dokumenten på webben måste de transformeras till nån sorts HTML. För 1.0 gjorde jag en del riktigt knepiga transformationer då mantrat där var ”vem behöver databaser när vi kan slänga in allt i ett gigantiskt XML-dokument och XPath()a oss fram” — för 2.0 kommer en vanlig old-school relationsdatabas att användas, så förhoppningsvis blir det bara en fråga om att ta befintliga stylesheets och trimma lite.

SOAP: Det här är väl mera på nice-to-have-listan snarare än release-blocker-listan, men det vore kul att åtminstone doppa tårna i hela webservicesträsket. Två problem är dock att jag aldrig har jobbat mot någon skarp web service från klientsidan, så jag vet inte riktigt hur ett WS-API ska kännas för att vara bra, samt att jag egentligen inte har en bra känsla för vilka rättskälleinformationsfunktioner som skulle vara vettiga att göra åtkomliga som webservices.

HTML: Allt ska ju bli HTML till slut, så viss koll på läget måste jag ju ha även här. Det blir förmodligen XHTML 1.0 Strict, en viss grad av divitis (den lösning jag skissade på här visade sig vara ohållbar när man kör den på exempelvis inkomstskattelagen och många kommentarer), och förmodligen tyvärr sämre semantisk meningsfullhet än i 1.0 (det största problemet är numrerade listor — för att sätta en kommentar bredvid en punkt i en punktlista kan jag inte representera själva punkten som en LI-tag — det går inte att placera en P-tag med kommentaren vid sidan av när man är i ett UL-kontext). Dock så ska ett flertal tabeller bort och ersättas med DL-listor, efter tipsen här.

CSS: Ärligt talat är inte 1.0 särskilt visuellt upphetsande. Det kanske inte kommer bli pastellfärger och rundade hörn, men en något mer genomtänkt visuell känsla hoppas jag kunna åstadkomma. Jag har med min nya op-center-setup möjlighet att testa på Firefox, IE, Konqueror, Opera och Safari, och ser med skräckblandad förtjusning fram emot utmaningen att få det hela att se bra ut utan att degenerera till tabellsoppa.

DHTML/AJAX: Inte bara för att det är en nödvändig beståndsdel om man vill vara web 2.0, mycket av wikifunktionaliten skulle vinna på att med lite AJAX göra det möjligt att redigera kommentarer rakt på sidan, a la flickr’s fotobeskrivningsmagi. Och vore det inte coolt om sökrutan föreslog lagtexter a la google suggest? Jag har kollat runt på vad för hjälpramverk som finns på javascriptutvecklingsscenen (jag har läst DHTML Utopia och såg inte alls fram enmot att göra all crossbrowserkompatibilitetssörja för hand) och fastnade tillslut för MochiKit (efter att ha kollat på Dojo, Prototype/script.aculo.us, YUI och den här artikeln) — att det beskrevs som kraftigt pythoninfluerat var ett tungt vägande skäl.

EBNF: En av de bästa sakerna med lagen.nu 1.0 var den EBNF-baserade tolkningen av lagtextreferenser i löpande text. Den är, i ärlighetens namn, mest ihophackad utan någon djupare förståelse för textparsning i den högre skolan, och det känns som att jag borde ägna lite tid åt SimpleParse-dokumentationen för att hantera andra typer av referenser, nu när jag ska hantera flera typer av rättskällor.

Python: Jag hamnar ofta i fällan att när jag, när jag lärt mig ett verktyg tillräckligt bra för att få riktigt jobb gjort med det, slutar lära mig mera. Under våren har jag dock gått tillbaks till grunderna för python och försökt utnyttja mer än de få procent av språket som 1.0 använder. Det blir vettig objektorientering, bättre utnyttjande av standardbiblioteket och kanske lite riktigt funktionell programmerings-tänk (första kapitlet i TPiP är en riktig ögonöppnare här). De tre viktigaste bitarna kommer nog bli Django för allt dynamiskt, BeautifulSoup för allt HTML-tröskande (tillsammans med SimpleParse enligt ovan), och ElementTree för allt XML-byggande.

MySQL: Och så ska ju saker lagras i en databas också. Jag är som tidigare nämnts skeptisk mot RDBMS-användande, men med tanke på hur fint django abstraherar bort det hela känns det motiverat att faktiskt plocka in en databasserver i mixen. Det blir förmodligen MySQL även om alla de coola kidsen använder Postgresql, men då jag redan har en MySQL-server körandes för WordPress, MediaWiki och lite annat, har jag än så länge inte sett nån verklig anledning att byta.

Ungefär så — det blir en ganska diger lista när man kollar på den. Vilket anknyter till den här bloggpostningens titel, det kan inte bli några djupa kunskaper inom varje enskild del (särskilt som en stor komponent inte är med här, nämligen domänkunskapen om just rättsinformation) men förhoppningsvis tillräckligt för att ro det hela i land.

Cachning och throttling för webspindlar i python

Om du inte ägnar dig åt att programmera webspindlar i Python är dagens post förmodligen inte av så stort intresse.

Men: Jag har ägnat långfredagen åt att bland annat putsa på några utökningar till urllib2. För länge sedan skrev jag något i stil med att
urllib2 var alldeles för komplext, men jag lutar nu åt att det ”bara” är underdokumenterat.

Jag behövde ett sätt att från ett pythonprogram hämta websidor utan att hämta samma sida två gånger i onödan. Jag ville dessutom att förfrågningarna inte skulle ske allt för tätt inpå varandra — jag ville med andra ord ha en cache och en throttling-mekanism. Det visar sig att urllib2’s modell med kedjade handlers som anropas efter varandra lät mig göra det hela på ett mycket smidigt sätt.

Här är resultatet: StandaloneRobot.py. Testkoden i slutet förklarar hur det funkar; en anropare kan observera hur cache/throttling har använts genom att titta efter x-cache och x-throttling-headers i resultatet.

Lagringsmekanismen för cachen är inte den smartaste, men den funkar. Den implementerar inte en ”riktig” cache såsom en sådan ska funka enligt HTTP/1.1, utan har en GET-förfrågan till en viss URL skickats så får alla efterföljande förfrågningar till samma URL det cacheade svaret — tills man raderar cachen.

Veckans Del.icio.us-länkar

Den här veckans intressanta länkar, från del.icio.us/staffanmalmgren

Part 7: Regression testing

(A series of blog posts about the tech behind lagen.nu. Earlier parts are here: first, second, third, fourth, fifth and sixth)

Like most developers that have been Test infected, I
try to create regression tests whenever I can. A project like
lagen.nu, which has no GUI, no event handling, no databases, just
operations on text files, is really well suited for automated
regression testing. However, when I started out, I didn’t do
test-first programming since I didn’t really have any idea of what
I was doing. As things solidified, I encountered a particular
section of the code that lended itself very nicely to regression
testing.

Now, when I say regression testing, I don’t neccesarily mean unit
testing. I’m not so concerned with testing classes at the method
level as my ”API” is really document oriented; a particular text
document sent into the program should result in the return of a
particular XML document. Basically, there are only two methods
that I’m testing:

  • The lawparser.parse() method: Given a section of law text,
    returns the same section with all references marked up, as
    described in part 5.
  • Law._txt_to_xml(), which, given a entire law as plaintext,
    returns the xml version, as described in part 4

Since both these tests operate in the fashion ”Send in really big
string, compare result with the expected other even bigger
string”, I found that pyunit didn’t work that well for me, as it’s
more centered around testing lots of methods in lots of classes,
where the testdata is so small that it’s comfortable having them
inside the test code.

Instead, I created my tests in the form of a bunch of text
files. For lawparser.parse, each file is just two paragraphs, the
first being the indata, and the second being the expected outdata:

    Vid ändring av en bolagsordning eller av en beviljad koncession
    gäller 3 § eller 4 a § i tillämpliga delar.

    Vid ändring av en bolagsordning eller av en beviljad koncession
    gäller <link section="3">3 §</link> eller <link section="4a">4 a §</link> i tillämpliga delar.
  

The test runner then becomes trivial:

def runtest(filename,verbose=False,quiet=False):
    (test,answer) = open(filename).read().split("\n\n", 1)
    p = LawParser(test,verbose)
    res = p.parse()
    if res.strip() == answer.strip():
        print "Pass: %s" % filename
        return True
    else:
        print "FAIL: %s" % filename
        if not quiet:
            print "----------------------------------------"
            print "EXPECTED:"
            print answer
            print "GOT:"
            print res
            print "----------------------------------------"
            return False
  

Similarly, the code to test Law._txt_to_xml() is also
pretty trivial. There are two differences: Since the indata is
larger and already split up in paragraphs, the indata and expected
result for a particular test is stored in separate files. This
also lets me edit the expected results file using nXML mode in
Emacs.

Comparing two XML documents is also a little trickier, in that
they can be equivalent, but still not match byte-for-byte (since
there can be semantically insignificant whitespace and similar
stuff). To avoid getting false alarms, I put both the expected
result file, as well as the actual result, trough tidy. This
ensures that their whitespacing will be equivalent, as well as
easy to read. Also, a good example of piping things to and from a
command in python:

def tidy_xml_string(xmlstring):
    """Neatifies a XML string and returns it"""
    (stdin,stdout) = os.popen2("tidy -q -n -xml --indent auto --char-encoding latin1")
    stdin.write(xmlstring)
    stdin.close()
    return stdout.read()
  

If the two documents still don’t match, it can be difficult to
pinpoint the exact place where they match. I could dump the
results to file and run command-line diff on them, but since there
exists a perfectly good diff implementation in the python standard
libraries I used that one instead:

    from difflib import Differ
    differ = Differ()
    diff = list(differ.compare(res.splitlines(), answer.splitlines()))
    print "\n".join(diff)+"\n"
  

The result is even easier to read than standard diff output, since
it points out the position on the line as well (maybe there’s a
command line flag for diff that does this?):

[...]
      suscipit non, venenatis ac, dictum ut, nulla. Praesent
      mattis.</p>
    </section>
-   <section id="1" element="2">
?                   ^^^

+   <section id="1" moment="2">
?                   ^^

      <p>Sed semper, ante non vehicula lobortis, leo urna sodales
      justo, sit amet mattis felis augue sit amet felis. Ut quis
[...]
  

So, that’s basically my entire test setup for now. I need to build
more infrastructure for testing the XSLT transform and the HTML
parsing code, but these two areas are the trickiest.

Since I can run these test methods without having a expected
return value, they are very useful as the main way of developing
new functionality: I specify the indata, and let the test function
just print the outdata. I can then work on new functionality
without having to manually specifying exactly how I want the
outdata to look (because this is actually somewhat difficult for
large documents), I just hack away until it sort of looks like I
want, and then just cut’n paste the outdata to the ”expected
result” file.

Part 5: Finding and formatting inline law references

(Earlier posts: 1, 2, 3, 4)

The second part of the convert-things-to-xml approach deals with
finding inline references to other paragraphs in the law
text. I’ve written about it in part in this
blog post
, but to recap:

Swedish law text contains lots and lots of internal and external
references to other sections. These references have a
semi-standardized format, but they are clearly meant to be parsed
by humans, not machines.

The simplest case is a single reference to a single section
(example from 4 §
Försäkringsrörelselagen (1982:713)
):

    Vid ändring av en bolagsordning eller av en beviljad koncession
    gäller 3 § i tillämpliga delar.
  

Here, the string ’3 §’ signifies a reference to the third section
in the current section of the current law. If we can identify and
mark that reference up, we can make the ’3 §’ into a hyperlink
leading to the definition of section 3. You know, the stuff
hyperlinking was designed for. Currently, this gets transformed
into the following XML (how the XML gets transformed into
clickable HTML is the subject of a later article):

    Vid ändring av en bolagsordning eller av en beviljad koncession
    gäller <link section="3">3 §</link> i tillämpliga delar.
  

For cases like that, the transformation is trivial, and could be
done with regexps or just simple string matching. But for cases
like this (Patentbesvärsrätten
96-837
):

    14 § 1 st. 6) och 6 § varumärkeslagen (1960:644)
  

or better yet, this (49
a § URL
, my personal favourite):

    Bestämmelserna i 2 § andra och tredje styckena, 3, 7--9 och 11 §§,
    12 § första stycket, 13, 15, 16, 18--20 och 23 §§, 24 § första
    stycket, 25--26 b, 26 d -- 26 f, 26 i -- 28, 31--38, 41, 42 och
    50--52 §§ skall tillämpas på bilder som avses i denna paragraf.
  

things get way more complicated. Enter EBNF-grammar-based parsing
with the dynamic duo that is SimpleParse and mxTextTools. Also,
the book Text Processing in
Python
by David Mertz should be mentioned, as it helped me in
the right direction when I realized regexes weren’t going to cut
it.

My previous
post
describes the actual EBNF grammar and how SimpleParse is
used to build a parse tree from it, so you might want to read that too.

However, that is really only half of the problem. After having a
tree of parsed tokens, that might (for a somewhat complicated
scenario) look like the following:

refs': '14 § 1 st. 6) och 6 § varumärkes|lagen (1960:644)'
    'ExternalRefs': '14 § 1 st. 6) och 6 § varumärkes|lagen (1960:644)'
        'MultipleGenericRefs': '14 § 1 st. 6) och 6 §'
            'GenericRefs': '14 § 1 st. 6)'
                'GenericRef': '14 § 1 st. 6)'
                    'SectionPieceRef': '14 § 1 st. 6)'
                        'SectionRef': '14 §'
                            'SectionRefID': '14'
                                'number': '14'
                            'Whitespace': ' '
                        'Whitespace': ' '
                        'PieceRef': '1 st. 6)'
                            'PieceRefID': '1'
                                'ordinal': '1'
                            'Whitespace': ' '
                            'PieceOrPieces': 'st.'
                            'Whitespace': ' '
                            'ItemRef': '6)'
                                'ItemRefID': '6'
                                    'number': '6'
                                'RightParen': ')'
            'WAndOrW': ' och '
                'Whitespace': ' '
                'AndOr': 'och'
                    'And': 'och'
                'Whitespace': ' '
            'GenericRefs': '6 §'
                'GenericRef': '6 §'
                    'SectionRef': '6 §'
                        'SectionRefID': '6'
                            'number': '6'
                        'Whitespace': ' '
        'Whitespace': ' '
        'ExternalLawRef': 'varumärkes|lagen (1960:644)'
            'NamedExternalLawRef': 'varumärkes|lagen (1960:644)'
                'word': 'varumärkes'
                'Pipe': '|'
                'LawSynonyms': 'lagen'
                'Whitespace': ' '
                'LawRef': '(1960:644)'
                    'LeftParen': '('
                    'LawRefID': '1960:644'
                        'digit': '1'
                        'digit': '9'
                        'digit': '6'
                        'digit': '0'
                        'Colon': ':'
                        'digit': '6'
                        'digit': '4'
                        'digit': '4'
                    'RightParen': ')'
  

, how do we turn it into the following XML?

    <link law="1960:644" section="14" piece="1" item="6">
      14 § 1 st. 6)
    </link>
    och 
    <link law="1960:644" section="6">
      6 §
    </link>
    varumärkes|lagen (
    <link law="1960:644">
      1960:644
    </link>
    )
  

Turns out this is a problem that can be solved in a rather generic
manner with a small amount of planning and a little
recursiveness. Basically, all tokens that end in ’Ref
should generally end up formatted as a <link>. All
tokens underneath such tokens that ends in ’RefID’ are
used to find the attributes for these tags. Start at the root,
then recurse depth-first over all child nodes until
done. Sometimes there are notes that end in Ref
underneath other nodes also ending in Ref, in those cases
it’s the top node that is turned into a <link> tag

Of course, there are exceptions and things to be aware of. Two of
these are illustrated in the above example. To correctly insert
the law reference (the SFS id ’1960:644’) for tag to be produced
from the MultipleGenericRefs token ’14 § 1 st. 6) och
6 §
’, we have to plan ahead, when dealing with the parent
node (ExternalRefs). The formatter has built-in knowledge
of the special handling needed, and, when encountering a
ExternalRefs node, finds the ExternalLawRef node
child, stores the underlying LawRefID value, and later
picks this value up when formatting the tags for the two
GenericRef tokens.

To make this looping and recursing easier, I build a OO wrapper
around the array-based parse tree that mxTextTools return. This
was the subject of another
post
.

Note also that the ExternalLawRef token did not result in
a <link> tag, but the underlying LawRefID
token did. This was a consious decision (I thought it looked
better that way), and was implemented by creating a number of
extra subroutines in the formatter. Basically, the main function
acts as a generic dispatcher, by looping over all subtokens in a
tree, and for each token, checks if there’s a corresponding
format_tokenname() function. If so, call that,
otherwise call a generic formatter (which may, recursivly, make
use of the generic dispatcher). The code is pretty simple, but is
indicative of how neat stuff like this can be done in dynamic
langugages:

        try:
            formatter = getattr(self,"format_"+part.tag)
            res = formatter(part)
        except AttributeError:
            res = self.format_tokentree(part)
  

The other wierd thing is that ’|’ sign in the term
varumärkes|lagen’. This is swedish for ’the trademark
law’, but since we like to write words together, this creates an
interesting challenge for creating the EBNF gramar. Basically, I
cannot find a way to match a word that ends in a specific suffix,
such as ’lagen’. The resulting parser is always ’greedy’,
so there seem to be no way of matching these words without
matching all words. So, to fix this, I preprocess the text
before putting it through the parser, using normal python regular
expressions, which can be non-greedy, to put in that ’|’
sign, which solves the problem. Then, after retrieving the string
from the tag formatter, I remove those signs.

One particular satisfying thing of the problem described in this
post is that how well it lends itselfs to automated regression
testing. Any new feature can easily be specified in a test case
before coding begins, and after it’s done, it’s easy to verify
that nothing that previously worked has been broken. More on
regression testing in a later post.

Part 3: Understanding what was fetched

(Earlier posts in this series: here
and here)

There are a lot of ways to extract data from a HTML file. You can
do simple string
searching
(by the way, why is the python documentation for
basic string objects hidden under the non-descript heading
”Sequence types”, and why is there no reference to that part of
the documentation from the separate
string module
, which hardly does anything?) and rexep
munging
, or you can use more
sophisticated
HTML
parsers
. Funnily enough, there are two of these in the Python
standard library, and both of them are callback based — why no
tree-based interface? If the HTML code is modern and well-formed,
you can even use a vast
array
of
XML tools (and if
it’s not, you can fix it with HTML Tidy).

I ended up using the BaseHTMLProcessor
approach from Dive
Into Python.
, which has a whole
chapter
devoted to the art of HTML parsing. Basically, you
subclass BaseHTMLProcessor, implementing callbacks for various
tags, which are called as these tags are encountered in the
document. Your class is responsible for keeping track of whatever
state (ie what depth you are in the document, what tags were
encountered before this one, and so on) that needs to be kept.

There are some things that are cumbersome with this approach. For
example, automatic HTML entity resolving would be good. The HTML
fragment
<h1>r&auml;ksm&ouml;rg&aring;s</h1gt;
represents a single header with the string ”räksmörgås” (a common
test phrase for
swedish programmers), and so it should only result in three
callbacks: start_h1, handle_data (which should
be called with the string ”räksmörgås”), and end_h1.

Instead, the following callbacks are called:

  • start_h1
  • handle_data (called with the string ’R’)
  • handle_entityref (called with the string ’auml’)
  • handle_data (called with the string ’ksm’)
  • handle_entityref (called with the string ’ouml’)

…you get the idea. There exists a
mapping
that helps with the entity resolving, but for the HTML
case, this could have been solved at a lower-level stage.

Still, for the parsing problems I have, the
callback-based/keep-your-own-goddam-state-approach works. Most of
the time I’m just concerned with finding the elements
in a table
, meaning I have to keep track of what cells I’ve
seen and when a new table row starts, things like that. As I go
along, build up a list of mappings or something similar, and then
just use that list once done. The calling code gets quite nice and
simple:

cl = SFSChangelogExtractor()
cl.feed(open("downloaded/lawinfo/%s.html" % self.basefile).read())
for c in cl.changelog:
    if c.item('SFS-nummer') == current_transitional_id: ...
  

(Note that the ’c’ object here is not a standard dictionary, but a
mapping-ish object that also keeps track of the order keys have
been inserted. That’s why it’s c.item('SFS-nummer') and
not c['SFS-nummer']. That, and the fact that I was too
lazy to implement the special
methods
needed to do a proper Duck Typed
dictionary.)

The one exception is the problem of finding all the plaintext in a
law text like this
one
, but it’s even easier: Just increment a counter whenever a
<pre> tag is encountered, decrement it when seing
</pre>. In handle_entityref and handle_text, just
check if the counter is > 0 and if so, append the text to a StringIO
object.

Lagen.nu behind the scenes

Now that lagen.nu has been out for
some time, it might be a good
idea to write down what I’ve learned from it so far, in blog
form. Much of the discussion will be centered around python, a
language I’m far from proficient in, but it’s possible that
someone will learn at least something from it.

First, take a look at this
post
that explains what lagen.nu is, from a user
perspective.

This post is about how the site is produced. When I started out, I
had no clear idea of what I wanted to do, other than to download
the text of all swedish laws and convert it to some sort of nice
HTML. I knew I wanted to do as much as possible with static HTML
files, and I had a hunch that XML would be involved in some way.

So, essentially, the code only needs to run off-line, with no
GUI required.

I thought about doing this in C#, since it would be a good
experience building project in a language for which expertise is
highly sought after. But since I’m no longer
programming for food
(actually I am, for another four days,
but still), I took the opportunity to do it in python, a language which I’ve
always liked but never become friends with.

From a high level, the code does the following:

  • Finds out what laws are available
  • Downloads the law text HTML documents
  • Converts the text to XML
  • Transforms the XML to HTML

There are some extra steps involved in creating the front page,
RSS feeds, and handling the verdicts database, but these are the
main steps.

The result of the program is a tree with static HTML files, ready
for deployment.

I started out by looking
for a good Python IDE
. I did not find it, and settled for Emacs
with python-mode.

Once set up with a recent version of python-mode, properly
configured, I had a nice light-weight development
environment. Here’s my minimal configuration (this goes into your
.emacs file):

(autoload 'python-mode "python-mode" "Python Mode." t)
(add-to-list 'auto-mode-alist '("\.py'" . python-mode))
(add-to-list 'interpreter-mode-alist '("python" . python-mode))
(setq py-python-command "C:\Python23\python.exe")
  

My code lives in classes, and to test things out, I have code at
the end of the main code file that looks sort of like the
following:

if __name__ == "__main__":
    vc = VerdictCollection()
    vc.get(2004,refreshidx=True)
  

(That is, if I want to test the get method of the
VerdictCollection class). To test the code, I just press
C-c C-c in the python editor window. The entire python buffer gets
sent to the python shell, and the last part (after if __name__
== "__main__":
) executes.

Things that are good about this environment:

  • Free, in both senses of the word
  • The intendation support really works, which is quite important with python
  • Reasonably fast edit-run cycle
  • The interactive python shell

Things that are bad:

  • I can’t debug stuff. It seems like it should be
    possible
    , but I have no pdb.exe, which seems to be a
    requirement. In particular, it would be nice to be able to
    automatically start debugging when an unhandled exception is
    raised.
  • Copy and paste from the *Python* buffer has character set
    problems. For example, if my code outputs a § sign, and I cut’n
    paste it into another file, emacs will complain:

    These default  coding systems were tried: 
    iso-latin-1-dos
    However, none of them safely encodes the target text.

    This is bogus, since the § sign is perfectly legal in latin-1.

I use the standard python.org distribution of Python 2.3 (I
haven’t gotten around to upgrading to 2.4 yet), not the ActiveState
one
. I tried it, and like the fact that the win32com module is
bundled, but the python.org version is a leaner download and has a
more usable HTML help application (particularly the good index).

To get a grip of how to do things with python, I’ve used the
online version of Mark Pilgrim’s Dive Into
Python
, as well as the Python
cookbook
. This, together with the reference manual, (the eff-bot
guide to) The Standard Python Library
and Text Processing in Python has
been all I need so far.