-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #2 from uw-it-aca/develop
Develop
- Loading branch information
Showing
26 changed files
with
717 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
[run] | ||
branch = True | ||
source = uw_r25/ | ||
include = uw_r25/* |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
include uw_r25/VERSION | ||
recursive-include uw_r25 * |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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', | ||
], | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
[R25] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
0.1 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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"))] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
Oops, something went wrong.