ucum-org / ucum

https://ucum.org
Other
48 stars 10 forks source link

Link to XSLT implementation #226

Open timbrisc opened 1 year ago

timbrisc commented 1 year ago

Issue migrated from trac ticket # 5828

component: help | priority: major

2022-08-03 14:23:40: Clinical.Terminology@mft.nhs.uk created the issue


In your implementation support section it says there is a link to be provided for the XSLT implementation. Please can you provide the link for this to enable us to implement this in the EPR data warehouse we use at our NHS Trust in the UK.

Many thanks,

Clinical Terminology Team Manchester University NHS Foundation Trust

gschadow commented 1 year ago

This ticket is orphaned because text from the original trac site had been mass purged (to which I quite frankly disagree, nothing should have been mass purged).

Once the text is brought back, we can look at that. I might have done this XSLT stuff once a long time ago, but it was never an actual semantic implementation.

chgessner commented 1 year ago

This refers to an implementation of a parser in XSLT which I did once a while ago. However, it never reached production stage. The motivation at that time was to create a set of tools to convert units as part of the "display process", i. e. converting some source to HTML without relying on XSLT-external code like Java.

gschadow commented 1 year ago

Funny, I have done something like that while still at Regenstrief when some southern Indiana town was flooded and I whipped up some CDA EHR viewer XSLT. Let's see if I can still find that ... here is something you might try:

<xsl:transform version="2.0"
           xmlns:saxon="http://saxon.sf.net/"
           xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
           xmlns:f="f"
           xmlns="urn:hl7-org:v3"
           exclude-result-prefixes="f saxon xsl">

   <xsl:variable name="ucum-essence" select="document('ucum-essence.xml')"/>
   <xsl:variable name="prefix" select="$ucum-essence/*/prefix"/>
   <xsl:variable name="metric-unit" select="$ucum-essence/*/base-unit|$ucum-essence/*/unit[@isMetric='yes']"/>
   <xsl:variable name="non-metric-unit" select="$ucum-essence/*/unit[not(@isMetric='yes')]"/>
   <xsl:variable name="prefix-regex" select="string-join($ucum-essence/*/prefix/@Code,'|')"/>
   <xsl:variable name="metric-regex" select="replace(string-join($metric-unit/@Code,'|'),'([\[\]\*\^])','\\$1')"/>
   <xsl:variable name="non-metric-regex" select="replace(string-join($non-metric-unit/@Code,'|'),'([\[\]\*\^])','\\$1')"/>

   <xsl:function name="f:formatUnit" saxon:memo-function="yes">
      <xsl:param name="code"/>
      <xsl:if test="$code">
     <xsl:analyze-string select="$code" regex="[/\.\(\)]">
        <xsl:matching-substring>
           <span class="unit-operator"><xsl:sequence select="if(.='.') then ' ' else ."/></span>
        </xsl:matching-substring>
        <xsl:non-matching-substring>
           <xsl:sequence select="f:formatAnnot(.)"/>
        </xsl:non-matching-substring>
     </xsl:analyze-string>
      </xsl:if>
   </xsl:function>

   <xsl:function name="f:formatAnnot" saxon:memo-function="yes">
      <xsl:param name="code"/>
      <xsl:analyze-string select="$code" regex="{'\{([^\}]+)\}'}">
     <xsl:matching-substring>
        <span class="unit-annotation"><xsl:sequence select="regex-group(1)"/></span>
     </xsl:matching-substring>
     <xsl:non-matching-substring>
        <xsl:sequence select="f:formatPart(.)"/>
     </xsl:non-matching-substring>
      </xsl:analyze-string>
   </xsl:function>

   <xsl:function name="f:formatPart" saxon:memo-function="yes">
      <xsl:param name="code"/>
      <xsl:analyze-string select="$code" regex="^({$prefix-regex})?({$metric-regex})([0-9]*)$">
     <xsl:matching-substring>
        <span class="unit-prefix"><xsl:sequence select="($prefix[@Code=regex-group(1)]/printSymbol/node(), regex-group(1))[1]"/></span>
        <xsl:variable name="printSymbol" select="$metric-unit[@Code=regex-group(2)]/printSymbol/node()"/>
        <span class="unit-atom"><xsl:sequence select="if($printSymbol) then $printSymbol else f:formatAtom(regex-group(2))"/></span>
        <xsl:if test="regex-group(3)">
           <sup class="unit-exponent"><xsl:sequence select="regex-group(3)"/></sup>
        </xsl:if>
     </xsl:matching-substring>
     <xsl:non-matching-substring>
        <xsl:sequence select="f:formatNonMetric(.)"/>
     </xsl:non-matching-substring>
      </xsl:analyze-string>
   </xsl:function>

   <xsl:function name="f:formatNonMetric" saxon:memo-function="yes">
      <xsl:param name="code"/>
      <xsl:analyze-string select="$code" regex="^({$non-metric-regex})([0-9]*)$">
     <xsl:matching-substring>
        <xsl:variable name="printSymbol" select="$non-metric-unit[@Code=regex-group(1)]/printSymbol/node()"/>
        <span class="unit-atom"><xsl:sequence select="if($printSymbol) then $printSymbol else f:formatAtom(regex-group(1))"/></span>
        <xsl:if test="regex-group(2)">
           <sup class="unit-exponent"><xsl:sequence select="regex-group(2)"/></sup>
        </xsl:if>
     </xsl:matching-substring>
     <xsl:non-matching-substring>
        <xsl:sequence select="f:formatAtom(.)"/>
     </xsl:non-matching-substring>
      </xsl:analyze-string>
   </xsl:function>

   <xsl:function name="f:formatAtom" saxon:memo-function="yes">
      <xsl:param name="code"/>
      <xsl:analyze-string select="$code" regex="^\[([^\]]+)\]$">
     <xsl:matching-substring>
        <span class="unit-bracket"><xsl:sequence select="f:formatSub(regex-group(1))"/></span>
     </xsl:matching-substring>
     <xsl:non-matching-substring>
        <xsl:sequence select="f:formatSub(.)"/>
     </xsl:non-matching-substring>
      </xsl:analyze-string>
   </xsl:function>

   <xsl:function name="f:formatSub" saxon:memo-function="yes">
      <xsl:param name="code"/>
      <xsl:analyze-string select="$code" regex="_(.+)$">
     <xsl:matching-substring>
        <sub class="unit-index"><xsl:sequence select="regex-group(1)"/></sub>
     </xsl:matching-substring>
     <xsl:non-matching-substring>
        <span class="unit-remainder"><xsl:sequence select="."/></span>
     </xsl:non-matching-substring>
      </xsl:analyze-string>
   </xsl:function>

</xsl:transform>

I called this the ucum-format-function.xsl and after xsl:import'ing that you can call f:formatUnit(@unit) and it will give you a pretty nice HTML with subscripts and superscripts, italics and all.