Studio: adding course re-run-centric static template rendering * initial HTML for dashboard states * initial HTML for new course re-run view/form * initial HTML placeholder for outline alert UI Conflicts: cms/templates/index.html Studio: adding styling for course re-run-centric views * adding new view/page mast-wizard type * refactoring create course/element form styling * adding course re-run view specific styling * adding courses processing styling (w/ alerts and status) Course rerun server-side updates: support display_name and DuplicateCourseError. Studio: further design revisions and tweaks from feedback * removing new window attribute from re-run control * removing links from processing courses * revising look/feel of dismiss action on dashboard + alert * correcting font-weight of dashboard processing title * adding extra space to course rerun action on dashboard * re-wording secondary cancel action on rerun view Conflicts: cms/templates/index.html Added interation on unsucceeded courses in dashboard Studio: removing 'rel=external' property from course re-run actions Studio: removing hover styles for processing courses Fixed value bug in split and set course listing to display run moved task.py for rerun
152 lines
5.6 KiB
Python
152 lines
5.6 KiB
Python
"""
|
|
Model Managers for Course Actions
|
|
"""
|
|
from django.db import models, transaction
|
|
|
|
|
|
class CourseActionStateManager(models.Manager):
|
|
"""
|
|
An abstract Model Manager class for Course Action State models.
|
|
This abstract class expects child classes to define the ACTION (string) field.
|
|
"""
|
|
class Meta:
|
|
"""Abstract manager class, with subclasses defining the ACTION (string) field."""
|
|
abstract = True
|
|
|
|
def find_all(self, exclude_args=None, **kwargs):
|
|
"""
|
|
Finds and returns all entries for this action and the given field names-and-values in kwargs.
|
|
The exclude_args dict allows excluding entries with the field names-and-values in exclude_args.
|
|
"""
|
|
return self.filter(action=self.ACTION, **kwargs).exclude(**(exclude_args or {})) # pylint: disable=no-member
|
|
|
|
def find_first(self, exclude_args=None, **kwargs):
|
|
"""
|
|
Returns the first entry for the this action and the given fields in kwargs, if found.
|
|
The exclude_args dict allows excluding entries with the field names-and-values in exclude_args.
|
|
|
|
Raises ItemNotFoundError if more than 1 entry is found.
|
|
|
|
There may or may not be greater than one entry, depending on the usage pattern for this Action.
|
|
"""
|
|
objects = self.find_all(exclude_args=exclude_args, **kwargs)
|
|
if len(objects) == 0:
|
|
raise CourseActionStateItemNotFoundError(
|
|
"No entry found for action {action} with filter {filter}, excluding {exclude}".format(
|
|
action=self.ACTION, # pylint: disable=no-member
|
|
filter=kwargs,
|
|
exclude=exclude_args,
|
|
))
|
|
else:
|
|
return objects[0]
|
|
|
|
def delete(self, entry_id):
|
|
"""
|
|
Deletes the entry with given id.
|
|
"""
|
|
self.filter(id=entry_id).delete()
|
|
|
|
|
|
class CourseActionUIStateManager(CourseActionStateManager):
|
|
"""
|
|
A Model Manager subclass of the CourseActionStateManager class that is aware of UI-related fields related
|
|
to state management, including "should_display" and "message".
|
|
"""
|
|
|
|
# add transaction protection to revert changes by get_or_create if an exception is raised before the final save.
|
|
@transaction.commit_on_success
|
|
def update_state(
|
|
self, course_key, new_state, should_display=True, message="", user=None, allow_not_found=False, **kwargs
|
|
):
|
|
"""
|
|
Updates the state of the given course for this Action with the given data.
|
|
If allow_not_found is True, automatically creates an entry if it doesn't exist.
|
|
Raises CourseActionStateException if allow_not_found is False and an entry for the given course
|
|
for this Action doesn't exist.
|
|
"""
|
|
state_object, created = self.get_or_create(course_key=course_key, action=self.ACTION) # pylint: disable=no-member
|
|
|
|
if created:
|
|
if allow_not_found:
|
|
state_object.created_user = user
|
|
else:
|
|
raise CourseActionStateItemNotFoundError(
|
|
"Cannot update non-existent entry for course_key {course_key} and action {action}".format(
|
|
action=self.ACTION, # pylint: disable=no-member
|
|
course_key=course_key,
|
|
))
|
|
|
|
# some state changes may not be user-initiated so override the user field only when provided
|
|
if user:
|
|
state_object.updated_user = user
|
|
|
|
state_object.state = new_state
|
|
state_object.should_display = should_display
|
|
state_object.message = message
|
|
|
|
# update any additional fields in kwargs
|
|
if kwargs:
|
|
for key, value in kwargs.iteritems():
|
|
setattr(state_object, key, value)
|
|
|
|
state_object.save()
|
|
return state_object
|
|
|
|
def update_should_display(self, entry_id, user, should_display):
|
|
"""
|
|
Updates the should_display field with the given value for the entry for the given id.
|
|
"""
|
|
return self.update(id=entry_id, updated_user=user, should_display=should_display)
|
|
|
|
|
|
class CourseRerunUIStateManager(CourseActionUIStateManager):
|
|
"""
|
|
A concrete model Manager for the Reruns Action.
|
|
"""
|
|
ACTION = "rerun"
|
|
|
|
class State(object):
|
|
"""
|
|
An Enum class for maintaining the list of possible states for Reruns.
|
|
"""
|
|
IN_PROGRESS = "in_progress"
|
|
FAILED = "failed"
|
|
SUCCEEDED = "succeeded"
|
|
|
|
def initiated(self, source_course_key, destination_course_key, user, display_name):
|
|
"""
|
|
To be called when a new rerun is initiated for the given course by the given user.
|
|
"""
|
|
self.update_state(
|
|
course_key=destination_course_key,
|
|
new_state=self.State.IN_PROGRESS,
|
|
user=user,
|
|
allow_not_found=True,
|
|
source_course_key=source_course_key,
|
|
display_name=display_name,
|
|
)
|
|
|
|
def succeeded(self, course_key):
|
|
"""
|
|
To be called when an existing rerun for the given course has successfully completed.
|
|
"""
|
|
self.update_state(
|
|
course_key=course_key,
|
|
new_state=self.State.SUCCEEDED,
|
|
)
|
|
|
|
def failed(self, course_key, exception):
|
|
"""
|
|
To be called when an existing rerun for the given course has failed with the given exception.
|
|
"""
|
|
self.update_state(
|
|
course_key=course_key,
|
|
new_state=self.State.FAILED,
|
|
message=exception.message,
|
|
)
|
|
|
|
|
|
class CourseActionStateItemNotFoundError(Exception):
|
|
"""An exception class for errors specific to Course Action states."""
|
|
pass
|