Skip to content

Commit

Permalink
Rename 'set_where_when' -> 'add_where_when'.
Browse files Browse the repository at this point in the history
Since a packet may have more than one entry under the 'WhereWhen' section.

Similarly, add an 'index' parameter to the pull_<blah> functions acting
on WhereWhen.

Finally, add some more details to the lxml gotchas section.
  • Loading branch information
timstaley committed Oct 8, 2014
1 parent 60f597a commit 4938d5d
Show file tree
Hide file tree
Showing 6 changed files with 180 additions and 120 deletions.
32 changes: 29 additions & 3 deletions documentation/source/intro.rst
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ common tasks, so that accessing those vital data elements is as simple as::

Rationale
---------
Voevent-parse aims to make dealing with VOEvent packets easy, while remaining
*voevent-parse* aims to make dealing with VOEvent packets easy, while remaining
small, flexible, and stable enough to be suitable for use as a dependency in a
range of larger projects.
To achieve this, we add a user-friendly layer on top of
Expand Down Expand Up @@ -66,8 +66,8 @@ Bug reports (or even better, pull requests) are welcomed. The source code and
issue tracker may be found at https://github.com/timstaley/voevent-parse.


lxml.objectify tips
-------------------
lxml.objectify 'gotchas'
------------------------
The objectify library has a few syntactic quirks which can trip up new users.
Firstly, you should be aware that the line ``root.foo`` actually returns
an object that acts like a *list* of all the children of the ``root`` element
Expand All @@ -90,6 +90,32 @@ e.g.::
print root.foo.text # 'sometext'
print len(root.foo.text) # 8. Sanity prevails!

Another 'gotcha' is that *creating* multiple child elements of the same
name is a bit unintuitive. Essentially, ``objectify`` works implicitly
if each element has only one child::

from lxml import objectify, etree
simple_root = objectify.Element('simple_root')
simple_root.layer1 = None
simple_root.layer1.layer2 = 5
print etree.tostring(simple_root, pretty_print=True)

But if there are multiple children then each child must be explicitly declared
as an lxml ``Element`` in order to co-exist with its siblings::

from lxml import objectify, etree
import math
siblings_root = objectify.Element('siblings')
siblings_root.bars = None
siblings_root.bars.append(objectify.Element('bar'))
siblings_root.bars.append(objectify.Element('bar'))
siblings_root.bars.bar[0] = math.pi
siblings_root.bars.bar[1] = 42
print etree.tostring(siblings_root, pretty_print=True)

... which is another reason to use *voevent-parse* as a user-friendly interface
for common operations.

For some more examples, you might also try:
http://www.saltycrane.com/blog/2011/07/example-parsing-xml-lxml-objectify/.

Expand Down
2 changes: 1 addition & 1 deletion examples/author_new_voevent.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@


# Now we set the sky location of our event:
vp.set_where_when(v,
vp.add_where_when(v,
coords=vp.Position2D(ra=123.5, dec=45, err=0.1,
units='deg',
system=vp.definitions.sky_coord_system.fk5),
Expand Down
2 changes: 1 addition & 1 deletion voeventparse/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
voevent_v2_0_schema,
load, loads, dump, dumps,
valid_as_v2_0, assert_valid_as_v2_0,
set_who, set_author, set_where_when,
set_who, set_author, add_where_when,
add_how, add_why, add_citations
)
import voeventparse.definitions as definitions
Expand Down
85 changes: 51 additions & 34 deletions voeventparse/convenience.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,16 +7,25 @@
Citation)


def pull_astro_coords(voevent):
"""Extracts the `AstroCoords` from the first `WhereWhen.ObservationLocation`.
def pull_astro_coords(voevent, index=0):
"""Extracts the `AstroCoords` from a given `WhereWhen.ObsDataLocation`.
Note that a packet may include multiple 'ObsDataLocation' entries
under the 'WhereWhen' section, for example giving locations of an object
moving over time. Most packets will have only one, however, so the
default is to just return co-ords extracted from the first.
Args:
voevent (:class:`voeventparse.voevent.Voevent`): Root node of the VOevent etree.
voevent (:class:`voeventparse.voevent.Voevent`): Root node of the
VOEvent etree.
index (int): Index of the ObsDataLocation to extract AstroCoords from.
Returns:
:py:class:`.Position2D`: The position defined by the first
ObservationLocation element under the WhereWhen section.
:py:class:`.Position2D`: The sky position defined in the
ObsDataLocation.
"""
ac = voevent.WhereWhen.ObsDataLocation.ObservationLocation.AstroCoords
od = voevent.WhereWhen.ObsDataLocation[index]
ac = od.ObservationLocation.AstroCoords
ac_sys = voevent.WhereWhen.ObsDataLocation.ObservationLocation.AstroCoordSystem
sys = ac_sys.attrib['id']

Expand All @@ -32,6 +41,42 @@ def pull_astro_coords(voevent):
return posn


def pull_isotime(voevent, index=0):
"""Extracts the event time from a given `WhereWhen.ObsDataLocation`.
Accesses a `WhereWhere.ObsDataLocation.ObservationLocation`
element and returns the AstroCoords.Time.TimeInstant.ISOTime element,
converted to a datetime.
Note that a packet may include multiple 'ObsDataLocation' entries
under the 'WhereWhen' section, for example giving locations of an object
moving over time. Most packets will have only one, however, so the
default is to access the first.
Args:
voevent (:class:`voeventparse.voevent.Voevent`): Root node of the VOevent
etree.
index (int): Index of the ObsDataLocation to extract an ISOtime from.
Returns:
:class:`datetime.datetime`: Specifically, we return a standard library
datetime object, i.e. one that is **timezone-naive** (that is,
agnostic about its timezone, see python docs).
This avoids an added dependency on pytz.
The details of the reference system for time and space are provided
in the AstroCoords object, but typically time reference is UTC.
"""
try:
od = voevent.WhereWhen.ObsDataLocation[index]
ol = od.ObservationLocation
isotime_str = str(ol.AstroCoords.Time.TimeInstant.ISOTime)
return datetime.datetime.strptime(isotime_str, "%Y-%m-%dT%H:%M:%S.%f")
except AttributeError:
return None


def pull_params(voevent):
"""Attempts to load the `What` section of a voevent as a nested dictionary.
Expand Down Expand Up @@ -69,34 +114,6 @@ def pull_params(voevent):
return result


def pull_isotime(voevent):
"""Extract the event time from the WhereWhen section, if present.
Accesses the first `WhereWhere.ObsDataLocation.ObservationLocation`
element and returns the AstroCoords.Time.TimeInstant.ISOTime element,
converted to a datetime.
Args:
voevent (:class:`voeventparse.voevent.Voevent`): Root node of the VOevent
etree.
Returns:
:class:`datetime.datetime`: Specifically, we return a standard library
datetime object, i.e. one that is **timezone-naive** (that is,
agnostic about its timezone, see python docs).
This avoids an added dependency on pytz.
The details of the reference system for time and space are provided
in the AstroCoords object, but typically time reference is UTC.
"""
try:
ol = voevent.WhereWhen.ObsDataLocation.ObservationLocation
isotime_str = str(ol.AstroCoords.Time.TimeInstant.ISOTime)
return datetime.datetime.strptime(isotime_str, "%Y-%m-%dT%H:%M:%S.%f")
except AttributeError:
return None


def prettystr(subtree):
"""Print an element tree with nice indentation.
Expand Down

0 comments on commit 4938d5d

Please sign in to comment.