|
4 | 4 | import random |
5 | 5 | import string |
6 | 6 | import time |
| 7 | +import uuid |
7 | 8 | from builtins import range |
8 | 9 | from builtins import str |
9 | 10 | from builtins import zip |
10 | 11 |
|
11 | 12 | import pytest |
12 | 13 | from django.db import IntegrityError |
13 | 14 | from django.db.utils import DataError |
| 15 | +from le_utils.constants import completion_criteria |
14 | 16 | from le_utils.constants import content_kinds |
| 17 | +from le_utils.constants import exercises |
| 18 | +from le_utils.constants import format_presets |
15 | 19 | from mixer.backend.django import mixer |
16 | 20 | from mock import patch |
17 | 21 | from past.utils import old_div |
18 | 22 |
|
19 | 23 | from . import testdata |
20 | 24 | from .base import StudioTestCase |
21 | 25 | from .testdata import create_studio_file |
| 26 | +from contentcuration.models import AssessmentItem |
22 | 27 | from contentcuration.models import Channel |
23 | 28 | from contentcuration.models import ContentKind |
24 | 29 | from contentcuration.models import ContentNode |
25 | 30 | from contentcuration.models import ContentTag |
| 31 | +from contentcuration.models import File |
26 | 32 | from contentcuration.models import FormatPreset |
27 | 33 | from contentcuration.models import generate_storage_url |
28 | 34 | from contentcuration.models import Language |
| 35 | +from contentcuration.models import License |
29 | 36 | from contentcuration.utils.db_tools import TreeBuilder |
30 | 37 | from contentcuration.utils.files import create_thumbnail_from_base64 |
31 | 38 | from contentcuration.utils.sync import sync_node |
@@ -869,3 +876,214 @@ def test_create_node_null_complete(self): |
869 | 876 | new_obj.save() |
870 | 877 | except IntegrityError: |
871 | 878 | self.fail("Caused an IntegrityError") |
| 879 | + |
| 880 | + |
| 881 | +class NodeCompletionTestCase(StudioTestCase): |
| 882 | + |
| 883 | + old_extra_fields = { |
| 884 | + "mastery_model": exercises.M_OF_N, |
| 885 | + "randomize": False, |
| 886 | + "m": 3, |
| 887 | + "n": 5, |
| 888 | + } |
| 889 | + |
| 890 | + new_extra_fields = { |
| 891 | + "randomize": False, |
| 892 | + "options": { |
| 893 | + "completion_criteria": { |
| 894 | + "threshold": { |
| 895 | + "mastery_model": exercises.M_OF_N, |
| 896 | + "m": 4, |
| 897 | + "n": 5, |
| 898 | + }, |
| 899 | + "model": completion_criteria.MASTERY, |
| 900 | + } |
| 901 | + } |
| 902 | + } |
| 903 | + |
| 904 | + def setUp(self): |
| 905 | + return super(NodeCompletionTestCase, self).setUpBase() |
| 906 | + |
| 907 | + def test_create_topic_set_complete_no_parent(self): |
| 908 | + new_obj = ContentNode(kind_id=content_kinds.TOPIC) |
| 909 | + new_obj.save() |
| 910 | + new_obj.mark_complete() |
| 911 | + self.assertTrue(new_obj.complete) |
| 912 | + |
| 913 | + def test_create_topic_set_complete_parent_no_title(self): |
| 914 | + channel = testdata.channel() |
| 915 | + new_obj = ContentNode(kind_id=content_kinds.TOPIC, parent=channel.main_tree) |
| 916 | + new_obj.save() |
| 917 | + new_obj.mark_complete() |
| 918 | + self.assertFalse(new_obj.complete) |
| 919 | + |
| 920 | + def test_create_topic_set_complete_parent_title(self): |
| 921 | + channel = testdata.channel() |
| 922 | + new_obj = ContentNode(title="yes", kind_id=content_kinds.TOPIC, parent=channel.main_tree) |
| 923 | + new_obj.save() |
| 924 | + new_obj.mark_complete() |
| 925 | + self.assertTrue(new_obj.complete) |
| 926 | + |
| 927 | + def test_create_video_set_complete_no_license(self): |
| 928 | + channel = testdata.channel() |
| 929 | + new_obj = ContentNode(title="yes", kind_id=content_kinds.VIDEO, parent=channel.main_tree) |
| 930 | + new_obj.save() |
| 931 | + File.objects.create(contentnode=new_obj, preset_id=format_presets.VIDEO_HIGH_RES, checksum=uuid.uuid4().hex) |
| 932 | + new_obj.mark_complete() |
| 933 | + self.assertFalse(new_obj.complete) |
| 934 | + |
| 935 | + def test_create_video_set_complete_custom_license_no_description(self): |
| 936 | + custom_licenses = list(License.objects.filter(is_custom=True).values_list("pk", flat=True)) |
| 937 | + channel = testdata.channel() |
| 938 | + new_obj = ContentNode(title="yes", kind_id=content_kinds.VIDEO, parent=channel.main_tree, license_id=custom_licenses[0], copyright_holder="Some person") |
| 939 | + new_obj.save() |
| 940 | + File.objects.create(contentnode=new_obj, preset_id=format_presets.VIDEO_HIGH_RES, checksum=uuid.uuid4().hex) |
| 941 | + new_obj.mark_complete() |
| 942 | + self.assertFalse(new_obj.complete) |
| 943 | + |
| 944 | + def test_create_video_set_complete_custom_license_with_description(self): |
| 945 | + custom_licenses = list(License.objects.filter(is_custom=True).values_list("pk", flat=True)) |
| 946 | + channel = testdata.channel() |
| 947 | + new_obj = ContentNode( |
| 948 | + title="yes", |
| 949 | + kind_id=content_kinds.VIDEO, |
| 950 | + parent=channel.main_tree, |
| 951 | + license_id=custom_licenses[0], |
| 952 | + license_description="don't do this!", |
| 953 | + copyright_holder="Some person" |
| 954 | + ) |
| 955 | + new_obj.save() |
| 956 | + File.objects.create(contentnode=new_obj, preset_id=format_presets.VIDEO_HIGH_RES, checksum=uuid.uuid4().hex) |
| 957 | + new_obj.mark_complete() |
| 958 | + self.assertTrue(new_obj.complete) |
| 959 | + |
| 960 | + def test_create_video_set_complete_copyright_holder_required_no_copyright_holder(self): |
| 961 | + required_holder = list(License.objects.filter(copyright_holder_required=True, is_custom=False).values_list("pk", flat=True)) |
| 962 | + channel = testdata.channel() |
| 963 | + new_obj = ContentNode(title="yes", kind_id=content_kinds.VIDEO, parent=channel.main_tree, license_id=required_holder[0]) |
| 964 | + new_obj.save() |
| 965 | + File.objects.create(contentnode=new_obj, preset_id=format_presets.VIDEO_HIGH_RES, checksum=uuid.uuid4().hex) |
| 966 | + new_obj.mark_complete() |
| 967 | + self.assertFalse(new_obj.complete) |
| 968 | + |
| 969 | + def test_create_video_set_complete_copyright_holder_required_copyright_holder(self): |
| 970 | + required_holder = list(License.objects.filter(copyright_holder_required=True, is_custom=False).values_list("pk", flat=True)) |
| 971 | + channel = testdata.channel() |
| 972 | + new_obj = ContentNode(title="yes", kind_id=content_kinds.VIDEO, parent=channel.main_tree, license_id=required_holder[0], copyright_holder="Some person") |
| 973 | + new_obj.save() |
| 974 | + File.objects.create(contentnode=new_obj, preset_id=format_presets.VIDEO_HIGH_RES, checksum=uuid.uuid4().hex) |
| 975 | + new_obj.mark_complete() |
| 976 | + self.assertTrue(new_obj.complete) |
| 977 | + |
| 978 | + def test_create_video_no_files(self): |
| 979 | + licenses = list(License.objects.filter(copyright_holder_required=False, is_custom=False).values_list("pk", flat=True)) |
| 980 | + channel = testdata.channel() |
| 981 | + new_obj = ContentNode(title="yes", kind_id=content_kinds.VIDEO, parent=channel.main_tree, license_id=licenses[0]) |
| 982 | + new_obj.save() |
| 983 | + new_obj.mark_complete() |
| 984 | + self.assertFalse(new_obj.complete) |
| 985 | + |
| 986 | + def test_create_video_thumbnail_only(self): |
| 987 | + licenses = list(License.objects.filter(copyright_holder_required=False, is_custom=False).values_list("pk", flat=True)) |
| 988 | + channel = testdata.channel() |
| 989 | + new_obj = ContentNode(title="yes", kind_id=content_kinds.VIDEO, parent=channel.main_tree, license_id=licenses[0]) |
| 990 | + new_obj.save() |
| 991 | + File.objects.create(contentnode=new_obj, preset_id=format_presets.VIDEO_THUMBNAIL, checksum=uuid.uuid4().hex) |
| 992 | + new_obj.mark_complete() |
| 993 | + self.assertFalse(new_obj.complete) |
| 994 | + |
| 995 | + def test_create_exercise_no_assessment_items(self): |
| 996 | + licenses = list(License.objects.filter(copyright_holder_required=False, is_custom=False).values_list("pk", flat=True)) |
| 997 | + channel = testdata.channel() |
| 998 | + new_obj = ContentNode(title="yes", kind_id=content_kinds.EXERCISE, parent=channel.main_tree, license_id=licenses[0], extra_fields=self.new_extra_fields) |
| 999 | + new_obj.save() |
| 1000 | + new_obj.mark_complete() |
| 1001 | + self.assertFalse(new_obj.complete) |
| 1002 | + |
| 1003 | + def test_create_exercise_invalid_assessment_item_no_question(self): |
| 1004 | + licenses = list(License.objects.filter(copyright_holder_required=False, is_custom=False).values_list("pk", flat=True)) |
| 1005 | + channel = testdata.channel() |
| 1006 | + new_obj = ContentNode(title="yes", kind_id=content_kinds.EXERCISE, parent=channel.main_tree, license_id=licenses[0], extra_fields=self.new_extra_fields) |
| 1007 | + new_obj.save() |
| 1008 | + AssessmentItem.objects.create(contentnode=new_obj, answers="[{\"correct\": true, \"text\": \"answer\"}]") |
| 1009 | + new_obj.mark_complete() |
| 1010 | + self.assertFalse(new_obj.complete) |
| 1011 | + |
| 1012 | + def test_create_exercise_invalid_assessment_item_no_answers(self): |
| 1013 | + licenses = list(License.objects.filter(copyright_holder_required=False, is_custom=False).values_list("pk", flat=True)) |
| 1014 | + channel = testdata.channel() |
| 1015 | + new_obj = ContentNode(title="yes", kind_id=content_kinds.EXERCISE, parent=channel.main_tree, license_id=licenses[0], extra_fields=self.new_extra_fields) |
| 1016 | + new_obj.save() |
| 1017 | + AssessmentItem.objects.create(contentnode=new_obj, question="This is a question") |
| 1018 | + new_obj.mark_complete() |
| 1019 | + self.assertFalse(new_obj.complete) |
| 1020 | + |
| 1021 | + def test_create_exercise_invalid_assessment_item_no_correct_answers(self): |
| 1022 | + licenses = list(License.objects.filter(copyright_holder_required=False, is_custom=False).values_list("pk", flat=True)) |
| 1023 | + channel = testdata.channel() |
| 1024 | + new_obj = ContentNode(title="yes", kind_id=content_kinds.EXERCISE, parent=channel.main_tree, license_id=licenses[0], extra_fields=self.new_extra_fields) |
| 1025 | + new_obj.save() |
| 1026 | + AssessmentItem.objects.create(contentnode=new_obj, question="This is a question", answers="[{\"correct\": false, \"text\": \"answer\"}]") |
| 1027 | + new_obj.mark_complete() |
| 1028 | + self.assertFalse(new_obj.complete) |
| 1029 | + |
| 1030 | + def test_create_exercise_valid_assessment_item_no_correct_answers_input(self): |
| 1031 | + licenses = list(License.objects.filter(copyright_holder_required=False, is_custom=False).values_list("pk", flat=True)) |
| 1032 | + channel = testdata.channel() |
| 1033 | + new_obj = ContentNode(title="yes", kind_id=content_kinds.EXERCISE, parent=channel.main_tree, license_id=licenses[0], extra_fields=self.new_extra_fields) |
| 1034 | + new_obj.save() |
| 1035 | + AssessmentItem.objects.create( |
| 1036 | + contentnode=new_obj, |
| 1037 | + question="This is a question", |
| 1038 | + answers="[{\"correct\": false, \"text\": \"answer\"}]", |
| 1039 | + type=exercises.INPUT_QUESTION |
| 1040 | + ) |
| 1041 | + new_obj.mark_complete() |
| 1042 | + self.assertTrue(new_obj.complete) |
| 1043 | + |
| 1044 | + def test_create_exercise_valid_assessment_items(self): |
| 1045 | + licenses = list(License.objects.filter(copyright_holder_required=False, is_custom=False).values_list("pk", flat=True)) |
| 1046 | + channel = testdata.channel() |
| 1047 | + new_obj = ContentNode(title="yes", kind_id=content_kinds.EXERCISE, parent=channel.main_tree, license_id=licenses[0], extra_fields=self.new_extra_fields) |
| 1048 | + new_obj.save() |
| 1049 | + AssessmentItem.objects.create(contentnode=new_obj, question="This is a question", answers="[{\"correct\": true, \"text\": \"answer\"}]") |
| 1050 | + new_obj.mark_complete() |
| 1051 | + self.assertTrue(new_obj.complete) |
| 1052 | + |
| 1053 | + def test_create_exercise_no_extra_fields(self): |
| 1054 | + licenses = list(License.objects.filter(copyright_holder_required=False, is_custom=False).values_list("pk", flat=True)) |
| 1055 | + channel = testdata.channel() |
| 1056 | + new_obj = ContentNode(title="yes", kind_id=content_kinds.EXERCISE, parent=channel.main_tree, license_id=licenses[0]) |
| 1057 | + new_obj.save() |
| 1058 | + AssessmentItem.objects.create(contentnode=new_obj, question="This is a question", answers="[{\"correct\": true, \"text\": \"answer\"}]") |
| 1059 | + new_obj.mark_complete() |
| 1060 | + self.assertFalse(new_obj.complete) |
| 1061 | + |
| 1062 | + def test_create_exercise_old_extra_fields(self): |
| 1063 | + licenses = list(License.objects.filter(copyright_holder_required=False, is_custom=False).values_list("pk", flat=True)) |
| 1064 | + channel = testdata.channel() |
| 1065 | + new_obj = ContentNode(title="yes", kind_id=content_kinds.EXERCISE, parent=channel.main_tree, license_id=licenses[0], extra_fields=self.old_extra_fields) |
| 1066 | + new_obj.save() |
| 1067 | + AssessmentItem.objects.create(contentnode=new_obj, question="This is a question", answers="[{\"correct\": true, \"text\": \"answer\"}]") |
| 1068 | + new_obj.mark_complete() |
| 1069 | + self.assertTrue(new_obj.complete) |
| 1070 | + |
| 1071 | + def test_create_exercise_bad_new_extra_fields(self): |
| 1072 | + licenses = list(License.objects.filter(copyright_holder_required=False, is_custom=False).values_list("pk", flat=True)) |
| 1073 | + channel = testdata.channel() |
| 1074 | + new_obj = ContentNode(title="yes", kind_id=content_kinds.EXERCISE, parent=channel.main_tree, license_id=licenses[0], extra_fields={ |
| 1075 | + "randomize": False, |
| 1076 | + "options": { |
| 1077 | + "completion_criteria": { |
| 1078 | + "threshold": { |
| 1079 | + "mastery_model": exercises.M_OF_N, |
| 1080 | + "n": 5, |
| 1081 | + }, |
| 1082 | + "model": completion_criteria.MASTERY, |
| 1083 | + } |
| 1084 | + } |
| 1085 | + }) |
| 1086 | + new_obj.save() |
| 1087 | + AssessmentItem.objects.create(contentnode=new_obj, question="This is a question", answers="[{\"correct\": true, \"text\": \"answer\"}]") |
| 1088 | + new_obj.mark_complete() |
| 1089 | + self.assertFalse(new_obj.complete) |
0 commit comments