Skip to content

Commit

Permalink
Fixes for Issue #34
Browse files Browse the repository at this point in the history
  * Updated README.md to fix URL to MDL main page and
    line number to bulletin write() method
  * bulletin write() method changed to accept a file or a os.PathLike object
  * The XML declaration line is now always written to a file
  * Updated test_bulletin.py due to change in write() method.
  * Due to change of bulletin.write() interface, this is version 1.4.0
  * Python 2 no longer supported. Future changes/bug fixes will not be
    backward compatible.
  * Updated TPG package to latest available version
  • Loading branch information
mgoberfield committed Apr 18, 2022
1 parent d1bb95b commit 9133ea9
Show file tree
Hide file tree
Showing 5 changed files with 245 additions and 339 deletions.
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ This repository is a scientific product and is not official communication of the

-------------------------------------------------------------------------------
# Generate IWXXM From TAC
This repository hosts software provided by the United States National Weather Service's [Meteorological Development Laboratory](https://vlab.ncep.noaa.gov/web/mdl) (MDL) that transforms Annex 3 Traditional Alphanumeric Code (TAC) forms into IWXXM format.
This repository hosts software provided by the United States National Weather Service's [Meteorological Development Laboratory](https://vlab.noaa.gov/web/mdl) (MDL) that transforms Annex 3 Traditional Alphanumeric Code (TAC) forms into IWXXM format.

The ICAO Meteorological Information Exchange Model (IWXXM) is a format for reporting weather information in eXtensible Markup Language (XML). The IWXXM XML schemas, developed and hosted by the WMO in association with ICAO, are used to encode aviation products described in the Meteorological Service for International Air Navigation, Annex 3 to the Convention on International Civil Aviation.

Expand Down Expand Up @@ -57,7 +57,7 @@ To illustrate the use of the software, the demo subdirectory contains two simple
## Bulletins
Every GIFTs encoder, after processing a TAC message successfully, returns an object of the class [Bulletin](https://github.com/NOAA-MDL/GIFTs/blob/master/gifts/common/bulletin.py). The Bulletin object has similarities to a python list object: it has a "length" (the number of IWXXM XML reports); can be indexed; can be iterated; and [ElementTree](https://docs.python.org/3/library/xml.etree.elementtree.html) reports added and removed with the usual python list operations. In addition to the built-in list operations, python's [print()](https://docs.python.org/3/library/functions.html#print) function will nicely format (for human eyes) the bulletin object and write out the complete XML document to a file (default is sys.stdout).

For international distribution, IWXXM reports, due to their increased character length and expanded character set, shall be sent over the Extended ATS Message Handling System (AMHS) as a File Transfer Body Part.<sup>2</sup> The Bulletin class provides a convenient [write()](https://github.com/NOAA-MDL/GIFTs/blob/master/gifts/common/bulletin.py#L177) method to generate the `<MeterologicalBulletin>`<sup>3</sup> XML document for transmission over the AMHS.
For international distribution, IWXXM reports, due to their increased character length and expanded character set, shall be sent over the Extended ATS Message Handling System (AMHS) as a File Transfer Body Part.<sup>2</sup> The Bulletin class provides a convenient [write()](https://github.com/NOAA-MDL/GIFTs/blob/master/gifts/common/bulletin.py#L189) method to generate the `<MeterologicalBulletin>`<sup>3</sup> XML document for transmission over the AMHS.

Because of the character length of the `<MeteorologicalBulletin>`, the File Transfer Body Part shall be a compressed file using the gzip protocol. By default, the `.encode()` method of the [Encoder](https://github.com/NOAA-MDL/GIFTs/blob/master/gifts/common/Encoder.py#L15) class is to generate an uncompressed file when the bulletin.write() method is invoked. To generate a compressed `<MeteorologicalBulletin>` file for transmission over the AMHS is to set the `compress` flag to True in the Bulletin object's write() method, like so:

Expand Down
57 changes: 30 additions & 27 deletions gifts/common/bulletin.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,6 @@ class Bulletin(object):
def __init__(self):

self._children = []
if sys.version_info[0] == 3:
self.encoding = 'unicode'
else:
self.encoding = 'UTF-8'

self.xmlFileNamePartA = re.compile(r'A_L[A-Z]{3}\d\d[A-Z]{4}\d{6}([ACR]{2}[A-Z])?_C_[A-Z]{4}')

def __len__(self):
Expand All @@ -44,9 +39,13 @@ def __str__(self):
#
# Pad it with spaces and newlines
self._addwhitespace()
xmlstring = ET.tostring(self.bulletin, encoding=self.encoding, method='xml')
if sys.version_info[0] == 3:
xmlstring = ET.tostring(self.bulletin, encoding='unicode', method='xml')
else:
xmlstring = ET.tostring(self.bulletin, encoding='UTF-8', method='xml')

self.bulletin = None
return xmlstring.replace(' />', '/>')
return xmlstring

def _addwhitespace(self):
tab = " "
Expand Down Expand Up @@ -169,22 +168,30 @@ def export(self):

def _write(self, obj, header, compress):

tree = ET.ElementTree(element=self.bulletin)
if header:
result = '{}\n{}'.format(self._wmoAHL, ET.tostring(self.bulletin, encoding=self.encoding, method='xml'))
else:
result = ET.tostring(self.bulletin, encoding=self.encoding, method='xml')
ahl_line = '{}\n'.format(self._wmoAHL)
obj.write(ahl_line.encode('UTF-8'))
try:
tree.write(obj, encoding='UTF-8', xml_declaration=True, method='xml', short_empty_elements=True)
except TypeError:
tree.write(obj, encoding='UTF-8', xml_declaration=True, method='xml')

if compress:
obj(result.encode('utf-8'))
else:
obj(result)
def _iswriteable(self, obj):
try:
return obj.writable() and obj.mode == 'wb'
except AttributeError:
try:
return isinstance(obj, file) and obj.mode == 'wb'
except NameError:
return False

def write(self, obj=None, header=False, compress=False):
"""Writes ElementTree to a file or stream.
obj - if none provided, XML is written to current working directory, or
character string as directory, or
a write() method
os.PathLike object such as a file object in 'wb' mode
header - boolean as to whether the WMO AHL line should be included as first line in file. If true,
the file is no longer valid XML.
Expand All @@ -208,17 +215,13 @@ def write(self, obj=None, header=False, compress=False):
except AttributeError:
self._export(canBeCompressed)
#
# If the object name is 'write'; Assume it's configured properly for writing
try:
if obj.__name__ == 'write':
self._write(obj, header, canBeCompressed)
return None

except AttributeError:
pass
# If the object name is writable and mode is correct
if self._iswriteable(obj):
self._write(obj, header, canBeCompressed)
return None
#
# Write to current directory if None, or to the directory path provided.
if obj is None or os.path.isdir(obj):
if obj is None or (isinstance(obj, str) and os.path.isdir(obj)):
if obj is None:
obj = os.getcwd()

Expand All @@ -231,12 +234,12 @@ def write(self, obj=None, header=False, compress=False):
if canBeCompressed:
_fh = gzip.open(fullpath, 'wb')
else:
_fh = open(fullpath, 'w')
_fh = open(fullpath, 'wb')

self._write(_fh.write, header, canBeCompressed)
self._write(_fh, header, canBeCompressed)
_fh.close()

return fullpath

else:
raise IOError('First argument is an unsupported type: %s' % str(type(obj)))
raise IOError('First argument is an unsupported type: %s' % (str(type(obj))))
Loading

0 comments on commit 9133ea9

Please sign in to comment.