Commit 2838dba1 by Arthur Barrett

fix pep8 violations

parent dd583ce4
......@@ -28,6 +28,7 @@ API_SETTINGS = {
#----------------------------------------------------------------------#
# API requests are routed through api_request() using the resource map.
def api_enabled(request, course_id):
'''
Returns True if the api is enabled for the course, otherwise False.
......@@ -35,9 +36,10 @@ def api_enabled(request, course_id):
course = _get_course(request, course_id)
return notes_enabled_for_course(course)
@login_required
def api_request(request, course_id, **kwargs):
'''
'''
Routes API requests to the appropriate action method and returns JSON.
Raises a 404 if the requested resource does not exist or notes are
disabled for the course.
......@@ -49,7 +51,7 @@ def api_request(request, course_id, **kwargs):
raise Http404
# Locate the requested resource
resource_map = API_SETTINGS.get('RESOURCE_MAP', {})
resource_map = API_SETTINGS.get('RESOURCE_MAP', {})
resource_name = kwargs.pop('resource')
resource_method = request.method
resource = resource_map.get(resource_name)
......@@ -59,7 +61,7 @@ def api_request(request, course_id, **kwargs):
raise Http404
if resource_method not in resource.keys():
log.debug('Resource "{0}" does not support method "{1}"'.format(resource_name, resource_method))
log.debug('Resource "{0}" does not support method "{1}"'.format(resource_name, resource_method))
raise Http404
# Execute the action associated with the resource
......@@ -75,7 +77,7 @@ def api_request(request, course_id, **kwargs):
# Format and output the results
data = None
response = result[0]
if len(result) == 2:
if len(result) == 2:
data = result[1]
formatted = api_format(request, response, data)
......@@ -86,10 +88,11 @@ def api_request(request, course_id, **kwargs):
return response
def api_format(request, response, data):
'''
Returns a two-element list containing the content type and content.
'''
'''
Returns a two-element list containing the content type and content.
'''
content_type = 'application/json'
if data is None:
content = ''
......@@ -97,6 +100,7 @@ def api_format(request, response, data):
content = json.dumps(data)
return [content_type, content]
def _get_course(request, course_id):
'''
Helper function to load and return a user's course.
......@@ -106,20 +110,22 @@ def _get_course(request, course_id):
#----------------------------------------------------------------------#
# API actions exposed via the resource map.
def index(request, course_id):
'''
Returns a list of annotation objects.
'''
Returns a list of annotation objects.
'''
MAX_LIMIT = API_SETTINGS.get('MAX_NOTE_LIMIT')
notes = Note.objects.order_by('id').filter(course_id=course_id,
user=request.user)[:MAX_LIMIT]
user=request.user)[:MAX_LIMIT]
return [HttpResponse(), [note.as_dict() for note in notes]]
def create(request, course_id):
'''
Receives an annotation object to create and returns a 303 with the read location.
'''
Receives an annotation object to create and returns a 303 with the read location.
'''
note = Note(course_id=course_id, user=request.user)
......@@ -135,9 +141,10 @@ def create(request, course_id):
return [response, None]
def read(request, course_id, note_id):
'''
Returns a single annotation object.
'''
Returns a single annotation object.
'''
try:
note = Note.objects.get(id=note_id)
......@@ -149,9 +156,10 @@ def read(request, course_id, note_id):
return [HttpResponse(), note.as_dict()]
def update(request, course_id, note_id):
'''
Updates an annotation object and returns a 303 with the read location.
'''
Updates an annotation object and returns a 303 with the read location.
'''
try:
note = Note.objects.get(id=note_id)
......@@ -174,8 +182,9 @@ def update(request, course_id, note_id):
return [response, None]
def delete(request, course_id, note_id):
'''
'''
Deletes the annotation object and returns a 204 with no content.
'''
try:
......@@ -190,12 +199,13 @@ def delete(request, course_id, note_id):
return [HttpResponse('', status=204), None]
def search(request, course_id):
'''
'''
Returns a subset of annotation objects based on a search query.
'''
MAX_LIMIT = API_SETTINGS.get('MAX_NOTE_LIMIT')
# search parameters
offset = request.GET.get('offset', '')
limit = request.GET.get('limit', '')
......@@ -222,7 +232,7 @@ def search(request, course_id):
# retrieve notes
notes = Note.objects.order_by('id').filter(**filters)
total = notes.count()
rows = notes[offset:offset+limit]
rows = notes[offset:offset + limit]
result = {
'total': total,
'rows': [note.as_dict() for note in rows]
......@@ -230,8 +240,9 @@ def search(request, course_id):
return [HttpResponse(), result]
def root(request, course_id):
'''
Returns version information about the API.
'''
Returns version information about the API.
'''
return [HttpResponse(), API_SETTINGS.get('META')]
......@@ -2,12 +2,12 @@ from django.db import models
from django.contrib.auth.models import User
from django.core.urlresolvers import reverse
from django.core.exceptions import ValidationError
import json
import logging
log = logging.getLogger(__name__)
class Note(models.Model):
user = models.ForeignKey(User, db_index=True)
course_id = models.CharField(max_length=255, db_index=True)
......@@ -18,7 +18,7 @@ class Note(models.Model):
range_start_offset = models.IntegerField()
range_end = models.CharField(max_length=2048)
range_end_offset = models.IntegerField()
tags = models.TextField(default="") # comma-separated string
tags = models.TextField(default="") # comma-separated string
created = models.DateTimeField(auto_now_add=True, null=True, db_index=True)
updated = models.DateTimeField(auto_now=True, db_index=True)
......@@ -68,4 +68,4 @@ class Note(models.Model):
'tags': self.tags.split(","),
'created': str(self.created),
'updated': str(self.updated)
}
\ No newline at end of file
}
......@@ -14,11 +14,12 @@ import logging
from . import utils, api, models
class UtilsTest(TestCase):
def setUp(self):
'''
def setUp(self):
'''
Setup a dummy course-like object with a tabs field that can be
accessed via attribute lookup.
accessed via attribute lookup.
'''
self.course = collections.namedtuple('DummyCourse', ['tabs'])
self.course.tabs = []
......@@ -35,20 +36,20 @@ class UtilsTest(TestCase):
Tests that notes are enabled when the course tab configuration contains
a tab with type "notes."
'''
self.course.tabs = [
{'type': 'foo'},
{'name': 'My Notes', 'type': 'notes'},
{'type':'bar'}]
self.course.tabs = [{'type': 'foo'},
{'name': 'My Notes', 'type': 'notes'},
{'type': 'bar'}]
self.assertTrue(utils.notes_enabled_for_course(self.course))
class ApiTest(TestCase):
def setUp(self):
self.client = Client()
# Mocks
api.api_enabled = self.mock_api_enabled(True)
api.api_enabled = self.mock_api_enabled(True)
# Create two accounts
self.password = 'abc'
......@@ -57,16 +58,16 @@ class ApiTest(TestCase):
self.instructor = User.objects.create_user('instructor', 'instructor@test.com', self.password)
self.course_id = 'HarvardX/CB22x/The_Ancient_Greek_Hero'
self.note = {
'user':self.student,
'course_id':self.course_id,
'uri':'/',
'text':'foo',
'quote':'bar',
'range_start':0,
'range_start_offset':0,
'range_end':100,
'range_end_offset':0,
'tags':'a,b,c'
'user': self.student,
'course_id': self.course_id,
'uri': '/',
'text': 'foo',
'quote': 'bar',
'range_start': 0,
'range_start_offset': 0,
'range_end': 100,
'range_end_offset': 0,
'tags': 'a,b,c'
}
def mock_api_enabled(self, is_enabled):
......@@ -84,7 +85,7 @@ class ApiTest(TestCase):
self.client.login(username=username, password=password)
def url(self, name, args={}):
args.update({'course_id':self.course_id})
args.update({'course_id': self.course_id})
return reverse(name, kwargs=args)
def create_notes(self, num_notes, create=True):
......@@ -100,12 +101,12 @@ class ApiTest(TestCase):
self.login()
resp = self.client.get(self.url('notes_api_root'))
self.assertEqual(resp.status_code, 200)
self.assertEqual(resp.status_code, 200)
self.assertNotEqual(resp.content, '')
content = json.loads(resp.content)
self.assertEqual(set(('name','version')), set(content.keys()))
self.assertEqual(set(('name', 'version')), set(content.keys()))
self.assertIsInstance(content['version'], int)
self.assertEqual(content['name'], 'Notes API')
......@@ -135,7 +136,7 @@ class ApiTest(TestCase):
def test_index_max_notes(self):
self.login()
MAX_LIMIT = api.API_SETTINGS.get('MAX_NOTE_LIMIT')
MAX_LIMIT = api.API_SETTINGS.get('MAX_NOTE_LIMIT')
num_notes = MAX_LIMIT + 1
self.create_notes(num_notes)
......@@ -155,11 +156,12 @@ class ApiTest(TestCase):
note_dict = notes[0].as_dict()
excluded_fields = ['id', 'user_id', 'created', 'updated']
note = dict([(k, v) for k,v in note_dict.items() if k not in excluded_fields])
note = dict([(k, v) for k, v in note_dict.items() if k not in excluded_fields])
resp = self.client.post(self.url('notes_api_notes'), json.dumps(note),
content_type='application/json',
HTTP_X_REQUESTED_WITH='XMLHttpRequest')
resp = self.client.post(self.url('notes_api_notes'),
json.dumps(note),
content_type='application/json',
HTTP_X_REQUESTED_WITH='XMLHttpRequest')
self.assertEqual(resp.status_code, 303)
self.assertEqual(len(resp.content), 0)
......@@ -168,9 +170,10 @@ class ApiTest(TestCase):
self.login()
for empty_test in [None, [], '']:
resp = self.client.post(self.url('notes_api_notes'), json.dumps(empty_test),
content_type='application/json',
HTTP_X_REQUESTED_WITH='XMLHttpRequest')
resp = self.client.post(self.url('notes_api_notes'),
json.dumps(empty_test),
content_type='application/json',
HTTP_X_REQUESTED_WITH='XMLHttpRequest')
self.assertEqual(resp.status_code, 500)
def test_create_note_missing_ranges(self):
......@@ -181,14 +184,15 @@ class ApiTest(TestCase):
note_dict = notes[0].as_dict()
excluded_fields = ['id', 'user_id', 'created', 'updated'] + ['ranges']
note = dict([(k, v) for k,v in note_dict.items() if k not in excluded_fields])
note = dict([(k, v) for k, v in note_dict.items() if k not in excluded_fields])
resp = self.client.post(self.url('notes_api_notes'), json.dumps(note),
content_type='application/json',
HTTP_X_REQUESTED_WITH='XMLHttpRequest')
resp = self.client.post(self.url('notes_api_notes'),
json.dumps(note),
content_type='application/json',
HTTP_X_REQUESTED_WITH='XMLHttpRequest')
self.assertEqual(resp.status_code, 500)
def test_read_note(self):
def test_read_note(self):
self.login()
notes = self.create_notes(3)
......@@ -220,12 +224,14 @@ class ApiTest(TestCase):
# set the student id to a different student (not the one that created the notes)
self.login(as_student=self.student2)
resp = self.client.get(self.url('notes_api_note', { 'note_id': note.id}))
resp = self.client.get(self.url('notes_api_note', {'note_id': note.id}))
self.assertEqual(resp.status_code, 403)
self.assertEqual(resp.content, '')
@unittest.skip("skipping update test stub")
def test_update_note(self): pass
def test_update_note(self):
pass
@unittest.skip("skipping search test stub")
def test_search_note(self): pass
def test_search_note(self):
pass
from django.conf.urls import patterns, url
id_regex = r"(?P<note_id>[0-9A-Fa-f]+)"
urlpatterns = patterns('notes.api',
url(r'^api$', 'api_request', {'resource':'root'}, name='notes_api_root'),
url(r'^api/annotations$', 'api_request', {'resource':'notes'}, name='notes_api_notes'),
url(r'^api/annotations/' + id_regex + r'$', 'api_request', {'resource':'note'}, name='notes_api_note'),
url(r'^api/search', 'api_request', {'resource':'search'}, name='notes_api_search')
)
url(r'^api$', 'api_request', {'resource': 'root'}, name='notes_api_root'),
url(r'^api/annotations$', 'api_request', {'resource': 'notes'}, name='notes_api_notes'),
url(r'^api/annotations/' + id_regex + r'$', 'api_request', {'resource': 'note'}, name='notes_api_note'),
url(r'^api/search', 'api_request', {'resource': 'search'}, name='notes_api_search')
)
from django.conf import settings
def notes_enabled_for_course(course):
'''
Returns True if the notes app is enabled for the course, False otherwise.
'''
# TODO: create a separate policy setting to enable/disable notes
tab_type = 'notes'
tabs = course.tabs
tab_found = next((True for t in tabs if t['type'] == tab_type), False)
return tab_found
Returns True if the notes app is enabled for the course, False otherwise.
In order for the app to be enabled it must be:
1) enabled globally via MITX_FEATURES.
2) present in the course tab configuration.
'''
tab_found = next((True for t in course.tabs if t['type'] == 'notes'), False)
feature_enabled = settings.MITX_FEATURES.get('ENABLE_STUDENT_NOTES')
return feature_enabled and tab_found
......@@ -6,6 +6,7 @@ from notes.models import Note
from notes.utils import notes_enabled_for_course
import json
@login_required
def notes(request, course_id):
''' Displays the student's notes. '''
......@@ -13,7 +14,7 @@ def notes(request, course_id):
course = get_course_with_access(request.user, course_id, 'load')
if not notes_enabled_for_course(course):
raise Http404
notes = Note.objects.filter(course_id=course_id, user=request.user).order_by('-created', 'uri')
json_notes = json.dumps([n.as_dict() for n in notes])
context = {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment