Commit 1d738527 by Mark Hoeber

Platform/Mobile API doc updates

parent e9759dc2
############
Change Log
############
*****************
January, 2015
*****************
.. list-table::
:widths: 10 70
:header-rows: 1
* - Date
- Change
* - 29 January 2015
- Added the :ref:`Get or Change User Status in a Course` section.
...@@ -7,6 +7,17 @@ ...@@ -7,6 +7,17 @@
import os import os
from path import path from path import path
import sys import sys
import mock
MOCK_MODULES = ['lxml', 'requests', 'xblock', 'fields', 'xblock.fields',
'frament', 'xblock.fragment', 'webob', 'multidict', 'webob.multidict', 'core',
'xblock.core', 'runtime', 'xblock.runtime', 'sortedcontainers', 'contracts',
'plugin', 'xblock.plugin', 'opaque_keys.edx.asides', 'asides',
'dogstats_wrapper', 'fs', 'fs.errors', 'edxmako', 'edxmako.shortcuts',
'shortcuts', 'crum', 'opaque_keys.edx.locator', 'LibraryLocator', 'Location']
for mod_name in MOCK_MODULES:
sys.modules[mod_name] = mock.Mock()
on_rtd = os.environ.get('READTHEDOCS', None) == 'True' on_rtd = os.environ.get('READTHEDOCS', None) == 'True'
...@@ -35,6 +46,8 @@ if not on_rtd: # only import and set the theme if we're building docs locally ...@@ -35,6 +46,8 @@ if not on_rtd: # only import and set the theme if we're building docs locally
# documentation root, use os.path.abspath to make it absolute, like shown here. # documentation root, use os.path.abspath to make it absolute, like shown here.
root = path('../../../..').abspath() root = path('../../../..').abspath()
sys.path.insert(0, root) sys.path.insert(0, root)
sys.path.append(root / "common/lib/xmodule")
sys.path.append(root / "lms/djangoapps")
sys.path.append(root / "lms/djangoapps/mobile_api") sys.path.append(root / "lms/djangoapps/mobile_api")
sys.path.append(root / "lms/djangoapps/mobile_api/course_info") sys.path.append(root / "lms/djangoapps/mobile_api/course_info")
sys.path.append(root / "lms/djangoapps/mobile_api/users") sys.path.append(root / "lms/djangoapps/mobile_api/users")
...@@ -54,7 +67,7 @@ sys.path.append('.') ...@@ -54,7 +67,7 @@ sys.path.append('.')
if on_rtd: if on_rtd:
os.environ['DJANGO_SETTINGS_MODULE'] = 'lms' os.environ['DJANGO_SETTINGS_MODULE'] = 'lms'
else: else:
os.environ['DJANGO_SETTINGS_MODULE'] = 'lms.envs.test' os.environ['DJANGO_SETTINGS_MODULE'] = 'lms'
# -- General configuration ----------------------------------------------------- # -- General configuration -----------------------------------------------------
...@@ -66,164 +79,9 @@ extensions = [ ...@@ -66,164 +79,9 @@ extensions = [
'sphinx.ext.todo', 'sphinx.ext.coverage', 'sphinx.ext.pngmath', 'sphinx.ext.todo', 'sphinx.ext.coverage', 'sphinx.ext.pngmath',
'sphinx.ext.mathjax', 'sphinx.ext.viewcode', 'sphinxcontrib.napoleon'] 'sphinx.ext.mathjax', 'sphinx.ext.viewcode', 'sphinxcontrib.napoleon']
# List of patterns, relative to source directory, that match files and
# directories to ignore when looking for source files.
exclude_patterns = ['build']
# Output file base name for HTML help builder.
htmlhelp_basename = 'edXDocs'
project = u'edX Platform API Version 0.5 Alpha' project = u'edX Platform API Version 0.5 Alpha'
copyright = u'2014, edX' copyright = u'2015, edX'
# --- Mock modules ------------------------------------------------------------
# Mock all the modules that the readthedocs build can't import
class Mock(object):
def __init__(self, *args, **kwargs):
pass
def __call__(self, *args, **kwargs):
return Mock()
@classmethod
def __getattr__(cls, name):
if name in ('__file__', '__path__'):
return '/dev/null'
elif name[0] == name[0].upper():
mockType = type(name, (), {})
mockType.__module__ = __name__
return mockType
else:
return Mock()
# The list of modules and submodules that we know give RTD trouble.
# Make sure you've tried including the relevant package in
# docs/share/requirements.txt before adding to this list.
MOCK_MODULES = [
'bson',
'bson.errors',
'bson.objectid',
'dateutil',
'dateutil.parser',
'fs',
'fs.errors',
'fs.osfs',
'lazy',
'mako',
'mako.template',
'matplotlib',
'matplotlib.pyplot',
'mock',
'numpy',
'oauthlib',
'oauthlib.oauth1',
'oauthlib.oauth1.rfc5849',
'PIL',
'pymongo',
'pyparsing',
'pysrt',
'requests',
'scipy.interpolate',
'scipy.constants',
'scipy.optimize',
'yaml',
'webob',
'webob.multidict',
]
if on_rtd:
for mod_name in MOCK_MODULES:
sys.modules[mod_name] = Mock()
# -----------------------------------------------------------------------------
# from http://djangosnippets.org/snippets/2533/
# autogenerate models definitions
import inspect
import types
from HTMLParser import HTMLParser
def force_unicode(s, encoding='utf-8', strings_only=False, errors='strict'):
"""
Similar to smart_unicode, except that lazy instances are resolved to
strings, rather than kept as lazy objects.
If strings_only is True, don't convert (some) non-string-like objects.
"""
if strings_only and isinstance(s, (types.NoneType, int)):
return s
if not isinstance(s, basestring,):
if hasattr(s, '__unicode__'):
s = unicode(s)
else:
s = unicode(str(s), encoding, errors)
elif not isinstance(s, unicode):
s = unicode(s, encoding, errors)
return s
class MLStripper(HTMLParser):
def __init__(self):
self.reset()
self.fed = []
def handle_data(self, d):
self.fed.append(d)
def get_data(self):
return ''.join(self.fed)
def strip_tags(html):
s = MLStripper()
s.feed(html)
return s.get_data()
def process_docstring(app, what, name, obj, options, lines):
"""Autodoc django models"""
# This causes import errors if left outside the function
from django.db import models
# If you want extract docs from django forms:
# from django import forms
# from django.forms.models import BaseInlineFormSet
# Only look at objects that inherit from Django's base MODEL class
if inspect.isclass(obj) and issubclass(obj, models.Model):
# Grab the field list from the meta class
fields = obj._meta._fields()
for field in fields:
# Decode and strip any html out of the field's help text
help_text = strip_tags(force_unicode(field.help_text))
# Decode and capitalize the verbose name, for use if there isn't
# any help text
verbose_name = force_unicode(field.verbose_name).capitalize()
if help_text:
# Add the model field to the end of the docstring as a param
# using the help text as the description
lines.append(u':param %s: %s' % (field.attname, help_text))
else:
# Add the model field to the end of the docstring as a param
# using the verbose name as the description
lines.append(u':param %s: %s' % (field.attname, verbose_name))
# Add the field's type to the docstring exclude_patterns = ['build', 'links.rst']
lines.append(u':type %s: %s' % (field.attname, type(field).__name__))
return lines
def setup(app):
"""Setup docsting processors"""
#Register the docstring processor with sphinx
app.connect('autodoc-process-docstring', process_docstring)
...@@ -171,4 +171,4 @@ Get the HTML for the course about page. ...@@ -171,4 +171,4 @@ Get the HTML for the course about page.
</article>\n </article>\n
</section>\n </section>\n
</section>" </section>"
} }
\ No newline at end of file
...@@ -19,6 +19,8 @@ The following tasks and endpoints are currently supported. ...@@ -19,6 +19,8 @@ The following tasks and endpoints are currently supported.
- /api/mobile/v0.5/users/{username} - /api/mobile/v0.5/users/{username}
* - :ref:`Get course enrollments for about a user<Get a User's Course Enrollments>` * - :ref:`Get course enrollments for about a user<Get a User's Course Enrollments>`
- /api/mobile/v0.5/users/{username}/course_enrollments/ - /api/mobile/v0.5/users/{username}/course_enrollments/
* - :ref:`Get or change user status in a course<Get or Change User Status in a Course>`
- /api/mobile/v0.5/users/{username}/course_status_info/{course_id}
* - :ref:`Get a course About page<Get the Course About Page>` * - :ref:`Get a course About page<Get the Course About Page>`
- /api/mobile/v0.5/course_info/{organization}/{course_number}/{course_run}/about - /api/mobile/v0.5/course_info/{organization}/{course_number}/{course_run}/about
* - :ref:`Get updates for a course<Get Course Updates>` * - :ref:`Get updates for a course<Get Course Updates>`
......
...@@ -35,6 +35,9 @@ With the edX Platform API, you can: ...@@ -35,6 +35,9 @@ With the edX Platform API, you can:
* Get :ref:`user details<Get User Details>` and :ref:`course enrollments<Get a * Get :ref:`user details<Get User Details>` and :ref:`course enrollments<Get a
User's Course Enrollments>` for a user. User's Course Enrollments>` for a user.
* :ref:`Get or change user status in a course <Get or Change User Status in a
Course>`
* Get :ref:`course information<Get the Course About Page>`, :ref:`updates<Get * Get :ref:`course information<Get the Course About Page>`, :ref:`updates<Get
Course Updates>`, and :ref:`handouts<Get Course Handouts>` for courses the Course Updates>`, and :ref:`handouts<Get Course Handouts>` for courses the
user is enrolled in. user is enrolled in.
......
...@@ -8,6 +8,7 @@ This page describes how to use the mobile user API to: ...@@ -8,6 +8,7 @@ This page describes how to use the mobile user API to:
* `Get User Details`_ * `Get User Details`_
* `Get a User's Course Enrollments`_ * `Get a User's Course Enrollments`_
* `Get or Change User Status in a Course`_
.. _Get User Details: .. _Get User Details:
...@@ -28,7 +29,7 @@ Users are redirected to this endpoint after logging in. ...@@ -28,7 +29,7 @@ Users are redirected to this endpoint after logging in.
You can use the **course_enrollments** value in the response to get a list of You can use the **course_enrollments** value in the response to get a list of
courses the user is enrolled in. courses the user is enrolled in.
**Example request**: **Example request**
``GET /api/mobile/v0.5/users/{username}`` ``GET /api/mobile/v0.5/users/{username}``
...@@ -165,4 +166,62 @@ Get information about the courses the currently logged in user is enrolled in. ...@@ -165,4 +166,62 @@ Get information about the courses the currently logged in user is enrolled in.
"start": "2013-02-05T05:00:00Z", "start": "2013-02-05T05:00:00Z",
"course_image": "/c4x/edX/DemoX/asset/images_course_image.jpg" "course_image": "/c4x/edX/DemoX/asset/images_course_image.jpg"
} }
}
.. _Get or Change User Status in a Course:
**************************************
Get or Change User Status in a Course
**************************************
.. .. autoclass:: mobile_api.users.views.UserCourseStatus
.. :members:
**Use Case**
Get or update the ID of the module that the specified user last visited in the
specified course.
**Example request**
``GET /api/mobile/v0.5/users/{username}/course_status_info/{course_id}``
.. code-block:: http
PATCH /api/mobile/v0.5/users/{username}/course_status_info/{course_id}
body:
last_visited_module_id={module_id}
modification_date={date}
The modification_date is optional. If it is present, the update will
only take effect if the modification_date is later than the
modification_date saved on the server.
**Response Values**
* last_visited_module_id: The ID of the last module visited by the user in the
course.
* last_visited_module_path: The ID of the modules in the path from the last
visited module to the course module.
**Example Response**
.. code-block:: json
HTTP 200 OK
Vary: Accept
Content-Type: text/html; charset=utf-8
Allow: GET, HEAD, OPTIONS
{
"last_visited_module_id": "i4x://edX/DemoX/html/6018785795994726950614ce7d0f38c5",
"last_visited_module_path": [
"i4x://edX/DemoX/html/6018785795994726950614ce7d0f38c5",
"i4x://edX/DemoX/vertical/26d89b08f75d48829a63520ed8b0037d",
"i4x://edX/DemoX/sequential/dbe8fc027bcb4fe9afb744d2e8415855",
"i4x://edX/DemoX/chapter/social_integration",
"i4x://edX/DemoX/course/Demo_Course"
]
} }
\ No newline at end of file
...@@ -70,8 +70,29 @@ class UserDetail(generics.RetrieveAPIView): ...@@ -70,8 +70,29 @@ class UserDetail(generics.RetrieveAPIView):
@mobile_view(is_user=True) @mobile_view(is_user=True)
class UserCourseStatus(views.APIView): class UserCourseStatus(views.APIView):
""" """
Endpoints for getting and setting meta data **Use Case**
about a user's status within a given course.
Get or update the ID of the module that the specified user last visited in the specified course.
**Example request**:
GET /api/mobile/v0.5/users/{username}/course_status_info/{course_id}
PATCH /api/mobile/v0.5/users/{username}/course_status_info/{course_id}
body:
last_visited_module_id={module_id}
modification_date={date}
The modification_date is optional. If it is present, the update will only take effect
if the modification_date is later than the modification_date saved on the server.
**Response Values**
* last_visited_module_id: The ID of the last module visited by the user in the course.
* last_visited_module_path: The ID of the modules in the path from the
last visited module to the course module.
""" """
http_method_names = ["get", "patch"] http_method_names = ["get", "patch"]
...@@ -142,20 +163,7 @@ class UserCourseStatus(views.APIView): ...@@ -142,20 +163,7 @@ class UserCourseStatus(views.APIView):
@mobile_course_access() @mobile_course_access()
def get(self, request, course, *args, **kwargs): # pylint: disable=unused-argument def get(self, request, course, *args, **kwargs): # pylint: disable=unused-argument
""" """
**Use Case** Get the ID of the module that the specified user last visited in the specified course.
Get meta data about user's status within a specific course
**Example request**:
GET /api/mobile/v0.5/users/{username}/course_status_info/{course_id}
**Response Values**
* last_visited_module_id: The id of the last module visited by the user in the given course
* last_visited_module_path: The ids of the modules in the path from the last visited module
to the course module
""" """
return self._get_course_info(request, course) return self._get_course_info(request, course)
...@@ -163,24 +171,7 @@ class UserCourseStatus(views.APIView): ...@@ -163,24 +171,7 @@ class UserCourseStatus(views.APIView):
@mobile_course_access() @mobile_course_access()
def patch(self, request, course, *args, **kwargs): # pylint: disable=unused-argument def patch(self, request, course, *args, **kwargs): # pylint: disable=unused-argument
""" """
**Use Case** Update the ID of the module that the specified user last visited in the specified course.
Update meta data about user's status within a specific course
**Example request**:
PATCH /api/mobile/v0.5/users/{username}/course_status_info/{course_id}
body:
last_visited_module_id={module_id}
modification_date={date}
modification_date is optional. If it is present, the update will only take effect
if modification_date is later than the modification_date saved on the server
**Response Values**
The same as doing a GET on this path
""" """
module_id = request.DATA.get("last_visited_module_id") module_id = request.DATA.get("last_visited_module_id")
modification_date_string = request.DATA.get("modification_date") modification_date_string = request.DATA.get("modification_date")
......
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