diff --git a/docs/en_us/platform_api/source/change_log.rst b/docs/en_us/platform_api/source/change_log.rst new file mode 100644 index 0000000000..87ca5f97c8 --- /dev/null +++ b/docs/en_us/platform_api/source/change_log.rst @@ -0,0 +1,16 @@ +############ +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. diff --git a/docs/en_us/platform_api/source/conf.py b/docs/en_us/platform_api/source/conf.py index f2951c453f..72fedfd9ed 100644 --- a/docs/en_us/platform_api/source/conf.py +++ b/docs/en_us/platform_api/source/conf.py @@ -7,6 +7,17 @@ import os from path import path 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' @@ -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. root = path('../../../..').abspath() 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/course_info") sys.path.append(root / "lms/djangoapps/mobile_api/users") @@ -54,7 +67,7 @@ sys.path.append('.') if on_rtd: os.environ['DJANGO_SETTINGS_MODULE'] = 'lms' else: - os.environ['DJANGO_SETTINGS_MODULE'] = 'lms.envs.test' + os.environ['DJANGO_SETTINGS_MODULE'] = 'lms' # -- General configuration ----------------------------------------------------- @@ -66,164 +79,9 @@ extensions = [ 'sphinx.ext.todo', 'sphinx.ext.coverage', 'sphinx.ext.pngmath', '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' -copyright = u'2014, edX' +copyright = u'2015, edX' -# --- Mock modules ------------------------------------------------------------ - -# Mock all the modules that the readthedocs build can't import +exclude_patterns = ['build', 'links.rst'] -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 - 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) diff --git a/docs/en_us/platform_api/source/course_info.rst b/docs/en_us/platform_api/source/course_info.rst index 796b9c8998..47c3d0c077 100644 --- a/docs/en_us/platform_api/source/course_info.rst +++ b/docs/en_us/platform_api/source/course_info.rst @@ -171,4 +171,4 @@ Get the HTML for the course about page. \n \n " - } + } \ No newline at end of file diff --git a/docs/en_us/platform_api/source/endpoints.rst b/docs/en_us/platform_api/source/endpoints.rst index 5b2e8cb949..eaccadb28d 100644 --- a/docs/en_us/platform_api/source/endpoints.rst +++ b/docs/en_us/platform_api/source/endpoints.rst @@ -19,6 +19,8 @@ The following tasks and endpoints are currently supported. - /api/mobile/v0.5/users/{username} * - :ref:`Get course enrollments for about a user` - /api/mobile/v0.5/users/{username}/course_enrollments/ + * - :ref:`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` - /api/mobile/v0.5/course_info/{organization}/{course_number}/{course_run}/about * - :ref:`Get updates for a course` diff --git a/docs/en_us/platform_api/source/overview.rst b/docs/en_us/platform_api/source/overview.rst index dec9e7514d..8caf35b932 100644 --- a/docs/en_us/platform_api/source/overview.rst +++ b/docs/en_us/platform_api/source/overview.rst @@ -35,6 +35,9 @@ With the edX Platform API, you can: * Get :ref:`user details` and :ref:`course enrollments` for a user. +* :ref:`Get or change user status in a course ` + * Get :ref:`course information`, :ref:`updates`, and :ref:`handouts` for courses the user is enrolled in. diff --git a/docs/en_us/platform_api/source/users.rst b/docs/en_us/platform_api/source/users.rst index 4658de8901..97d5c0e8e7 100644 --- a/docs/en_us/platform_api/source/users.rst +++ b/docs/en_us/platform_api/source/users.rst @@ -8,6 +8,7 @@ This page describes how to use the mobile user API to: * `Get User Details`_ * `Get a User's Course Enrollments`_ +* `Get or Change User Status in a Course`_ .. _Get User Details: @@ -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 courses the user is enrolled in. -**Example request**: +**Example request** ``GET /api/mobile/v0.5/users/{username}`` @@ -165,4 +166,62 @@ Get information about the courses the currently logged in user is enrolled in. "start": "2013-02-05T05:00:00Z", "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 diff --git a/lms/djangoapps/mobile_api/users/views.py b/lms/djangoapps/mobile_api/users/views.py index a21a06a3ac..8e98d39f98 100644 --- a/lms/djangoapps/mobile_api/users/views.py +++ b/lms/djangoapps/mobile_api/users/views.py @@ -70,8 +70,29 @@ class UserDetail(generics.RetrieveAPIView): @mobile_view(is_user=True) class UserCourseStatus(views.APIView): """ - Endpoints for getting and setting meta data - about a user's status within a given course. + **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} + + 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"] @@ -142,20 +163,7 @@ class UserCourseStatus(views.APIView): @mobile_course_access() def get(self, request, course, *args, **kwargs): # pylint: disable=unused-argument """ - **Use Case** - - 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 + Get the ID of the module that the specified user last visited in the specified course. """ return self._get_course_info(request, course) @@ -163,24 +171,7 @@ class UserCourseStatus(views.APIView): @mobile_course_access() def patch(self, request, course, *args, **kwargs): # pylint: disable=unused-argument """ - **Use Case** - - 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 - + Update the ID of the module that the specified user last visited in the specified course. """ module_id = request.DATA.get("last_visited_module_id") modification_date_string = request.DATA.get("modification_date")