course_info_model.py 6.11 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14
"""
Views for viewing, adding, updating and deleting course updates.

Current db representation:
{
    "_id" : locationjson,
    "definition" : {
        "data" : "<ol>[<li><h2>date</h2>content</li>]</ol>"},
        "items" : [{"id": ID, "date": DATE, "content": CONTENT}]
        "metadata" : ignored
    }
}
"""

15
import logging
16
import re
17

18
from django.http import HttpResponseBadRequest
19 20
from django.utils.translation import ugettext as _

21
from cms.djangoapps.contentstore.push_notification import enqueue_push_course_update
22 23 24 25
from openedx.core.lib.xblock_utils import get_course_update_items
from xmodule.html_module import CourseInfoModule
from xmodule.modulestore.django import modulestore
from xmodule.modulestore.exceptions import ItemNotFoundError
26

27 28
# # This should be in a class which inherits from XmlDescriptor
log = logging.getLogger(__name__)
Calen Pennington committed
29 30


31
def get_course_updates(location, provided_id, user_id):
32 33
    """
    Retrieve the relevant course_info updates and unpack into the model which the client expects:
34
    [{id : index, date : string, content : html string}]
35 36
    """
    try:
37
        course_updates = modulestore().get_item(location)
38
    except ItemNotFoundError:
39
        course_updates = modulestore().create_item(user_id, location.course_key, location.block_type, location.block_id)
40

41
    course_update_items = get_course_update_items(course_updates, _get_index(provided_id))
42
    return _get_visible_update(course_update_items)
43

Calen Pennington committed
44

45
def update_course_updates(location, update, passed_id=None, user=None):
46
    """
47 48 49 50 51 52 53
    Either add or update the given course update.
    Add:
        If the passed_id is absent or None, the course update is added.
        If push_notification_selected is set in the update, a celery task for the push notification is created.
    Update:
        It will update it if it has a passed_id which has a valid value.
        Until updates have distinct values, the passed_id is the location url + an index into the html structure.
54 55
    """
    try:
56
        course_updates = modulestore().get_item(location)
57
    except ItemNotFoundError:
58
        course_updates = modulestore().create_item(user.id, location.course_key, location.block_type, location.block_id)
Calen Pennington committed
59

60 61
    course_update_items = list(reversed(get_course_update_items(course_updates)))

62
    if passed_id is not None:
63 64 65 66 67 68 69 70 71
        passed_index = _get_index(passed_id)
        # oldest update at start of list
        if 0 < passed_index <= len(course_update_items):
            course_update_dict = course_update_items[passed_index - 1]
            course_update_dict["date"] = update["date"]
            course_update_dict["content"] = update["content"]
            course_update_items[passed_index - 1] = course_update_dict
        else:
            return HttpResponseBadRequest(_("Invalid course update id."))
72
    else:
73 74 75 76 77 78 79
        course_update_dict = {
            "id": len(course_update_items) + 1,
            "date": update["date"],
            "content": update["content"],
            "status": CourseInfoModule.STATUS_VISIBLE
        }
        course_update_items.append(course_update_dict)
80
        enqueue_push_course_update(update, location.course_key)
81 82

    # update db record
83 84 85 86 87
    save_course_update_items(location, course_updates, course_update_items, user)
    # remove status key
    if "status" in course_update_dict:
        del course_update_dict["status"]
    return course_update_dict
88 89


90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116
def _make_update_dict(update):
    """
    Return course update item as a dictionary with required keys ('id', "date" and "content").
    """
    return {
        "id": update["id"],
        "date": update["date"],
        "content": update["content"],
    }


def _get_visible_update(course_update_items):
    """
    Filter course update items which have status "deleted".
    """
    if isinstance(course_update_items, dict):
        # single course update item
        if course_update_items.get("status") != CourseInfoModule.STATUS_DELETED:
            return _make_update_dict(course_update_items)
        else:
            # requested course update item has been deleted (soft delete)
            return {"error": _("Course update not found."), "status": 404}

    return ([_make_update_dict(update) for update in course_update_items
             if update.get("status") != CourseInfoModule.STATUS_DELETED])


117
# pylint: disable=unused-argument
118
def delete_course_update(location, update, passed_id, user):
119
    """
120 121 122
    Don't delete course update item from db.
    Delete the given course_info update by settings "status" flag to 'deleted'.
    Returns the resulting course_updates.
123 124
    """
    if not passed_id:
125
        return HttpResponseBadRequest()
Calen Pennington committed
126

127
    try:
128
        course_updates = modulestore().get_item(location)
129
    except ItemNotFoundError:
130
        return HttpResponseBadRequest()
Calen Pennington committed
131

132 133 134 135 136 137 138 139 140
    course_update_items = list(reversed(get_course_update_items(course_updates)))
    passed_index = _get_index(passed_id)

    # delete update item from given index
    if 0 < passed_index <= len(course_update_items):
        course_update_item = course_update_items[passed_index - 1]
        # soft delete course update item
        course_update_item["status"] = CourseInfoModule.STATUS_DELETED
        course_update_items[passed_index - 1] = course_update_item
141 142

        # update db record
143 144 145 146 147 148 149 150 151 152 153 154 155 156
        save_course_update_items(location, course_updates, course_update_items, user)
        return _get_visible_update(course_update_items)
    else:
        return HttpResponseBadRequest(_("Invalid course update id."))


def _get_index(passed_id=None):
    """
    From the url w/ index appended, get the index.
    """
    if passed_id:
        index_matcher = re.search(r'.*?/?(\d+)$', passed_id)
        if index_matcher:
            return int(index_matcher.group(1))
Calen Pennington committed
157

158 159
    # return 0 if no index found
    return 0
Calen Pennington committed
160 161


162 163 164 165 166 167
def save_course_update_items(location, course_updates, course_update_items, user=None):
    """
    Save list of course_updates data dictionaries in new field ("course_updates.items")
    and html related to course update in 'data' ("course_updates.data") field.
    """
    course_updates.items = course_update_items
168
    course_updates.data = ""
169 170

    # update db record
171
    modulestore().update_item(course_updates, user.id)
172 173

    return course_updates