Apply code review comments and fix tests
This commit is contained in:
@@ -9,7 +9,7 @@ from django.test.client import Client
|
||||
from django.contrib.auth.models import User
|
||||
|
||||
from xmodule.contentstore.django import contentstore
|
||||
from xmodule.modulestore import LegacyPublishState, ModuleStoreEnum, mongo
|
||||
from xmodule.modulestore import PublishState, ModuleStoreEnum, mongo
|
||||
from xmodule.modulestore.inheritance import own_metadata
|
||||
from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase
|
||||
from xmodule.modulestore.tests.factories import CourseFactory, ItemFactory
|
||||
@@ -151,16 +151,16 @@ class CourseTestCase(ModuleStoreTestCase):
|
||||
# create a Draft vertical
|
||||
vertical = self.store.get_item(course_id.make_usage_key('vertical', self.TEST_VERTICAL), depth=1)
|
||||
draft_vertical = self.store.convert_to_draft(vertical.location, self.user.id)
|
||||
self.assertEqual(self.store.compute_publish_state(draft_vertical), LegacyPublishState.draft)
|
||||
self.assertEqual(self.store.compute_publish_state(draft_vertical), PublishState.draft)
|
||||
|
||||
# create a Private (draft only) vertical
|
||||
private_vertical = self.store.create_item(self.user.id, course_id, 'vertical', self.PRIVATE_VERTICAL)
|
||||
self.assertEqual(self.store.compute_publish_state(private_vertical), LegacyPublishState.private)
|
||||
self.assertEqual(self.store.compute_publish_state(private_vertical), PublishState.private)
|
||||
|
||||
# create a Published (no draft) vertical
|
||||
public_vertical = self.store.create_item(self.user.id, course_id, 'vertical', self.PUBLISHED_VERTICAL)
|
||||
public_vertical = self.store.publish(public_vertical.location, self.user.id)
|
||||
self.assertEqual(self.store.compute_publish_state(public_vertical), LegacyPublishState.public)
|
||||
self.assertEqual(self.store.compute_publish_state(public_vertical), PublishState.public)
|
||||
|
||||
# add the new private and new public as children of the sequential
|
||||
sequential = self.store.get_item(course_id.make_usage_key('sequential', self.SEQUENTIAL))
|
||||
@@ -197,7 +197,7 @@ class CourseTestCase(ModuleStoreTestCase):
|
||||
|
||||
def verify_item_publish_state(item, publish_state):
|
||||
"""Verifies the publish state of the item is as expected."""
|
||||
if publish_state in (LegacyPublishState.private, LegacyPublishState.draft):
|
||||
if publish_state in (PublishState.private, PublishState.draft):
|
||||
self.assertTrue(getattr(item, 'is_draft', False))
|
||||
else:
|
||||
self.assertFalse(getattr(item, 'is_draft', False))
|
||||
@@ -210,18 +210,18 @@ class CourseTestCase(ModuleStoreTestCase):
|
||||
return item
|
||||
|
||||
# verify that the draft vertical is draft
|
||||
vertical = get_and_verify_publish_state('vertical', self.TEST_VERTICAL, LegacyPublishState.draft)
|
||||
vertical = get_and_verify_publish_state('vertical', self.TEST_VERTICAL, PublishState.draft)
|
||||
for child in vertical.get_children():
|
||||
verify_item_publish_state(child, LegacyPublishState.draft)
|
||||
verify_item_publish_state(child, PublishState.draft)
|
||||
|
||||
# make sure that we don't have a sequential that is not in draft mode
|
||||
sequential = get_and_verify_publish_state('sequential', self.SEQUENTIAL, LegacyPublishState.public)
|
||||
sequential = get_and_verify_publish_state('sequential', self.SEQUENTIAL, PublishState.public)
|
||||
|
||||
# verify that we have the private vertical
|
||||
private_vertical = get_and_verify_publish_state('vertical', self.PRIVATE_VERTICAL, LegacyPublishState.private)
|
||||
private_vertical = get_and_verify_publish_state('vertical', self.PRIVATE_VERTICAL, PublishState.private)
|
||||
|
||||
# verify that we have the public vertical
|
||||
public_vertical = get_and_verify_publish_state('vertical', self.PUBLISHED_VERTICAL, LegacyPublishState.public)
|
||||
public_vertical = get_and_verify_publish_state('vertical', self.PUBLISHED_VERTICAL, PublishState.public)
|
||||
|
||||
# verify verticals are children of sequential
|
||||
for vert in [vertical, private_vertical, public_vertical]:
|
||||
@@ -332,7 +332,7 @@ class CourseTestCase(ModuleStoreTestCase):
|
||||
it'll return public in that case
|
||||
"""
|
||||
supposed_state = self.store.compute_publish_state(item)
|
||||
if supposed_state == LegacyPublishState.draft and isinstance(item.runtime.modulestore, DraftModuleStore):
|
||||
if supposed_state == PublishState.draft and isinstance(item.runtime.modulestore, DraftModuleStore):
|
||||
# see if the draft differs from the published
|
||||
published = self.store.get_item(item.location, revision=ModuleStoreEnum.RevisionOption.published_only)
|
||||
if item.get_explicitly_set_fields_by_scope() != published.get_explicitly_set_fields_by_scope():
|
||||
@@ -345,13 +345,13 @@ class CourseTestCase(ModuleStoreTestCase):
|
||||
# checking children: if published differs from item, return draft
|
||||
return supposed_state
|
||||
# published == item in all respects, so return public
|
||||
return LegacyPublishState.public
|
||||
elif supposed_state == LegacyPublishState.public and item.location.category in DIRECT_ONLY_CATEGORIES:
|
||||
return PublishState.public
|
||||
elif supposed_state == PublishState.public and item.location.category in DIRECT_ONLY_CATEGORIES:
|
||||
if not all([
|
||||
self.store.has_item(child_loc, revision=ModuleStoreEnum.RevisionOption.draft_only)
|
||||
for child_loc in item.children
|
||||
]):
|
||||
return LegacyPublishState.draft
|
||||
return PublishState.draft
|
||||
else:
|
||||
return supposed_state
|
||||
else:
|
||||
|
||||
@@ -150,20 +150,6 @@ def course_image_url(course):
|
||||
return path
|
||||
|
||||
|
||||
def compute_publish_state(xblock):
|
||||
"""
|
||||
Returns whether this xblock is draft, public, or private.
|
||||
|
||||
Returns:
|
||||
LegacyPublishState.draft - content is in the process of being edited, but still has a previous
|
||||
version deployed to LMS
|
||||
LegacyPublishState.public - content is locked and deployed to LMS
|
||||
LegacyPublishState.private - content is editable and not deployed to LMS
|
||||
"""
|
||||
|
||||
return modulestore().compute_publish_state(xblock)
|
||||
|
||||
|
||||
def is_currently_visible_to_students(xblock):
|
||||
"""
|
||||
Returns true if there is a published version of the xblock that is currently visible to students.
|
||||
|
||||
@@ -11,8 +11,8 @@ from django.conf import settings
|
||||
from xmodule.modulestore.exceptions import ItemNotFoundError
|
||||
from edxmako.shortcuts import render_to_response
|
||||
|
||||
from xmodule.modulestore import ModuleStoreEnum
|
||||
from xmodule.modulestore.django import modulestore
|
||||
from xmodule.modulestore import LegacyPublishState
|
||||
|
||||
from xblock.core import XBlock
|
||||
from xblock.django.request import webob_to_django_response, django_to_webob_request
|
||||
@@ -21,7 +21,7 @@ from xblock.fields import Scope
|
||||
from xblock.plugin import PluginMissingError
|
||||
from xblock.runtime import Mixologist
|
||||
|
||||
from contentstore.utils import get_lms_link_for_item, compute_publish_state
|
||||
from contentstore.utils import get_lms_link_for_item
|
||||
from contentstore.views.helpers import get_parent_xblock, is_unit, xblock_type_display_name
|
||||
from contentstore.views.item import create_xblock_info
|
||||
|
||||
@@ -100,8 +100,8 @@ def subsection_handler(request, usage_key_string):
|
||||
can_view_live = False
|
||||
subsection_units = item.get_children()
|
||||
for unit in subsection_units:
|
||||
state = compute_publish_state(unit)
|
||||
if state in (LegacyPublishState.public, LegacyPublishState.draft):
|
||||
has_published = modulestore().has_item(unit.location, revision=ModuleStoreEnum.RevisionOption.published_only)
|
||||
if has_published:
|
||||
can_view_live = True
|
||||
break
|
||||
|
||||
@@ -178,6 +178,10 @@ def container_handler(request, usage_key_string):
|
||||
# about the block's ancestors and siblings for use by the Unit Outline.
|
||||
xblock_info = create_xblock_info(xblock, include_ancestor_info=is_unit_page)
|
||||
|
||||
# On the unit page only, add 'has_changes' to indicate when there are changes that can be discarded.
|
||||
if is_unit_page:
|
||||
xblock_info['has_changes'] = modulestore().has_changes(xblock.location)
|
||||
|
||||
# Create the link for preview.
|
||||
preview_lms_base = settings.FEATURES.get('PREVIEW_LMS_BASE')
|
||||
# need to figure out where this item is in the list of children as the
|
||||
|
||||
@@ -636,9 +636,9 @@ def create_xblock_info(xblock, data=None, metadata=None, include_ancestor_info=F
|
||||
"id": unicode(xblock.location),
|
||||
"display_name": xblock.display_name_with_default,
|
||||
"category": xblock.category,
|
||||
"published": published,
|
||||
"edited_on": get_default_time_display(xblock.subtree_edited_on) if xblock.subtree_edited_on else None,
|
||||
"edited_by": safe_get_username(xblock.subtree_edited_by),
|
||||
"published": published,
|
||||
"published_on": get_default_time_display(xblock.published_date) if xblock.published_date else None,
|
||||
"published_by": safe_get_username(xblock.published_by),
|
||||
'studio_url': xblock_studio_url(xblock),
|
||||
@@ -646,7 +646,7 @@ def create_xblock_info(xblock, data=None, metadata=None, include_ancestor_info=F
|
||||
"release_date": release_date,
|
||||
"release_date_from": _get_release_date_from(xblock) if release_date else None,
|
||||
"currently_visible_to_students": currently_visible_to_students,
|
||||
"publish_state": _compute_publish_state(xblock, child_info) if not xblock.category == 'course' else None
|
||||
"visibility_state": _compute_visibility_state(xblock, child_info) if not xblock.category == 'course' else None
|
||||
}
|
||||
if data is not None:
|
||||
xblock_info["data"] = data
|
||||
@@ -659,63 +659,71 @@ def create_xblock_info(xblock, data=None, metadata=None, include_ancestor_info=F
|
||||
return xblock_info
|
||||
|
||||
|
||||
class PublishState(object):
|
||||
class VisibilityState(object):
|
||||
"""
|
||||
Represents the possible publish states for an xblock:
|
||||
live - the block and all of its children are live to students (except for staff only items)
|
||||
ready - the block and all of its children are ready to go live in the future
|
||||
unscheduled - the block and all of its children are unscheduled
|
||||
has_unpublished_content - the block or its children have unpublished content that is not staff only
|
||||
Represents the possible visibility states for an xblock:
|
||||
|
||||
live - the block and all of its descendants are live to students (excluding staff only items)
|
||||
Note: Live means both published and released.
|
||||
|
||||
ready - the block is ready to go live and all of its descendants are live or ready (excluding staff only items)
|
||||
Note: content is ready when it is published and scheduled with a release date in the future.
|
||||
|
||||
unscheduled - the block and all of its descendants have no release date (excluding staff only items)
|
||||
Note: it is valid for items to be published with no release date in which case they are still unscheduled.
|
||||
|
||||
needs_attention - the block or its descendants are not fully live, ready or unscheduled (excluding staff only items)
|
||||
For example: one subsection has draft content, or there's both unreleased and released content in one section.
|
||||
|
||||
staff_only - all of the block's content is to be shown to staff only
|
||||
Note: staff only items do not affect their parent's state.
|
||||
"""
|
||||
live = 'live'
|
||||
ready = 'ready'
|
||||
unscheduled = 'unscheduled'
|
||||
has_unpublished_content = 'has_unpublished_content'
|
||||
unscheduled = 'unscheduled' # unscheduled
|
||||
needs_attention = 'needs_attention'
|
||||
staff_only = 'staff_only'
|
||||
|
||||
|
||||
def _compute_publish_state(xblock, child_info):
|
||||
def _compute_visibility_state(xblock, child_info):
|
||||
"""
|
||||
Returns the current publish state for the specified xblock and its children
|
||||
"""
|
||||
if xblock.visible_to_staff_only:
|
||||
return PublishState.staff_only
|
||||
return VisibilityState.staff_only
|
||||
elif is_unit(xblock) and modulestore().has_changes(xblock.location):
|
||||
return PublishState.has_unpublished_content
|
||||
return VisibilityState.needs_attention
|
||||
is_unscheduled = xblock.start == DEFAULT_START_DATE
|
||||
is_live = datetime.now(UTC) > xblock.start
|
||||
children = child_info and child_info['children']
|
||||
if children and len(children) > 0:
|
||||
all_staff_only = True
|
||||
all_unscheduled = True
|
||||
all_live = True
|
||||
for child in child_info['children']:
|
||||
child_state = child['publish_state']
|
||||
if child_state == PublishState.has_unpublished_content:
|
||||
child_state = child['visibility_state']
|
||||
if child_state == VisibilityState.needs_attention:
|
||||
return child_state
|
||||
elif not child_state == PublishState.staff_only:
|
||||
elif not child_state == VisibilityState.staff_only:
|
||||
all_staff_only = False
|
||||
if not child_state == PublishState.unscheduled:
|
||||
if not child_state == VisibilityState.unscheduled:
|
||||
all_unscheduled = False
|
||||
if not child_state == PublishState.live:
|
||||
if not child_state == VisibilityState.live:
|
||||
all_live = False
|
||||
if all_staff_only:
|
||||
return PublishState.staff_only
|
||||
return VisibilityState.staff_only
|
||||
elif all_unscheduled:
|
||||
if not is_unscheduled:
|
||||
return PublishState.has_unpublished_content
|
||||
else:
|
||||
return PublishState.unscheduled
|
||||
return VisibilityState.unscheduled if is_unscheduled else VisibilityState.needs_attention
|
||||
elif all_live:
|
||||
return PublishState.live
|
||||
return VisibilityState.live if is_live else VisibilityState.needs_attention
|
||||
else:
|
||||
return PublishState.ready
|
||||
return VisibilityState.ready if not is_unscheduled else VisibilityState.needs_attention
|
||||
if is_unscheduled:
|
||||
return PublishState.unscheduled
|
||||
elif datetime.now(UTC) > xblock.start:
|
||||
return PublishState.live
|
||||
return VisibilityState.unscheduled
|
||||
elif is_live:
|
||||
return VisibilityState.live
|
||||
else:
|
||||
return PublishState.ready
|
||||
return VisibilityState.ready
|
||||
|
||||
|
||||
def _create_xblock_ancestor_info(xblock):
|
||||
|
||||
@@ -8,6 +8,7 @@ from contentstore.tests.utils import CourseTestCase
|
||||
from contentstore.utils import reverse_course_url, add_instructor
|
||||
from contentstore.views.access import has_course_access
|
||||
from contentstore.views.course import course_outline_initial_state
|
||||
from contentstore.views.item import create_xblock_info, VisibilityState
|
||||
from course_action_state.models import CourseRerunState
|
||||
from contentstore.views.item import create_xblock_info
|
||||
from contentstore.views.item import create_xblock_info, PublishState
|
||||
@@ -230,7 +231,7 @@ class TestCourseOutline(CourseTestCase):
|
||||
self.assertEqual(json_response['category'], 'course')
|
||||
self.assertEqual(json_response['id'], 'i4x://MITx/999/course/Robot_Super_Course')
|
||||
self.assertEqual(json_response['display_name'], 'Robot Super Course')
|
||||
self.assertIsNone(json_response['publish_state'])
|
||||
self.assertIsNone(json_response['visibility_state'])
|
||||
|
||||
# Now verify the first child
|
||||
children = json_response['child_info']['children']
|
||||
@@ -239,7 +240,7 @@ class TestCourseOutline(CourseTestCase):
|
||||
self.assertEqual(first_child_response['category'], 'chapter')
|
||||
self.assertEqual(first_child_response['id'], 'i4x://MITx/999/chapter/Week_1')
|
||||
self.assertEqual(first_child_response['display_name'], 'Week 1')
|
||||
self.assertEqual(first_child_response['publish_state'], PublishState.unscheduled)
|
||||
self.assertEqual(first_child_response['visibility_state'], VisibilityState.unscheduled)
|
||||
self.assertTrue(len(first_child_response['child_info']['children']) > 0)
|
||||
|
||||
# Finally, validate the entire response for consistency
|
||||
|
||||
@@ -19,11 +19,11 @@ from contentstore.views.component import (
|
||||
component_handler, get_component_templates
|
||||
)
|
||||
|
||||
from contentstore.views.item import create_xblock_info, ALWAYS, PublishState
|
||||
from contentstore.views.item import create_xblock_info, ALWAYS, VisibilityState
|
||||
from contentstore.tests.utils import CourseTestCase
|
||||
from student.tests.factories import UserFactory
|
||||
from xmodule.capa_module import CapaDescriptor
|
||||
from xmodule.modulestore import LegacyPublishState, ModuleStoreEnum
|
||||
from xmodule.modulestore import ModuleStoreEnum
|
||||
from xmodule.modulestore.django import modulestore
|
||||
from xmodule.modulestore.tests.factories import ItemFactory
|
||||
from xmodule.x_module import STUDIO_VIEW, STUDENT_VIEW
|
||||
@@ -425,19 +425,6 @@ class TestEditItem(ItemTest):
|
||||
|
||||
self.course_update_url = reverse_usage_url("xblock_handler", self.usage_key)
|
||||
|
||||
def verify_publish_state(self, usage_key, expected_publish_state):
|
||||
"""
|
||||
Helper method that gets the item from the module store and verifies that the publish state is as expected.
|
||||
Returns the item corresponding to the given usage_key.
|
||||
"""
|
||||
item = self.get_item_from_modulestore(
|
||||
usage_key,
|
||||
(expected_publish_state == LegacyPublishState.private) or
|
||||
(expected_publish_state == LegacyPublishState.draft)
|
||||
)
|
||||
self.assertEqual(expected_publish_state, self.store.compute_publish_state(item))
|
||||
return item
|
||||
|
||||
def test_delete_field(self):
|
||||
"""
|
||||
Sending null in for a field 'deletes' it
|
||||
@@ -543,15 +530,18 @@ class TestEditItem(ItemTest):
|
||||
self.assertEqual(unit1_usage_key, children[2])
|
||||
self.assertEqual(unit2_usage_key, children[1])
|
||||
|
||||
def _is_location_published(self, location):
|
||||
return modulestore().has_item(location, revision=ModuleStoreEnum.RevisionOption.published_only)
|
||||
|
||||
def test_make_public(self):
|
||||
""" Test making a private problem public (publishing it). """
|
||||
# When the problem is first created, it is only in draft (because of its category).
|
||||
self.verify_publish_state(self.problem_usage_key, LegacyPublishState.private)
|
||||
self.assertFalse(self._is_location_published(self.problem_usage_key))
|
||||
self.client.ajax_post(
|
||||
self.problem_update_url,
|
||||
data={'publish': 'make_public'}
|
||||
)
|
||||
self.verify_publish_state(self.problem_usage_key, LegacyPublishState.public)
|
||||
self.assertTrue(self._is_location_published(self.problem_usage_key))
|
||||
|
||||
def test_make_draft(self):
|
||||
""" Test creating a draft version of a public problem. """
|
||||
@@ -564,7 +554,8 @@ class TestEditItem(ItemTest):
|
||||
self.problem_update_url,
|
||||
data={'publish': 'discard_changes'}
|
||||
)
|
||||
published = self.verify_publish_state(self.problem_usage_key, LegacyPublishState.public)
|
||||
self.assertTrue(self._is_location_published(self.problem_usage_key))
|
||||
published = modulestore().get_item(self.problem_usage_key, revision=ModuleStoreEnum.RevisionOption.published_only)
|
||||
self.assertIsNone(published.due)
|
||||
|
||||
def test_republish(self):
|
||||
@@ -576,7 +567,7 @@ class TestEditItem(ItemTest):
|
||||
}
|
||||
|
||||
# When the problem is first created, it is only in draft (because of its category).
|
||||
self.verify_publish_state(self.problem_usage_key, LegacyPublishState.private)
|
||||
self.assertFalse(self._is_location_published(self.problem_usage_key))
|
||||
|
||||
# Republishing when only in draft will update the draft but not cause a public item to be created.
|
||||
self.client.ajax_post(
|
||||
@@ -588,7 +579,7 @@ class TestEditItem(ItemTest):
|
||||
}
|
||||
}
|
||||
)
|
||||
self.verify_publish_state(self.problem_usage_key, LegacyPublishState.private)
|
||||
self.assertFalse(self._is_location_published(self.problem_usage_key))
|
||||
draft = self.get_item_from_modulestore(self.problem_usage_key, verify_is_draft=True)
|
||||
self.assertEqual(draft.display_name, new_display_name)
|
||||
|
||||
@@ -609,7 +600,7 @@ class TestEditItem(ItemTest):
|
||||
}
|
||||
}
|
||||
)
|
||||
self.verify_publish_state(self.problem_usage_key, LegacyPublishState.public)
|
||||
self.assertTrue(self._is_location_published(self.problem_usage_key))
|
||||
published = modulestore().get_item(
|
||||
self.problem_usage_key,
|
||||
revision=ModuleStoreEnum.RevisionOption.published_only
|
||||
@@ -625,7 +616,8 @@ class TestEditItem(ItemTest):
|
||||
self.problem_update_url,
|
||||
data={'publish': 'make_public'}
|
||||
)
|
||||
published = self.verify_publish_state(self.problem_usage_key, LegacyPublishState.public)
|
||||
self.assertTrue(self._is_location_published(self.problem_usage_key))
|
||||
published = modulestore().get_item(self.problem_usage_key, revision=ModuleStoreEnum.RevisionOption.published_only)
|
||||
|
||||
# Update the draft version and check that published is different.
|
||||
self.client.ajax_post(
|
||||
@@ -659,7 +651,8 @@ class TestEditItem(ItemTest):
|
||||
self.problem_update_url,
|
||||
data={'publish': 'make_public'}
|
||||
)
|
||||
published = self.verify_publish_state(self.problem_usage_key, LegacyPublishState.public)
|
||||
self.assertTrue(self._is_location_published(self.problem_usage_key))
|
||||
published = modulestore().get_item(self.problem_usage_key, revision=ModuleStoreEnum.RevisionOption.published_only)
|
||||
|
||||
# Now make a draft
|
||||
self.client.ajax_post(
|
||||
@@ -704,8 +697,8 @@ class TestEditItem(ItemTest):
|
||||
|
||||
# The unit and its children should be private initially
|
||||
unit_update_url = reverse_usage_url('xblock_handler', unit_usage_key)
|
||||
self.verify_publish_state(unit_usage_key, LegacyPublishState.private)
|
||||
self.verify_publish_state(html_usage_key, LegacyPublishState.private)
|
||||
self.assertFalse(self._is_location_published(unit_usage_key))
|
||||
self.assertFalse(self._is_location_published(html_usage_key))
|
||||
|
||||
# Make the unit public and verify that the problem is also made public
|
||||
resp = self.client.ajax_post(
|
||||
@@ -713,8 +706,8 @@ class TestEditItem(ItemTest):
|
||||
data={'publish': 'make_public'}
|
||||
)
|
||||
self.assertEqual(resp.status_code, 200)
|
||||
self.verify_publish_state(unit_usage_key, LegacyPublishState.public)
|
||||
self.verify_publish_state(html_usage_key, LegacyPublishState.public)
|
||||
self.assertTrue(self._is_location_published(unit_usage_key))
|
||||
self.assertTrue(self._is_location_published(html_usage_key))
|
||||
|
||||
# Make a draft for the unit and verify that the problem also has a draft
|
||||
resp = self.client.ajax_post(
|
||||
@@ -725,8 +718,10 @@ class TestEditItem(ItemTest):
|
||||
}
|
||||
)
|
||||
self.assertEqual(resp.status_code, 200)
|
||||
self.verify_publish_state(unit_usage_key, LegacyPublishState.draft)
|
||||
self.verify_publish_state(html_usage_key, LegacyPublishState.draft)
|
||||
self.assertTrue(self._is_location_published(unit_usage_key))
|
||||
self.assertTrue(self._is_location_published(html_usage_key))
|
||||
self.assertTrue(modulestore().get_item(unit_usage_key).has_changes())
|
||||
self.assertTrue(modulestore().get_item(html_usage_key).has_changes())
|
||||
|
||||
|
||||
class TestEditSplitModule(ItemTest):
|
||||
@@ -1243,10 +1238,14 @@ class TestXBlockPublishingInfo(ItemTest):
|
||||
"""
|
||||
Unit tests for XBlock's outline handling.
|
||||
"""
|
||||
def _create_child(self, parent, category, display_name, publish_item=False):
|
||||
FIRST_SUBSECTION_PATH = [0]
|
||||
FIRST_UNIT_PATH = [0, 0]
|
||||
SECOND_UNIT_PATH = [0, 1]
|
||||
|
||||
def _create_child(self, parent, category, display_name, publish_item=False, staff_only=False):
|
||||
return ItemFactory.create(
|
||||
parent_location=parent.location, category=category, display_name=display_name,
|
||||
user_id=self.user.id, publish_item=publish_item
|
||||
user_id=self.user.id, publish_item=publish_item, visible_to_staff_only=staff_only
|
||||
)
|
||||
|
||||
def _get_child(self, xblock_info, index):
|
||||
@@ -1291,87 +1290,122 @@ class TestXBlockPublishingInfo(ItemTest):
|
||||
xblock.display_name = display_name
|
||||
self.store.update_item(xblock, self.user.id)
|
||||
|
||||
def test_empty_chapter_publishing_info(self):
|
||||
def _verify_visibility_state(self, xblock_info, expected_state, path=None):
|
||||
"""
|
||||
Verify the publish state of an item in the xblock_info. If no path is provided
|
||||
then the root item is verified.
|
||||
"""
|
||||
if path:
|
||||
direct_child = self._get_child(xblock_info, path[0])
|
||||
remaining_path = path[1:] if len(path) > 1 else None
|
||||
self._verify_visibility_state(direct_child, expected_state, remaining_path)
|
||||
else:
|
||||
self.assertEqual(xblock_info['visibility_state'], expected_state)
|
||||
|
||||
def test_empty_chapter(self):
|
||||
empty_chapter = self._create_child(self.course, 'chapter', "Empty Chapter")
|
||||
xblock_info = self._get_xblock_info(empty_chapter.location)
|
||||
self.assertEqual(xblock_info['publish_state'], PublishState.unscheduled)
|
||||
self.assertEqual(xblock_info['visibility_state'], VisibilityState.unscheduled)
|
||||
|
||||
def test_empty_section_publishing_info(self):
|
||||
def test_empty_sequential(self):
|
||||
chapter = self._create_child(self.course, 'chapter', "Test Chapter")
|
||||
self._create_child(chapter, 'sequential', "Empty Sequential")
|
||||
xblock_info = self._get_xblock_info(chapter.location)
|
||||
self.assertEqual(xblock_info['publish_state'], PublishState.unscheduled)
|
||||
self.assertEqual(self._get_child(xblock_info, 0)['publish_state'], PublishState.unscheduled)
|
||||
self.assertEqual(xblock_info['visibility_state'], VisibilityState.unscheduled)
|
||||
self.assertEqual(self._get_child(xblock_info, 0)['visibility_state'], VisibilityState.unscheduled)
|
||||
|
||||
def test_published_unit_publishing_info(self):
|
||||
def test_published_unit(self):
|
||||
chapter = self._create_child(self.course, 'chapter', "Test Chapter")
|
||||
sequential = self._create_child(chapter, 'sequential', "Test Sequential")
|
||||
self._create_child(sequential, 'vertical', "Published Unit", publish_item=True)
|
||||
self._create_child(sequential, 'vertical', "Staff Only Unit", staff_only=True)
|
||||
self._set_release_date(chapter.location, datetime.now(UTC) + timedelta(days=1))
|
||||
xblock_info = self._get_xblock_info(chapter.location)
|
||||
self.assertEqual(xblock_info['publish_state'], PublishState.ready)
|
||||
sequential_child_info = self._get_child(xblock_info, 0)
|
||||
self.assertEqual(sequential_child_info['publish_state'], PublishState.ready)
|
||||
unit_child_info = self._get_child(sequential_child_info, 0)
|
||||
self.assertEqual(unit_child_info['publish_state'], PublishState.ready)
|
||||
self._verify_visibility_state(xblock_info, VisibilityState.ready)
|
||||
self._verify_visibility_state(xblock_info, VisibilityState.ready, path=self.FIRST_SUBSECTION_PATH)
|
||||
self._verify_visibility_state(xblock_info, VisibilityState.ready, path=self.FIRST_UNIT_PATH)
|
||||
self._verify_visibility_state(xblock_info, VisibilityState.staff_only, path=self.SECOND_UNIT_PATH)
|
||||
|
||||
def test_released_unit_publishing_info(self):
|
||||
def test_released_unit(self):
|
||||
chapter = self._create_child(self.course, 'chapter', "Test Chapter")
|
||||
sequential = self._create_child(chapter, 'sequential', "Test Sequential")
|
||||
self._create_child(sequential, 'vertical', "Published Unit", publish_item=True)
|
||||
self._create_child(sequential, 'vertical', "Staff Only Unit", staff_only=True)
|
||||
self._set_release_date(chapter.location, datetime.now(UTC) - timedelta(days=1))
|
||||
xblock_info = self._get_xblock_info(chapter.location)
|
||||
self.assertEqual(xblock_info['publish_state'], PublishState.live)
|
||||
sequential_child_info = self._get_child(xblock_info, 0)
|
||||
self.assertEqual(sequential_child_info['publish_state'], PublishState.live)
|
||||
unit_child_info = self._get_child(sequential_child_info, 0)
|
||||
self.assertEqual(unit_child_info['publish_state'], PublishState.live)
|
||||
self._verify_visibility_state(xblock_info, VisibilityState.live)
|
||||
self._verify_visibility_state(xblock_info, VisibilityState.live, path=self.FIRST_SUBSECTION_PATH)
|
||||
self._verify_visibility_state(xblock_info, VisibilityState.live, path=self.FIRST_UNIT_PATH)
|
||||
self._verify_visibility_state(xblock_info, VisibilityState.staff_only, path=self.SECOND_UNIT_PATH)
|
||||
|
||||
def test_partially_released_section_publishing_info(self):
|
||||
def test_unpublished_changes(self):
|
||||
chapter = self._create_child(self.course, 'chapter', "Test Chapter")
|
||||
sequential = self._create_child(chapter, 'sequential', "Test Sequential")
|
||||
unit = self._create_child(sequential, 'vertical', "Published Unit", publish_item=True)
|
||||
self._create_child(sequential, 'vertical', "Staff Only Unit", staff_only=True)
|
||||
self._set_display_name(unit.location, 'Updated Unit')
|
||||
xblock_info = self._get_xblock_info(chapter.location)
|
||||
self._verify_visibility_state(xblock_info, VisibilityState.needs_attention)
|
||||
self._verify_visibility_state(xblock_info, VisibilityState.needs_attention, path=self.FIRST_SUBSECTION_PATH)
|
||||
self._verify_visibility_state(xblock_info, VisibilityState.needs_attention, path=self.FIRST_UNIT_PATH)
|
||||
self._verify_visibility_state(xblock_info, VisibilityState.staff_only, path=self.SECOND_UNIT_PATH)
|
||||
|
||||
def test_partially_released_section(self):
|
||||
chapter = self._create_child(self.course, 'chapter', "Test Chapter")
|
||||
released_sequential = self._create_child(chapter, 'sequential', "Released Sequential")
|
||||
self._create_child(released_sequential, 'vertical', "Released Unit", publish_item=True)
|
||||
self._create_child(released_sequential, 'vertical', "Staff Only Unit", staff_only=True)
|
||||
self._set_release_date(chapter.location, datetime.now(UTC) - timedelta(days=1))
|
||||
published_sequential = self._create_child(chapter, 'sequential', "Published Sequential")
|
||||
self._create_child(published_sequential, 'vertical', "Published Unit", publish_item=True)
|
||||
self._create_child(published_sequential, 'vertical', "Staff Only Unit", staff_only=True)
|
||||
self._set_release_date(published_sequential.location, datetime.now(UTC) + timedelta(days=1))
|
||||
xblock_info = self._get_xblock_info(chapter.location)
|
||||
|
||||
# Verify the state of the released sequential
|
||||
released_sequential_child_info = self._get_child(xblock_info, 0)
|
||||
released_unit_child_info = self._get_child(released_sequential_child_info, 0)
|
||||
self.assertEqual(released_unit_child_info['publish_state'], PublishState.live)
|
||||
self.assertEqual(released_sequential_child_info['publish_state'], PublishState.live)
|
||||
self._verify_visibility_state(xblock_info, VisibilityState.live, path=[0])
|
||||
self._verify_visibility_state(xblock_info, VisibilityState.live, path=[0, 0])
|
||||
self._verify_visibility_state(xblock_info, VisibilityState.staff_only, path=[0, 1])
|
||||
|
||||
# Verify the state of the published sequential
|
||||
public_sequential_child_info = self._get_child(xblock_info, 1)
|
||||
public_unit_child_info = self._get_child(public_sequential_child_info, 0)
|
||||
self.assertEqual(public_sequential_child_info['publish_state'], PublishState.ready)
|
||||
self.assertEqual(public_unit_child_info['publish_state'], PublishState.ready)
|
||||
self._verify_visibility_state(xblock_info, VisibilityState.ready, path=[1])
|
||||
self._verify_visibility_state(xblock_info, VisibilityState.ready, path=[1, 0])
|
||||
self._verify_visibility_state(xblock_info, VisibilityState.staff_only, path=[1, 1])
|
||||
|
||||
# Finally verify the state of the chapter
|
||||
self.assertEqual(xblock_info['publish_state'], PublishState.ready)
|
||||
self._verify_visibility_state(xblock_info, VisibilityState.ready)
|
||||
|
||||
def test_unpublished_changes_publishing_info(self):
|
||||
chapter = self._create_child(self.course, 'chapter', "Test Chapter")
|
||||
sequential = self._create_child(chapter, 'sequential', "Test Sequential")
|
||||
unit = self._create_child(sequential, 'vertical', "Published Unit", publish_item=True)
|
||||
self._set_display_name(unit.location, 'Updated Unit')
|
||||
xblock_info = self._get_xblock_info(chapter.location)
|
||||
self.assertEqual(xblock_info['publish_state'], PublishState.has_unpublished_content)
|
||||
sequential_child_info = self._get_child(xblock_info, 0)
|
||||
self.assertEqual(sequential_child_info['publish_state'], PublishState.has_unpublished_content)
|
||||
unit_child_info = self._get_child(sequential_child_info, 0)
|
||||
self.assertEqual(unit_child_info['publish_state'], PublishState.has_unpublished_content)
|
||||
|
||||
def test_staff_only_publishing_info(self):
|
||||
def test_staff_only(self):
|
||||
chapter = self._create_child(self.course, 'chapter', "Test Chapter")
|
||||
sequential = self._create_child(chapter, 'sequential', "Test Sequential")
|
||||
unit = self._create_child(sequential, 'vertical', "Published Unit")
|
||||
self._set_staff_only(unit.location, True)
|
||||
xblock_info = self._get_xblock_info(chapter.location)
|
||||
self.assertEqual(xblock_info['publish_state'], PublishState.staff_only)
|
||||
sequential_child_info = self._get_child(xblock_info, 0)
|
||||
self.assertEqual(sequential_child_info['publish_state'], PublishState.staff_only)
|
||||
unit_child_info = self._get_child(sequential_child_info, 0)
|
||||
self.assertEqual(unit_child_info['publish_state'], PublishState.staff_only)
|
||||
self._verify_visibility_state(xblock_info, VisibilityState.staff_only)
|
||||
self._verify_visibility_state(xblock_info, VisibilityState.staff_only, path=self.FIRST_SUBSECTION_PATH)
|
||||
self._verify_visibility_state(xblock_info, VisibilityState.staff_only, path=self.FIRST_UNIT_PATH)
|
||||
|
||||
def test_unscheduled_section_with_live_subsection(self):
|
||||
chapter = self._create_child(self.course, 'chapter', "Test Chapter")
|
||||
sequential = self._create_child(chapter, 'sequential', "Test Sequential")
|
||||
self._create_child(sequential, 'vertical', "Published Unit", publish_item=True)
|
||||
self._create_child(sequential, 'vertical', "Staff Only Unit", staff_only=True)
|
||||
self._set_release_date(sequential.location, datetime.now(UTC) - timedelta(days=1))
|
||||
xblock_info = self._get_xblock_info(chapter.location)
|
||||
self._verify_visibility_state(xblock_info, VisibilityState.needs_attention)
|
||||
self._verify_visibility_state(xblock_info, VisibilityState.live, path=self.FIRST_SUBSECTION_PATH)
|
||||
self._verify_visibility_state(xblock_info, VisibilityState.live, path=self.FIRST_UNIT_PATH)
|
||||
self._verify_visibility_state(xblock_info, VisibilityState.staff_only, path=self.SECOND_UNIT_PATH)
|
||||
|
||||
def test_unreleased_section_with_live_subsection(self):
|
||||
chapter = self._create_child(self.course, 'chapter', "Test Chapter")
|
||||
sequential = self._create_child(chapter, 'sequential', "Test Sequential")
|
||||
self._create_child(sequential, 'vertical', "Published Unit", publish_item=True)
|
||||
self._create_child(sequential, 'vertical', "Staff Only Unit", staff_only=True)
|
||||
self._set_release_date(chapter.location, datetime.now(UTC) + timedelta(days=1))
|
||||
self._set_release_date(sequential.location, datetime.now(UTC) - timedelta(days=1))
|
||||
xblock_info = self._get_xblock_info(chapter.location)
|
||||
self._verify_visibility_state(xblock_info, VisibilityState.needs_attention)
|
||||
self._verify_visibility_state(xblock_info, VisibilityState.live, path=self.FIRST_SUBSECTION_PATH)
|
||||
self._verify_visibility_state(xblock_info, VisibilityState.live, path=self.FIRST_UNIT_PATH)
|
||||
self._verify_visibility_state(xblock_info, VisibilityState.staff_only, path=self.SECOND_UNIT_PATH)
|
||||
|
||||
Reference in New Issue
Block a user