Skip to content

Commit

Permalink
Merge eac3713 into cd53956
Browse files Browse the repository at this point in the history
  • Loading branch information
fanglinfang committed Mar 7, 2022
2 parents cd53956 + eac3713 commit 86e8bea
Show file tree
Hide file tree
Showing 4 changed files with 127 additions and 73 deletions.
113 changes: 65 additions & 48 deletions uw_myplan/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,51 +16,68 @@
logger = logging.getLogger(__name__)


def get_plan(regid, year, quarter, terms=4):
dao = MyPlan_DAO()
url = get_plan_url(regid, year, quarter, terms)

response = dao.getURL(url, {"Accept": "application/json"})
logger.debug(
{'url': url, 'status': response.status, 'data': response.data})
if response.status != 200:
raise DataFailureException(url, response.status, str(response.data))

data = json.loads(response.data)

plan = MyPlan()
for term_data in data:
term = MyPlanTerm()
term.year = term_data["Term"]["Year"]
term.quarter = term_data["Term"]["Quarter"]

term.course_search_href = term_data["CourseSearchHref"]
term.degree_audit_href = term_data["DegreeAuditHref"]
term.myplan_href = term_data["MyPlanHref"]
term.registration_href = term_data["RegistrationHref"]
term.registered_courses_count = int(
term_data["RegisteredCoursesCount"])
term.registered_sections_count = int(
term_data["RegisteredSectionsCount"])

for course_data in term_data["Courses"]:
course = MyPlanCourse()
course.curriculum_abbr = course_data["CurriculumAbbreviation"]
course.course_number = course_data["CourseNumber"]

is_available = course_data["RegistrationAvailable"]
course.registrations_available = is_available

for section_data in course_data["Sections"]:
section = MyPlanCourseSection()
section.section_id = section_data["SectionId"]
course.sections.append(section)

term.courses.append(course)
plan.terms.append(term)
return plan


def get_plan_url(regid, year, quarter, terms=4):
return "/plan/v1/{year},{quarter},{terms},{uwregid}".format(
year=year, quarter=quarter, terms=terms, uwregid=regid)
class Plan(object):

def __init__(self, actas=None):
self.dao = MyPlan_DAO()

def _get_plan_url(self, regid, year, quarter, terms):
return "/plan/v1/{year},{quarter},{terms},{uwregid}".format(
year=year, quarter=quarter, terms=terms, uwregid=regid)

def _get_resource(self, regid, year, quarter, terms,
clear_cached_token=False):
if clear_cached_token:
self.dao.clear_access_token()
return self.dao.getURL(
self._get_plan_url(regid, year, quarter, terms),
{"Accept": "application/json"})

def get_plan(self, regid, year, quarter, terms=4):
response = self._get_resource(regid, year, quarter, terms)
if response.status == 200:
return self._process_data(json.loads(response.data))

if response.status == 401 or response.status == 403:
# clear cached access token, retry once
response = self._get_resource(
regid, year, quarter, terms, clear_cached_token=True)
if response.status == 200:
return self._process_data(json.loads(response.data))

raise DataFailureException(
self._get_plan_url(regid, year, quarter, terms),
response.status, str(response.data))

def _process_data(self, jdata):
plan = MyPlan()
for term_data in jdata:
term = MyPlanTerm()
term.year = term_data["Term"]["Year"]
term.quarter = term_data["Term"]["Quarter"]

term.course_search_href = term_data["CourseSearchHref"]
term.degree_audit_href = term_data["DegreeAuditHref"]
term.myplan_href = term_data["MyPlanHref"]
term.registration_href = term_data["RegistrationHref"]
term.registered_courses_count = int(
term_data["RegisteredCoursesCount"])
term.registered_sections_count = int(
term_data["RegisteredSectionsCount"])

for course_data in term_data["Courses"]:
course = MyPlanCourse()
course.curriculum_abbr = course_data["CurriculumAbbreviation"]
course.course_number = course_data["CourseNumber"]

is_available = course_data["RegistrationAvailable"]
course.registrations_available = is_available

for section_data in course_data["Sections"]:
section = MyPlanCourseSection()
section.section_id = section_data["SectionId"]
course.sections.append(section)

term.courses.append(course)
plan.terms.append(term)
return plan
27 changes: 17 additions & 10 deletions uw_myplan/dao.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
from restclients_core.exceptions import DataFailureException

logger = logging.getLogger(__name__)
myplan_access_token_url = "/oauth2/token"


class MyPlan_Auth_DAO(DAO):
Expand All @@ -18,18 +19,22 @@ def service_name(self):
def _is_cacheable(self, method, url, headers, body=None):
return True

def clear_token_from_cache(self):
self.clear_cached_response(myplan_access_token_url)

def get_auth_token(self, secret):
url = "/oauth2/token"
headers = {"Authorization": "Basic {}".format(secret),
"Content-type": "application/x-www-form-urlencoded"}

response = self.postURL(url, headers, "grant_type=client_credentials")
logger.debug(
{'url': url,
'status': response.status,
'data': response.data})
response = self.postURL(
myplan_access_token_url, headers, "grant_type=client_credentials")
if response.status != 200:
raise DataFailureException(url, response.status, response.data)
logger.error(
{'url': myplan_access_token_url,
'status': response.status,
'data': response.data})
raise DataFailureException(
myplan_access_token_url, response.status, response.data)

data = json.loads(response.data)
return data.get("access_token", "")
Expand Down Expand Up @@ -63,10 +68,12 @@ def service_mock_paths(self):
return [abspath(os.path.join(dirname(__file__), "resources"))]

def _custom_headers(self, method, url, headers, body):
headers = {}
if not headers:
headers = {}
secret = self.get_service_setting("AUTH_SECRET", "")
if secret:
logger.info("AUTH_SECRET: {}...{}".format(
secret[:10], secret[-10:],))
headers["Authorization"] = self.auth_dao.get_auth_token(secret)
return headers

def clear_access_token(self):
self.auth_dao.clear_token_from_cache()
11 changes: 11 additions & 0 deletions uw_myplan/tests/test_dao.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import mock
from commonconf import override_settings
from restclients_core.exceptions import DataFailureException
from restclients_core.models import MockHTTP
from uw_myplan.dao import MyPlan_Auth_DAO, MyPlan_DAO
from uw_myplan.utils import (
fdao_myplan_override, fdao_myplan_auth_override)
Expand All @@ -22,6 +23,16 @@ def test_get_auth_token(self):
self.assertIsNotNone(
MyPlan_Auth_DAO().get_auth_token("test1"))

@mock.patch.object(MyPlan_Auth_DAO, "postURL")
def test_get_auth_token(self, mock):
response = MockHTTP()
response.status = 404
response.data = "Not Found"
mock.return_value = response
self.assertRaises(
DataFailureException,
MyPlan_Auth_DAO().get_auth_token, "test1")

def test_no_auth_header(self):
headers = MyPlan_DAO()._custom_headers("GET", "/", {}, "")
self.assertFalse("Authorization" in headers)
Expand Down
49 changes: 34 additions & 15 deletions uw_myplan/tests/test_myplan.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,28 +2,40 @@
# SPDX-License-Identifier: Apache-2.0

from unittest import TestCase
from uw_myplan import get_plan, get_plan_url
import mock
from restclients_core.models import MockHTTP
from restclients_core.exceptions import DataFailureException
from uw_myplan import Plan


class MyPlanTestData(TestCase):
class PlanTest(TestCase):
def test_plan_url(self):
self.assertEquals(
get_plan_url(
Plan()._get_plan_url(
"9136CCB8F66711D5BE060004AC494FFE", 2013, "spring", 2), (
"/plan/v1/2013,spring,2,"
"9136CCB8F66711D5BE060004AC494FFE"))

self.assertEquals(
get_plan_url(
"9136CCB8F66711D5BE060004AC494FFE", 2012, "summer"), (
"/plan/v1/2012,summer,4,"
"9136CCB8F66711D5BE060004AC494FFE"))
@mock.patch.object(Plan, "_get_resource")
def test_error_401(self, mock):
response = MockHTTP()
response.status = 403
response.data = "Not Authorized"
mock.return_value = response
self.assertRaises(
DataFailureException,
Plan().get_plan,
"9136CCB8F66711D5BE060004AC494FFE",
2013,
"spring",
terms=4)

def test_javerage(self):
plan = get_plan(regid="9136CCB8F66711D5BE060004AC494FFE",
year=2013,
quarter="spring",
terms=4)
plan = Plan().get_plan(
regid="9136CCB8F66711D5BE060004AC494FFE",
year=2013,
quarter="spring",
terms=4)
self.assertEquals(len(plan.terms), 4)

self.assertEquals(plan.terms[0].year, 2013)
Expand Down Expand Up @@ -67,10 +79,17 @@ def test_javerage(self):
self.assertEquals(term_data.courses[0].sections[1].section_id, 'AA')
self.assertEquals(term_data.courses[0].sections[2].section_id, 'AB')

resp = Plan()._get_resource(
"9136CCB8F66711D5BE060004AC494FFE", 2013, "spring",
4, clear_cached_token=True)
self.assertIsNotNone(resp)

def test_json(self):
plan = get_plan(regid="9136CCB8F66711D5BE060004AC494FFE",
year=2013, quarter="spring",
terms=4)
plan = Plan().get_plan(
regid="9136CCB8F66711D5BE060004AC494FFE",
year=2013,
quarter="spring",
terms=4)
json_data = plan.json_data()
term_data = json_data["terms"][0]
self.assertEquals(
Expand Down

0 comments on commit 86e8bea

Please sign in to comment.