aurorawatchuk - a Python wrapper to the AuroraWatch UK status API

This notebook demonstrates use of the aurorawatchuk Python module for the AuroraWatch UK current status and activity API. The documentation is hosted at http://aurorawatchuk.readthedocs.io/en/latest/. For more information about AuroraWatch UK see http://aurorawatch.lancs.ac.uk/; for information about the API and usage restrictions see http://aurorawatch.lancs.ac.uk/api-info/.

Installation

The easiest way to install the module is to run pip from the command line:

pip install aurorawatchuk

Developers may wish to clone the git repository and install it in developer mode:

git clone https://github.com/stevemarple/python-aurorawatchuk.git
cd python-aurorawatchuk
python setup.py develop

Importing the module

When the module is installed you can use it with Python 2.7 and Python 3.x. First import the module into your Python program and then set the HTTP referer. In this example we'll use ipython tutorial but you should set it to something appropriate for your program. Next create an instance of the class.


In [1]:
import aurorawatchuk
aurorawatchuk.user_agent = 'Python aurorawatchuk module (ipython tutorial)'
awuk = aurorawatchuk.AuroraWatchUK()

Now you are ready to access the current status and activity values. The Python module takes care of updating the information for you when it has expired.

Status, activity and descriptions

The module provides three main interfaces to the API, each of which is accessed as an attribute of the AuroraWatchUK object. They are:

  • status: The current status level.
  • activity: The current and recent activity values and status levels.
  • descriptions: The descriptions, meanings etc associated with each alert level.

The information available from the status attribute is a subset of that from the activity attribute, however the status XML document it requests is smaller than the activity XML document. Therefore unless you require the actual activity values it is more efficient to access the status attribute. Unlike the status and activity values the descriptions don't normally change. However, by fetching the descriptions from the AuroraWatch UK API you can ensure the descriptions you use will always match those on the AuroraWatch UK web site.

Accessing the status level

The status level can be retrieved with:


In [2]:
stat = awuk.status
print(stat)


<aurorawatchuk.Status object at 0x7f6fe6ba0a10>

The value returned is an instance of the Status type, and it contains 4 attributes:

  • level: the name of the status level.
  • updated: a datetime.datetime representation of when the information retrieved was last updated.
  • expires: the expiry time for the XML document, extracted from the HTTP header response. The value is given in seconds since the epoch (see the time module for further information).
  • messages: a list of Message objects. The list may be empty. See Messages.

There are four AuroraWatch UK status levels: green, yellow, amber and red. Each has a defined meaning, description and color. For more information see http://aurorawatch.lancs.ac.uk/alerts.


In [3]:
print("The current status level is {awuk.status.level}".format(awuk=awuk))


The current status level is green

The example above accesses the status information only. You will often need to cross-reference the alert status with the descriptions document to find the description, meaning and color for the current alert status. Some additional attributes are provided which do this for you:

  • status_description: The description of the current status level.
  • status_meaning: The meaning of the current status level.
  • status_color: The color to be used for the current status level. The format is #RRGGBB where the red (RR), green (GG) and blue (BB) values are expressed as 16 bit hexadecimal values. This is the same format used in HTML documents.

There is also a status_level attribute which behaves similarly to accessing the level attribute of the Status object (status.level) except when exceptions are disabled. It is recommended that you normally use status_level instead of status.level.

Now we can replicate the status information given on the front page of AuroraWatch UK:


In [4]:
print("AuroraWatch UK status: {awuk.status_description}".format(awuk=awuk))


AuroraWatch UK status: No significant activity

Further explanation can be retrieved from the status_meaning attribute:


In [5]:
print("The current status level is {awuk.status_level}, that means '{awuk.status_meaning}'".format(awuk=awuk))


The current status level is green, that means 'Aurora is unlikely to be visible by eye or camera from anywhere in the UK.'

Accessing the activity values

The activity values are retrieved from the activity attribute.This returns an Activity object that contains a number of useful attributes:

  • latest: The latest hourly activity information as a single ActivityValue object.
  • all: A list containing the 24 most recent hourly ActivityValue objects.
  • thresholds: An OrderedDict of the lowest activity threshold (>=) for each status level.

Each ActivityValue object holds the information relevant to a single hourly activity period:

  • level: The status level.
  • value: The geomagnetic disturbance value in nanotesla (rounded to the nearest 0.1nT).
  • datetime: The start time of the hourly activity period.

This is most easily demonstrated by printing the latest activity information.


In [6]:
print("The current status level is {awuk.activity.latest.level}.".format(awuk=awuk))
print("The current disturbance value is {awuk.activity.latest.value} nT.".format(awuk=awuk))
print("The hourly activity period began at {awuk.activity.latest.datetime}".format(awuk=awuk))


The current status level is green.
The current disturbance value is 5.7 nT.
The hourly activity period began at 2017-02-15 13:00:00

The time is returned as a datetime object so we can use any of the formatting specifiers for datetime, see https://docs.python.org/library/datetime.html


In [7]:
print("The hour began at {awuk.activity.latest.datetime:%I%P on %x}.".format(awuk=awuk))


The hour began at 01pm on 02/15/17.

The activity information for the last 24 hours can be accessed from the activity.all attributes. This returns a list of 24 ActivityValue objects. The API only provides current and recent activity values, historical values are not available.

Important: the status level is determined from the unrounded activity value. Do not attempt to determine the status level from the activity value returned by the API.

Accessing the descriptions

Often you can let the module cross-reference the descriptions for you, and the status_color, status_description and status_meaning attributes do just that. Sometimes however you may need to know the description and meanings for any alert level, not just the current status level. The descriptions are returned in increasing alert level as an OrderedDict.


In [8]:
print(awuk.descriptions)


OrderedDict([('green', {'color': '#33ff33', 'meaning': 'Aurora is unlikely to be visible by eye or camera from anywhere in the UK.', 'description': 'No significant activity'}), ('yellow', {'color': '#ffff00', 'meaning': 'Aurora may be visible by eye from Scotland and may be visible by camera from Scotland, northern England and Northern Ireland.', 'description': 'Minor geomagnetic activity'}), ('amber', {'color': '#ff9900', 'meaning': 'Aurora is likely to be visible by eye from Scotland, northern England and Northern Ireland; possibly visible from elsewhere in the UK. Photographs of aurora are likely from anywhere in the UK.', 'description': 'Amber alert: possible aurora'}), ('red', {'color': '#ff0000', 'meaning': 'It is likely that aurora will be visible by eye and camera from anywhere in the UK.', 'description': 'Red alert: aurora likely'})])

Using color

If you use color to convey the alert level then please use the same colors as AuroraWatch UK. The module makes this information easy to access:


In [9]:
print("The current status level is {awuk.status_level}, web color {awuk.status_color}.".format(awuk=awuk))


The current status level is green, web color #33ff33.

Avoiding exceptions

If for some reason the status information is not available (for example, due to a network or server problem) then an exception is raised. In some cases this might be undesirable and you might prefer to print an 'unknown' message instead. If so then create the AuroraWatchUK oject as below:


In [10]:
awuk = aurorawatchuk.AuroraWatchUK(raise_=False)

Note the trailing underscore (raise_) to distinguish from the raise keyword.

It is also necessary to use the special attribute status_level, instead of accessing the level attribute of the status attribute (status.level).

When the AuroraWatchUK object is created with raise_=False and the status cannot be determined the unknown color is mid-grey (#777777); this can be adjusted with the unknown_status_color keyword argument to the constructor.


In [11]:
# Use black for the color when the status cannot be determined
awuk = aurorawatchuk.AuroraWatchUK(raise_=False, unknown_status_color='#000000')

Avoiding unintended updates

It is important to be aware that as the module refreshes (and caches) its internal information to stay correct it is possible that the information could change between accesses. For instance, consider the following code.


In [12]:
import aurorawatchuk
import time

aurorawatchuk.user_agent = 'Python aurorawatchuk module (ipython tutorial)'

for n in range(3):
    print("The AuroraWatch UK status is {awuk.status_level}.".format(awuk=awuk))
    # Status level might be updated here
    print("The color for status level {awuk.status_level} is {awuk.status_color}.".format(awuk=awuk))
    time.sleep(2)


The AuroraWatch UK status is green.
The color for status level green is #33ff33.
The AuroraWatch UK status is green.
The color for status level green is #33ff33.
The AuroraWatch UK status is green.
The color for status level green is #33ff33.

The status information might be refreshed between these two print statements, and it could be different. The status information might even change between printing the status level and its color in the second print statement!

Some of these problems can be avoided by taking a copy of the status or activity attributes. An easier method is to use the AuroraWatchUK_SS class which takes a snapshot of the status or activity when first requested, and does not update afterwards; create a new object later when you are ready for an update to occur.

The code above is then written as:


In [13]:
import aurorawatchuk
import aurorawatchuk.snapshot
import time

aurorawatchuk.user_agent = 'Python aurorawatchuk module (ipython tutorial)'

for n in range(3):
    awuk = aurorawatchuk.snapshot.AuroraWatchUK_SS()
    print("The AuroraWatch UK status is {awuk.status_level}.".format(awuk=awuk))
    print("The color for status level {awuk.status_level} is {awuk.status_color}.".format(awuk=awuk))
    time.sleep(2)


The AuroraWatch UK status is green.
The color for status level green is #33ff33.
The AuroraWatch UK status is green.
The color for status level green is #33ff33.
The AuroraWatch UK status is green.
The color for status level green is #33ff33.

Now the status level is fetched (or accessed from the internal cache) at the start of each loop. The status level will not change between the two print statements and the status level and its color will always be printed correctly.

Messages

The API includes the facility for important messages to be broadcast to users. Each message is represented by an object of type Message with the following attributes:

  • description: The message text.
  • expires: The message expiration time, as a datetime.datetime object. Messages should not be displayed after this time.
  • id: A unique identifier for the message.
  • priority: The priority for the message; high suggests that an alerting notification might be appropriate, low suggests a quiet (non-alerting) notification is more appropriate. Test messages have the priority set to test and should not be displayed in normal use. Messages with any other priority should not be displayed.
  • url: An optional URL.

Messages can be obtained from either the status or activity attributes. The following code demonstrates how to check and display any messages.


In [14]:
# Use the snapshot version of the class to avoid an unwanted updates occuring,
# such as between the test for messages and displaying them.
import aurorawatchuk
import aurorawatchuk.snapshot
awuk = aurorawatchuk.snapshot.AuroraWatchUK_SS()

# If not using activity the status information is more efficient
messages = awuk.status.messages

if len(messages) == 0:
    print("There are no messages.")
else:
    for n in range(len(messages)):
        print('Message #{n}: '.format(n=n))
        print('    {mesg.description}'.format(mesg=messages[n]))


There are no messages.

Setting the cache directory

The module uses a cache directory where the parsed XML documents are saved. The results are read in when the module first starts up, ensuring that calls to the aurorawatchuk module made by separate processes (e.g., jobs started from a crontab file) observe the minimum interval between requests. The cache directory is determined by appdirs.user_config_dir('aurorawatchuk'). For applications where another location may be preferred (such as a tmpfs file system on the Raspberry Pi) then the location of the cache directory can be adjusted by a user config file. The system-dependent config file name is given by os.path.join(appdirs.user_config_dir('aurorawatchuk'), 'config.ini'). An example config file is shown below:

[aurorawatchuk]
cache_dir = /home/pi/tmpfs/aurorawatchuk

Reporting bugs

Please report any bugs by creating a new issue on the Github repository page, https://github.com/stevemarple/python-aurorawatchuk/issues

MIT License

Copyright (c) 2017 Steve Marple

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.