Skip to content
This repository has been archived by the owner on Nov 10, 2021. It is now read-only.

Commit

Permalink
Merge remote-tracking branch 'aca/master' into develop
Browse files Browse the repository at this point in the history
  • Loading branch information
jeffFranklin committed Mar 15, 2016
2 parents 8125371 + 6fdfb44 commit 638486e
Show file tree
Hide file tree
Showing 14 changed files with 372 additions and 52 deletions.
11 changes: 8 additions & 3 deletions restclients/cache_implementation.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
import json
import bmemcached
import logging
import threading


logger = logging.getLogger(__name__)
Expand Down Expand Up @@ -253,15 +254,19 @@ def _get_key(self, service, url):
return "%s-%s" % (service, url)

def _get_client(self):
if self.client:
return self.client
thread_id = threading.current_thread().ident
if not hasattr(MemcachedCache, "_memcached_cache"):
MemcachedCache._memcached_cache = {}

if thread_id in MemcachedCache._memcached_cache:
return MemcachedCache._memcached_cache[thread_id]

servers = settings.RESTCLIENTS_MEMCACHED_SERVERS
username = getattr(settings, "RESTCLIENTS_MEMCACHED_USER", None)
password = getattr(settings, "RESTCLIENTS_MEMCACHED_PASS", None)

client = bmemcached.Client(servers, username, password)

self.client = client
MemcachedCache._memcached_cache[thread_id] = client

return client
18 changes: 15 additions & 3 deletions restclients/dao.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,10 @@
from restclients.dao_implementation.trumba import CalendarFile
from restclients.dao_implementation.digitlib import File as DigitlibFile
from restclients.dao_implementation.grad import File as GradFile
from restclients.dao_implementation.libraries import File as LibrariesFile
from restclients.dao_implementation.library.mylibinfo import (
File as MyLibInfoFile)
from restclients.dao_implementation.library.currics import (
File as LibCurricsFile)
from restclients.dao_implementation.myplan import File as MyPlanFile
from restclients.dao_implementation.hfs import File as HfsFile
from restclients.dao_implementation.uwnetid import File as UwnetidFile
Expand Down Expand Up @@ -280,13 +283,22 @@ def _getDAO(self):
return self._getModule('RESTCLIENTS_HFS_DAO_CLASS', HfsFile)


class Libraries_DAO(MY_DAO):
class MyLibInfo_DAO(MY_DAO):
def getURL(self, url, headers):
return self._getURL('libraries', url, headers)

def _getDAO(self):
return self._getModule('RESTCLIENTS_LIBRARIES_DAO_CLASS',
LibrariesFile)
MyLibInfoFile)


class LibCurrics_DAO(MY_DAO):
def getURL(self, url, headers):
return self._getURL('libcurrics', url, headers)

def _getDAO(self):
return self._getModule('RESTCLIENTS_LIBCURRICS_DAO_CLASS',
LibCurricsFile)


class MyPlan_DAO(MY_DAO):
Expand Down
Empty file.
47 changes: 47 additions & 0 deletions restclients/dao_implementation/library/currics.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
"""
Contains UW Libraries Currics DAO implementations.
"""

from django.conf import settings
from restclients.dao_implementation.live import get_con_pool, get_live_url
from restclients.dao_implementation.mock import get_mockdata_url,\
post_mockdata_url, delete_mockdata_url, put_mockdata_url
from restclients.mock_http import MockHTTP
import re


class File(object):
"""
The File DAO implementation returns generally static content. Use this
DAO with this configuration:
RESTCLIENTS_LIBCURRICS_DAO_CLASS =
'restclients.dao_implementation.libraries.File'
"""
def getURL(self, url, headers):
return get_mockdata_url("libcurrics", "file", url, headers)


LIB_MAX_POOL_SIZE = 10
LIB_SOCKET_TIMEOUT = 15


class Live(object):
"""
This DAO provides real data. It requires further configuration, e.g.
RESTCLIENTS_LIBCURRICS_HOST = '...'
"""
pool = None

def getURL(self, url, headers):
if Live.pool is None:
Live.pool = get_con_pool(
settings.RESTCLIENTS_LIBCURRICS_HOST,
max_pool_size=LIB_MAX_POOL_SIZE)

return get_live_url(Live.pool,
'GET',
settings.RESTCLIENTS_LIBCURRICS_HOST,
url,
headers=headers,
service_name='libcurrics')
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,7 @@ def getURL(self, url, headers):
class Live(object):
"""
This DAO provides real data. It requires further configuration, e.g.
RESTCLIENTS_LIBRARIES_HOST =
'https://mylibinfo.lib.washington.edu/mylibinfo/v1/'
RESTCLIENTS_LIBRARIES_HOST = '...'
"""
pool = None

Expand Down
33 changes: 0 additions & 33 deletions restclients/library/__init__.py
Original file line number Diff line number Diff line change
@@ -1,33 +0,0 @@
"""
This is the interface for interacting with the UW Libraries Web Service.
"""

import logging
from restclients.dao import Libraries_DAO
from restclients.exceptions import DataFailureException
from restclients.util.timer import Timer
from restclients.util.log import log_info


INVALID_USER_MSG = "User not found"
logger = logging.getLogger(__name__)


def get_resource(url):
dao = Libraries_DAO()
timer = Timer()
response = dao.getURL(url, {})
log_info(logger,
"%s ==status==> %s" % (url, response.status),
timer)

if response.status != 200:
raise DataFailureException(url, response.status, response.data)

#'Bug' with lib API causing requests with no/invalid user to return a 200
if INVALID_USER_MSG in response.data:
raise DataFailureException(url, 404, response.data)

logger.debug("%s ==data==> %s" % (url, response.data))

return response.data
91 changes: 91 additions & 0 deletions restclients/library/currics.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
"""
This is the interface for interacting with the UW Libraries Currics
Web Service.
"""

import json
from urllib import quote
from restclients.dao import LibCurrics_DAO
from restclients.exceptions import DataFailureException
from restclients.models.library import SubjectGuide, Library, Librarian


subject_guide_url_prefix = '/currics_db/api/v1/data/course'


def get_subject_guide_for_section_params(year, quarter, curriculum_abbr,
course_number, section_id=None):
"""
Returns a SubjectGuide model for the passed section params:
year: year for the section term (4-digits)
quarter: quarter (AUT, WIN, SPR, or SUM)
curriculum_abbr: curriculum abbreviation
course_number: course number
section_id: course section identifier (optional)
"""
url = '%s/%s/%s/%s/%s/%s' % (
subject_guide_url_prefix, year, quarter.upper(),
quote(curriculum_abbr.upper()), course_number, section_id.upper())
headers = {'Accept': 'application/json'}

response = LibCurrics_DAO().getURL(url, headers)

if response.status != 200:
raise DataFailureException(url, response.status, response.data)

data = json.loads(response.data)
return _subject_guide_from_json(data['subjectGuide'])


def get_subject_guide_for_section(section):
"""
Returns a SubjectGuide model for the passed SWS section model.
"""
return get_subject_guide_for_section_params(
section.year, section.quarter[:3], section.curriculum_abbr,
section.course_number, section.section_id)


def get_subject_guide_for_canvas_course_sis_id(course_sis_id):
"""
Returns a SubjectGuide model for the passed Canvas course SIS ID.
"""
(year, quarter, curriculum_abbr, course_number,
section_id) = course_sis_id.split('-', 4)
return get_subject_guide_for_section_params(
year, quarter[:3], curriculum_abbr, course_number, section_id)


def _subject_guide_from_json(data):
subject_guide = SubjectGuide()
subject_guide.contact_url = data.get('askUsLink', None)
subject_guide.contact_text = data.get('askUsText', None)
subject_guide.discipline = data.get('discipline', None)
subject_guide.librarian_url = data.get('findLibrarianLink', None)
subject_guide.librarian_text = data.get('findLibrarianText', None)
subject_guide.guide_url = data.get('guideLink', None)
subject_guide.guide_text = data.get('guideText', None)
subject_guide.faq_url = data.get('howDoILink', None)
subject_guide.faq_text = data.get('howDoIText', None)
subject_guide.writing_guide_url = data.get('writingGuideLink', None)
subject_guide.writing_guide_text = data.get('writingGuideText', None)
subject_guide.libraries = []
subject_guide.librarians = []

for libdata in data.get('libraries', []):
library = Library()
library.name = libdata.get('name', None)
library.description = libdata.get('description', None)
library.url = libdata.get('url', None)
subject_guide.libraries.append(library)

for libdata in data.get('librarians', []):
librarian = Librarian()
librarian.name = libdata.get('name', None)
librarian.email = libdata.get('email', None)
librarian.phone = libdata.get('telephone', None)
librarian.url = libdata.get('url', None)
subject_guide.librarians.append(librarian)

return subject_guide
27 changes: 25 additions & 2 deletions restclients/library/mylibinfo.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,37 @@
import logging
import json
from restclients.models.library import MyLibAccount
from restclients.library import get_resource
from restclients.dao import MyLibInfo_DAO
from restclients.exceptions import DataFailureException
from restclients.util.timer import Timer
from restclients.util.log import log_info


INVALID_USER_MSG = "User not found"
RESPONSE_STYLES = ['html', 'json']
url_prefix = "/mylibinfo/v1/?id="
logger = logging.getLogger(__name__)


def get_resource(url):
timer = Timer()
response = MyLibInfo_DAO().getURL(url, {})
log_info(logger,
"%s ==status==> %s" % (url, response.status),
timer)

if response.status != 200:
raise DataFailureException(url, response.status, response.data)

# 'Bug' with lib API causing requests with no/invalid user to return a 200
if INVALID_USER_MSG in response.data:
raise DataFailureException(url, 404, response.data)

logger.debug("%s ==data==> %s" % (url, response.data))

return response.data


def get_account(netid, timestamp=None):
"""
The Libraries object has a method for getting information
Expand Down Expand Up @@ -49,7 +72,7 @@ def _account_from_json(body):
account_data = json.loads(body)
except Exception as ex:
raise Exception("Unable to parse library data: %s. Exception: %s" % (
body, ex))
body, ex))
account = MyLibAccount()
account.fines = account_data["fines"]
account.holds_ready = account_data["holds_ready"]
Expand Down
31 changes: 31 additions & 0 deletions restclients/models/library.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,37 @@
from django.db import models


class Library(models.Model):
""" Represents a library.
"""
name = models.CharField(max_length=250, null=True)
description = models.CharField(max_length=1000, null=True)
url = models.CharField(max_length=250, null=True)


class Librarian(models.Model):
""" Represents a librarian.
"""
name = models.CharField(max_length=250, null=True)
email = models.CharField(max_length=100, null=True)
phone = models.CharField(max_length=20, null=True)
url = models.CharField(max_length=250, null=True)


class SubjectGuide(models.Model):
contact_url = models.CharField(max_length=250, null=True)
contact_text = models.CharField(max_length=1000, null=True)
discipline = models.CharField(max_length=250, null=True)
librarian_url = models.CharField(max_length=250, null=True)
librarian_text = models.CharField(max_length=1000, null=True)
guide_url = models.CharField(max_length=250, null=True)
guide_text = models.CharField(max_length=1000, null=True)
faq_url = models.CharField(max_length=250, null=True)
faq_text = models.CharField(max_length=1000, null=True)
writing_guide_url = models.CharField(max_length=250, null=True)
writing_guide_text = models.CharField(max_length=1000, null=True)


class MyLibAccount(models.Model):
holds_ready = models.IntegerField(max_length=8)
fines = models.DecimalField(max_digits=8, decimal_places=2)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
{
"subjectGuide": {
"askUsLink": "http://www.lib.washington.edu/about/contact",
"askUsText": "UW librarians are also available to help answer your general questions by email, phone, text, or 24/7 real-time chat.",
"discipline": "Mathematics",
"findLibrarianLink": "http://guides.lib.uw.edu/research/subject-librarians",
"findLibrarianText": "At UW Libraries, there's a dedicated librarian for Mathematics who can help you with your research needs. Get in touch with them by phone or email.",
"guideLink": "http://guides.lib.uw.edu/friendly.php?s=research/math",
"guideText": "Mathematics, Pure and Applied",
"howDoILink": "http://guides.lib.uw.edu/research/faq",
"howDoIText": "This online guide addresses common research and library tasks with short video tutorials and links to other UW help guides.",
"librarians": [
{
"email": "javerage@uw.edu",
"name": "J Average",
"telephone": "111-111-1111",
"url": "http://guides.lib.washington.edu/Javerage"
},
{
"email": "baverage@uw.edu",
"name": "B Average",
"telephone": "111-111-1112",
"url": "http://guides.lib.washington.edu/Baverage"
}
],
"libraries": [
{
"description": "The Mathematics Research Library provides research help and access to materials in Applied Mathematics, Mathematics, and Statistics.",
"name": "Mathematics Research Library",
"url": "http://www.lib.washington.edu/math"
}
],
"writingGuideLink": "http://guides.lib.uw.edu/research/citations",
"writingGuideText": "Find citation style guides, citation management tools, and writing/grammar help."
}
}
Loading

0 comments on commit 638486e

Please sign in to comment.