Apply code review comments and fix tests

This commit is contained in:
Andy Armstrong
2014-07-25 17:10:43 -04:00
committed by cahrens
parent 9b98604975
commit ef581e1146
29 changed files with 366 additions and 297 deletions

View File

@@ -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:

View File

@@ -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.

View File

@@ -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

View File

@@ -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):

View File

@@ -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

View File

@@ -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)