def get_message(self, chat, next_lesson, is_additional, *args, **kwargs) -> Message: _response_data = { 'lesson': chat.state.unitLesson.lesson, 'unitLesson': chat.state.unitLesson, 'course': chat.enroll_code.courseUnit.course, 'author': chat.user, 'activity': chat.state.activity, 'is_test': chat.is_test, 'is_preview': chat.enroll_code.isPreview, 'is_trial': chat.is_trial, } resp = Response(**_response_data) resp.save() _data = { 'contenttype': 'response', 'content_id': resp.id, 'input_type': 'options', # This is needed to track the last response to handle status 'lesson_to_answer_id': chat.state.unitLesson.id, 'chat': chat, 'owner': chat.user, 'kind': 'response', 'userMessage': True, 'is_additional': is_additional } message = Message(**_data) message.save() return message
def perform_update(self, serializer): chat_id = self.request.data.get('chat_id') message = self.get_object() chat = Chat.objects.get(id=chat_id, user=self.request.user) activity = chat.state and chat.state.activity is_in_node = lambda node: message.chat.state.fsmNode.name == node # Check if message is not in current chat if not message.chat or message.chat != chat: return # Chat add unit lesson if is_chat_add_lesson(message): message.chat = chat text = self.request.data.get('text') option = self.request.data.get('option') course_unit = message.chat.enroll_code.courseUnit unit = course_unit.unit if message.input_type == 'options' and is_in_node('HAS_UNIT_ANSWER'): message = self.next_handler.next_point( current=message.content, chat=chat, message=message, request=self.request ) chat.next_point = message chat.save() serializer.save(chat=chat, timestamp=timezone.now()) if is_in_node('GET_UNIT_NAME_TITLE'): if course_unit and unit: if not message.content_id: lesson = Lesson.objects.create(title=text, addedBy=self.request.user, kind=Lesson.ORCT_QUESTION, text='') lesson.treeID = lesson.id lesson.save() ul = UnitLesson.create_from_lesson( lesson=lesson, unit=unit, kind=UnitLesson.COMPONENT, order='APPEND', ) chat.state.unitLesson = ul chat.state.save() else: ul = message.content if not message.timestamp: serializer.save( content_id=ul.id, timestamp=timezone.now(), chat=chat, text=text, contenttype='unitlesson' ) else: serializer.save() if is_in_node('GET_UNIT_QUESTION'): ul = message.content ul.lesson.text = text ul.lesson.save() if not message.timestamp: serializer.save( content_id=ul.id, timestamp=timezone.now(), chat=chat, contenttype='unitlesson', text=text ) else: serializer.save() if is_in_node('GET_UNIT_ANSWER'): # create answer ul = message.content if not message.timestamp: answer = Lesson.objects.create( title='Answer', text=text, addedBy=self.request.user, kind=Lesson.ANSWER, ) answer.save_root() unit_lesson_answer = UnitLesson.create_from_lesson( unit=ul.unit, lesson=answer, parent=ul, kind=UnitLesson.ANSWERS ) # chat.next_point = message chat.save() serializer.save(content_id=ul.id, timestamp=timezone.now(), chat=chat, contenttype='unitlesson', text=text) else: serializer.save() if is_in_node('GET_HAS_UNIT_ANSWER'): yes_no_map = { 'yes': True, 'no': False } ul = message.content has_answer = yes_no_map.get(self.request.data.get('option')) if has_answer is None: raise ValueError("Recieved not valid response from user") ul.lesson.kind = Lesson.ORCT_QUESTION if has_answer else Lesson.BASE_EXPLANATION ul.lesson.save() message.text = self.request.data.get('option') message.save() if message.input_type == 'text' and not is_chat_add_lesson(message): message.chat = chat text = self.request.data.get('text') if message.lesson_to_answer.sub_kind == Lesson.EQUATION: text = text.strip("$") text = '.. math:: ' + text resp = StudentResponse(text=text) # convert base64 attachment string to django File data_attachment = self.request.data.get('attachment') if data_attachment and data_attachment.startswith('data:image'): format, image_string = data_attachment.split(';base64,') extension = format.split('/')[-1].split('+')[0] name = '{}.{}'.format('canvas', extension) resp.attachment = ContentFile(base64.b64decode(image_string), name=name) if not message.content_id: resp.lesson = message.lesson_to_answer.lesson resp.unitLesson = message.lesson_to_answer resp.course = message.chat.enroll_code.courseUnit.course resp.author = self.request.user resp.activity = activity resp.is_test = chat.is_test resp.is_preview = chat.enroll_code.isPreview resp.sub_kind = resp.lesson.sub_kind else: resp = message.content resp.text = text resp.is_trial = chat.is_trial resp.save() if not message.timestamp: message.content_id = resp.id chat.next_point = message chat.last_modify_timestamp = timezone.now() chat.save() serializer.save(content_id=resp.id, timestamp=timezone.now(), chat=chat) else: serializer.save() message_is_response = message.contenttype == 'response' lesson_has_sub_kind = message.lesson_to_answer and message.lesson_to_answer.sub_kind content_is_not_additional = not message.content and not message.is_additional mc_selfeval = None if message_is_response and lesson_has_sub_kind and content_is_not_additional: resp_text = '' if message.lesson_to_answer.sub_kind == Lesson.MULTIPLE_CHOICES: selected_items = self.request.data.get('selected') try: selected = selected_items[str(message.id)]['choices'] except KeyError: # here request.data is like {"option":1,"chat_id":9,"selected":{"116":{"choices":[0]}}} selected_msg_ids = self.request.data.get( 'selected' ).keys() # selected_messages == tuple with keys of this dict {"116":{"choices":[0]}} - it will be ("116",) msg_ids = Message.objects.filter(id__in=selected_msg_ids, chat=chat).values_list('id', flat=True) correct_ids = set(msg_ids).intersection( set(int(i) for i in selected_items.keys()) ) selected_choices = [] for i in correct_ids: selected_choices.append(selected_items[str(i)]['choices']) selected = chain(*selected_choices) resp_text = '[selected_choices] ' + ' '.join(str(i) for i in selected) correct_choices = set([_[0] for _ in message.lesson_to_answer.lesson.get_correct_choices()]) selected_choices = set([_ for _ in chain(*selected_choices)]) if not (correct_choices - selected_choices or correct_choices ^ selected_choices): mc_selfeval = StudentResponse.CORRECT elif selected_choices & correct_choices: mc_selfeval = StudentResponse.CLOSE else: mc_selfeval = StudentResponse.DIFFERENT resp = StudentResponse(text=resp_text) # tes, preview flags resp.is_test = chat.is_test resp.selfeval = mc_selfeval or None resp.is_preview = chat.enroll_code.isPreview resp.is_trial = chat.is_trial resp.kind = message.lesson_to_answer.kind resp.sub_kind = message.lesson_to_answer.sub_kind resp.lesson = message.lesson_to_answer.lesson resp.unitLesson = message.lesson_to_answer resp.course = message.chat.enroll_code.courseUnit.course resp.author = self.request.user resp.activity = activity resp.save() if not message.timestamp: serializer.save(content_id=resp.id, timestamp=timezone.now(), chat=chat, response_to_check=resp) else: serializer.save() return if message.input_type == 'options' and message.kind != 'button': if ( message.contenttype == 'uniterror' and 'selected' in self.request.data ): # user selected error model message.chat = chat try: selected = self.request.data.get( 'selected' )[str(message.id)]['errorModel'] except KeyError: selected = [] uniterror = message.content uniterror.save_response(user=self.request.user, response_list=selected) if not message.chat.is_live: get_additional_messages(uniterror.response, chat) chat.next_point = self.next_handler.next_point( current=message.content, chat=chat, message=message, request=self.request ) chat.last_modify_timestamp = timezone.now() chat.save() serializer.save(chat=chat) elif message.contenttype == 'NoneType' and message.kind == 'abort': # user selected abort model message.chat = chat try: selected = self.request.data.get( 'selected' )[str(message.id)]['errorModel'] except KeyError: selected = [] if not message.chat.is_live and selected: get_help_messages(chat) chat.next_point = self.next_handler.next_point( current=message.content, chat=chat, message=message, request=self.request ) chat.last_modify_timestamp = timezone.now() chat.save() serializer.save(chat=chat) elif message.content_id and not message.student_error: # confidence and selfeval message.chat = chat opt_data = self.request.data.get('option') resp = message.content if chat.state and chat.state.fsmNode.node_name_is_one_of('GET_CONFIDENCE'): resp.confidence = opt_data text = resp.get_confidence_display() else: resp.selfeval = opt_data text = resp.get_selfeval_display() # FIX if response was correct - user will not go to `else` section and response status should be set if resp.selfeval == StudentResponse.CORRECT: resp.status = DONE_STATUS message.text = text resp.save() chat.next_point = message chat.last_modify_timestamp = timezone.now() chat.save() serializer.save(content_id=resp.id, chat=chat, text=text) else: # message.chat = chat selfeval = self.request.data.get('option') resp = message.student_error resp.status = selfeval resp.save() # pass status to main response resp.response.status = selfeval resp.response.save() chat.next_point = message chat.last_modify_timestamp = timezone.now() chat.save() message.text = selfeval message.save() serializer.save(text=selfeval, chat=chat) if message.kind == 'button' and not (message.content_id and message.content and message.content.sub_kind): chat.last_modify_timestamp = timezone.now() chat.next_point = self.next_handler.next_point( current=message.content, chat=chat, message=message, request=self.request, ) chat.save()
class TagsTest(TestCase): def setUp(self): self.user = User.objects.create_user(username='******', password='******') self.course = Course(title='test_title', addedBy=self.user) self.course.save() self.concept = Concept(title='test title', addedBy=self.user) self.concept.save() self.lesson = Lesson(title='ugh', text='brr', addedBy=self.user, kind=Lesson.ORCT_QUESTION) self.lesson.save_root() self.lesson.add_concept_link(self.concept, ConceptLink.TESTS, self.user) self.unit = Unit(title='test unit title', addedBy=self.user) self.unit.save() self.unit_lesson = UnitLesson(unit=self.unit, addedBy=self.user, treeID=42, lesson=self.lesson) self.unit_lesson.save() self.response = Response(course=self.course, lesson=self.lesson, author=self.user, unitLesson=self.unit_lesson, confidence=Response.GUESS, title='test title', text='test text') self.response.save() self.context = { 'actionTarget': '/ct/courses/1/units/1/', 'ul': self.unit_lesson, 'test_text': 'This is a test text', 'r': self.response } def render_template(self, string, context=None): context = context or {} context = Context(context) return Template(string).render(context) @unpack @data( ('{{ test_text | md2html }}', '<p>This is a test text</p>\n'), ('{{ actionTarget | get_object_url:ul }}', '/ct/courses/1/units/1/lessons/1/'), ('{{ actionTarget | get_home_url:ul }}', '/ct/courses/1/units/1/lessons/1/'), ('{{ actionTarget | get_thread_url:r }}', '/ct/courses/1/units/1/lessons/1/faq/1/'), ('{{ actionTarget | get_tasks_url:ul }}', '/ct/courses/1/units/1/lessons/1/tasks/'), ('{{ actionTarget | get_dummy_navbar }}', '<li><a href="/ct/courses/1/">Course</a></li>'), ) def test_all_filters(self, template_variable, expected_result): rendered = self.render_template('{% load ct_extras %}' + template_variable, context=self.context) self.assertEqual(rendered, expected_result) @patch('ct.templatetags.ct_extras.pypandoc') def test_md2html_pandoc_exception(self, pypandoc): pypandoc.convert.side_effect = StandardError rendered = self.render_template( '{% load ct_extras %}' '{{ test_text | md2html }}', context=self.context) self.assertEqual(rendered, self.context['test_text']) @data('get_base_url', 'get_path_type') def test_get_base_url_exception(self, helper): with self.assertRaises(ValueError): getattr(inspect.getmodule(self), helper).__call__('/ct/courses/1/units/1/lessons/1/', baseToken='non_existent_token') def test_get_object_url_exception(self): self.context[ 'ul'] = self.unit # Unit object does not have get_url method rendered = self.render_template( '{% load ct_extras %}' '{{ actionTarget | get_object_url:ul }}', context=self.context) self.assertEqual(rendered, '/ct/courses/1/units/1/unit/1/teach/') @patch('ct.templatetags.ct_extras.timezone') def test_display_datetime(self, timezone_patched): saved_time = timezone.now() timezone_patched.now.return_value = saved_time context = {'dt': saved_time - timedelta(1)} rendered = self.render_template( '{% load ct_extras %}' '{{ dt|display_datetime }}', context=context) self.assertEqual(rendered, '1 day ago') def test_find_audio(self): result = find_audio('test tag head .. audio:: test tag tail \n', 4) self.assertEqual(result, (14, 39, 'test tag tail')) def test_audio_html(self): """ Function should return string for embeding audio into html. """ result = audio_html('audio.mp3') self.assertEqual( result, '''<audio controls><source src="audio.ogg" type="audio/ogg"><source src="audio.mp3" ''' '''type="audio/mpeg">no support for audio!</audio>''') def test_video_html(self): """ Function should return string for embeding youtube or vimeo link. """ result = video_html('youtube:test_video_path') self.assertIn('test_video_path', result) self.assertIn('src="https://www.youtube.com/embed/', result) result = video_html('vimeo:test_video_path') self.assertIn('test_video_path', result) self.assertIn('src="https://player.vimeo.com/video/', result) def test_video_html_with_exception(self): """ Test exception handling. """ result = video_html('youtube') self.assertEqual(result, 'ERROR: bad video source: youtube') result = video_html('some_new_cdn:test_video_path') self.assertEqual(result, 'ERROR: unknown video sourceDB: some_new_cdn') def test_add_replace_temporary_markers(self): """ Test add_temporary_markers and replace_temporary_markers in tandem. """ result = add_temporary_markers( 'test tag head .. audio:: test tag tail \n', find_audio) self.assertEqual( result, ('test tag head mArKeR:0:\n', [('mArKeR:0:', 'test tag tail')])) result = replace_temporary_markers(result[0], audio_html, result[1]) self.assertEqual( result, '''test tag head <audio controls><source src="test tag tail.ogg" type="audio/ogg">''' '''<source src="test tag tail.mp3" type="audio/mpeg">no support for audio!</audio>\n''' )
class MyTestCase(TestCase): models_to_check = tuple() context_should_contain_keys = tuple() def setUp(self): self.username, self.password = '******', 'test' self.user = User.objects.create_user(self.username, '*****@*****.**', self.password) self.instructor = Instructor.objects.create(user=self.user, institution='institute', what_do_you_teach='something') self.username2, self.password2 = 'test1', 'test' self.user2 = User.objects.create_user(self.username2, '*****@*****.**', self.password2) self.instructor2 = Instructor.objects.create(user=self.user2, institution='institute', what_do_you_teach='something') self.unit = Unit(title='Test title', addedBy=self.user) self.unit.save() self.course = Course(title='Test title', description='test description', access='Public', enrollCode='111', lockout='222', addedBy=self.user) self.course.save() self.courseunit = CourseUnit( unit=self.unit, course=self.course, order=0, addedBy=self.user, releaseTime=timezone.now() ) self.courseunit.save() self.lesson = Lesson(title='title', text='text', addedBy=self.user) self.lesson.save() self.unitlesson = UnitLesson( unit=self.unit, order=0, lesson=self.lesson, addedBy=self.user, treeID=self.lesson.id ) self.unitlesson.save() self.resp1 = Response( unitLesson=self.unitlesson, kind=Response.ORCT_RESPONSE, lesson=self.lesson, course=self.course, text="Some text user may respond", author=self.user, status=NEED_HELP_STATUS, selfeval=Response.DIFFERENT ) self.resp1.save() self.resp2 = Response( unitLesson=self.unitlesson, kind=Response.ORCT_RESPONSE, lesson=self.lesson, course=self.course, text="Some text user may be responded 2", author=self.user, status=NEED_HELP_STATUS, selfeval=Response.DIFFERENT ) self.resp2.save() self.default_data = {} self.client.login(username=self.username, password=self.password) self.url = reverse('ctms:course_settings', kwargs={'pk': self.course.id}) def get_page(self): return self.client.get(self.url) def post_data(self, data={'name': 'some test name'}): response = self.client.post(self.url, data, follow=True) return response def get_client_method(self, method='post'): client_method = getattr(self.client, method) if not client_method: raise KeyError('self.client has no property {}'.format(method)) return client_method def post_valid_data(self, data={'name': 'some test name'}, method='post'): client_method = self.get_client_method(method) copied_data = {} if getattr(self, 'default_data', False): copied_data.update(self.default_data) copied_data.update(data) response = client_method(self.url, copied_data, follow=True) return response def post_invalid_data(self, data={'name': ''}, method='post'): client_method = self.get_client_method(method) copied_data = {} if getattr(self, 'default_data', False): copied_data.update(self.default_data) copied_data.update(data) response = client_method(self.url, copied_data, follow=True) return response def get_my_courses(self): return Course.objects.filter( models.Q(addedBy=self.user) | models.Q(role__role=Role.INSTRUCTOR, role__user=self.user) ) def get_test_course(self): return Course.objects.get(id=self.course.id) def get_test_unitlessons(self): return self.courseunit.unit.unitlesson_set.filter( kind=UnitLesson.COMPONENT, order__isnull=False ).order_by('order').annotate( responses_count=models.Count('response') ) def get_test_unitlesson(self): return self.courseunit.unit.unitlesson_set.filter( kind=UnitLesson.COMPONENT, order__isnull=False ).order_by('order').annotate( responses_count=models.Count('response') )[0] get_test_courslet = get_test_unitlesson get_test_response = lambda self: self.get_test_responses()[0] def get_test_courseunit(self): return CourseUnit.objects.get(id=self.courseunit.id) def get_test_responses(self): return Response.objects.filter( unitLesson=self.unitlesson, kind=Response.ORCT_RESPONSE, lesson=self.lesson, course=self.course, ) def get_model_counts(self, **kwargs): if isinstance(self.models_to_check, (list, tuple)): return {model: model.objects.filter().count() for model in self.models_to_check} return {self.models_to_check: self.models_to_check.objects.filter().count()} def validate_model_counts(self, first_counts, second_counts, must_equal=False): if isinstance(self.models_to_check, (list, tuple)): all_models = self.models_to_check else: all_models = [self.models_to_check] for model in all_models: if must_equal: self.assertEqual( first_counts[model], second_counts[model], "{} ({}) != {} ({}), with must_equal={}".format( model, first_counts[model], model, second_counts[model], must_equal ) ) else: self.assertNotEqual( first_counts[model], second_counts[model], "{} ({}) == {} ({}), with must_equal={}".format( model, first_counts[model], model, second_counts[model], must_equal ) ) def check_context_keys(self, response): for key in self.context_should_contain_keys: self.assertIn(key, response.context) def am_i_instructor(self, method='GET'): methods_map = {'GET', self.client.get, 'POST', self.client.post} client_method = methods_map.get(method) self.assertIsNotNone(client_method) if getattr(self, 'url'): if getattr(self, 'NEED_INSTRUCTOR'): response = client_method(self.url) if getattr(self, 'instructor'): self.assertEqual(response.status_code, 200) self.instructor.delete() response = client_method(self.url) self.assertEqual(response.status_code, 403) else: self.assertEqual(response.status_code, 403) else: response = client_method(self.url) self.assertEqual(response.status_code, 200)
class TagsTest(TestCase): def setUp(self): self.user = User.objects.create_user(username="******", password="******") self.course = Course(title="test_title", addedBy=self.user) self.course.save() self.concept = Concept(title="test title", addedBy=self.user) self.concept.save() self.lesson = Lesson(title="ugh", text="brr", addedBy=self.user, kind=Lesson.ORCT_QUESTION) self.lesson.save_root() self.lesson.add_concept_link(self.concept, ConceptLink.TESTS, self.user) self.unit = Unit(title="test unit title", addedBy=self.user) self.unit.save() self.unit_lesson = UnitLesson(unit=self.unit, addedBy=self.user, treeID=42, lesson=self.lesson) self.unit_lesson.save() self.response = Response( course=self.course, lesson=self.lesson, author=self.user, unitLesson=self.unit_lesson, confidence=Response.GUESS, title="test title", text="test text", ) self.response.save() self.context = { "actionTarget": "/ct/courses/1/units/1/", "ul": self.unit_lesson, "test_text": "This is a test text", "r": self.response, } def render_template(self, string, context=None): context = context or {} context = Context(context) return Template(string).render(context) @unpack @data( ("{{ test_text | md2html }}", "<p>This is a test text</p>\n"), ("{{ actionTarget | get_object_url:ul }}", "/ct/courses/1/units/1/lessons/1/"), ("{{ actionTarget | get_home_url:ul }}", "/ct/courses/1/units/1/lessons/1/"), ("{{ actionTarget | get_thread_url:r }}", "/ct/courses/1/units/1/lessons/1/faq/1/"), ("{{ actionTarget | get_tasks_url:ul }}", "/ct/courses/1/units/1/lessons/1/tasks/"), ("{{ actionTarget | get_dummy_navbar }}", '<li><a href="/ct/courses/1/">Course</a></li>'), ) def test_all_filters(self, template_variable, expected_result): rendered = self.render_template("{% load ct_extras %}" + template_variable, context=self.context) self.assertEqual(rendered, expected_result) @patch("ct.templatetags.ct_extras.pypandoc") def test_md2html_pandoc_exception(self, pypandoc): pypandoc.convert.side_effect = StandardError rendered = self.render_template("{% load ct_extras %}" "{{ test_text | md2html }}", context=self.context) self.assertEqual(rendered, self.context["test_text"]) @data("get_base_url", "get_path_type") def test_get_base_url_exception(self, helper): with self.assertRaises(ValueError): getattr(inspect.getmodule(self), helper).__call__( "/ct/courses/1/units/1/lessons/1/", baseToken="non_existent_token" ) def test_get_object_url_exception(self): self.context["ul"] = self.unit # Unit object does not have get_url method rendered = self.render_template( "{% load ct_extras %}" "{{ actionTarget | get_object_url:ul }}", context=self.context ) self.assertEqual(rendered, "/ct/courses/1/units/1/unit/1/teach/") @patch("ct.templatetags.ct_extras.timezone") def test_display_datetime(self, timezone_patched): saved_time = timezone.now() timezone_patched.now.return_value = saved_time context = {"dt": saved_time - timedelta(1)} rendered = self.render_template("{% load ct_extras %}" "{{ dt|display_datetime }}", context=context) self.assertEqual(rendered, "1 day ago") def test_find_audio(self): result = find_audio("test tag head .. audio:: test tag tail \n", 4) self.assertEqual(result, (14, 39, "test tag tail")) def test_audio_html(self): """ Function should return string for embeding audio into html. """ result = audio_html("audio.mp3") self.assertEqual( result, """<audio controls><source src="audio.ogg" type="audio/ogg"><source src="audio.mp3" """ """type="audio/mpeg">no support for audio!</audio>""", ) def test_video_html(self): """ Function should return string for embeding youtube or vimeo link. """ result = video_html("youtube:test_video_path") self.assertIn("test_video_path", result) self.assertIn('src="https://www.youtube.com/embed/', result) result = video_html("vimeo:test_video_path") self.assertIn("test_video_path", result) self.assertIn('src="https://player.vimeo.com/video/', result) def test_video_html_with_exception(self): """ Test exception handling. """ result = video_html("youtube") self.assertEqual(result, "ERROR: bad video source: youtube") result = video_html("some_new_cdn:test_video_path") self.assertEqual(result, "ERROR: unknown video sourceDB: some_new_cdn") def test_add_replace_temporary_markers(self): """ Test add_temporary_markers and replace_temporary_markers in tandem. """ result = add_temporary_markers("test tag head .. audio:: test tag tail \n", find_audio) self.assertEqual(result, ("test tag head mArKeR:0:\n", [("mArKeR:0:", "test tag tail")])) result = replace_temporary_markers(result[0], audio_html, result[1]) self.assertEqual( result, """test tag head <audio controls><source src="test tag tail.ogg" type="audio/ogg">""" """<source src="test tag tail.mp3" type="audio/mpeg">no support for audio!</audio>\n""", )
def perform_update(self, serializer): chat_id = self.request.data.get('chat_id') message = self.get_object() node = message.chat.state.fsmNode chat = Chat.objects.get(id=chat_id, user=self.request.user) activity = chat.state and chat.state.activity def is_in_node(node: str) -> bool: return message.chat.state.fsmNode.name == node # Check if message is not in current chat if not message.chat or message.chat != chat: return # Do procced the user response in the FSMNode if hasattr(node._plugin, 'handler'): node._plugin.handler(message, chat, self.request, self.next_handler) return if is_chat_add_faq(message) and is_in_node('GET_NEW_FAQ'): message.text = self.request.data.get('option', 'no') message.save() chat.last_modify_timestamp = timezone.now() chat.save() if message.input_type == 'text' and not message.sub_kind == 'add_faq': message.chat = chat text = self.request.data.get('text') if message.lesson_to_answer.sub_kind == Lesson.EQUATION: text = text.strip("$") text = '.. math:: ' + text resp = StudentResponse(text=text) # convert base64 attachment string to django File data_attachment = self.request.data.get('attachment') if data_attachment and data_attachment.startswith('data:image'): format, image_string = data_attachment.split(';base64,') extension = format.split('/')[-1].split('+')[0] name = '{}-{}.{}'.format('canvas', key_secret_generator(), extension) resp.attachment = ContentFile(base64.b64decode(image_string), name=name) if not message.content_id: resp.lesson = message.lesson_to_answer.lesson resp.unitLesson = message.lesson_to_answer resp.course = message.chat.enroll_code.courseUnit.course resp.author = self.request.user resp.activity = activity resp.is_test = chat.is_test resp.is_preview = chat.enroll_code.isPreview resp.sub_kind = resp.lesson.sub_kind else: resp = message.content resp.text = text resp.is_trial = chat.is_trial resp.save() if not message.timestamp: message.content_id = resp.id chat.next_point = message chat.last_modify_timestamp = timezone.now() chat.save() serializer.save(content_id=resp.id, timestamp=timezone.now(), chat=chat) else: serializer.save() message_is_response = message.contenttype == 'response' lesson_has_sub_kind = message.lesson_to_answer and message.lesson_to_answer.sub_kind content_is_not_additional = not message.content and not message.is_additional mc_selfeval = None if (message_is_response and lesson_has_sub_kind and content_is_not_additional) or ( message_is_response and message.lesson_to_answer and message.lesson_to_answer.sub_kind == 'choices' and not content_is_not_additional): resp_text = '' if message.lesson_to_answer.sub_kind == Lesson.MULTIPLE_CHOICES: selected_items = self.request.data.get('selected') try: selected = selected_items[str(message.id)]['choices'] except KeyError: # here request.data is like {"option":1,"chat_id":9,"selected":{"116":{"choices":[0]}}} selected_msg_ids = list( self.request.data.get('selected').keys()) # selected_messages == tuple with keys of this dict {"116":{"choices":[0]}} - it will be ("116",) msg_ids = Message.objects.filter(id__in=selected_msg_ids, chat=chat).values_list( 'id', flat=True) correct_ids = set(msg_ids).intersection( set(int(i) for i in list(selected_items.keys()))) selected_choices = [] for i in correct_ids: selected_choices.append( selected_items[str(i)]['choices']) selected = chain(*selected_choices) resp_text = '[selected_choices] ' + ' '.join( str(i) for i in selected) correct_choices = set([ _[0] for _ in message.lesson_to_answer.lesson.get_correct_choices() ]) selected_choices = set([_ for _ in chain(*selected_choices)]) if not (correct_choices - selected_choices or correct_choices ^ selected_choices): mc_selfeval = StudentResponse.CORRECT elif selected_choices & correct_choices: mc_selfeval = StudentResponse.CLOSE else: mc_selfeval = StudentResponse.DIFFERENT resp = StudentResponse(text=resp_text) # tes, preview flags resp.is_test = chat.is_test resp.selfeval = mc_selfeval or None resp.is_preview = chat.enroll_code.isPreview resp.is_trial = chat.is_trial resp.kind = message.lesson_to_answer.kind resp.sub_kind = message.lesson_to_answer.sub_kind resp.lesson = message.lesson_to_answer.lesson resp.unitLesson = message.lesson_to_answer resp.course = message.chat.enroll_code.courseUnit.course resp.author = self.request.user resp.activity = activity resp.save() if not message.timestamp: serializer.save(content_id=resp.id, timestamp=timezone.now(), chat=chat, response_to_check=resp) else: serializer.save() return if (is_in_node('GET_NEW_FAQ_TITLE') or is_in_node('GET_NEW_FAQ_DESCRIPTION')) \ and message_is_response and message.sub_kind == 'add_faq': text = self.request.data.get('text') faq_request = message.content if is_in_node('GET_NEW_FAQ_TITLE'): faq_request.title = text else: faq_request.text = text faq_request.save() faq_request.notify_instructors() message.text = text message.save() if is_in_node('GET_FOR_FAQ_ANSWER'): message.text = self.request.data.get('option', 'help') message.save() if is_in_node('GET_UNDERSTANDING'): message.text = self.request.data.get('option', 'no') message.save() if message.input_type == 'options' and message.kind != 'button': if (message.contenttype == 'uniterror' and 'selected' in self.request.data): # user selected error model message.chat = chat try: selected = self.request.data.get('selected')[str( message.id)]['errorModel'] except KeyError: selected = [] uniterror = message.content uniterror.save_response(user=self.request.user, response_list=selected) message.text = message.get_html() message.save() if not message.chat.is_live: get_additional_messages(uniterror.response, chat, selected) chat.next_point = self.next_handler.next_point( current=message.content, chat=chat, message=message, request=self.request) chat.last_modify_timestamp = timezone.now() chat.save() serializer = self.get_serializer(message, data=self.request.data) serializer.is_valid() serializer.save(chat=chat) elif (message.kind == 'add_faq' and message.sub_kind == 'add_faq') or \ (message.kind == 'get_faq_answer' and message.sub_kind == 'get_faq_answer') or \ (message.kind == 'ask_faq_understanding'): pass elif (message.contenttype == 'unitlesson' and message.kind == 'faqs' and 'selected' in self.request.data): message.chat = chat try: selected = self.request.data.get('selected')[str( message.id)]['faqModel'] except KeyError: selected = [] if selected: c_faq_data().update_one( { "chat_id": chat.id, "ul_id": message.content.id }, { "$set": { "faqs": { str(faq_id): { 'status': { "done": False } } for faq_id in selected } } }) # Save presented in message FAQs to avoid futher bugs in wrong dadta selection message.text = message.get_html() message.save() chat.next_point = self.next_handler.next_point( current=message.content, chat=chat, message=message, request=self.request) chat.last_modify_timestamp = timezone.now() chat.save() serializer = self.get_serializer(message, data=self.request.data) serializer.is_valid() serializer.save(chat=chat) elif message.contenttype == 'NoneType' and message.kind == 'abort': # user selected abort model message.chat = chat try: selected = self.request.data.get('selected')[str( message.id)]['errorModel'] except KeyError: selected = [] if not message.chat.is_live and selected: get_help_messages(chat) chat.next_point = self.next_handler.next_point( current=message.content, chat=chat, message=message, request=self.request) chat.last_modify_timestamp = timezone.now() chat.save() serializer.save(chat=chat) elif message.content_id and not message.student_error: # confidence and selfeval message.chat = chat opt_data = self.request.data.get('option') resp = message.content if chat.state and chat.state.fsmNode.node_name_is_one_of( 'GET_CONFIDENCE', 'ADDITIONAL_GET_CONFIDENCE'): resp.confidence = opt_data text = resp.get_confidence_display() else: resp.selfeval = opt_data text = resp.get_selfeval_display() # FIX if response was correct - user will not go to `else` section and response status should be set if not resp.is_locked: resp.status = EVAL_TO_STATUS_MAP.get(opt_data) message.text = text resp.save() chat.next_point = message chat.last_modify_timestamp = timezone.now() chat.save() serializer.save(content_id=resp.id, chat=chat, text=text) else: # message.chat = chat selfeval = self.request.data.get('option') resp = message.student_error resp.status = selfeval resp.save() # CRITICAL note - do not override response status # pass status to main response ONLY in case of absence the status at all # is_locked status is setted in TRANSITION node in chat FSM if not resp.response.is_locked: resp.response.status = selfeval resp.response.save() chat.next_point = message chat.last_modify_timestamp = timezone.now() chat.save() message.text = selfeval message.save() serializer.save(text=selfeval, chat=chat) if message.kind == 'button' and not (message.content_id and message.content and message.content.sub_kind): chat.last_modify_timestamp = timezone.now() chat.next_point = self.next_handler.next_point( current=message.content, chat=chat, message=message, request=self.request, ) chat.save()
class MyTestCase(TestCase): models_to_check = tuple() context_should_contain_keys = tuple() def setUp(self): self.username, self.password = '******', 'test' self.user = User.objects.create_user(self.username, '*****@*****.**', self.password) self.instructor = Instructor.objects.create(user=self.user, institution='institute', what_do_you_teach='something') self.username2, self.password2 = 'test1', 'test' self.user2 = User.objects.create_user(self.username2, '*****@*****.**', self.password2) self.instructor2 = Instructor.objects.create(user=self.user2, institution='institute', what_do_you_teach='something') self.unit = Unit(title='Test title', addedBy=self.user) self.unit.save() self.course = Course(title='Test title', description='test description', access='Public', enrollCode='111', lockout='222', addedBy=self.user) self.course.save() self.courseunit = CourseUnit( unit=self.unit, course=self.course, order=0, addedBy=self.user, releaseTime=timezone.now() ) self.courseunit.save() self.lesson = Lesson(title='title', text='text', addedBy=self.user) self.lesson.save() self.unitlesson = UnitLesson( unit=self.unit, order=0, lesson=self.lesson, addedBy=self.user, treeID=self.lesson.id ) self.unitlesson.save() self.resp1 = Response( unitLesson=self.unitlesson, kind=Response.ORCT_RESPONSE, lesson=self.lesson, course=self.course, text="Some text user may respond", author=self.user, status=NEED_HELP_STATUS, selfeval=Response.DIFFERENT ) self.resp1.save() self.resp2 = Response( unitLesson=self.unitlesson, kind=Response.ORCT_RESPONSE, lesson=self.lesson, course=self.course, text="Some text user may be responded 2", author=self.user, status=NEED_HELP_STATUS, selfeval=Response.DIFFERENT ) self.resp2.save() self.default_data = {} self.client.login(username=self.username, password=self.password) self.url = reverse('ctms:course_settings', kwargs={'pk': self.course.id}) def get_page(self): return self.client.get(self.url) def post_data(self, data={'name': 'some test name'}): response = self.client.post(self.url, data, follow=True) return response def get_client_method(self, method='post'): client_method = getattr(self.client, method) if not client_method: raise KeyError('self.client has no property {}'.format(method)) return client_method def post_valid_data(self, data={'name': 'some test name'}, method='post'): client_method = self.get_client_method(method) copied_data = {} if getattr(self, 'default_data', False): copied_data.update(self.default_data) copied_data.update(data) response = client_method(self.url, copied_data, follow=True) return response def post_invalid_data(self, data={'name': ''}, method='post'): client_method = self.get_client_method(method) copied_data = {} if getattr(self, 'default_data', False): copied_data.update(self.default_data) copied_data.update(data) response = client_method(self.url, copied_data, follow=True) return response def get_my_courses(self): return Course.objects.filter(addedBy=self.user) def get_test_course(self): return Course.objects.get(id=self.course.id) def get_test_unitlessons(self): return self.courseunit.unit.unitlesson_set.filter( kind=UnitLesson.COMPONENT, order__isnull=False ).order_by('order').annotate( responses_count=models.Count('response') ) def get_test_unitlesson(self): return self.courseunit.unit.unitlesson_set.filter( kind=UnitLesson.COMPONENT, order__isnull=False ).order_by('order').annotate( responses_count=models.Count('response') )[0] get_test_courslet = get_test_unitlesson get_test_response = lambda self: self.get_test_responses()[0] def get_test_courseunit(self): return CourseUnit.objects.get(id=self.courseunit.id) def get_test_responses(self): return Response.objects.filter( unitLesson=self.unitlesson, kind=Response.ORCT_RESPONSE, lesson=self.lesson, course=self.course, ) def get_model_counts(self, **kwargs): if isinstance(self.models_to_check, (list, tuple)): return {model: model.objects.filter().count() for model in self.models_to_check} return {self.models_to_check: self.models_to_check.objects.filter().count()} def validate_model_counts(self, first_counts, second_counts, must_equal=False): if isinstance(self.models_to_check, (list, tuple)): all_models = self.models_to_check else: all_models = [self.models_to_check] for model in all_models: if must_equal: self.assertEqual( first_counts[model], second_counts[model], "{} ({}) != {} ({}), with must_equal={}".format( model, first_counts[model], model, second_counts[model], must_equal ) ) else: self.assertNotEqual( first_counts[model], second_counts[model], "{} ({}) == {} ({}), with must_equal={}".format( model, first_counts[model], model, second_counts[model], must_equal ) ) def check_context_keys(self, response): for key in self.context_should_contain_keys: self.assertIn(key, response.context) def am_i_instructor(self, method='GET'): methods_map = {'GET', self.client.get, 'POST', self.client.post} client_method = methods_map.get(method) self.assertIsNotNone(client_method) if getattr(self, 'url'): if getattr(self, 'NEED_INSTRUCTOR'): response = client_method(self.url) if getattr(self, 'instructor'): self.assertEqual(response.status_code, 200) self.instructor.delete() response = client_method(self.url) self.assertEqual(response.status_code, 403) else: self.assertEqual(response.status_code, 403) else: response = client_method(self.url) self.assertEqual(response.status_code, 200)