114 lines
3.9 KiB
Python
114 lines
3.9 KiB
Python
"""
|
|
Utilities for course updates.
|
|
"""
|
|
|
|
import hashlib
|
|
from datetime import datetime
|
|
|
|
from lms.djangoapps.courseware.courses import get_course_info_section_block
|
|
from openedx.core.djangoapps.user_api.course_tag.api import get_course_tag, set_course_tag
|
|
|
|
STATUS_VISIBLE = 'visible'
|
|
STATUS_DELETED = 'deleted'
|
|
|
|
VIEW_WELCOME_MESSAGE_KEY = 'view-welcome-message'
|
|
|
|
|
|
def _calculate_update_hash(update):
|
|
"""
|
|
Returns a hash of the content of a course update. Does not need to be secure.
|
|
"""
|
|
hasher = hashlib.md5()
|
|
hasher.update(update['content'].encode('utf-8'))
|
|
return hasher.hexdigest()
|
|
|
|
|
|
def _get_dismissed_hashes(user, course_key):
|
|
"""
|
|
Returns a list of dismissed hashes, or None if all updates have been dismissed.
|
|
"""
|
|
view_welcome_message = get_course_tag(user, course_key, VIEW_WELCOME_MESSAGE_KEY)
|
|
if view_welcome_message == 'False': # legacy value, which dismisses all updates
|
|
return None
|
|
return view_welcome_message.split(',') if view_welcome_message else []
|
|
|
|
|
|
def _add_dismissed_hash(user, course_key, new_hash):
|
|
"""
|
|
Add a new hash to the list of previously dismissed updates.
|
|
|
|
Overwrites a 'False' value with the current hash. Though we likely won't end up in that situation, since
|
|
a 'False' value will never show the update to the user to dismiss in the first place.
|
|
"""
|
|
hashes = _get_dismissed_hashes(user, course_key) or []
|
|
hashes.append(new_hash)
|
|
set_course_tag(user, course_key, VIEW_WELCOME_MESSAGE_KEY, ','.join(hashes))
|
|
|
|
|
|
def _safe_parse_date(date):
|
|
"""
|
|
Since this is used solely for ordering purposes, use today's date as a default
|
|
"""
|
|
try:
|
|
return datetime.strptime(date, '%B %d, %Y')
|
|
except ValueError: # occurs for ill-formatted date values
|
|
return datetime.today()
|
|
|
|
|
|
def get_ordered_updates(request, course):
|
|
"""
|
|
Returns all public course updates in reverse chronological order, including dismissed ones.
|
|
"""
|
|
info_block = get_course_info_section_block(request, request.user, course, 'updates')
|
|
if not info_block:
|
|
return []
|
|
|
|
info_block = getattr(info_block, '_xmodule', info_block)
|
|
ordered_updates = [update for update in info_block.items if update.get('status') == STATUS_VISIBLE]
|
|
ordered_updates.sort(
|
|
key=lambda item: (_safe_parse_date(item['date']), item['id']),
|
|
reverse=True
|
|
)
|
|
for update in ordered_updates:
|
|
update['content'] = info_block.runtime.service(info_block, "replace_urls").replace_urls(update['content'])
|
|
return ordered_updates
|
|
|
|
|
|
def get_current_update_for_user(request, course):
|
|
"""
|
|
Returns the current (most recent) course update HTML.
|
|
|
|
Some rules about when we show updates:
|
|
- If the newest update has not been dismissed yet, it gets returned.
|
|
- If the newest update has been dismissed, we will return None.
|
|
- Will return a previously-dismissed newest update if it has been edited since being dismissed.
|
|
- If a current update is deleted and an already dismissed update is now the newest one, we don't want to show that.
|
|
"""
|
|
updates = get_ordered_updates(request, course)
|
|
if not updates:
|
|
return None
|
|
|
|
dismissed_hashes = _get_dismissed_hashes(request.user, course.id)
|
|
if dismissed_hashes is None: # all updates dismissed
|
|
return None
|
|
|
|
update_hash = _calculate_update_hash(updates[0])
|
|
if update_hash in dismissed_hashes: # pylint: disable=unsupported-membership-test
|
|
return None
|
|
|
|
return updates[0]['content']
|
|
|
|
|
|
def dismiss_current_update_for_user(request, course):
|
|
"""
|
|
Marks the current course update for this user as dismissed.
|
|
|
|
See get_current_update_for_user for what "current course update" means in practice.
|
|
"""
|
|
updates = get_ordered_updates(request, course)
|
|
if not updates:
|
|
return None
|
|
|
|
update_hash = _calculate_update_hash(updates[0])
|
|
_add_dismissed_hash(request.user, course.id, update_hash)
|