Posts Tagged ‘XML’

Jack of all trades, master of none

lördag, juni 24th, 2006

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.

Allt du lärt dig om webutveckling är fel

onsdag, augusti 31st, 2005

Ett uttryckligt designmål för lagen.nu har varit att göra så mycket som möjligt med statiska filer. Det var en nödvändighet av prestandaskäl när tjänsten körde på en 166Mhz Pentium, och det är även idag en vettig målsättning, inte minst ur robusthets- och säkerhetssynpunkt.

Så länge som en webtjänst inte förändras på grundval av användarnas interaktion finns det inte någon strikt anledning att göra den med dynamisk teknik som ASP, PHP, CGI-script, servlets/JSP etc. Det kan förstås finnas utvecklingsmässiga fördelar; i princip alla nya tekniker för webutveckling utom möjligtvis XSLT är anpassat för dynamisk sidgenerering, så man når kanske sina utvecklingsmål snabbare med en dynamisk lösning. Men jag har ingen brådska på det viset.

Etiketteringen är dock den första funktionen som tar emot data från användaren, och har som sådan berett lite extra problem. Det första problemet jag hade att lösa var att jag ville att funktionen bara skulle vara tillgänglig för de som explicit ville ha den under en testperiod. I dynamiska system gör man oftast liknande saker med inloggningsförfaranden och sessionsobjekt, samt villkorlig kod som, beroende på inloggningsstatus, spottar ur sig lämplig HTML-gränssnittskod.

Men i en statisk lösning kan man inte göra sådant på servern. Däremot kan klienten åläggas att göra en hel del dynamiska trick! Lösningen fick bli att den gränssnittsdetalj som används för etiketteditering initialt gömdes med en style="display: none;", för att sedan, när sidan laddats klart, eventuellt göras synlig baserat på om kakan tags är satt till enabled med javascript i den här stilen (elementet heter “comment” av hysteriska skäl):

    if (GetCookie('tags') == 'enabled')
    {
	if (document.getElementById('comment'))
        {
	    document.getElementById('comment').style.display="block";
         }
    }

Naturligtvis kan man inte använda en sådan här lösning om den detalj som man eventuellt vill gömma innehåller någon typ av hemlig information — det är trivialt för en angripare att kolla på HTML-koden för att hitta informationen.

Ett annat designmål med lagen.nu är att så långt som möjligt undvika att använda en traditionell RDBMS som MySQL, SQL Server, Postgres etc. Även här rör det sig om en önskan att hålla saker så enkelt som möjligt genom att undvika en separat körande process, som dessutom implementerar sin helt egen rättighetsmodell (vanligtvis), kräver sina egna backup- och synkroniseringsrutiner. En databasserver är, om man tänker efter, bara ett uppsvällt userland-filsystem med en väldigt bra ls :-)

På lagen.nu jobbar jag framförallt med dokument - lagtexter och liknande, som är väldigt naturligt att representera som filer i ett filträd, snarare än BLOBar i en databas. Men även för datamodeller som traditionellt skulle representeras i en RDBMS har jag valt att använda filer - XML, närmare bestämt. Ett exempel är kopplingen rättsfall - lagparagrafer. Varje rättsfall kan referera noll eller flera lagparagrafer (inklusive i olika lagar), och varje lagparagraf kan naturligtvis refereras från många rättsfall. Istället för att ha en Verdicts-tabell, en LawParagraphs-dito, och sen en många-till-många-mappingtabell har jag ett stort XML-dokument med grundläggande uppgifter om alla rättsfall, ordnade efter lagar och paragrafer. Sen länkar jag in relevanta rättsfall under XSLT-transformationen, beskrivet här (under “Accessing data in other documents”). XML-dokumentet innehåller redundant data, men eftersom det inte är den ursprungliga källan till informationen (det skapas av bara farten under den vanliga transformationen av rättsfallen) är det ok.

Den stora fördelen med detta är att mycket funktionalitet kan implementeras i XSLT istället för python eller SQL (vissa skulle ifrågasätta om det är en fördel, men jag tycker det har sin inneboende charm).

Etiketter har en liknande datamodell med en många-till-många-relation mellan etiketter och lagar, och även här är tagdatabasen en stor XML-fil (eller, än så länge är den ganska liten, men ge den tid).

För att ta emot den nya informationen måste jag tyvärr frångå mina principer om en totalt statisk sajt. Ett litet CGI-script tar emot formulärdatan, uppdaterar etikettdatabasen, och ansvarar för att fixa till HTML-filen: när en användare lägger till en etikett på en lag måste den statiska HTML-filen som utgör den formatterade lagen ändras för att inkludera den nya etikettexten.

Och här råkar vi på ett problem som inte finns i en helt statisk sajt: prestandakrav.

Sidorna på lagen.nu genereras vanligtvis (i sista steget) från XML till XHTML genom en XSLT-transformation från helvetet. Den gör mycket magiska saker, och den tar lång tid - den mest krävande lagtexten, Rättegångsbalken, tar strax under 45 sekunder. Det är inte OK om användaren måste vänta så länge för att sätta en tag. Istället opererar CGI-scriptet på den färdiggenererade HTML-filen genom att mmap’a den, söka sig fram till en markör-kommentar, och dumpar sedan in den nya taglistan.

För att tokfort hitta relevanta taggar för lagen i fråga använder jag cElementTree , och sen spottar jag ut HTML på gammalt klassiskt f.write("<a href='foo'>bar</a>")-manér.

Det är dock fortfarande långsamt att lägga till stora mängder etiketter på en lag, detta på grund av lite suboptimalitet på ett annat ställe som jag för tillfället helst sopar under mattan. Dessutom skalar det inte, men när jag får så mycket etiketter att det blir ett problem ska jag jubla.

Så, för att summera: Undvik dynamiska webutvecklingsramverk, och undvik relationsdatabaser. För att vara mer tvärtemot etablerad webutvecklingskutym skulle jag behöva programmera i assembler.

Gränssnittet för etikettering i sig är däremot en dynamisk HTML-festival i miniatyr. Imorgon ska jag berätta om min nyvunna fascination för den här utskällda webdesigngrenen.

Part 6: Transforming with XSLT

lördag, december 18th, 2004

Now, after having downloaded, analysed and XML-ified the lawtext, theres just one step left: Convert them to HTML. Enter XSLT.

XSLT is a complex beast, and I find that it’s better to approach it as a programming language, albeit with an unusual syntax, than a stylesheet language. I have one master stylesheet that I use for the transformation (now over 600 lines of XSLT code), and during devlopment I found out quite a few tricks. By the way, if you want to see the original XML code for any law on lagen.nu, just add the suffix “.xml” to the url, ie http://lagen.nu/1960:729.xml.

Creating useful tooltips: An important aspect of the work done with lagen.nu is that I’m trying to not only produce more astethically pleasing layouts of the text, I try to cross-reference as much as possible, to really take advantage of the medium.

As one example: The internal references that were discussed in the last post, should of course be transformed to ordinary hypertext links, on the form <a href=”#P26c”>26 c §</a>, so that you quickly can jump around in the document and follow the references. But we can do better than that, by extracting the first 20 or so words from section 26 c, and stick them into the title attribute of the a tag:

    <a href="#P26c" title="Ägaren till en byggnad eller ett
  bruksföremål">26 c §</a>
  

Now, if you hover with the mouse over the link (assuming you have browser hard/software where these things are possible), the title text will be shown as a tooltip. This makes it even easier to make sense of a section, since you get an instant reminder of what the referenced section says — in many cases you don’t even have to click on the link.

So, initially, my plan was to have things like these tooltip strings prepared in the XML document, and just do a very simple transform into HTML. But as the work progressed, I found that I was almost always able to do it in XSLT instead. This is the relevant part of the link template:

<xsl:attribute name="title">
  <xsl:variable name="text">
    <xsl:choose>
	<xsl:when test="$hasChapters and $sectionOneCount = 1">
	  <xsl:value-of select="/law/chapter/section[@id=$section]/p[position() = $piece]"/>
	</xsl:when>
	<xsl:when test="$chapter != ''"><xsl:value-of select="/law/chapter[@id=$chapter]/section[@id=$section]/p[position() = $piece]"/></xsl:when>
	<xsl:otherwise><xsl:value-of select="/law/section[@id=$section]/p[position() = $piece]"/></xsl:otherwise> 
    </xsl:choose>
  </xsl:variable>
  
  <xsl:variable name="real-width">
    <xsl:call-template name="tune-width">
	<xsl:with-param name="txt" select="$text"/>
	<xsl:with-param name="width" select="160"/>
	<xsl:with-param name="def" select="160"/>
    </xsl:call-template>
  </xsl:variable>
  <xsl:value-of select="normalize-space(substring($text, 1, $real-width - 1))" />
</xsl:attribute>
  

The variables $chapter, $section, and $piece gets their values earlier in the template, and are set to the place the link goes to. $hasChapters and $sectionOneCount are set globally for the document and are used to tell what kind of section numbering this particular lawtext is using. There are three variants commonly used:

  • No chapters, just simple ascending section numbering
  • Chapters with restarting section numbering (ie, regardless of the number of sections in chapter 1, the first section in chapter 2 will be numbered ‘1 §’ — ie, we need to know the chapter as well as the section — just ‘17 §’ is ambigious)
  • Chapters with continous section numbering (ie, if the last section in chapter 1 is ‘16 §’, the first section of chapter 2 will be numbered ‘17 §’ — ie, the section number is never needed to unambigosly determine what ‘17 §’ refers to).

The code constructs a XPath expression and finds the node that the link refers to, and stores it in the variable text. Then, it trims the string down to a suitable length (max 160 chars, in this case) by using the user-defined function tune-witdh, together with normalize-space and substring. tune-width ensures that we end the string on a word boundary. The result of all this is assigned to the title attribute.

Generating a TOC: Again, if you look at the swedish copyright law, you will notice a big blue link titled “Visa innehållsförteckning”. Clicking on this yields (if you have a browser that supports javascript and DOM level 1) a table of contents (TOC), generated from chapter headings and other headlines. It starts out as hidden, since it usually is in the way, but sometimes it’s very useful.

The XML document in itself do not contain any TOC data. To generate the TOC, we use a number of mode-specific templates that extract the relevant information from headlines contained in the document, all triggered by a <xsl:apply-templates> call:

<div id="toc" style="display:none;">
  <xsl:apply-templates select="law/chapter[(headline or section)]" mode="toc"/>
</div>

...

<xsl:template match="headline" mode="toc">
  <xsl:if test="@level = '1'">
	<div class="l1">
	  <a>
	    <xsl:attribute name="href">
	      <xsl:choose>
		<xsl:when test="substring(.,1,5) = 'AVD. '">#R<xsl:value-of select="@id"/></xsl:when>
		<xsl:when test="../../chapter">#K<xsl:value-of select="../@id"/></xsl:when>
		<xsl:otherwise>#R<xsl:value-of select="@id"/></xsl:otherwise>
	      </xsl:choose>
	    </xsl:attribute>
	    <xsl:value-of select="."/>
	  </a>
	</div>
  </xsl:if>
  <xsl:if test="@level = '2'">
	<div class="l2">
	  <a href="#R{@id}"><xsl:value-of select="."/></a>
	</div>
  </xsl:if>
</xsl:template>
<xsl:template match="section" mode="toc"/>

<xsl:template match="changes" mode="toc"/>
<xsl:template match="appendix" mode="toc"/>
<xsl:template match="preamble" mode="toc"/>
  

Things to note: The text-to-XML conversion is responsible for determining the ‘level’ of the headlines. Level 1 headlines are usually associated with a chapter (though not always), and we use some tests to determine if that is the case. If so, the resulting link uses a “#K<number>” anchor fragment, otherwise a “#R<number>” fragment. “K” is for “kapitel” (chapter), while “R” is for “rubrik” (headline). Not strictly neccesary, but I prefer a link that explicitly says “this link goes to chapter 4″, rather than “this link goes to the 14th headline”, particularly as the number of headlines can change in the future, which would make the link point to the wrong place.

I have a number of “empty” templates. They are needed, since if I don’t have them, the base template kicks in and just copies the entire text, which seriously messes up the TOC. Now, I should be able to limit that with the select attribute of the <xsl:apply-templates> tag, but I have been unsuccesful (the reason I select both headlines AND sections, then do nothing with the sections, is that I’ve been experimenting with using the first lines of each section in the TOC as well, but that came out too messy).

Accessing data in other documents: To properly understand a section in a law, it helps to read court verdicts that reference it. In part 2, I described how to fetch data from Domstolsväsendets rättsinformation, which has this data. Basically, I have a python code snipped that goes through all 1800+ verdicts, uses the parser that was mentioned in part 5 to find references to laws and sections therein, and then generates a “cache.xml” file, that contains references to all verdicts, sorted by law, chapter and section, like this:

<?xml version="1.0" encoding="iso-8859-1"?>
<verdicts>
<law id="1736:0123_2">
<chapter id="9">
  <section id="5">
    <verdictref caseid="NJA 2002 s. 577 (alt. NJA 2002:70)" casenumber="T3933-01" id="2002/758" verdictdate="2002-11-22">
      Gäldenär, som hade flera skulder till en borgenär, har ansetts ha rätt att destinera sin be
    </verdictref>
  </section>
</chapter>
<chapter id="10">
  <section id="1">
    <verdictref caseid="NJA 2000 s. 667 (alt. NJA 2000:97)" casenumber="T4689-98" id="2000/772" verdictdate="2000-12-18">
      Sedan A och B var för sig ställt pantsäkerhet för C:s lån i en bank, har banken gjort sig f
    </verdictref>
    <verdictref caseid="NJA 2000 s. 88 (alt. NJA 2000:13)" casenumber="Ö3863-98" id="2000/923" verdictdate="2000-02-23">
      Ett bolags upplåtelse av företagshypotek har ansetts bli sakrättsligt gällande när företags
    </verdictref>
  </section>
  [...]
  

In the top level of the stylesheet, I select the relevant nodeset from this cache document, and store it in a variable. To be able to select, I first need to know the id of the law (the ‘SFS-nummer’), and so I pass it as a command line parameter:

<xsl:param name="lawid"/>
<xsl:variable name="relevantVerdicts"
  select="document('generated/verdict-xml/cache.xml')//law[@id=$lawid]"/>
  
Then, as I process each section, I check the nodeset to see if there’s any verdicts relevant to the section:
<xsl:variable name="sectionChapterVerdicts" select="$relevantVerdicts//chapter[@id=$chapterid]/section[@id=$sectionid]/verdictref"/>
<xsl:if test="$sectionChapterVerdicts">
<div class="metadata">
<xsl:for-each select="$sectionChapterVerdicts">
  <xsl:variable name="linktext">
    <xsl:choose>
      <xsl:when test="@caseid"><xsl:value-of select="@caseid"/></xsl:when>
      <xsl:when test="@casenumber">Målnummer <xsl:value-of select="@casenumber"/></xsl:when>
      <xsl:when test="@diaryid">Diarienummer <xsl:value-of select="@diaryid"/></xsl:when>
      <xsl:otherwise>Vägledande dom</xsl:otherwise>
    </xsl:choose>
  </xsl:variable>    
  <xsl:variable name="real-width">
    <xsl:call-template name="tune-width">
      <xsl:with-param name="txt" select="."/>
      <xsl:with-param name="width" select="80"/>
      <xsl:with-param name="def" select="80"/>
    </xsl:call-template>
  </xsl:variable>
  <a href="/dom/{@id}"><xsl:value-of select="$linktext"/></a>: <xsl:value-of select="normalize-space(substring(., 1, $real-width - 1))" />... (<xsl:value-of select="@verdictdate"/>)<br/>
</xsl:for-each>
</div>
</xsl:if>
  

Again, a little extra work is done to make sure the explaining text is cut at a word boundary (we could‘ve done that in the python code that generates cache.xml), and also that the text for the actual link makes sense. You see, different courts have different systems for assigning ID’s to cases: HD (the swedish supreme court) uses the page number as the case is published in that year’s anthology of relevant verdicts (Nytt Juridiskt Arkiv, NJA), other uses a court-specific identifier, and some use a derivative of the date when it was handled. Since these identifiers represent different things, they are also represented with different attributes in the XML file, and a little <xsl:choose> trickery selects the most appropriate ID.

Optimization hints: Some of the law texts are quite big, and processing them can take a long time. To speed things up, here are some of the things I did:

  • Don’t use the python interface to libxsl! I started out with this, but it turned out to take twice or even three times as long for a transformation, as compared to running command-line xsltproc (win32 binaries) through an os.system() call.
  • Use the –profile switch to xsltproc. It’s pointless to optimize if you don’t know where the bottlenecks are, and with xsltproc it’s so easy to find them.
  • Store frequently-referenced nodes, nodesets and values in variables, instead of selecting them through XPath queries all the time. Yeah, this one is kind of obvious, but it really helps, particularly in those “inner” templates that are used all the time.

Just by using the above tips, the transformation of my standard test case (1960:729) went from 90 seconds to three. Still, I have other test cases that still takes several minutes, so clearly there’s still some work to do…

Part 4: Converting stuff to XML

torsdag, december 16th, 2004

(If you missed part 1-3, they are here, here and here)

If you look at a sample law text as they are presented in SFST, they are 100% plaintext. In order to convert them to semantically sensible XML, we must look for patterns in the plaintext, to identify things like headlines, start of sections and references.

I did this with a two-part approach. First; I break down the text into it’s individual paragraphs and determine what each paragraph is. This analysis operate on a ‘block level’ — either a block of text is a headline, part of an ordered list, the start of a section etc, or it isn’t. A block can’t be half headline and half ordered list.

Secondly, if the paragraph can contain references to other parts of the law (or parts of other laws, for that matter), I analyze the text in greater detail to find and resolve these references. This step operates on a ‘token’ or character level – a block of text can contain zero, one or many of these references.

The first part is fairly easy, at least conceptually. I wrote a bunch of functions like is_chapter(p), is_section(p), is_headline(p), where each individual function just performs a simple test. These are then used from a simple loop that uses a bunch of local variables to keep track of what kind of structures we’ve encountered so far – a so-called state machine.

These functions started out as very simple regexp-based inline expressions, but as I encountered more and more exceptions to my simple rules, their complexity grew. The body of current swedish law is over 250 years old, and consistency has not been the law makers forte. Here’s an example of how to recognize the start of a section:

    re_SectionId       = re.compile(r'^(\d+ ?\w?) §[ \.]') # used for both match+sub
    re_SectionIdOld    = re.compile(r'^§ (\d+ ?\w?).')     # as used in eg 1810:0926

    def is_section(self,p):
        section_id = self.section_id(p)
        if section_id == None:
            return False
        if section_id == '1':
            if self.verbose: print "is_section: The section numbering's restarting"
            return True
        # now, if this sectionid is less than last section id, the
        # section is probably just a reference and not really the
        # start of a new section. One example of that is
        # /1991:1469#K1P7S1. We use util.numsort to get section id's
        # like "26 g" correct.
        a = [self.current_section,section_id]
        
        if a == util.numsort(a):
            # ok, the sort order's still the same, which means the potential new section has a larger ID
            if self.verbose: print "is_section: '%s' looks like the start of the section, and it probably is (%s < %s)" % (
                p[:30], self.current_section, section_id)
            return True
        else:
            if self.verbose: print "is_section: Even though '%s' looks like the start of the section, the numbering's wrong (%s > %s)" % (
                p[:30], self.current_section, section_id)
            return False
    
    def section_id(self,p):
        match = self.re_SectionId.match(p)
        if match:
            return match.group(1).replace(" ","")
        else:
            match = self.re_SectionIdOld.match(p)
            if match:
                return match.group(1).replace(" ","")
            else:
                return None
  

So, the start of a section looks like ‘<number> [letter] §’, unless it looks like ‘§ <number> [letter]’, and as long as the section id is larger than the previous section id, unless it’s restarting at 1, and also taking into account that section ids can contain an optional letter (like ‘26 g’). Simple as that.

For example, below is the start of the swedish copyright law (which has, during development, been my foremost test case). Now, if you don’t read Swedish, just know that the first paragraph signifies the start of chapter one (”1 Kap.”), the second the start of a section(”1 §”), and then there are three items in an ordered list, and finally a plain ol’ paragraph of text. (By the way, for my swedish readers: The swedish legal term ‘paragraf’ is NOT the same as the english typographical term ‘paragraph’, but rather translates into ’section’. When the english word ‘paragraph’ is used, it is in the same sense as the swedish word ’stycke’)

1 Kap. Upphovsrättens föremål och innehåll

1 § Den som har skapat ett litterärt eller konstnärligt verk har
upphovsrätt till verket oavsett om det är

1. skönlitterär eller beskrivande framställning i skrift eller tal,

2. datorprogram,

[…other items in the ordered list omitted for brevity…]

Till litterära verk hänförs kartor, samt även andra i teckning eller
grafik eller i plastisk form utförda verk av beskrivande art.

[…more things omitted for brevity…]

2 § Upphovsrätt innefattar, med de inskränkningar som nedan stadgas,
uteslutande rätt att förfoga över verket genom att framställa exemplar
därav och genom att göra det tillgängligt för allmänheten, i
ursprungligt eller ändrat skick, i översättning eller bearbetning, i
annan litteratur- eller konstart eller i annan teknik.
  

So, to make sense of this, the following state transitions are done:

  • For the first paragraph, is_chapter() will return True, so we transition to the in_chapter state. This will emit a <chapter id=”1″> tag to the result, along with the text.
  • For the second paragraph, is_section() will return True, transitioning us to the in_section stage as well. It’s important to realize that these states are mostly independent; we can be in a chapter w/o being in a section, and vice versa (some laws have only sections, no chapters). This transition will of course emit a a <chapter id=”1″> tag to the result.
  • For the third paragraph, is_ordered_list() will return True, transitioning us to the in_ordered_list state, and emitting <ol><li>1. skönlitterär eller […]</li> to the result. A couple of things to note about this:
    • These tags may look like HTML tags, but they’re not. They do, in this case, share the same semantics, though.
    • A HTML ordered list (<ol>) keeps track of it’s numbering, and any user-agent should add the numbers to the displayed result. Since this is not HTML, we don’t do that, and instead keep the number that was in the original text. Mostly because we’re not sure that no ordered list in the entire body of swedish law doesn’t contain sequence gaps or things like ‘26 g’.
  • For the fourth, is_ordered_list() will again return true, but now we’re already in the in_ordered_list state, so we don’t emit the initial <ol> tag.
  • Then we omit some boring junk, and then we encounter a normal paragraph. There is no function named is_ordinary_paragraph(), this is just infered from the fact that none of our other test matched. Now, the start of a normal paragraph is implicit evidence that our ordered list must have ended, and so the code to transition into in_normal_paragraph state checks to see if we’re in the in_ordered_list state, and if so, emits the trailing </ol>. After that, the normal paragraph gets emitted as <p>Till litterära verk […]</p>.
  • Similarly, when we encounter the start of a new section (”2 § Upphovsrätt innefattar“), we transition out of any ordered lists we might be in, out of the in_section state, emit </section>, and then transition into the same state again.

The largest part of this high-level parsing was to find all the different structures and discover the implicit state transitions that needed to take place. One interesting problem is determining wheter a given paragraph is a headline or just a really short ordinary paragraph. Well, it turns out that a headline never ends with a period, and a ordinary paragraph always does. Except for headlines that end in “m.m.” (swedish for “etc.”). And ordinary paragraph that end with “,”, “och” or “samt”. But a headline is always followed by a section, so we can peek ahead to see if the next paragraph matches is_section(). Except for those cases where a headline is followed by ANOTHER headline, which is then followed by a section…

A lot of work, and there are still a lot of places where we break (for example, things like definition lists, nested ordered lists, and tables, are not yet supported). Furthermore, tweaking to satisfy one case can easily make other cases break. I will return to this problem in the part about regression testing.

Some other things: As you may have guessed by the usage of the term “emit”, I construct the XML by hand in a single pass. This means that I write XML data straight to a file, without using DOM or anything similar. It was just easier to start out that way. I am thinking of overhauling the state machine mechanism a bit, and I might take a DOM approach then.

Some other parts of the code that constructs XML documents (like the code that handle court verdicts) use DOM, and it works pretty fine, with one small exception: It does not play nice with xml fragments in string form. I have a separate class to find law references in text (to be covered in deeper detail in the next post), and the interface of that class is a plain string one: it returns strings like “<link law=”1960:644″ piece=”2″ section=”1″>1 § 2 st.</link> varumärkeslagen“. Since there isn’t any method on individual element objects like ele.ParseAndAppendMixedContent(), I first have to create a ‘fake’ XML document and transform it into a node tree with parseString (from minidom):

    s = '<?xml version="1.0" encoding="iso-8859-1"?><%s>%s</%s>' % (
                        element, string_containing_mixed_xml_content, element)
    fragment = parseString(s)
    subele = fragment.documentElement
    ele.appendChild(subele)
  

Another drawback of writing XML the raw way is that there is no guarantee that your output will be valid. Special care is taken to escape things like < and &, and to ensure the document is well-formed, it’s also run through HTML Tidy (with the options -q -n -xml –indent auto –char-encoding latin1), which, despite it’s name, is an excellent tool to tidy up XML as well.

Legal document standards, part 2

fredag, december 3rd, 2004

Rasmus blogs about open standards and open access for law texts, a topic of great interest to me as of lately (or ‘obsession’, apparently :-). We both agree that there are too little of that, and that the much-heralded open governement could do better in this area.

Anyway, I came across an interesting report, a summary from a conference held about two years ago. It featured various people working with legal information systems, both in the government and private companies, sharing their views on standardisation of document formats and systems. There are views from the people behind Rixlex, Infodata and Notisum, amongst others, but also an interesting view into the state of legal information standards in Norway. They seem to be way ahead of Sweden in this area.

The general consensus seemed to be “standardization is good, and we should do it”, but with no real commitments or timeplans. Maybe there has been developments that I don’t know about since then. This was, after all, two years ago.

Meanwhile, if you want to do interesting stuff today with the body of swedish law, such as making a WAP version or performing graph analysis of all references contained in the 7500+ texts, just download my completely non-{standardized,documented} XML version and go nuts!

There are now at least three document standards, or efforts to create such, for marking up law texts and other legal doucments on my radar: uscfrag (mentioned earlier), used by Cornell University for marking up US Code, LegalXML which seems to be US-centric, and LEXML, which appears to be more EU-centric. It even has it’s own Sourceforge page!

I had no idea so much was going on in so many committees when I started working on the XMLization of swedish law. In a way I’m glad that I didn’t, since I probably would have focused too much on adhereing to these emerging standards and less time to, you know, getting things done. Or worse, just waited for them to actually finish.

The US Code in XML

måndag, november 22nd, 2004

From this groklaw post, I learned that Cornell University has transformed the entire US Code to XML. Now, I’m not well versed in US Law, but this is basically all federal laws currently in effect in USA, right? This is a very cool project. I’ve started to look around, but seemingly as I only barely can navigate around in swedish law texts, there’s a lot of exploring to be done. If you want to just read the laws, this seems to be a good place to start.

(By the way, could any US law scholar tell me whether the US constitution is a part of USC, or if it’s a separate collection of documents? I cannot seem to find them, only references to it.)

As I’ve been working on a project with similar goals for the last few months, I’m particularly interested in the XML format looks. I’ve only started to scratch the surface, but initially it seems to be somewhat more presentation-oriented (and way more complex) than the simple structures I use.

Maybe this format could be used anyway, or at least a XSLT transformation to transfer from Lagen.nu-ML (not yet documented) to uscfrag.