diff --git a/lms/djangoapps/django_comment_client/tests/test_utils.py b/lms/djangoapps/django_comment_client/tests/test_utils.py index efc6e6c7a3..5132ccc7c5 100644 --- a/lms/djangoapps/django_comment_client/tests/test_utils.py +++ b/lms/djangoapps/django_comment_client/tests/test_utils.py @@ -1,9 +1,14 @@ +from datetime import datetime +from django.core.urlresolvers import reverse from django.test import TestCase +from django.test.utils import override_settings from student.tests.factories import UserFactory, CourseEnrollmentFactory from django_comment_common.models import Role, Permission from factories import RoleFactory import django_comment_client.utils as utils - +from xmodule.modulestore.tests.factories import CourseFactory, ItemFactory +from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase +from courseware.tests.tests import TEST_DATA_MONGO_MODULESTORE class DictionaryTestCase(TestCase): def test_extract(self): @@ -29,128 +34,6 @@ class DictionaryTestCase(TestCase): self.assertEqual(utils.merge_dict(d1, d2), expected) -class CategorySortTestCase(TestCase): - def setUp(self): - self.category_map = { - 'entries': { - u'General': { - 'sort_key': u'General' - } - }, - 'subcategories': { - u'Tests': { - 'sort_key': u'Tests', - 'subcategories': {}, - 'entries': { - u'Quizzes': { - 'sort_key': None - }, u'All': { - 'sort_key': None - }, u'Final Exam': { - 'sort_key': None - }, - } - }, - u'Assignments': { - 'sort_key': u'Assignments', - 'subcategories': {}, - 'entries': { - u'Homework': { - 'sort_key': None - }, - u'All': { - 'sort_key': None - }, - } - } - } - } - - def test_alpha_sort_true(self): - expected_true = { - 'entries': { - u'General': { - 'sort_key': u'General' - } - }, - 'children': [u'Assignments', u'General', u'Tests'], - 'subcategories': { - u'Tests': { - 'sort_key': u'Tests', - 'subcategories': {}, - 'children': [u'All', u'Final Exam', u'Quizzes'], - 'entries': { - u'All': { - 'sort_key': 'All' - }, u'Final Exam': { - 'sort_key': 'Final Exam' - }, u'Quizzes': { - 'sort_key': 'Quizzes' - } - } - }, - u'Assignments': { - 'sort_key': u'Assignments', - 'subcategories': {}, - 'children': [u'All', u'Homework'], - 'entries': { - u'Homework': { - 'sort_key': 'Homework' - }, - u'All': { - 'sort_key': 'All' - }, - } - } - } - } - - utils.sort_map_entries(self.category_map, True) - self.assertEqual(self.category_map, expected_true) - - def test_alpha_sort_false(self): - expected_false = { - 'entries': { - u'General': { - 'sort_key': u'General' - } - }, - 'children': [u'Assignments', u'General', u'Tests'], - 'subcategories': { - u'Tests': { - 'sort_key': u'Tests', - 'subcategories': {}, - 'children': [u'Quizzes', u'All', u'Final Exam'], - 'entries': { - u'Quizzes': { - 'sort_key': None - }, u'All': { - 'sort_key': None - }, u'Final Exam': { - 'sort_key': None - }, - } - }, - u'Assignments': { - 'sort_key': u'Assignments', - 'subcategories': {}, - 'children': [u'All', u'Homework'], - 'entries': { - u'Homework': { - 'sort_key': None - }, - u'All': { - 'sort_key': None - }, - } - } - } - } - - utils.sort_map_entries(self.category_map, False) - self.assertEqual(self.category_map, expected_false) - - class AccessUtilsTestCase(TestCase): def setUp(self): self.course_id = 'edX/toy/2012_Fall' @@ -179,3 +62,416 @@ class AccessUtilsTestCase(TestCase): ret = utils.has_forum_access('student', self.course_id, 'NotARole') self.assertFalse(ret) + + +@override_settings(MODULESTORE=TEST_DATA_MONGO_MODULESTORE) +class CoursewareContextTestCase(ModuleStoreTestCase): + def setUp(self): + self.course = CourseFactory.create(org="TestX", number="101", display_name="Test Course") + self.discussion1 = ItemFactory.create( + parent_location=self.course.location, + category="discussion", + discussion_id="discussion1", + discussion_category="Chapter", + discussion_target="Discussion 1" + ) + self.discussion2 = ItemFactory.create( + parent_location=self.course.location, + category="discussion", + discussion_id="discussion2", + discussion_category="Chapter / Section / Subsection", + discussion_target="Discussion 2" + ) + + def test_missing_commentable_id(self): + thread = {"commentable_id": "non-inline"} + self.assertEqual(utils.get_courseware_context(thread, self.course), None) + + def test_basic(self): + def test_discussion(discussion, expected_title): + thread = {"commentable_id": discussion.discussion_id} + courseware_context = utils.get_courseware_context(thread, self.course) + self.assertEqual(set(courseware_context.keys()), set(["courseware_url", "courseware_title"])) + self.assertEqual( + courseware_context["courseware_url"], + reverse( + "jump_to", + kwargs={ + "course_id": self.course.location.course_id, + "location": discussion.location + } + ) + ) + self.assertEqual( + courseware_context["courseware_title"], + expected_title + ) + + test_discussion(self.discussion1, "Chapter / Discussion 1") + test_discussion(self.discussion2, " Subsection / Discussion 2") + + +@override_settings(MODULESTORE=TEST_DATA_MONGO_MODULESTORE) +class CategoryMapTestCase(ModuleStoreTestCase): + def setUp(self): + self.course = CourseFactory.create(org="TestX", number="101", display_name="Test Course") + # Courses get a default discussion topic on creation, so remove it + self.course.discussion_topics = {} + self.course.save() + self.discussion_num = 0 + self.maxDiff = None #pylint: disable=C0103 + + def create_discussion(self, discussion_category, discussion_target, **kwargs): + self.discussion_num += 1 + ItemFactory.create( + parent_location=self.course.location, + category="discussion", + discussion_id="discussion{}".format(self.discussion_num), + discussion_category=discussion_category, + discussion_target=discussion_target, + **kwargs + ) + + def assertCategoryMapEquals(self, expected): + self.assertEqual( + utils.get_discussion_category_map(self.course), + expected + ) + + def test_empty(self): + self.assertEqual( + utils.get_discussion_category_map(self.course), + {"entries": {}, "subcategories": {}, "children": []} + ) + + def test_configured_topics(self): + self.course.discussion_topics = { + "Topic A": {"id": "Topic_A"}, + "Topic B": {"id": "Topic_B"}, + "Topic C": {"id": "Topic_C"} + } + self.assertCategoryMapEquals( + { + "entries": { + "Topic A": {"id": "Topic_A", "sort_key": "Topic A"}, + "Topic B": {"id": "Topic_B", "sort_key": "Topic B"}, + "Topic C": {"id": "Topic_C", "sort_key": "Topic C"}, + }, + "subcategories": {}, + "children": ["Topic A", "Topic B", "Topic C"] + } + ) + + def test_single_inline(self): + self.create_discussion("Chapter", "Discussion") + self.assertCategoryMapEquals( + { + "entries": {}, + "subcategories": { + "Chapter": { + "entries": { + "Discussion": { + "id": "discussion1", + "sort_key": None + } + }, + "subcategories": {}, + "children": ["Discussion"] + } + }, + "children": ["Chapter"] + } + ) + + def test_tree(self): + self.create_discussion("Chapter 1", "Discussion 1") + self.create_discussion("Chapter 1", "Discussion 2") + self.create_discussion("Chapter 2", "Discussion") + self.create_discussion("Chapter 2 / Section 1 / Subsection 1", "Discussion") + self.create_discussion("Chapter 2 / Section 1 / Subsection 2", "Discussion") + self.create_discussion("Chapter 3 / Section 1", "Discussion") + + self.assertCategoryMapEquals( + { + "entries": {}, + "subcategories": { + "Chapter 1": { + "entries": { + "Discussion 1": { + "id": "discussion1", + "sort_key": None + }, + "Discussion 2": { + "id": "discussion2", + "sort_key": None + } + }, + "subcategories": {}, + "children": ["Discussion 1", "Discussion 2"] + }, + "Chapter 2": { + "entries": { + "Discussion": { + "id": "discussion3", + "sort_key": None + } + }, + "subcategories": { + "Section 1": { + "entries": {}, + "subcategories": { + "Subsection 1": { + "entries": { + "Discussion": { + "id": "discussion4", + "sort_key": None + } + }, + "subcategories": {}, + "children": ["Discussion"] + }, + "Subsection 2": { + "entries": { + "Discussion": { + "id": "discussion5", + "sort_key": None + } + }, + "subcategories": {}, + "children": ["Discussion"] + } + }, + "children": ["Subsection 1", "Subsection 2"] + } + }, + "children": ["Discussion", "Section 1"] + }, + "Chapter 3": { + "entries": {}, + "subcategories": { + "Section 1": { + "entries": { + "Discussion": { + "id": "discussion6", + "sort_key": None + } + }, + "subcategories": {}, + "children": ["Discussion"] + } + }, + "children": ["Section 1"] + } + }, + "children": ["Chapter 1", "Chapter 2", "Chapter 3"] + } + ) + + def test_start_date_filter(self): + now = datetime.now() + later = datetime.max + self.create_discussion("Chapter 1", "Discussion 1", start=now) + self.create_discussion("Chapter 1", "Discussion 2", start=later) + self.create_discussion("Chapter 2", "Discussion", start=now) + self.create_discussion("Chapter 2 / Section 1 / Subsection 1", "Discussion", start=later) + self.create_discussion("Chapter 2 / Section 1 / Subsection 2", "Discussion", start=later) + self.create_discussion("Chapter 3 / Section 1", "Discussion", start=later) + + self.assertCategoryMapEquals( + { + "entries": {}, + "subcategories": { + "Chapter 1": { + "entries": { + "Discussion 1": { + "id": "discussion1", + "sort_key": None + } + }, + "subcategories": {}, + "children": ["Discussion 1"] + }, + "Chapter 2": { + "entries": { + "Discussion": { + "id": "discussion3", + "sort_key": None + } + }, + "subcategories": {}, + "children": ["Discussion"] + } + }, + "children": ["Chapter 1", "Chapter 2"] + } + ) + + def test_sort_inline_explicit(self): + self.create_discussion("Chapter", "Discussion 1", sort_key="D") + self.create_discussion("Chapter", "Discussion 2", sort_key="A") + self.create_discussion("Chapter", "Discussion 3", sort_key="E") + self.create_discussion("Chapter", "Discussion 4", sort_key="C") + self.create_discussion("Chapter", "Discussion 5", sort_key="B") + + self.assertCategoryMapEquals( + { + "entries": {}, + "subcategories": { + "Chapter": { + "entries": { + "Discussion 1": { + "id": "discussion1", + "sort_key": "D" + }, + "Discussion 2": { + "id": "discussion2", + "sort_key": "A" + }, + "Discussion 3": { + "id": "discussion3", + "sort_key": "E" + }, + "Discussion 4": { + "id": "discussion4", + "sort_key": "C" + }, + "Discussion 5": { + "id": "discussion5", + "sort_key": "B" + } + }, + "subcategories": {}, + "children": [ + "Discussion 2", + "Discussion 5", + "Discussion 4", + "Discussion 1", + "Discussion 3" + ] + } + }, + "children": ["Chapter"] + } + ) + + def test_sort_configured_topics_explicit(self): + self.course.discussion_topics = { + "Topic A": {"id": "Topic_A", "sort_key": "B"}, + "Topic B": {"id": "Topic_B", "sort_key": "C"}, + "Topic C": {"id": "Topic_C", "sort_key": "A"} + } + self.assertCategoryMapEquals( + { + "entries": { + "Topic A": {"id": "Topic_A", "sort_key": "B"}, + "Topic B": {"id": "Topic_B", "sort_key": "C"}, + "Topic C": {"id": "Topic_C", "sort_key": "A"}, + }, + "subcategories": {}, + "children": ["Topic C", "Topic A", "Topic B"] + } + ) + + def test_sort_alpha(self): + self.course.discussion_sort_alpha = True + self.course.save() + self.create_discussion("Chapter", "Discussion D") + self.create_discussion("Chapter", "Discussion A") + self.create_discussion("Chapter", "Discussion E") + self.create_discussion("Chapter", "Discussion C") + self.create_discussion("Chapter", "Discussion B") + + self.assertCategoryMapEquals( + { + "entries": {}, + "subcategories": { + "Chapter": { + "entries": { + "Discussion D": { + "id": "discussion1", + "sort_key": "Discussion D" + }, + "Discussion A": { + "id": "discussion2", + "sort_key": "Discussion A" + }, + "Discussion E": { + "id": "discussion3", + "sort_key": "Discussion E" + }, + "Discussion C": { + "id": "discussion4", + "sort_key": "Discussion C" + }, + "Discussion B": { + "id": "discussion5", + "sort_key": "Discussion B" + } + }, + "subcategories": {}, + "children": [ + "Discussion A", + "Discussion B", + "Discussion C", + "Discussion D", + "Discussion E" + ] + } + }, + "children": ["Chapter"] + } + ) + + def test_sort_intermediates(self): + self.create_discussion("Chapter B", "Discussion 2") + self.create_discussion("Chapter C", "Discussion") + self.create_discussion("Chapter A", "Discussion 1") + self.create_discussion("Chapter B", "Discussion 1") + self.create_discussion("Chapter A", "Discussion 2") + + self.assertCategoryMapEquals( + { + "entries": {}, + "subcategories": { + "Chapter A": { + "entries": { + "Discussion 1": { + "id": "discussion3", + "sort_key": None + }, + "Discussion 2": { + "id": "discussion5", + "sort_key": None + } + }, + "subcategories": {}, + "children": ["Discussion 1", "Discussion 2"] + }, + "Chapter B": { + "entries": { + "Discussion 1": { + "id": "discussion4", + "sort_key": None + }, + "Discussion 2": { + "id": "discussion1", + "sort_key": None + } + }, + "subcategories": {}, + "children": ["Discussion 1", "Discussion 2"] + }, + "Chapter C": { + "entries": { + "Discussion": { + "id": "discussion2", + "sort_key": None + } + }, + "subcategories": {}, + "children": ["Discussion"] + } + }, + "children": ["Chapter A", "Chapter B", "Chapter C"] + } + ) diff --git a/lms/djangoapps/django_comment_client/utils.py b/lms/djangoapps/django_comment_client/utils.py index 3e0699f2ef..691334f0d7 100644 --- a/lms/djangoapps/django_comment_client/utils.py +++ b/lms/djangoapps/django_comment_client/utils.py @@ -70,10 +70,10 @@ def get_discussion_id_map(course): def get_discussion_category_map(course): initialize_discussion_info(course) - return filter_unstarted_categories(_DISCUSSIONINFO[course.id]['category_map']) + return _filter_unstarted_categories(_DISCUSSIONINFO[course.id]['category_map']) -def filter_unstarted_categories(category_map): +def _filter_unstarted_categories(category_map): now = datetime.now(UTC()) @@ -111,7 +111,7 @@ def filter_unstarted_categories(category_map): return result_map -def sort_map_entries(category_map, sort_alpha): +def _sort_map_entries(category_map, sort_alpha): things = [] for title, entry in category_map["entries"].items(): if entry["sort_key"] == None and sort_alpha: @@ -119,7 +119,7 @@ def sort_map_entries(category_map, sort_alpha): things.append((title, entry)) for title, category in category_map["subcategories"].items(): things.append((title, category)) - sort_map_entries(category_map["subcategories"][title], sort_alpha) + _sort_map_entries(category_map["subcategories"][title], sort_alpha) category_map["children"] = [x[0] for x in sorted(things, key=lambda x: x[1]["sort_key"])] @@ -199,7 +199,7 @@ def initialize_discussion_info(course): "sort_key": entry.get("sort_key", topic), "start_date": datetime.now(UTC())} - sort_map_entries(category_map, course.discussion_sort_alpha) + _sort_map_entries(category_map, course.discussion_sort_alpha) _DISCUSSIONINFO[course.id]['id_map'] = discussion_id_map _DISCUSSIONINFO[course.id]['category_map'] = category_map