Skip to content

Commit

Permalink
Merge pull request #2 from uw-it-aca/develop
Browse files Browse the repository at this point in the history
Develop
  • Loading branch information
jlaney authored Sep 6, 2017
2 parents 0f097a8 + 8d77f25 commit 05b6912
Show file tree
Hide file tree
Showing 26 changed files with 717 additions and 0 deletions.
4 changes: 4 additions & 0 deletions .coveragerc
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
[run]
branch = True
source = uw_r25/
include = uw_r25/*
19 changes: 19 additions & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
sudo: false
language: python
python:
- '2.7'
- '3.4'
- '3.5'
- '3.6'
before_script:
- pip install -e .
- pip install pep8
- pip install nose2
- pip install coverage
- pip install commonconf
- pip install python-coveralls
script:
- pep8 uw_r25/ --exclude=uw_r25/tests
- coverage run --source=uw_r25 uw_r25/test.py -v
after_script:
- coveralls
2 changes: 2 additions & 0 deletions MANIFEST.in
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
include uw_r25/VERSION
recursive-include uw_r25 *
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,2 +1,5 @@
[![Build Status](https://api.travis-ci.org/uw-it-aca/uw-restclients-r25.svg?branch=master)](https://travis-ci.org/uw-it-aca/uw-restclients-r25)
[![Coverage Status](https://coveralls.io/repos/uw-it-aca/uw-restclients-r25/badge.png?branch=master)](https://coveralls.io/r/uw-it-aca/uw-restclients-r25?branch=master)

# uw-restclients-r25
REST client for the UW R25 Web Service
40 changes: 40 additions & 0 deletions setup.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import os
from setuptools import setup

README = """
See the README on `GitHub
<https://github.com/uw-it-aca/uw-restclients-r25>`_.
"""

# The VERSION file is created by travis-ci, based on the tag name
version_path = 'uw_r25/VERSION'
VERSION = open(os.path.join(os.path.dirname(__file__), version_path)).read()
VERSION = VERSION.replace("\n", "")

# allow setup.py to be run from any path
os.chdir(os.path.normpath(os.path.join(os.path.abspath(__file__), os.pardir)))

setup(
name='UW-RestClients-R25',
version=VERSION,
packages=['uw_r25'],
author="UW-IT AXDD",
author_email="[email protected]",
include_package_data=True,
install_requires=['UW-RestClients-Core>0.8,<1.0',
'lxml',
'unittest2',
],
license='Apache License, Version 2.0',
description=('A library for connecting to the UW R25 API'),
long_description=README,
url="https://github.com/uw-it-aca/uw-restclients-r25",
classifiers=[
'Intended Audience :: Developers',
'License :: OSI Approved :: Apache Software License',
'Operating System :: OS Independent',
'Programming Language :: Python',
'Programming Language :: Python :: 2.7',
'Programming Language :: Python :: 3.6',
],
)
1 change: 1 addition & 0 deletions travis-ci/test.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
[R25]
1 change: 1 addition & 0 deletions uw_r25/VERSION
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
0.1
26 changes: 26 additions & 0 deletions uw_r25/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
from uw_r25.dao import R25_DAO
from restclients_core.exceptions import DataFailureException
from lxml import etree


nsmap = {"r25": "http://www.collegenet.com/r25",
"xhtml": "http://www.w3.org/1999/xhtml"}


def get_resource(url):
"""
Issue a GET request to R25 with the given url
and return a response as an etree element.
"""
response = R25_DAO().getURL(url, {"Accept": "text/xml"})
if response.status != 200:
raise DataFailureException(url, response.status, response.data)

tree = etree.fromstring(response.data.strip())

# XHTML response is an error response
xhtml = tree.xpath("//xhtml:html", namespaces=nsmap)
if len(xhtml):
raise DataFailureException(url, 500, response.data)

return tree
13 changes: 13 additions & 0 deletions uw_r25/dao.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import logging
import os
import re
from os.path import abspath, dirname
from restclients_core.dao import DAO


class R25_DAO(DAO):
def service_name(self):
return 'r25'

def service_mock_paths(self):
return [abspath(os.path.join(dirname(__file__), "resources"))]
75 changes: 75 additions & 0 deletions uw_r25/events.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
from uw_r25.models import Event, BindingReservation
from uw_r25 import nsmap, get_resource
from uw_r25.reservations import reservations_from_xml
try:
from urllib import urlencode, quote
except:
from urllib.parse import urlencode, quote


def get_event_by_id(event_id):
url = "/r25ws/servlet/wrd/run/event.xml?event_id=%s" % event_id
return events_from_xml(get_resource(url))[0]


def get_event_by_alien_id(alien_id):
url = "/r25ws/servlet/wrd/run/event.xml?alien_uid=%s" % quote(alien_id)
event = events_from_xml(get_resource(url))
return event[0] if event else None


def get_events(**kwargs):
"""
Return a list of events matching the passed filter.
Supported kwargs are listed at
http://knowledge25.collegenet.com/display/WSW/events.xml
"""
url = "/r25ws/servlet/wrd/run/events.xml"
if len(kwargs):
url += "?%s" % urlencode(kwargs)

return events_from_xml(get_resource(url))


def events_from_xml(tree):
events = []
for node in tree.xpath("r25:event", namespaces=nsmap):
event = Event()
event.event_id = node.xpath("r25:event_id", namespaces=nsmap)[0].text
event.alien_uid = node.xpath("r25:alien_uid", namespaces=nsmap)[0].text
event.name = node.xpath("r25:event_name", namespaces=nsmap)[0].text
event.title = node.xpath("r25:event_title", namespaces=nsmap)[0].text
event.start_date = node.xpath("r25:start_date",
namespaces=nsmap)[0].text
event.end_date = node.xpath("r25:end_date", namespaces=nsmap)[0].text
event.state = node.xpath("r25:state", namespaces=nsmap)[0].text
event.parent_id = node.xpath("r25:parent_id", namespaces=nsmap)[0].text
event.cabinet_id = node.xpath("r25:cabinet_id",
namespaces=nsmap)[0].text
event.cabinet_name = node.xpath("r25:cabinet_name",
namespaces=nsmap)[0].text

event.binding_reservations = []
event.reservations = []
for pnode in node.xpath("r25:profile", namespaces=nsmap):
event.binding_reservations += binding_reservations_from_xml(pnode)
event.reservations += reservations_from_xml(pnode)
events.append(event)

return events


def binding_reservations_from_xml(tree):
binding_reservations = []
for node in tree.xpath("//r25:binding_reservation", namespaces=nsmap):
bind_res = BindingReservation()
bind_res.bound_reservation_id = node.xpath("r25:bound_reservation_id",
namespaces=nsmap)[0].text
bind_res.primary_reservation = node.xpath("r25:primary_reservation",
namespaces=nsmap)[0].text
bind_res.name = node.xpath("r25:bound_name", namespaces=nsmap)[0].text
bind_res.bound_event_id = node.xpath("r25:bound_event_id",
namespaces=nsmap)[0].text
binding_reservations.append(bind_res)

return binding_reservations
109 changes: 109 additions & 0 deletions uw_r25/models.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
from restclients_core import models


class Event(models.Model):
DRAFT_STATE = "0"
TENTATIVE_STATE = "1"
CONFIRMED_STATE = "2"
SEALED_STATE = "3"
DENIED_STATE = "4"
CANCELLED_STATE = "99"

STATE_CHOICES = (
(DRAFT_STATE, "Draft"),
(TENTATIVE_STATE, "Tentative"),
(CONFIRMED_STATE, "Confirmed"),
(SEALED_STATE, "Sealed"),
(DENIED_STATE, "Denied"),
(CANCELLED_STATE, "Cancelled"),
)

event_id = models.IntegerField()
alien_uid = models.CharField(max_length=100, null=True)
name = models.CharField(max_length=100)
title = models.CharField(max_length=100)
start_date = models.DateField()
end_date = models.DateField()
state = models.CharField(max_length=2, choices=STATE_CHOICES)
parent_id = models.IntegerField(null=True)
cabinet_id = models.IntegerField(null=True)
cabinet_name = models.CharField(max_length=100, null=True)

def state_name(self):
return dict(self.STATE_CHOICES)[self.state]

def parent(self):
if not hasattr(self, "_parent"):
self._parent = None
if self.parent_id is not None:
from uw_r25.events import get_event_by_id
self._parent = get_event_by_id(self.parent_id)
return self._parent

def children(self):
if not hasattr(self, "_children"):
from uw_r25.events import get_events
self._children = get_events(parent_id=self.event_id)
return self._children

def cabinet(self):
if self.cabinet_id is not None:
if self.cabinet_id == self.event_id:
return self
else:
from uw_r25.events import get_event_by_id
return get_event_by_id(self.cabinet_id)

class Meta:
db_table = "restclients_r25_event"


class Space(models.Model):
space_id = models.IntegerField()
name = models.CharField(max_length=100)
formal_name = models.CharField(max_length=200)

class Meta:
db_table = "restclients_r25_space"


class Reservation(models.Model):
STANDARD_STATE = "1"
EXCEPTION_STATE = "2"
WARNING_STATE = "3"
OVERRIDE_STATE = "4"
CANCELLED_STATE = "99"

STATE_CHOICES = (
(STANDARD_STATE, "Standard"),
(EXCEPTION_STATE, "Exception"),
(WARNING_STATE, "Warning"),
(OVERRIDE_STATE, "Override"),
(CANCELLED_STATE, "Cancelled"),
)

reservation_id = models.IntegerField()
state = models.CharField(max_length=2, choices=STATE_CHOICES)
start_datetime = models.DateTimeField()
end_datetime = models.DateTimeField()
event_id = models.IntegerField()
event_name = models.CharField(max_length=64)
profile_name = models.CharField(max_length=32)
contact_name = models.CharField(max_length=64)
contact_email = models.CharField(max_length=64)

def state_name(self):
return dict(self.STATE_CHOICES)[self.state]

class Meta:
db_table = "restclients_r25_reservation"


class BindingReservation(models.Model):
bound_reservation_id = models.IntegerField()
primary_reservation = models.IntegerField()
name = models.CharField(max_length=200)
bound_event_id = models.IntegerField()

class Meta:
db_table = "restclients_r25_binding_reservation"
86 changes: 86 additions & 0 deletions uw_r25/reservations.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
from uw_r25.models import Reservation
from uw_r25 import nsmap, get_resource
from uw_r25.spaces import space_reservation_from_xml
try:
from urllib import urlencode
except:
from urllib.parse import urlencode


def get_reservation_by_id(reservation_id):
url = "/r25ws/servlet/wrd/run/reservation.xml?rsrv_id=%s" % reservation_id
return reservations_from_xml(get_resource(url))[0]


def get_reservations(**kwargs):
"""
Return a list of reservations matching the passed filter.
Supported kwargs are listed at
http://knowledge25.collegenet.com/display/WSW/reservations.xml
"""
kwargs["scope"] = "extended"
url = "/r25ws/servlet/wrd/run/reservations.xml"
if len(kwargs):
url += "?%s" % urlencode(kwargs)

return reservations_from_xml(get_resource(url))


def reservations_from_xml(tree):
try:
profile_name = tree.xpath("r25:profile_name", namespaces=nsmap)[0].text
except:
profile_name = None

reservations = []
for node in tree.xpath("r25:reservation", namespaces=nsmap):
reservation = Reservation()
reservation.reservation_id = node.xpath("r25:reservation_id",
namespaces=nsmap)[0].text
reservation.start_datetime = node.xpath("r25:reservation_start_dt",
namespaces=nsmap)[0].text
reservation.end_datetime = node.xpath("r25:reservation_end_dt",
namespaces=nsmap)[0].text
reservation.state = node.xpath("r25:reservation_state",
namespaces=nsmap)[0].text
if profile_name:
reservation.profile_name = profile_name
else:
reservation.profile_name = node.xpath("r25:profile_name",
namespaces=nsmap)[0].text

try:
pnode = node.xpath("r25:space_reservation", namespaces=nsmap)[0]
reservation.space_reservation = space_reservation_from_xml(pnode)
except IndexError:
reservation.space_reservation = None

try:
enode = node.xpath("r25:event", namespaces=nsmap)[0]
reservation.event_id = enode.xpath("r25:event_id",
namespaces=nsmap)[0].text
reservation.event_name = enode.xpath("r25:event_name",
namespaces=nsmap)[0].text

rnode = enode.xpath("r25:role", namespaces=nsmap)[0]
cnode = rnode.xpath("r25:contact", namespaces=nsmap)[0]
reservation.contact_name = cnode.xpath("r25:contact_name",
namespaces=nsmap)[0].text
try:
anode = cnode.xpath("r25:address", namespaces=nsmap)[0]
reservation.contact_email = anode.xpath("r25:email",
namespaces=nsmap)
[0].text
except IndexError:
reservation.contact_email = None

except IndexError:
enode = tree.getparent()
reservation.event_id = enode.xpath("r25:event_id",
namespaces=nsmap)[0].text
reservation.event_name = enode.xpath("r25:event_name",
namespaces=nsmap)[0].text

reservations.append(reservation)

return reservations
Loading

0 comments on commit 05b6912

Please sign in to comment.