diff options
| author | 2017-10-04 21:55:00 -0400 | |
|---|---|---|
| committer | 2017-10-04 21:55:00 -0400 | |
| commit | 61dc07ef400be3613dc4fa395bca161455a402c1 (patch) | |
| tree | 39aed9dafdee33e50387201c79f2f813bd8b5199 | |
| download | nwscap-61dc07ef400be3613dc4fa395bca161455a402c1.tar.gz nwscap-61dc07ef400be3613dc4fa395bca161455a402c1.tar.bz2 nwscap-61dc07ef400be3613dc4fa395bca161455a402c1.zip | |
Initial commit of original 2014 code
| -rw-r--r-- | LICENSE | 26 | ||||
| -rw-r--r-- | capatomcustom.xsl | 446 | ||||
| -rw-r--r-- | dst_check.xsl | 142 | ||||
| -rwxr-xr-x | nwscap.py | 132 |
4 files changed, 746 insertions, 0 deletions
@@ -0,0 +1,26 @@ +Copyright 2013-2014 Daniel Washburn + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this +list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, +this list of conditions and the following disclaimer in the documentation +and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its contributors +may be used to endorse or promote products derived from this software without +specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/capatomcustom.xsl b/capatomcustom.xsl new file mode 100644 index 0000000..b9a5726 --- /dev/null +++ b/capatomcustom.xsl @@ -0,0 +1,446 @@ +<?xml version="1.0" encoding="UTF-8"?> +<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" + xmlns:atom="http://www.w3.org/2005/Atom" + xmlns:cap="urn:oasis:names:tc:emergency:cap:1.1" + xmlns:ha="http://www.alerting.net/namespace/index_1.0" version="1.0"> + + <xsl:import href="dst_check.xsl"/> + + <xsl:output method="html" + indent="yes" + omit-xml-declaration="yes"/> + + + <!-- Feed header --> + <xsl:template match="cap:alert"> + <table width="525" cellspacing="2" cellpadding="0" border="0"> + <tr valign="top"> + <xsl:variable name="lcletters">abcdefghijklmnopqrstuvwxyz</xsl:variable> + <xsl:variable name="ucletters">ABCDEFGHIJKLMNOPQRSTUVWXYZ</xsl:variable> + <xsl:variable name="st"> + <xsl:value-of select="substring(substring-after(cap:identifier/text(),'NOAA-NWS-ALERTS-'),1,2)"/> + </xsl:variable> + <xsl:variable name="lcst"> + <xsl:value-of select="translate(substring(substring-after(cap:identifier/text(),'NOAA-NWS-ALERTS-'),1,2),$ucletters,$lcletters)"/> + </xsl:variable> + + <xsl:variable name="forecastoffice"> + <xsl:value-of select="cap:info/cap:senderName/text()"/> + </xsl:variable> + + <td width="100%" colspan="2"><p><a name="contents" id="contents"></a><a href="http://www.weather.gov/">Home</a> > <a href="http://alerts.weather.gov/">Alerts</a> > + <a border="0"> + <xsl:attribute name="href">http://alerts.weather.gov/cap/<xsl:copy-of select="$lcst"/>.php?x=1</xsl:attribute> + <xsl:choose> + <xsl:when test="contains(cap:note/text(),'(')"> + <xsl:value-of select="substring-before(substring-after(cap:note/text(),'('),')')"/> + </xsl:when> + <xsl:otherwise> + <xsl:copy-of select="$st"/> + </xsl:otherwise> + </xsl:choose> + </a> + > <xsl:value-of select="cap:info/cap:event/text()"/> Issued by <xsl:copy-of select="$forecastoffice"/></p> + <table style="width: 100%; margin-top: 5px; background-color: #e0e0e0; font-family: sans-serif; border: 1px solid black;"> + <tr> + <td style="width: 25px; font-weight: bold; vertical-align: text-top; text-align: right;">Message:</td> + <td> + <xsl:value-of select="cap:identifier/text()"/><span style="text-align: left;"> from </span><xsl:value-of select="cap:sender/text()"/> + </td> + </tr> + <tr> + <td style="width: 25px; font-weight: bold; vertical-align: text-top; text-align: right;">Sent:</td> + <td> + <xsl:value-of select="substring(normalize-space(cap:sent/text()), 12, 5)"/> + <xsl:call-template name="timeZoneName"> + <xsl:with-param name="offset" select="substring(normalize-space(cap:sent/text()), 20, 6)"/> + <xsl:with-param name="year" select="substring(normalize-space(cap:sent/text()), 1, 4)"/> + <xsl:with-param name="month" select="substring(normalize-space(cap:sent/text()), 6, 2)"/> + <xsl:with-param name="day" select="substring(normalize-space(cap:sent/text()), 9, 2)"/> + <xsl:with-param name="st" select="$lcst"/> + </xsl:call-template> + <span style="text-align: left;"> on </span> + <xsl:value-of select="substring(normalize-space(cap:sent/text()), 6, 2)"/>-<xsl:value-of select="substring(normalize-space(cap:sent/text()), 9, 2)"/>-<xsl:value-of select="substring(normalize-space(cap:sent/text()), 1, 4)"/> + </td> + </tr> + <tr> + <td style="width: 25px; font-weight: bold; vertical-align: text-top; text-align: right;">Effective:</td> + <td> + <xsl:value-of select="substring(normalize-space(cap:info/cap:effective/text()), 12, 5)"/> + <xsl:call-template name="timeZoneName"> + <xsl:with-param name="offset" select="substring(normalize-space(cap:info/cap:effective/text()), 20, 6)"/> + <xsl:with-param name="year" select="substring(normalize-space(cap:info/cap:effective/text()), 1, 4)"/> + <xsl:with-param name="month" select="substring(normalize-space(cap:info/cap:effective/text()), 6, 2)"/> + <xsl:with-param name="day" select="substring(normalize-space(cap:info/cap:effective/text()), 9, 2)"/> + <xsl:with-param name="st" select="$lcst"/> + </xsl:call-template> + <span style="text-align: left;"> on </span> + <xsl:value-of select="substring(normalize-space(cap:info/cap:effective/text()), 6, 2)"/>-<xsl:value-of select="substring(normalize-space(cap:info/cap:effective/text()), 9, 2)"/>-<xsl:value-of select="substring(normalize-space(cap:info/cap:effective/text()), 1, 4)"/> + </td> + </tr> + <tr> + <td style="width: 25px; font-weight: bold; vertical-align: text-top; text-align: right;">Expires:</td> + <td> + <xsl:value-of select="substring(normalize-space(cap:info/cap:expires/text()), 12, 5)"/> + <xsl:call-template name="timeZoneName"> + <xsl:with-param name="offset" select="substring(normalize-space(cap:info/cap:expires/text()), 20, 6)"/> + <xsl:with-param name="year" select="substring(normalize-space(cap:info/cap:expires/text()), 1, 4)"/> + <xsl:with-param name="month" select="substring(normalize-space(cap:info/cap:expires/text()), 6, 2)"/> + <xsl:with-param name="day" select="substring(normalize-space(cap:info/cap:expires/text()), 9, 2)"/> + <xsl:with-param name="st" select="$lcst"/> + </xsl:call-template> + <span style="text-align: left;"> on </span> + <xsl:value-of select="substring(normalize-space(cap:info/cap:expires/text()), 6, 2)"/>-<xsl:value-of select="substring(normalize-space(cap:info/cap:expires/text()), 9, 2)"/>-<xsl:value-of select="substring(normalize-space(cap:info/cap:expires/text()), 1, 4)"/> + </td> + </tr> + <tr> + <td colspan="2"> + <table style="background-color: #ffffff; width: 100%; margin-top: 5px; border: 1px solid black;"> + <tr> + <td style="width: 25px; font-weight: bold; vertical-align: text-top; text-align: right;">Event:</td> + <td style="font-size: 1.2em; font-weight: bold;"><xsl:value-of select="cap:info/cap:event/text()"/></td> + </tr> + <tr> + <td style="width: 25px; font-weight: bold; vertical-align: text-top; text-align: right;">Alert:</td> + <td style="font-size: 1.0em;"> + <xsl:variable name="descrip"> + <xsl:call-template name="globalReplace"> + <xsl:with-param name="outputString" select="substring(cap:info/cap:description/text(),1)"/> + <xsl:with-param name="target" select="'
*'"/> + <xsl:with-param name="replacement" select="'

*'"/> + </xsl:call-template> + </xsl:variable> + <xsl:variable name="descrip2"> + <xsl:call-template name="globalReplace"> + <xsl:with-param name="outputString" select="$descrip"/> + <xsl:with-param name="target" select="'
.'"/> + <xsl:with-param name="replacement" select="'

.'"/> + </xsl:call-template> + </xsl:variable> + <xsl:variable name="descrip3"> + <xsl:call-template name="globalReplace"> + <xsl:with-param name="outputString" select="$descrip2"/> + <xsl:with-param name="target" select="'...
THE'"/> + <xsl:with-param name="replacement" select="'...

THE'"/> + </xsl:call-template> + </xsl:variable> + <xsl:variable name="descrip4"> + <xsl:call-template name="globalReplace"> + <xsl:with-param name="outputString" select="$descrip3"/> + <xsl:with-param name="target" select="'...
A'"/> + <xsl:with-param name="replacement" select="'...

A'"/> + </xsl:call-template> + </xsl:variable> + <!-- force a space before newline to facilitate url links --> + <xsl:variable name="descrip5"> + <xsl:call-template name="globalReplace"> + <xsl:with-param name="outputString" select="$descrip4"/> + <xsl:with-param name="target" select="'
'"/> + <xsl:with-param name="replacement" select="' 
'"/> + </xsl:call-template> + </xsl:variable> + <!-- make links hot --> + <xsl:variable name="descrip6"> + <xsl:call-template name="hotlink"> + <xsl:with-param name="text" select="$descrip5"/> + <xsl:with-param name="searchterm1" select="'HTTP'"/> + </xsl:call-template> + </xsl:variable> + <pre style="font-size: 1.0em;"> + <xsl:copy-of select="$descrip6"/> + </pre> + </td> + </tr> + <tr> + <td style="width: 25px; font-weight: bold; vertical-align: text-top; text-align: right;">Instructions:</td> + <td style="font-size: 1.0em;"> + <xsl:value-of select="cap:info/cap:instruction/text()"/> + </td> + </tr> + <tr> + <td style="width: 25px; font-weight: bold; vertical-align: text-top; text-align: right;">Target Area:</td> + <td> + <table style="background-color: #e0e0e0; margin-right: 20px; font-weight: bold;"> + <tr> + <td colspan="2"> + <xsl:variable name="areas1"> + <xsl:call-template name="globalReplace"> + <xsl:with-param name="outputString" select="cap:info/cap:area/cap:areaDesc/text()"/> + <xsl:with-param name="target" select="'/'"/> + <xsl:with-param name="replacement" select="'
'"/> + </xsl:call-template> + </xsl:variable> + <xsl:variable name="areas"> + <xsl:call-template name="globalReplace"> + <xsl:with-param name="outputString" select="$areas1"/> + <xsl:with-param name="target" select="';'"/> + <xsl:with-param name="replacement" select="'
'"/> + </xsl:call-template> + </xsl:variable> + <xsl:variable name="firstchar" select="substring($areas,1,1)"/> + <xsl:choose> + <xsl:when test = "$firstchar = '
'"> <!-- first character is a carriage return --> + <xsl:call-template name="br-replace"> + <xsl:with-param name="text" select="substring($areas,2)"/> + </xsl:call-template> + </xsl:when> + <xsl:otherwise> + <xsl:call-template name="br-replace"> + <xsl:with-param name="text" select="$areas"/> + </xsl:call-template> + </xsl:otherwise> + </xsl:choose> + </td> + </tr> + </table> + </td> + </tr> + </table> + </td> + </tr> + <tr> + <td style="width: 25px; font-weight: bold; vertical-align: text-top; text-align: right;">Forecast Office:</td> + <td><xsl:value-of select="cap:info/cap:senderName/text()"/></td> + </tr> + <tr> + <td colspan="2"> + <table style="background-color: #ffffff; width: 100%; margin-top: 5px; border: 1px solid black;"> + <tr> + <td style="width: 25px; font-weight: bold; vertical-align: text-top; text-align: right;">Raw CAP Message:</td> + <td style="font-size: 0.9em; color: #606060"> + <pre> + <xsl:apply-templates select="/cap:alert" mode="escape"/> + </pre> + </td> + </tr> + </table> + </td> + </tr> + </table> + </td> + </tr> + </table> + <!-- end of main content --> + </xsl:template> + + + <xsl:template match="*" mode="escape"> + <!-- Begin opening tag --> + <xsl:text><</xsl:text> + <xsl:value-of select="name()"/> + + <!-- Attributes --> + <xsl:for-each select="@*"> + <xsl:text> </xsl:text> + <xsl:value-of select="name()"/> + <xsl:text>='</xsl:text> + <xsl:call-template name="escape-xml"> + <xsl:with-param name="text" select="."/> + </xsl:call-template> + <xsl:text>'</xsl:text> + </xsl:for-each> + + <!-- End opening tag --> + <xsl:text>></xsl:text> + + <!-- Content (child elements, text nodes, and PIs) --> + <xsl:apply-templates select="node()" mode="escape" /> + + <!-- Closing tag --> + <xsl:text></</xsl:text> + <xsl:value-of select="name()"/> + <xsl:text>> </xsl:text> + </xsl:template> + + <xsl:template match="text()" mode="escape"> + <xsl:call-template name="escape-xml"> + <xsl:with-param name="text" select="."/> + </xsl:call-template> + </xsl:template> + + <xsl:template match="processing-instruction()" mode="escape"> + <xsl:text><?</xsl:text> + <xsl:value-of select="name()"/> + <xsl:text> </xsl:text> + <xsl:call-template name="escape-xml"> + <xsl:with-param name="text" select="."/> + </xsl:call-template> + <xsl:text>?></xsl:text> + </xsl:template> + + <xsl:template name="escape-xml"> + <xsl:param name="text"/> + <xsl:if test="$text != ''"> + <xsl:variable name="head" select="substring($text, 1, 1)"/> + <xsl:variable name="tail" select="substring($text, 2)"/> + <xsl:choose> + <xsl:when test="$head = '&'">&amp;</xsl:when> + <xsl:when test="$head = '<'">&lt;</xsl:when> + <xsl:when test="$head = '>'">&gt;</xsl:when> + <xsl:when test="$head = '"'">&quot;</xsl:when> + <xsl:when test="$head = "'"">&apos;</xsl:when> + <xsl:otherwise><xsl:value-of select="$head"/></xsl:otherwise> + </xsl:choose> + <xsl:call-template name="escape-xml"> + <xsl:with-param name="text" select="$tail"/> + </xsl:call-template> + </xsl:if> + </xsl:template> + + <!-- Time Zone function --> + <xsl:template name="timeZoneName"> + <xsl:param name="offset"/> + <xsl:param name="year"/> + <xsl:param name="month"/> + <xsl:param name="day"/> + <xsl:param name="st"/> + + <!-- determine if its DST or not, based on run time --> + <xsl:variable name="dst_start"> + <xsl:call-template name="day-light-savings-start"> + <xsl:with-param name="year" select="$year"/> + </xsl:call-template> + </xsl:variable> + + <xsl:variable name="dst_end"> + <xsl:call-template name="day-light-savings-end"> + <xsl:with-param name="year" select="$year"/> + </xsl:call-template> + </xsl:variable> + + <xsl:variable name="tod_jul"> + <xsl:call-template name="date-to-absolute-day"> + <xsl:with-param name="year" select="$year"/> + <xsl:with-param name="month" select="$month"/> + <xsl:with-param name="day" select="$day"/> + </xsl:call-template> + </xsl:variable> + + <xsl:variable name="dst_flag"> + <xsl:choose> + <xsl:when test="$dst_start <= $tod_jul and $dst_end >= $tod_jul">dst</xsl:when> + <xsl:otherwise>nodst</xsl:otherwise> + </xsl:choose> + </xsl:variable> + + <xsl:if test="$offset = '+10:00'"> <xsl:text> ChST</xsl:text> </xsl:if> + <xsl:if test="$offset = '-11:00'"> <xsl:text> SST</xsl:text> </xsl:if> + <xsl:if test="$offset = '-10:00'"> <xsl:text> HST</xsl:text> </xsl:if> + <xsl:if test="$offset = '-00:00'"> <xsl:text> GMT</xsl:text> </xsl:if> + <xsl:if test="$offset = '+00:00'"> <xsl:text> GMT</xsl:text> </xsl:if> + + <xsl:choose> + <xsl:when test="$dst_flag = 'nodst'"> + <xsl:if test="$offset = '-09:00'"> <xsl:text> AKST</xsl:text> </xsl:if> + <xsl:if test="$offset = '-08:00'"> <xsl:text> PST</xsl:text> </xsl:if> + <xsl:if test="$offset = '-07:00'"> <xsl:text> MST</xsl:text> </xsl:if> + <xsl:if test="$offset = '-06:00'"> <xsl:text> CST</xsl:text> </xsl:if> + <xsl:if test="$offset = '-05:00'"> <xsl:text> EST</xsl:text> </xsl:if> + <xsl:if test="$offset = '-04:00'"> <xsl:text> AST</xsl:text> </xsl:if> + </xsl:when> + <xsl:otherwise> + <xsl:choose> + <xsl:when test="$st = 'az'"> + <xsl:if test="$offset = '-08:00'"> <xsl:text> PST</xsl:text> </xsl:if> + <xsl:if test="$offset = '-07:00'"> <xsl:text> MST</xsl:text> </xsl:if> + <xsl:if test="$offset = '-06:00'"> <xsl:text> CST</xsl:text> </xsl:if> + </xsl:when> + <xsl:otherwise> + <xsl:if test="$offset = '-08:00'"> <xsl:text> AKDT</xsl:text> </xsl:if> + <xsl:if test="$offset = '-07:00'"> <xsl:text> PDT</xsl:text> </xsl:if> + <xsl:if test="$offset = '-06:00'"> <xsl:text> MDT</xsl:text> </xsl:if> + <xsl:if test="$offset = '-05:00'"> <xsl:text> CDT</xsl:text> </xsl:if> + <xsl:if test="$offset = '-04:00'"> <xsl:text> EDT</xsl:text> </xsl:if> + <xsl:if test="$offset = '-03:00'"> <xsl:text> ADT</xsl:text> </xsl:if> + </xsl:otherwise> + </xsl:choose> + </xsl:otherwise> + </xsl:choose> + + </xsl:template> + + <!-- Replace function --> + <xsl:template name="globalReplace"> + <xsl:param name="outputString"/> + <xsl:param name="target"/> + <xsl:param name="replacement"/> + <xsl:choose> + <xsl:when test="contains($outputString,$target)"> + + <xsl:value-of select= + "concat(substring-before($outputString,$target),$replacement)"/> + <xsl:call-template name="globalReplace"> + <xsl:with-param name="outputString" select="substring-after($outputString,$target)"/> + <xsl:with-param name="target" select="$target"/> + <xsl:with-param name="replacement" select="$replacement"/> + </xsl:call-template> + </xsl:when> + <xsl:otherwise> + <xsl:value-of select="$outputString"/> + </xsl:otherwise> + </xsl:choose> + </xsl:template> + + <!-- Replace new lines with html <br> tags --> + <xsl:template name="br-replace"> + <xsl:param name="text"/> + <xsl:variable name="cr" select="'
'"/> + <xsl:choose> + <!-- If the value of the $text parameter contains carriage ret --> + <xsl:when test="contains($text,$cr)"> + <!-- Return the substring of $text before the carriage return --> + <xsl:value-of select="substring-before($text,$cr)"/> + <!-- And construct a <br/> element --> + <br/> + <!-- + | Then invoke this same br-replace template again, passing the + | substring *after* the carriage return as the new "$text" to + | consider for replacement + +--> + <xsl:call-template name="br-replace"> + <xsl:with-param name="text" select="substring-after($text,$cr)"/> + </xsl:call-template> + </xsl:when> + <xsl:otherwise> + <xsl:value-of select="$text"/> + </xsl:otherwise> + </xsl:choose> + </xsl:template> + + <!-- If it looks like a link, make it hot --> + <xsl:template name="hotlink"> + <xsl:param name="text"/> + <xsl:param name="searchterm1"/> + + <xsl:variable name="lcletters">abcdefghijklmnopqrstuvwxyz</xsl:variable> + <xsl:variable name="ucletters">ABCDEFGHIJKLMNOPQRSTUVWXYZ</xsl:variable> + + <xsl:choose> + <xsl:when test="contains($text,$searchterm1)"> + + <!-- grab data before the searchterm --> + <xsl:value-of select="substring-before($text,$searchterm1)"/> + <a border="0"> + <xsl:attribute name="href"> + <!-- determine actual link by taking data from the searchterm to + either the space or the newline - whichever is first --> + <xsl:value-of select="translate(concat($searchterm1,substring-before(substring-after($text,$searchterm1),' ')),$ucletters,$lcletters)"/> + </xsl:attribute> + <xsl:value-of select="translate(concat($searchterm1,substring-before(substring-after($text,$searchterm1),' ')),$ucletters,$lcletters)"/> </a> + + <!-- data after the link - check for more links --> + <xsl:call-template name="hotlink"> + <xsl:with-param name="text" select="substring-after(substring-after($text,$searchterm1),' ')"/> + <xsl:with-param name="searchterm1" select="$searchterm1"/> + </xsl:call-template> + </xsl:when> + <xsl:otherwise> + <xsl:value-of select="$text"/> + </xsl:otherwise> + </xsl:choose> + </xsl:template> + + <!-- Ignore anything else --> + <xsl:template match="*"></xsl:template> + +</xsl:stylesheet> diff --git a/dst_check.xsl b/dst_check.xsl new file mode 100644 index 0000000..16d530b --- /dev/null +++ b/dst_check.xsl @@ -0,0 +1,142 @@ +<?xml version="1.0"?>
+<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
+<xsl:output method="html" encoding="UTF-8"/>
+
+<!-- DST related templates -->
+ <xsl:template name="date-to-absolute-day">
+ <xsl:param name="year"/>
+ <xsl:param name="month"/>
+ <xsl:param name="day"/>
+
+ <xsl:call-template name="julian-day-to-absolute-day">
+ <xsl:with-param name="j-day">
+ <xsl:call-template name="julian-date-to-julian-day">
+ <xsl:with-param name="year" select="$year"/>
+ <xsl:with-param name="month" select="$month"/>
+ <xsl:with-param name="day" select="$day"/>
+ </xsl:call-template>
+ </xsl:with-param>
+ </xsl:call-template>
+ </xsl:template>
+
+ <xsl:template name="absolute-day-to-date">
+ <xsl:param name="abs-day"/>
+
+ <xsl:call-template name="absolute-day-to-date">
+ <xsl:with-param name="j-day">
+ <xsl:call-template name="absolute-day-to-julian-day">
+ <xsl:with-param name="abs-day" select="$abs-day"/>
+ </xsl:call-template>
+ </xsl:with-param>
+ </xsl:call-template>
+ </xsl:template>
+
+ <xsl:template name="julian-day-to-absolute-day">
+ <xsl:param name="j-day"/>
+ <xsl:value-of select="$j-day - 1721425"/>
+ </xsl:template>
+
+ <xsl:template name="absolute-day-to-julian-day">
+ <xsl:param name="abs-day"/>
+ <xsl:value-of select="$abs-day + 1721425"/>
+ </xsl:template>
+
+ <xsl:template name="julian-date-to-julian-day">
+ <xsl:param name="year"/>
+ <xsl:param name="month"/>
+ <xsl:param name="day"/>
+
+ <xsl:value-of select="(1461 * ($year + 4800 + ($month - 14) div 12)) div 4 + (367 * ($month - 2 - 12 * (($month - 14) div 12))) div 12 - (3 * (($year + 4900 + ($month - 14) div 12) div 100)) div 4 + $day - 32075"/>
+
+ </xsl:template>
+
+ <xsl:template name="last-day-of-month">
+ <xsl:param name="year"/>
+ <xsl:param name="month"/>
+ <xsl:choose>
+ <xsl:when test="$month = 2 and not($year mod 4) and ($year mod 100 or not($year mod 400))">
+ <xsl:value-of select="29"/>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:value-of select="substring('312831303130313130313031', 2 * $month - 1,2)"/>
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:template>
+
+ <xsl:template name="k-day-on-or-before-abs-day">
+ <xsl:param name="abs-day"/>
+ <xsl:param name="k"/>
+ <xsl:value-of select="$abs-day - (($abs-day - $k) mod 7)"/>
+ </xsl:template>
+
+ <xsl:template name="n-th-k-day">
+ <!-- The n'th occurance of k in the given month -->
+ <!-- Positive n counts from beginning of month; negative from end. -->
+ <xsl:param name="n"/>
+ <!-- k = the day of the week (0=Sun) -->
+ <xsl:param name="k"/>
+ <xsl:param name="month"/>
+ <xsl:param name="year"/>
+
+ <xsl:choose>
+ <xsl:when test="$n > 0">
+ <xsl:variable name="k-day-on-or-before">
+ <xsl:variable name="abs-day">
+ <xsl:call-template name="date-to-absolute-day">
+ <xsl:with-param name="year" select="$year"/>
+ <xsl:with-param name="month" select="$month"/>
+ <xsl:with-param name="day" select="7"/>
+ </xsl:call-template>
+ </xsl:variable>
+ <xsl:call-template name="k-day-on-or-before-abs-day">
+ <xsl:with-param name="abs-day" select="$abs-day"/>
+ <xsl:with-param name="k" select="$k"/>
+ </xsl:call-template>
+ </xsl:variable>
+ <xsl:value-of select="$k-day-on-or-before + 7 * ($n - 1)"/>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:variable name="k-day-on-or-before">
+ <xsl:variable name="abs-day">
+ <xsl:call-template name="date-to-absolute-day">
+ <xsl:with-param name="month" select="$month"/>
+ <xsl:with-param name="day">
+ <xsl:call-template name="last-day-of-month">
+ <xsl:with-param name="month" select="$month"/>
+ <xsl:with-param name="year" select="$year"/>
+ </xsl:call-template>
+ </xsl:with-param>
+ <xsl:with-param name="year" select="$year"/>
+ </xsl:call-template>
+ </xsl:variable>
+ <xsl:call-template name="k-day-on-or-before-abs-day">
+ <xsl:with-param name="abs-day" select="$abs-day"/>
+ <xsl:with-param name="k" select="$k"/>
+ </xsl:call-template>
+ </xsl:variable>
+ <xsl:value-of select="$k-day-on-or-before + 7 * ($n + 1)"/>
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:template>
+
+ <xsl:template name="day-light-savings-start">
+ <xsl:param name="year"/>
+ <xsl:call-template name="n-th-k-day">
+ <xsl:with-param name="n" select="2"/>
+ <xsl:with-param name="k" select="0"/>
+ <xsl:with-param name="month" select="3"/>
+ <xsl:with-param name="year" select="$year"/>
+ </xsl:call-template>
+ </xsl:template>
+
+ <xsl:template name="day-light-savings-end">
+ <xsl:param name="year"/>
+ <xsl:call-template name="n-th-k-day">
+ <xsl:with-param name="n" select="-1"/>
+ <xsl:with-param name="k" select="0"/>
+ <xsl:with-param name="month" select="10"/>
+ <xsl:with-param name="year" select="$year"/>
+ </xsl:call-template>
+ </xsl:template>
+
+</xsl:stylesheet>
diff --git a/nwscap.py b/nwscap.py new file mode 100755 index 0000000..939a5eb --- /dev/null +++ b/nwscap.py @@ -0,0 +1,132 @@ +#!/usr/bin/env python +"""Parse active NOAA NWS CAP advisories and output an ATOM feed""" + +__author__ = 'Daniel Washburn' +__copyright__ = 'Copyright (c) 2013, 2014, All rights reserved.' +__license__ = 'http://opensource.org/licenses/BSD-3-Clause' + +import cgi +import getopt +import sys +from lxml import etree +from lxml.etree import SubElement + +ATOM_URI = 'http://www.w3.org/2005/Atom' +ATOM_NAMESPACE = {'atom': ATOM_URI} +CAP_URI = 'urn:oasis:names:tc:emergency:cap:1.1' +CAP_NAMESPACE = {'cap': CAP_URI} +CAP_SCHEMA_URL = 'http://docs.oasis-open.org/emergency/cap/v1.1/errata/approved/cap.xsd' +CAP_XSLT_URL = 'capatomcustom.xsl' + +CAP_SCHEMA = etree.XMLSchema(etree.parse(CAP_SCHEMA_URL)) +CAP_XSLT = xslt_transform = etree.XSLT(etree.parse(CAP_XSLT_URL)) + +ATOM_PARSER = etree.XMLParser(ns_clean=True, remove_comments=True) +CAP_PARSER = etree.XMLParser(ns_clean=True, remove_comments=True, + remove_blank_text=True, schema=CAP_SCHEMA) + +def usage(): + print """nwscap.py [-h] -t (region|zone) -a area + +-h|--help Print this message and exit +-t|--type Area type. must be either region or zone +-a|--area The area to report on +""" + +try: + opts, args = getopt.gnu_getopt(sys.argv[1:], + "ht:a:", + ["help", "type=", "area="]) +except getopt.GetoptError as err: + print str(err) + usage() + sys.exit(2) +area_type=False +area=False +url=False + +for o, a in opts: + if o in ("-h", "--help"): + usage() + sys.exit() + elif o in ("-t", "--type"): + if a in ("region", "zone"): + area_type = a + else: + print """type must be one of: region, zone""" + usage() + sys.exit() + elif o in ("-a", "--area"): + area = a + else: + assert False, "unhandled option" + +if area_type and area: + if area_type == "region": + url='https://alerts.weather.gov/cap/' + area + '.php?x=0' + elif area_type == "zone": + url='https://alerts.weather.gov/cap/wwaatmget.php?x=' + area + '&y=0' +else: + print """Both type and area must be specified""" + usage() + sys.exit() + +in_feed = etree.parse(url, ATOM_PARSER) + +out_feed = etree.XML('''<?xml version='1.0' encoding='UTF-8' standalone='yes'?> + <feed xmlns='http://www.w3.org/2005/Atom'/>''') +SubElement(out_feed, 'id').text = "%s" % \ + in_feed.xpath('/atom:feed/atom:id', + namespaces=ATOM_NAMESPACE)[0].text +SubElement(out_feed, 'updated').text = "%s" % \ + in_feed.xpath('/atom:feed/atom:updated', + namespaces=ATOM_NAMESPACE)[0].text +out_feed_author = SubElement(out_feed, 'author') +SubElement(out_feed_author, 'name').text = "%s" % \ + in_feed.xpath('/atom:feed/atom:author/atom:name', + namespaces=ATOM_NAMESPACE)[0].text +SubElement(out_feed, 'title').text = "%s" % \ + in_feed.xpath('/atom:feed/atom:title', + namespaces=ATOM_NAMESPACE)[0].text +SubElement(out_feed, 'link', href="%s" % \ + in_feed.xpath('/atom:feed/atom:link/@href', + namespaces=ATOM_NAMESPACE)[0]) + +# get URLs of active alerts +entry_titles = in_feed.xpath('/atom:feed/atom:entry/atom:title', + namespaces=ATOM_NAMESPACE) +alert_links=in_feed.xpath('/atom:feed/atom:entry/atom:link/@href', + namespaces=ATOM_NAMESPACE) + +no_alerts_title = 'There are no active watches, warnings or advisories' +if no_alerts_title in entry_titles[0].text: + alert_links = [] + +for link in alert_links: + cap=etree.parse(link, CAP_PARSER) + cap_entry = SubElement(out_feed, 'entry') + SubElement(cap_entry, 'id').text = "%s" % \ + cap.xpath('/cap:alert/cap:identifier', + namespaces=CAP_NAMESPACE)[0].text + + cap_entry_author = SubElement(cap_entry, 'author') + + SubElement(cap_entry_author, 'name').text = "%s" % \ + cap.xpath('/cap:alert/cap:sender', + namespaces=CAP_NAMESPACE)[0].text + + SubElement(cap_entry, 'title').text = "%s" % \ + cap.xpath('/cap:alert/cap:info/cap:headline', + namespaces=CAP_NAMESPACE)[0].text + + SubElement(cap_entry, 'published').text = "%s" % \ + cap.xpath('/cap:alert/cap:sent', + namespaces=CAP_NAMESPACE)[0].text + + SubElement(cap_entry, 'link', href=link) + + SubElement(cap_entry, "content", type="text/html").text = "%s" % \ + etree.tostring(xslt_transform(cap), pretty_print=True) + +out_tree = etree.ElementTree(out_feed) +print(etree.tostring(out_tree, pretty_print=True, method='xml')) |