Commit 8d6c774d by Dmitry Viskov Committed by Tyler Hallada

Support multi-leaf tagging (#660)

parent 250a0633
...@@ -463,10 +463,11 @@ class TagsDistributionPresenter(CourseAPIPresenterMixin, CoursePresenter): ...@@ -463,10 +463,11 @@ class TagsDistributionPresenter(CourseAPIPresenterMixin, CoursePresenter):
self.available_tags = {} self.available_tags = {}
for item in tags_distribution_data.values(): for item in tags_distribution_data.values():
for tag_key, tag_value in item['tags'].iteritems(): for tag_key, tag_values in item['tags'].iteritems():
if tag_key not in self.available_tags: if tag_key not in self.available_tags:
self.available_tags[tag_key] = set() self.available_tags[tag_key] = set()
self.available_tags[tag_key].add(tag_value) for tag_value in tag_values:
self.available_tags[tag_key].add(tag_value)
return self.available_tags return self.available_tags
def get_tags_content_nav(self, key, selected=None): def get_tags_content_nav(self, key, selected=None):
...@@ -508,7 +509,7 @@ class TagsDistributionPresenter(CourseAPIPresenterMixin, CoursePresenter): ...@@ -508,7 +509,7 @@ class TagsDistributionPresenter(CourseAPIPresenterMixin, CoursePresenter):
""" """
updated_structure[node_id] = origin_structure[node_id] updated_structure[node_id] = origin_structure[node_id]
updated_structure[node_id]['parent'] = parent_id if parent_id else None updated_structure[node_id]['parent'] = parent_id if parent_id else None
for child_id in origin_structure[node_id]["children"]: for child_id in origin_structure[node_id].get("children", []):
_update_node(updated_structure, origin_structure, child_id, origin_structure[node_id]['id']) _update_node(updated_structure, origin_structure, child_id, origin_structure[node_id]['id'])
updated_structure = OrderedDict() updated_structure = OrderedDict()
...@@ -525,22 +526,23 @@ class TagsDistributionPresenter(CourseAPIPresenterMixin, CoursePresenter): ...@@ -525,22 +526,23 @@ class TagsDistributionPresenter(CourseAPIPresenterMixin, CoursePresenter):
for item in tags_distribution_data.values(): for item in tags_distribution_data.values():
if key in item['tags']: if key in item['tags']:
tag_value = item['tags'][key] tag_values = item['tags'][key]
if tag_value not in result: for tag_value in tag_values:
index += 1 if tag_value not in result:
result[tag_value] = { index += 1
'id': tag_value, result[tag_value] = {
'index': index, 'id': tag_value,
'name': tag_value, 'index': index,
'num_modules': 0, 'name': tag_value,
'total_submissions': 0, 'num_modules': 0,
'correct_submissions': 0, 'total_submissions': 0,
'incorrect_submissions': 0 'correct_submissions': 0,
} 'incorrect_submissions': 0
result[tag_value]['num_modules'] += 1 }
result[tag_value]['total_submissions'] += item['total_submissions'] result[tag_value]['num_modules'] += 1
result[tag_value]['correct_submissions'] += item['correct_submissions'] result[tag_value]['total_submissions'] += item['total_submissions']
result[tag_value]['incorrect_submissions'] += item['incorrect_submissions'] result[tag_value]['correct_submissions'] += item['correct_submissions']
result[tag_value]['incorrect_submissions'] += item['incorrect_submissions']
for tag_val, item in result.iteritems(): for tag_val, item in result.iteritems():
item.update({ item.update({
...@@ -562,30 +564,40 @@ class TagsDistributionPresenter(CourseAPIPresenterMixin, CoursePresenter): ...@@ -562,30 +564,40 @@ class TagsDistributionPresenter(CourseAPIPresenterMixin, CoursePresenter):
available_tags = self.get_available_tags() available_tags = self.get_available_tags()
intermediate = OrderedDict() intermediate = OrderedDict()
def _get_tags_info(av_tags, tags):
"""
Helper function to return information about all tags connected with the current item.
"""
data = {}
for av_tag_key in av_tags:
if av_tag_key in tags and tags[av_tag_key]:
data[av_tag_key] = u', '.join(tags[av_tag_key])
else:
data[av_tag_key] = None
return data
for item in tags_distribution_data.values(): for item in tags_distribution_data.values():
if tag_key in item['tags'] and tag_value == slugify(item['tags'][tag_key]): if tag_key in item['tags']:
val = { for item_tag_val in item['tags'][tag_key]:
'id': item['id'], if tag_value == slugify(item_tag_val):
'name': item['id'], val = {
'total_submissions': item['total_submissions'], 'id': item['id'],
'correct_submissions': item['correct_submissions'], 'name': item['id'],
'incorrect_submissions': item['incorrect_submissions'], 'total_submissions': item['total_submissions'],
'correct_percent': utils.math.calculate_percent(item['correct_submissions'], 'correct_submissions': item['correct_submissions'],
item['total_submissions']), 'incorrect_submissions': item['incorrect_submissions'],
'incorrect_percent': utils.math.calculate_percent(item['incorrect_submissions'], 'correct_percent': utils.math.calculate_percent(item['correct_submissions'],
item['total_submissions']), item['total_submissions']),
'url': reverse('courses:performance:learning_outcomes_answers_distribution', 'incorrect_percent': utils.math.calculate_percent(item['incorrect_submissions'],
kwargs={'course_id': self.course_id, item['total_submissions']),
'tag_value': tag_value, 'url': reverse('courses:performance:learning_outcomes_answers_distribution',
'problem_id': item['id']}) kwargs={'course_id': self.course_id,
} 'tag_value': tag_value,
if available_tags: 'problem_id': item['id']})
for av_tag_key in available_tags: }
if av_tag_key in item['tags']: if available_tags:
val[av_tag_key] = item['tags'][av_tag_key] val.update(_get_tags_info(available_tags, item['tags']))
else: intermediate[item['id']] = val
val[av_tag_key] = None
intermediate[item['id']] = val
result = [] result = []
index = 0 index = 0
......
...@@ -393,10 +393,11 @@ class TagsDistributionDataFactory(CourseStructureFactory): ...@@ -393,10 +393,11 @@ class TagsDistributionDataFactory(CourseStructureFactory):
def get_expected_available_tags(self): def get_expected_available_tags(self):
tags = {} tags = {}
for item in self.tags_data_per_homework_assigment: for item in self.tags_data_per_homework_assigment:
for key, val in item['tags'].iteritems(): for key, vals in item['tags'].iteritems():
if key not in tags: for val in vals:
tags[key] = set() if key not in tags:
tags[key].add(val) tags[key] = set()
tags[key].add(val)
return tags return tags
def get_expected_learning_outcome_tags_content_nav(self, key): def get_expected_learning_outcome_tags_content_nav(self, key):
...@@ -415,25 +416,25 @@ class TagsDistributionDataFactory(CourseStructureFactory): ...@@ -415,25 +416,25 @@ class TagsDistributionDataFactory(CourseStructureFactory):
for val in self.tags_data_per_homework_assigment: for val in self.tags_data_per_homework_assigment:
if tag_key in val['tags']: if tag_key in val['tags']:
tag_value = val['tags'][tag_key] for tag_value in val['tags'][tag_key]:
if tag_value not in expected: if tag_value not in expected:
index += 1 index += 1
expected[tag_value] = { expected[tag_value] = {
"id": tag_value, "id": tag_value,
"index": index, "index": index,
"name": tag_value, "name": tag_value,
"total_submissions": 0, "total_submissions": 0,
"correct_submissions": 0, "correct_submissions": 0,
"incorrect_submissions": 0, "incorrect_submissions": 0,
"num_modules": 0 "num_modules": 0
} }
incorrect_submissions = val["total_submissions"] - val["correct_submissions"] incorrect_submissions = val["total_submissions"] - val["correct_submissions"]
expected[tag_value]["total_submissions"] += val["total_submissions"] * k expected[tag_value]["total_submissions"] += val["total_submissions"] * k
expected[tag_value]["correct_submissions"] += val["correct_submissions"] * k expected[tag_value]["correct_submissions"] += val["correct_submissions"] * k
expected[tag_value]["incorrect_submissions"] += incorrect_submissions * k expected[tag_value]["incorrect_submissions"] += incorrect_submissions * k
expected[tag_value]["num_modules"] += k expected[tag_value]["num_modules"] += k
url_template = '/courses/{}/performance/learning_outcomes/{}/' url_template = '/courses/{}/performance/learning_outcomes/{}/'
...@@ -458,33 +459,43 @@ class TagsDistributionDataFactory(CourseStructureFactory): ...@@ -458,33 +459,43 @@ class TagsDistributionDataFactory(CourseStructureFactory):
url_template = '/courses/{}/performance/learning_outcomes/{}/problems/{}/' url_template = '/courses/{}/performance/learning_outcomes/{}/problems/{}/'
def _get_tags_info(av_tags, tags):
"""
Helper function to return information about all tags connected with the current item.
"""
data = {}
if av_tags:
for av_tag_key in av_tags:
if av_tag_key in tags and tags[av_tag_key]:
data[av_tag_key] = u', '.join(tags[av_tag_key])
else:
data[av_tag_key] = None
return data
for i in xrange(1, self._count_of_homework_assignments + 1): for i in xrange(1, self._count_of_homework_assignments + 1):
num = 0 num = 0
for val in self.tags_data_per_homework_assigment: for val in self.tags_data_per_homework_assigment:
num += 1 num += 1
if tag_key in val['tags'] and val['tags'][tag_key] == tag_value: if tag_key in val['tags']:
display_name = 'Homework %d Problem %d' % (i, num) for val_tag_value in val['tags'][tag_key]:
incorrect_submissions = val["total_submissions"] - val["correct_submissions"] if val_tag_value == tag_value:
new_item_id = 'i4x://edX/DemoX/problem/%s' % hashlib.md5(display_name).hexdigest() display_name = 'Homework %d Problem %d' % (i, num)
index += 1 incorrect_submissions = val["total_submissions"] - val["correct_submissions"]
new_item = { new_item_id = 'i4x://edX/DemoX/problem/%s' % hashlib.md5(display_name).hexdigest()
'id': new_item_id, index += 1
'index': index, new_item = {
'name': ', '.join(['Demo Course', 'Homework %d' % i, display_name]), 'id': new_item_id,
'total_submissions': val['total_submissions'], 'index': index,
'correct_submissions': val['correct_submissions'], 'name': ', '.join(['Demo Course', 'Homework %d' % i, display_name]),
'incorrect_submissions': incorrect_submissions, 'total_submissions': val['total_submissions'],
'correct_percent': utils.math.calculate_percent(val['correct_submissions'], 'correct_submissions': val['correct_submissions'],
val['total_submissions']), 'incorrect_submissions': incorrect_submissions,
'incorrect_percent': utils.math.calculate_percent(incorrect_submissions, 'correct_percent': utils.math.calculate_percent(val['correct_submissions'],
val['total_submissions']), val['total_submissions']),
'url': url_template.format(self.course_id, slugify(tag_value), new_item_id) 'incorrect_percent': utils.math.calculate_percent(incorrect_submissions,
} val['total_submissions']),
if available_tags: 'url': url_template.format(self.course_id, slugify(tag_value), new_item_id)
for av_tag_key in available_tags: }
if av_tag_key in val['tags']: new_item.update(_get_tags_info(available_tags, val['tags']))
new_item[av_tag_key] = val['tags'][av_tag_key] expected.append(new_item)
else:
new_item[av_tag_key] = None
expected.append(new_item)
return expected return expected
...@@ -545,9 +545,9 @@ class CoursePerformanceLearningOutcomesViewTestMixin(CoursePerformanceViewTestMi ...@@ -545,9 +545,9 @@ class CoursePerformanceLearningOutcomesViewTestMixin(CoursePerformanceViewTestMi
class CoursePerformanceLearningOutcomesContentViewTests(CoursePerformanceLearningOutcomesViewTestMixin, TestCase): class CoursePerformanceLearningOutcomesContentViewTests(CoursePerformanceLearningOutcomesViewTestMixin, TestCase):
viewname = 'courses:performance:learning_outcomes' viewname = 'courses:performance:learning_outcomes'
tags_factory_init_data = [{"total_submissions": 21, "correct_submissions": 5, tags_factory_init_data = [{"total_submissions": 21, "correct_submissions": 5,
"tags": {"difficulty": "Hard", "learning_outcome": "Learned a few things"}}, "tags": {"difficulty": ["Hard"], "learning_outcome": ["Learned a few things"]}},
{"total_submissions": 11, "correct_submissions": 10, {"total_submissions": 11, "correct_submissions": 10,
"tags": {"difficulty": "Easy", "learning_outcome": "Learned nothing"}}] "tags": {"difficulty": ["Easy"], "learning_outcome": ["Learned nothing"]}}]
@httpretty.activate @httpretty.activate
def test_invalid_course(self): def test_invalid_course(self):
...@@ -568,17 +568,17 @@ class CoursePerformanceLearningOutcomesContentViewTests(CoursePerformanceLearnin ...@@ -568,17 +568,17 @@ class CoursePerformanceLearningOutcomesContentViewTests(CoursePerformanceLearnin
class CoursePerformanceLearningOutcomesSectionViewTests(CoursePerformanceLearningOutcomesViewTestMixin, TestCase): class CoursePerformanceLearningOutcomesSectionViewTests(CoursePerformanceLearningOutcomesViewTestMixin, TestCase):
viewname = 'courses:performance:learning_outcomes_section' viewname = 'courses:performance:learning_outcomes_section'
tags_factory_init_data = [{"total_submissions": 41, "correct_submissions": 10, tags_factory_init_data = [{"total_submissions": 41, "correct_submissions": 10,
"tags": {"difficulty": "Hard", "learning_outcome": "Learned a few things"}}, "tags": {"difficulty": ["Hard"], "learning_outcome": ["Learned a few things"]}},
{"total_submissions": 25, "correct_submissions": 25, {"total_submissions": 25, "correct_submissions": 25,
"tags": {"difficulty": "Easy", "learning_outcome": "Learned nothing"}}, "tags": {"difficulty": ["Easy"], "learning_outcome": ["Learned nothing"]}},
{"total_submissions": 17, "correct_submissions": 16, {"total_submissions": 17, "correct_submissions": 16,
"tags": {"learning_outcome": "Learned everything"}}, "tags": {"learning_outcome": ["Learned everything"]}},
{"total_submissions": 10, "correct_submissions": 5, {"total_submissions": 10, "correct_submissions": 5,
"tags": {"difficulty": "Hard"}}, "tags": {"difficulty": ["Hard"]}},
{"total_submissions": 35, "correct_submissions": 31, {"total_submissions": 35, "correct_submissions": 31,
"tags": {"learning_outcome": "Learned nothing"}}, "tags": {"learning_outcome": ["Learned nothing"]}},
{"total_submissions": 105, "correct_submissions": 10, {"total_submissions": 105, "correct_submissions": 10,
"tags": {"difficulty": "Hard", "learning_outcome": "Learned everything"}}] "tags": {"difficulty": ["Hard"], "learning_outcome": ["Learned everything"]}}]
def path(self, **kwargs): def path(self, **kwargs):
kwargs.update({ kwargs.update({
...@@ -612,17 +612,17 @@ class CoursePerformanceLearningOutcomesAnswersDistributionViewTests( ...@@ -612,17 +612,17 @@ class CoursePerformanceLearningOutcomesAnswersDistributionViewTests(
viewname = 'courses:performance:learning_outcomes_answers_distribution' viewname = 'courses:performance:learning_outcomes_answers_distribution'
tags_factory_init_data = [{"total_submissions": 41, "correct_submissions": 10, tags_factory_init_data = [{"total_submissions": 41, "correct_submissions": 10,
"tags": {"difficulty": "Hard", "learning_outcome": "Learned a few things"}}, "tags": {"difficulty": ["Hard"], "learning_outcome": ["Learned a few things"]}},
{"total_submissions": 25, "correct_submissions": 25, {"total_submissions": 25, "correct_submissions": 25,
"tags": {"difficulty": "Easy", "learning_outcome": "Learned nothing"}}, "tags": {"difficulty": ["Easy"], "learning_outcome": ["Learned nothing"]}},
{"total_submissions": 17, "correct_submissions": 16, {"total_submissions": 17, "correct_submissions": 16,
"tags": {"learning_outcome": "Learned everything"}}, "tags": {"learning_outcome": ["Learned everything"]}},
{"total_submissions": 10, "correct_submissions": 5, {"total_submissions": 10, "correct_submissions": 5,
"tags": {"difficulty": "Hard"}}, "tags": {"difficulty": ["Hard"]}},
{"total_submissions": 35, "correct_submissions": 31, {"total_submissions": 35, "correct_submissions": 31,
"tags": {"learning_outcome": "Learned nothing"}}, "tags": {"learning_outcome": ["Learned nothing"]}},
{"total_submissions": 105, "correct_submissions": 10, {"total_submissions": 105, "correct_submissions": 10,
"tags": {"difficulty": "Hard", "learning_outcome": "Learned everything"}}] "tags": {"difficulty": ["Hard"], "learning_outcome": ["Learned everything"]}}]
def path(self, **kwargs): def path(self, **kwargs):
kwargs.update({ kwargs.update({
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment