Skip to content
This repository was archived by the owner on Jan 19, 2022. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 9 additions & 9 deletions pylmod/__init__.py
Original file line number Diff line number Diff line change
@@ -1,25 +1,25 @@

"""
PyLmod is a module that implements MIT Learning Modules API in python
PyLmod is a module that implements MIT Learning Modules API in Python
"""
import os.path
from pkg_resources import get_distribution, DistributionNotFound

from pylmod.client import Client
from pylmod.stellargradebook import StellarGradeBook
from pylmod.gradebook import GradeBook
from pylmod.membership import Membership

try:
_dist = get_distribution('pylmod')
DIST = get_distribution('pylmod')
# Normalize case for Windows systems
dist_loc = os.path.normcase(_dist.location)
here = os.path.normcase(__file__)
if not here.startswith(os.path.join(dist_loc, 'pylmod')):
DIST_LOC = os.path.normcase(DIST.location)
HERE = os.path.normcase(__file__)
if not HERE.startswith(os.path.join(DIST_LOC, 'pylmod')):
# not installed, but there is another version that *is*
raise DistributionNotFound
except DistributionNotFound:
__version__ = 'Please install this project with setup.py'
else:
__version__ = _dist.version
__version__ = DIST.version


__all__ = ['Client', 'StellarGradeBook', ]
__all__ = ['Client', 'GradeBook', 'Membership']
129 changes: 129 additions & 0 deletions pylmod/base.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
"""
Python class representing interface to MIT Learning Modules web service.
"""

import json
import logging
import requests


log = logging.getLogger(__name__) # pylint: disable=C0103


class Base(object):
"""
The Base class provides the transport for accessing MIT LM web service.

The Base class implements the functions that underlie the HTTP calls to
the LM web service. It shouldn't be instantiated directly as it is
inherited by the classes that implement the API.

Attributes:
cert: The certificate used to authenticate access to LM web service
urlbase: The URL of the LM web service
"""

GETS = {'academicterms': '',
'academicterm': '/{termCode}',
'gradebook': '?uuid={uuid}'}

GBUUID = 'STELLAR:/project/mitxdemosite'
TIMEOUT = 200 # connection timeout, seconds

verbose = True
gradebookid = None

def __init__(
self,
cert,
urlbase='https://learning-modules.mit.edu:8443/service/gradebook',
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

wouldn't this be different in membership and gradebook? If so, we should probably remove the gradebook here and have the sub classes add in their own specific endpoint base.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This could be a new issue if you want to get this in

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I actually had that code in one my previous commits but took it out during one of the churns. I'll add the issue and replace the code.

):
"""
Initialize Base instance.

- urlbase: URL base for gradebook API
(still needs certs); default False
"""
# pem with private and public key application certificate for access
self.cert = cert

self.urlbase = urlbase
self.ses = requests.Session()
self.ses.cert = cert
self.ses.timeout = self.TIMEOUT # connection timeout
self.ses.verify = True # verify site certificate

log.debug("------------------------------------------------------")
log.info("[PyLmod] init base=%s", urlbase)

def rest_action(self, func, url, **kwargs):
"""Routine to do low-level REST operation, with retry"""
cnt = 1
while cnt < 10:
cnt += 1
try:
return self.rest_action_actual(func, url, **kwargs)
except requests.ConnectionError, err:
log.error(
"[StellarGradeBook] Error - connection error in "
"rest_action, err=%s", err
)
log.info(" Retrying...")
except requests.Timeout, err:
log.exception(
"[StellarGradeBook] Error - timeout in "
"rest_action, err=%s", err
)
log.info(" Retrying...")
raise Exception(
"[StellarGradeBook] rest_action failure: exceed max retries"
)

def rest_action_actual(self, func, url, **kwargs):
"""Routine to do low-level REST operation"""
log.info('Running request to %s', url)
resp = func(url, timeout=self.TIMEOUT, verify=False, **kwargs)
try:
retdat = json.loads(resp.content)
except Exception:
log.exception(resp.content)
raise
return retdat

def get(self, service, params=None, **kwargs):
"""
Generic GET operation for retrieving data from Learning Modules API
Example:
gbk.get('students/{gradebookId}', params=params, gradebookId=gbid)
"""
urlfmt = '{base}/' + service + self.GETS.get(service, '')
url = urlfmt.format(base=self.urlbase, **kwargs)
if params is None:
params = {}
return self.rest_action(self.ses.get, url, params=params)

def post(self, service, data, **kwargs):
"""
Generic POST operation for sending data to Learning Modules API.
data should be a JSON string or a dict. If it is not a string,
it is turned into a JSON string for the POST body.
"""
urlfmt = '{base}/' + service
url = urlfmt.format(base=self.urlbase, **kwargs)
if not (type(data) == str or type(data) == unicode):
data = json.dumps(data)
headers = {'content-type': 'application/json'}
return self.rest_action(self.ses.post, url, data=data, headers=headers)

def delete(self, service, data, **kwargs):
"""
Generic DELETE operation for Learning Modules API.
"""
urlfmt = '{base}/' + service
url = urlfmt.format(base=self.urlbase, **kwargs)
if not (type(data) == str or type(data) == unicode):
data = json.dumps(data)
headers = {'content-type': 'application/json'}
return self.rest_action(
self.ses.delete, url, data=data, headers=headers
)
32 changes: 18 additions & 14 deletions pylmod/client.py
Original file line number Diff line number Diff line change
@@ -1,26 +1,30 @@
"""
Python interface to MIT Learning Module
Contains the Client class for pylmod that exposes all API classes.
"""
import logging
from pylmod.stellargradebook import StellarGradeBook
from pylmod.gradebook import GradeBook
from pylmod.membership import Membership

log = logging.getLogger(__name__) # pylint: disable=C0103


class Client(StellarGradeBook): # pylint: disable=too-few-public-methods
class Client(GradeBook, Membership): # pylint: disable=too-few-public-methods
"""
Python class representing interface to MIT Learning Modules.
Python class representing interface to MIT Learning Modules API.

Use Client class to incorporate multiple Learning Modules APIs.

Example usage:
sg = Client('ichuang-cert.pem')
ats = sg.get('academicterms')
gradebook = Client('ichuang-cert.pem')
ats = gradebook.get('academicterms')
tc = ats['data'][0]['termCode']
sg.get('academicterm',termCode=tc)
students = sg.get_students()
assignments = sg.get_assignments()
sg.create_assignment('midterm1', 'mid1', 1.0, 100.0, '11-04-2013')
sid, student = sg.get_student_by_email(email)
aid, assignment = sg.get_assignment_by_name('midterm1')
sg.set_grade(aid, sid, 95.2)
sg.spreadsheet2gradebook(datafn)
gradebook.get('academicterm',termCode=tc)
students = gradebook.get_students()
assignments = gradebook.get_assignments()
gradebook.create_assignment('midterm1', 'mid1', 1.0, 100.0, '11-04-2013')
sid, student = gradebook.get_student_by_email(email)
aid, assignment = gradebook.get_assignment_by_name('midterm1')
gradebook.set_grade(aid, sid, 95.2)
gradebook.spreadsheet2gradebook(datafn)
"""
pass
Loading