Example #1
0
    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()
Example #2
0
    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()