course_info_model.py 6.63 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 re
16
import logging
17

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

21 22
from xmodule.modulestore.exceptions import ItemNotFoundError
from xmodule.modulestore.django import modulestore
23
from xmodule.html_module import CourseInfoModule
24

25
from openedx.core.lib.xblock_utils import get_course_update_items
26
from cms.djangoapps.contentstore.push_notification import enqueue_push_course_update
27

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


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

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

Calen Pennington committed
45

46
def update_course_updates(location, update, passed_id=None, user=None):
47
    """
48 49 50 51 52 53 54
    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.
55 56
    """
    try:
57
        course_updates = modulestore().get_item(location)
58
    except ItemNotFoundError:
59
        course_updates = modulestore().create_item(user.id, location.course_key, location.block_type, location.block_id)
Calen Pennington committed
60

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

63
    if passed_id is not None:
64 65 66 67 68 69 70 71 72
        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."))
73
    else:
74 75 76 77 78 79 80
        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)
81
        enqueue_push_course_update(update, location.course_key)
82 83

    # update db record
84 85 86 87 88
    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
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 117
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])


118
# pylint: disable=unused-argument
119
def delete_course_update(location, update, passed_id, user):
120
    """
121 122 123
    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.
124 125
    """
    if not passed_id:
126
        return HttpResponseBadRequest()
Calen Pennington committed
127

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

133 134 135 136 137 138 139 140 141
    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
142 143

        # update db record
144 145 146 147 148 149 150 151 152 153 154 155 156 157
        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
158

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


163 164 165 166 167 168 169 170
def _get_html(course_updates_items):
    """
    Method to create course_updates_html from course_updates items
    """
    list_items = []
    for update in reversed(course_updates_items):
        # filter course update items which have status "deleted".
        if update.get("status") != CourseInfoModule.STATUS_DELETED:
171 172
            list_items.append(u"<article><h2>{date}</h2>{content}</article>".format(**update))
    return u"<section>{list_items}</section>".format(list_items="".join(list_items))
173 174 175 176 177 178 179 180 181 182 183


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
    course_updates.data = _get_html(course_update_items)

    # update db record
184
    modulestore().update_item(course_updates, user.id)
185 186

    return course_updates