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/.
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
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.
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.
The status level can be retrieved with:
In [2]:
stat = awuk.status
print(stat)
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 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))
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 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 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 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.
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)
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))
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')
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 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)
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.
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]))
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
Please report any bugs by creating a new issue on the Github repository page, https://github.com/stevemarple/python-aurorawatchuk/issues
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.