예제 #1
0
파일: faq.py 프로젝트: cjlee112/socraticqs2
    def next_edge(self, edge, fsmStack, request, useCurrent=False, **kwargs):
        fsm = edge.fromNode.fsm
        next_node = edge.toNode
        ul_id = c_chat_context().find_one({
            "chat_id": fsmStack.id
        }).get('actual_ul_id')
        if fsmStack.next_point.text.lower() == 'yes':
            actual_faq_id = c_chat_context().find_one({
                "chat_id": fsmStack.id
            }).get('actual_faq_id', None)
            faq = Response.objects.filter(id=int(actual_faq_id)).first()
            try:
                ob, _ = InquiryCount.objects.get_or_create(
                    response=faq, addedBy=request.user)
            except InquiryCount.MultipleObjectsReturned:
                ob = InquiryCount.objects.filter(
                    response=faq,
                    addedBy=request.user).order_by('-atime').first()
            faq.notify_instructors()
            c_chat_context().update_one(
                {"chat_id": fsmStack.id},
                {"$set": {
                    "actual_inquiry_id": ob.id
                }},
            )

            faq_answers = faq.response_set.all()
            if faq_answers:
                next_node = fsm.get_node('SHOW_FAQ_ANSWERS')
                c_faq_data().update_one(
                    {
                        "chat_id": fsmStack.id,
                        "ul_id": ul_id
                    }, {
                        "$set": {
                            "faqs.{}.answers".format(actual_faq_id):
                            [{
                                "done": False,
                                "answer_id": answer.id
                            } for answer in faq_answers]
                        }
                    })
            else:
                next_node = fsm.get_node('WILL_TRY_MESSAGE_2')
        else:
            show_another_faq = False
            for key, value in list(
                    self.get_pending_faqs(chat_id=fsmStack.id,
                                          ul_id=ul_id).items()):
                if not value.get('status').get('done', False):
                    show_another_faq = True
                    break
            if show_another_faq:
                next_node = fsm.get_node('SHOW_FAQ_BY_ONE')

        return next_node
예제 #2
0
파일: faq.py 프로젝트: cjlee112/socraticqs2
    def start_event(self, node, fsmStack, request, **kwargs):
        """
        Event handler for START node.
        """
        unit = fsmStack.state.get_data_attr('unit')
        fsmStack.state.title = 'Study: %s' % unit.title

        try:  # use unitStatus if provided
            unitStatus = fsmStack.state.get_data_attr('unitStatus')
        except AttributeError:  # create new, empty unitStatus
            unitStatus = UnitStatus(unit=unit, user=request.user)
            unitStatus.save()
            fsmStack.state.set_data_attr('unitStatus', unitStatus)

        unit_lesson, chat = kwargs.get('unitlesson'), kwargs.get('chat')
        fsmStack.state.unitLesson = unit_lesson
        fsmStack.state.set_data_attr('updates', kwargs.get('updates', False))
        fsmStack.state.set_data_attr('new_faqs', kwargs.get(
            'new_faqs', False)) if kwargs.get('new_faqs') else None

        faqs_for_ul = unit_lesson.response_set.filter(
            ~Q(author=request.user),
            kind=Response.STUDENT_QUESTION,
            is_preview=False,
            is_test=False).exclude(title__isnull=True).exclude(
                title__exact='').exists()
        if faqs_for_ul:
            c_faq_data().update_one(
                {
                    "chat_id": chat.id,
                    "ul_id": unit_lesson.id
                }, {"$set": {
                    "faqs": {}
                }},
                upsert=True)
        _next = 'show_faq' if faqs_for_ul else 'ask_new_faq'

        return fsmStack.state.transition(fsmStack,
                                         request,
                                         _next,
                                         useCurrent=True,
                                         **kwargs)
예제 #3
0
    def get_faqs(self):
        if self.text:
            return self.text
        # FIXME UPDATE -> FAQ -> get_faqs ['updates', 'new_faqs'], this transition potentially cause bugs
        state = self.chat.state
        updates = state.get_data_attr(
            'updates'
        ) if state and 'updates' in state.load_json_data() else None
        new_faqs = state.get_data_attr(
            'new_faqs'
        ) if state and 'new_faqs' in state.load_json_data() else None
        faqs = None
        faq_list = self.content.response_set.filter(
            ~Q(author=self.owner),
            kind=Response.STUDENT_QUESTION,
            is_preview=False,
            is_test=False
        ).exclude(title__isnull=True).exclude(title__exact='')\
            .annotate(num_inquiry=Count('inquirycount')).order_by('-num_inquiry')

        if updates and new_faqs:
            faq_list = faq_list.filter(
                id__in=[faq.get('faq_id') for faq in new_faqs])

        if faq_list:
            checked_faqs = c_faq_data().find_one(
                {
                    'chat_id': self.chat.id,
                    "ul_id": self.content_id
                }, {
                    'faqs': 1,
                    '_id': 0
                })
            faq_str = (
                '<li><div class="chat-check chat-selectable {}" data-selectable-attribute="faqModel" '
                'data-selectable-value="{:d}"></div><h3>{}</h3></li>')
            faqs = reduce(lambda x, y: x + y, [
                faq_str.format(
                    'chat-selectable-selected' if str(x.id) in list(
                        checked_faqs.get('faqs', {}).keys()) else '', x.id,
                    x.title if x.title else Truncator(x.text).words(
                        72, truncate=' ...')) for x in faq_list
            ])
        return '<ul class="chat-select-list">{}</ul>'.format(
            faqs or '<li><h3>There are no faqs to display.</h3></li>')
예제 #4
0
파일: faq.py 프로젝트: cjlee112/socraticqs2
    def next_edge(self, edge, fsmStack, request, useCurrent=False, **kwargs):
        fsm = edge.fromNode.fsm

        ul_id = c_chat_context().find_one({
            "chat_id": fsmStack.id
        }).get('actual_ul_id')
        actual_faq_id = c_chat_context().find_one({
            "chat_id": fsmStack.id
        }).get('actual_faq_id', None)
        if actual_faq_id:
            faq_answers = c_faq_data().find_one({
                "chat_id":
                fsmStack.id,
                "ul_id":
                ul_id,
                "faqs.{}.answers.done".format(actual_faq_id):
                False
            })
            next_node = fsm.get_node(
                'SHOW_FAQ_ANSWERS') if faq_answers else fsm.get_node(
                    'ASK_UNDERSTANDING')
            return next_node

        return edge.toNode
예제 #5
0
파일: faq.py 프로젝트: cjlee112/socraticqs2
 def get_pending_faqs(chat_id, ul_id):
     # TODO change to the Assignment expressions in Python3.8
     faq_data = c_faq_data().find_one({"chat_id": chat_id, "ul_id": ul_id})
     return faq_data.get('faqs', {}) if faq_data else {}
예제 #6
0
    def get_message(self, chat, request, current=None, message=None):
        stack_pattern = QUESTION_STACK_PATTERN.format(request.user.id, chat.id)
        faq_response_pattern = QUESTION_STACK_PATTERN.format(request.user.id, chat.id)
        is_additional = chat.state.fsmNode.fsm.fsm_name_is_one_of('additional', 'resource')
        next_lesson = chat.state.unitLesson

        if hasattr(self._plugin, 'get_message'):
            return self._plugin.get_message(chat, next_lesson, is_additional, node=self)

        if self.node_name_is_one_of('LESSON'):
            input_type = 'custom'
            kind = next_lesson.lesson.kind
            try:
                if is_additional:
                    raise UnitLesson.DoesNotExist
                unitStatus = chat.state.get_data_attr('unitStatus')
                next_ul = unitStatus.unit.unitlesson_set.get(order=unitStatus.order+1)
                # Add CONTINUE button here
                if (next_ul and next_ul.lesson.kind in SIMILAR_KINDS and
                    kind in SIMILAR_KINDS or
                    next_ul.lesson.kind == Lesson.ORCT_QUESTION):
                    input_type = 'options'
                    kind = 'button'
            except UnitLesson.DoesNotExist:
                pass
            message = Message.objects.get_or_create(
                contenttype='unitlesson',
                content_id=next_lesson.id,
                chat=chat,
                owner=chat.user,
                input_type=input_type,
                kind=kind,
                is_additional=is_additional)[0]
        if self.name == 'ASK':
            SUB_KIND_TO_KIND_MAP = {
                'choices': 'button',
            }
            SUBKIND_TO_INPUT_TYPE_MAP = {
                'choices': 'options',
            }
            sub_kind = next_lesson.lesson.sub_kind
            _data = {
                'contenttype': 'unitlesson',
                'content_id': next_lesson.id,
                'chat': chat,
                'owner': chat.user,
                'input_type': 'custom',  # SUBKIND_TO_INPUT_TYPE_MAP.get(sub_kind, 'custom'),
                'kind': next_lesson.lesson.kind,  # SUB_KIND_TO_KIND_MAP.get(sub_kind, next_lesson.lesson.kind),
                'is_additional': is_additional
            }
            if not self.fsm.fsm_name_is_one_of('live_chat'):
                message, created = Message.objects.get_or_create(**_data)
            else:
                message = Message(**_data)
                message.save()
            find_crit = {
                "stack_id": stack_pattern
            }
            c_chat_stack().update_one(
                find_crit,
                {"$push": {"stack": next_lesson.id}},
                upsert=True)
            # Fallback method to pass ul_id's throught messages
            if request.session.get(stack_pattern):
                if isinstance(request.session[stack_pattern], list):
                    request.session[stack_pattern].append(next_lesson.id)
                else:
                    request.session[stack_pattern] = [request.session[stack_pattern]]
                    request.session[stack_pattern].append(next_lesson.id)
            else:
                request.session[stack_pattern] = [next_lesson.id]

        if self.name == 'ADDITIONAL_ASK':
            SUB_KIND_TO_KIND_MAP = {
                'choices': 'button',
            }
            SUBKIND_TO_INPUT_TYPE_MAP = {
                'choices': 'options',
            }
            sub_kind = next_lesson.lesson.sub_kind
            _data = {
                'contenttype': 'unitlesson',
                'content_id': next_lesson.id,
                'chat': chat,
                'owner': chat.user,
                'input_type': 'custom',  # SUBKIND_TO_INPUT_TYPE_MAP.get(sub_kind, 'custom'),
                'kind': 'message',  # SUB_KIND_TO_KIND_MAP.get(sub_kind, next_lesson.lesson.kind),
                'is_additional': is_additional
            }
            if not self.fsm.fsm_name_is_one_of('live_chat'):
                message, created = Message.objects.get_or_create(**_data)
            else:
                message = Message(**_data)
                message.save()
            find_crit = {
                "stack_id": stack_pattern
            }
            c_chat_stack().update_one(
                find_crit,
                {"$push": {"stack": next_lesson.id}},
                upsert=True)
            # Fallback method to pass ul_id's throught messages
            if request.session.get(stack_pattern):
                if isinstance(request.session[stack_pattern], list):
                    request.session[stack_pattern].append(next_lesson.id)
                else:
                    request.session[stack_pattern] = [request.session[stack_pattern]]
                    request.session[stack_pattern].append(next_lesson.id)
            else:
                request.session[stack_pattern] = [next_lesson.id]

        if self.node_name_is_one_of('ABORTS'):
            message = Message.objects.create(
                owner=chat.user,
                chat=chat,
                kind='message',
                input_type='custom',
                is_additional=is_additional,
                text=self.title
            )
        if self.node_name_is_one_of('GET_ABORTS'):
            message = Message.objects.get_or_create(
                contenttype='NoneType',
                kind='abort',
                input_type='options',
                chat=chat,
                owner=chat.user,
                userMessage=False,
                is_additional=is_additional)[0]
        if self.node_name_is_one_of('GET_ANSWER'):
            find_crit = {
                "stack_id": stack_pattern
            }
            stack = None
            try:
                document = c_chat_stack().find_and_modify(
                    query=find_crit,
                    update={"$pop": {"stack": 1}})
                stack = document.get('stack', [])
            except ConnectionFailure:
                pass
            if request.session.get(stack_pattern):
                if isinstance(request.session[stack_pattern], list):
                    fallback_ul_id = request.session[stack_pattern].pop()
                elif isinstance(request.session[stack_pattern], int):
                    fallback_ul_id = request.session[stack_pattern]
                else:
                    fallback_ul_id = current.id
            unit_lesson_id = stack.pop() if stack else fallback_ul_id
            lesson_to_answer = UnitLesson.objects.filter(id=unit_lesson_id).first()
            _data = {
                'contenttype': 'response',
                'input_type': 'text',
                'lesson_to_answer': lesson_to_answer,
                'chat': chat,
                'owner': chat.user,
                'kind': 'response',
                'userMessage': True,
                'is_additional': is_additional
            }
            if lesson_to_answer.lesson.sub_kind == 'choices':
                _data.update(dict(
                    input_type='options',

                ))

            if not self.fsm.fsm_name_is_one_of('live_chat'):
                message = Message.objects.get_or_create(**_data)[0]
            else:
                message = Message(**_data)
                message.save()

        if self.node_name_is_one_of('ADDITIONAL_GET_ANSWER'):
            find_crit = {
                "stack_id": stack_pattern
            }
            stack = None
            try:
                document = c_chat_stack().find_and_modify(
                    query=find_crit,
                    update={"$pop": {"stack": 1}})
                stack = document.get('stack', [])
            except ConnectionFailure:
                pass
            if request.session.get(stack_pattern):
                if isinstance(request.session[stack_pattern], list):
                    fallback_ul_id = request.session[stack_pattern].pop()
                elif isinstance(request.session[stack_pattern], int):
                    fallback_ul_id = request.session[stack_pattern]
                else:
                    fallback_ul_id = current.id
            unit_lesson_id = stack.pop() if stack else fallback_ul_id
            lesson_to_answer = UnitLesson.objects.filter(id=unit_lesson_id).first()
            _data = {
                'contenttype': 'response',
                'input_type': 'text',
                'lesson_to_answer': lesson_to_answer,
                'chat': chat,
                'owner': chat.user,
                'kind': 'response',
                'userMessage': True,
                'is_additional': is_additional
            }
            if lesson_to_answer.lesson.sub_kind == 'choices':
                _data.update(dict(
                    input_type='options',

                ))

            if not self.fsm.fsm_name_is_one_of('live_chat'):
                message = Message.objects.get_or_create(**_data)[0]
            else:
                message = Message(**_data)
                message.save()

        if self.node_name_is_one_of('CONFIDENCE'):
            # current here is Response instance
            if isinstance(current, Response):
                response_to_chk = current
                answer = current.unitLesson.get_answers().first()
            else:
                response_to_chk = message.response_to_check
                if not message.lesson_to_answer:
                    answer = message.response_to_check.unitLesson.get_answers().first()
                else:
                    answer = message.lesson_to_answer.get_answers().first()
            message = Message.objects.create(  # get_or_create
                contenttype='unitlesson',
                response_to_check=response_to_chk,
                input_type='custom',
                text=self.title,
                chat=chat,
                owner=chat.user,
                kind=answer.kind,
                is_additional=is_additional)

        if self.node_name_is_one_of('ADDITIONAL_CONFIDENCE'):
            # current here is Response instance
            if isinstance(current, Response):
                response_to_chk = current
                answer = current.unitLesson.get_answers().first()
            else:
                response_to_chk = message.response_to_check
                if not message.lesson_to_answer:
                    answer = message.response_to_check.unitLesson.get_answers().first()
                else:
                    answer = message.lesson_to_answer.get_answers().first()
            message = Message.objects.create(  # get_or_create
                contenttype='unitlesson',
                response_to_check=response_to_chk,
                input_type='custom',
                text=self.title,
                chat=chat,
                owner=chat.user,
                kind=answer.kind,
                is_additional=is_additional)

        if self.node_name_is_one_of('GET_CONFIDENCE'):
            _data = dict(
                contenttype='response',
                content_id=message.response_to_check.id,
                input_type='options',
                chat=chat,
                owner=chat.user,
                kind='response',
                userMessage=True,
                is_additional=is_additional,
            )
            # here was Message.objects.create for all fsm's except live_chat. for live_chat fsm here was get_or_create
            message = Message(**_data)
            message.save()

        if self.node_name_is_one_of('ADDITIONAL_GET_CONFIDENCE'):
            _data = dict(
                contenttype='response',
                content_id=message.response_to_check.id,
                input_type='options',
                chat=chat,
                owner=chat.user,
                kind='response',
                userMessage=True,
                is_additional=is_additional,
            )
            # here was Message.objects.create for all fsm's except live_chat. for live_chat fsm here was get_or_create
            message = Message(**_data)
            message.save()

        if self.node_name_is_one_of('CORRECT_ANSWER'):
            lesson = message.response_to_check.unitLesson.lesson if chat.is_live else message.content.unitLesson.lesson
            correct_choices = lesson.get_correct_choices()
            if correct_choices:
                correct_title = lesson.get_choice_title(correct_choices[0][0])
                correct_description = lesson.get_choice_description(correct_choices[0][0])
            else:
                answer = message.content.unitLesson.get_answers().first()
                correct_title = answer.lesson.title if answer else 'Answer title'
                correct_description = mark_safe(md2html(answer.lesson.text)) if answer else 'Answer description'
            message = Message.objects.create(
                owner=chat.user,
                chat=chat,
                kind='message',
                input_type='custom',
                is_additional=is_additional,
                text="""
                    <b>You got it right, the correct answer is: {}</b>
                    <br>
                    {}
                """
                .format(correct_title, correct_description)
            )

        if self.node_name_is_one_of('ADDITIONAL_CORRECT_ANSWER'):
            lesson = message.response_to_check.unitLesson.lesson if chat.is_live else message.content.unitLesson.lesson
            correct_choices = lesson.get_correct_choices()
            if correct_choices:
                correct_title = lesson.get_choice_title(correct_choices[0][0])
                correct_description = lesson.get_choice_description(correct_choices[0][0])
            else:
                answer = message.content.unitLesson.get_answers().first()
                correct_title = answer.lesson.title if answer else 'Answer title'
                correct_description = mark_safe(md2html(answer.lesson.text)) if answer else 'Answer description'
            message = Message.objects.create(
                owner=chat.user,
                chat=chat,
                kind='message',
                input_type='custom',
                is_additional=is_additional,
                text="""
                    <b>You got it right, the correct answer is: {}</b>
                    <br>
                    {}
                """
                .format(correct_title, correct_description)
            )

        if self.node_name_is_one_of('INCORRECT_ANSWER'):
            lesson = message.response_to_check.unitLesson.lesson if chat.is_live else message.content.unitLesson.lesson
            correct_choices = lesson.get_correct_choices()
            if correct_choices:
                correct_title = lesson.get_choice_title(correct_choices[0][0])
                correct_description = lesson.get_choice_description(correct_choices[0][0])
            else:
                answer = message.content.unitLesson.get_answers().first()
                correct_title = answer.lesson.title if answer else 'Answer title'
                correct_description = mark_safe(md2html(answer.lesson.text)) if answer else 'Answer description'
            message = Message.objects.create(
                owner=chat.user,
                chat=chat,
                lesson_to_answer=message.response_to_check.unitLesson if chat.is_live else message.content.unitLesson,
                response_to_check=message.response_to_check if chat.is_live else current,
                kind='message',
                input_type='custom',
                is_additional=is_additional,
                text="""
                    <b>The correct answer is: {}</b>
                    <br>
                    {}
                """
                .format(correct_title, correct_description)
            )

        if self.node_name_is_one_of('ADDITIONAL_INCORRECT_ANSWER'):
            lesson = message.response_to_check.unitLesson.lesson if chat.is_live else message.content.unitLesson.lesson
            correct_choices = lesson.get_correct_choices()
            if correct_choices:
                correct_title = lesson.get_choice_title(correct_choices[0][0])
                correct_description = lesson.get_choice_description(correct_choices[0][0])
            else:
                answer = message.content.unitLesson.get_answers().first()
                correct_title = answer.lesson.title if answer else 'Answer title'
                correct_description = mark_safe(md2html(answer.lesson.text)) if answer else 'Answer description'
            message = Message.objects.create(
                owner=chat.user,
                chat=chat,
                lesson_to_answer=message.response_to_check.unitLesson if chat.is_live else message.content.unitLesson,
                response_to_check=message.response_to_check if chat.is_live else current,
                kind='message',
                input_type='custom',
                is_additional=is_additional,
                text="""
                    <b>The correct answer is: {}</b>
                    <br>
                    {}
                """
                .format(correct_title, correct_description)
            )

        if self.node_name_is_one_of('INCORRECT_CHOICE'):
            lesson = message.lesson_to_answer.lesson
            selected = [int(i) for i in message.response_to_check.text.split('[selected_choices] ')[1].split()]
            incorrect_description = lesson.get_choice_description(selected[0]) if selected else ''
            my_choices = []
            for i, c in message.response_to_check.lesson.get_choices():
                if i in selected:
                    my_choices.append(c.split(' ', 1)[1])
            if not my_choices:
                my_choices.append('Nothing')
            message = Message.objects.create(
                owner=chat.user,
                chat=chat,
                kind='message',
                input_type='custom',
                is_additional=is_additional,
                text="""
                    <b>You selected: {}</b>
                    <br>
                    {}
                """
                .format(
                    my_choices[0] if len(my_choices) == 1 else '<br>' + ''.join(
                        ['<h3>{}</h3>'.format(_) for _ in my_choices]),
                    incorrect_description
                )
            )

        if self.node_name_is_one_of('ADDITIONAL_INCORRECT_CHOICE'):
            lesson = message.lesson_to_answer.lesson
            selected = [int(i) for i in message.response_to_check.text.split('[selected_choices] ')[1].split()]
            incorrect_description = lesson.get_choice_description(selected[0]) if selected else ''
            my_choices = []
            for i, c in message.response_to_check.lesson.get_choices():
                if i in selected:
                    my_choices.append(c.split(' ', 1)[1])
            if not my_choices:
                my_choices.append('Nothing')
            message = Message.objects.create(
                owner=chat.user,
                chat=chat,
                kind='message',
                input_type='custom',
                is_additional=is_additional,
                text="""
                    <b>You selected: {}</b>
                    <br>
                    {}
                """
                .format(
                    my_choices[0] if len(my_choices) == 1 else '<br>' + ''.join(
                        ['<h3>{}</h3>'.format(_) for _ in my_choices]),
                    incorrect_description
                )
            )

            # here was Message.objects.create for all fsm's except live_chat. for live_chat fsm here was get_or_create
        if self.node_name_is_one_of("WAIT_ASSESS"):
            if isinstance(current, Response):
                resp_to_chk = current
            else:
                resp_to_chk = message.response_to_check
            message = Message.objects.get_or_create(
                chat=chat,
                text=self.title,
                kind='button',
                response_to_check=resp_to_chk,
                is_additional=is_additional,
                owner=chat.user,
            )[0]

        if self.node_name_is_one_of('ASSESS'):
            # current here is Response instance
            if isinstance(current, Response):
                response_to_chk = current
                answer = current.unitLesson.get_answers().first()
            else:
                response_to_chk = message.response_to_check
                if not message.lesson_to_answer:
                    answer = message.response_to_check.unitLesson.get_answers().first()
                else:
                    answer = message.lesson_to_answer.get_answers().first()
            message = Message.objects.get_or_create(
                contenttype='unitlesson',
                response_to_check=response_to_chk,
                input_type='custom',
                content_id=answer.id,
                chat=chat,
                owner=chat.user,
                kind=answer.kind,
                is_additional=is_additional)[0]

        if self.node_name_is_one_of('ADDITIONAL_ASSESS'):
            # current here is Response instance
            if isinstance(current, Response):
                response_to_chk = current
                answer = current.unitLesson.get_answers().first()
            else:
                response_to_chk = message.response_to_check
                if not message.lesson_to_answer:
                    answer = message.response_to_check.unitLesson.get_answers().first()
                else:
                    answer = message.lesson_to_answer.get_answers().first()
            message = Message.objects.get_or_create(
                contenttype='unitlesson',
                response_to_check=response_to_chk,
                input_type='custom',
                content_id=answer.id,
                chat=chat,
                owner=chat.user,
                kind=answer.kind,
                is_additional=is_additional)[0]

        if self.node_name_is_one_of('GET_ASSESS'):
            _data = dict(
                contenttype='response',
                content_id=message.response_to_check.id if message.response_to_check else None,
                input_type='options',
                chat=chat,
                owner=chat.user,
                kind='response',
                userMessage=True,
                is_additional=is_additional
            )
            # here was Message.objects.create for all fsm's except live_chat. for live_chat fsm here was get_or_create
            message = Message(**_data)
            message.save()

        if self.node_name_is_one_of('ADDITIONAL_GET_ASSESS'):
            _data = dict(
                contenttype='response',
                content_id=message.response_to_check.id if message.response_to_check else None,
                input_type='options',
                chat=chat,
                owner=chat.user,
                kind='response',
                userMessage=True,
                is_additional=is_additional
            )
            # here was Message.objects.create for all fsm's except live_chat. for live_chat fsm here was get_or_create
            message = Message(**_data)
            message.save()

        if self.node_name_is_one_of('GRADING'):
            GraderClass = GRADERS.get(message.content.unitLesson.lesson.sub_kind)
            if GraderClass:
                grader = GraderClass(message.content.unitLesson, message.content)
                # grade method must be called to actually do the work
                grader.grade
                text = 'Your answer is {}!'.format(grader.message)
            else:
                text = "No such grader! Grading could not be applied."
            message = Message.objects.create(
                owner=chat.user,
                chat=chat,
                kind='message',
                input_type='custom',
                is_additional=is_additional,
                text=text
            )
        if self.node_name_is_one_of('ADDITIONAL_GRADING'):
            GraderClass = GRADERS.get(message.content.unitLesson.lesson.sub_kind)
            if GraderClass:
                grader = GraderClass(message.content.unitLesson, message.content)
                # grade method must be called to actually do the work
                grader.grade
                text = 'Your answer is {}!'.format(grader.message)
            else:
                text = "No such grader! Grading could not be applied."
            message = Message.objects.create(
                owner=chat.user,
                chat=chat,
                kind='message',
                input_type='custom',
                is_additional=is_additional,
                text=text
            )

        if self.node_name_is_one_of('STUDENTERROR'):
            resolve_message = Message.objects.filter(
                contenttype='unitlesson',
                content_id=next_lesson.id,
                chat=chat,
                owner=chat.user,
                input_type='custom',
                kind='message',
                timestamp__isnull=True,
                is_additional=True).first()
            message = Message.objects.get_or_create(
                contenttype='unitlesson',
                content_id=resolve_message.student_error.errorModel.id,
                chat=chat,
                owner=chat.user,
                student_error=resolve_message.student_error,
                input_type='options',
                kind='button',
                is_additional=True)[0]
            c_chat_stack().update_one(
                {"stack_id": stack_pattern},
                {"$set": {"additional_stack": {
                    "em_id": resolve_message.student_error.errorModel.id,
                    "student_error_id": resolve_message.student_error.id
                }}},
                upsert=True)
        if self.node_name_is_one_of('RESOLVE'):
            SUB_KIND_TO_KIND_MAP = {
                'choices': 'button',
            }
            SUBKIND_TO_INPUT_TYPE_MAP = {
                'choices': 'options',
            }
            sub_kind = next_lesson.lesson.sub_kind
            _data = {
                'contenttype': 'unitlesson',
                'content_id': next_lesson.id,
                'chat': chat,
                'owner': chat.user,
                'input_type': 'custom',  # SUBKIND_TO_INPUT_TYPE_MAP.get(sub_kind, 'custom'),
                'kind': 'message',  # SUB_KIND_TO_KIND_MAP.get(sub_kind, next_lesson.lesson.kind),
                'is_additional': is_additional
            }
            if not self.fsm.fsm_name_is_one_of('live_chat'):
                filter_data = _data.copy()
                filter_data.update({'timestamp__isnull': True})
                message = Message.objects.filter(**filter_data).first()
                if not message:
                    message = Message.objects.create(**_data)
            else:
                message = Message(**_data)
                message.save()
            if next_lesson.lesson.kind == 'orct':
                c_chat_stack().update_one(
                    {"stack_id": stack_pattern},
                    {"$push": {"stack": next_lesson.id}},
                    upsert=True)
                # Fallback method to pass ul_id's throught messages
                if request.session.get(stack_pattern):
                    if isinstance(request.session[stack_pattern], list):
                        request.session[stack_pattern].append(next_lesson.id)
                    else:
                        request.session[stack_pattern] = [request.session[stack_pattern]]
                        request.session[stack_pattern].append(next_lesson.id)
                else:
                    request.session[stack_pattern] = [next_lesson.id]
        if self.node_name_is_one_of('HELP_RESOLVE'):
            message = Message.objects.get_or_create(
                contenttype='unitlesson',
                content_id=next_lesson.id,
                chat=chat,
                owner=chat.user,
                input_type='options',
                kind='button',
                timestamp__isnull=True,
                is_additional=True)[0]
        if self.node_name_is_one_of('MESSAGE_NODE'):
            additional_info = c_chat_stack().find_one(
                {"stack_id": stack_pattern}, {"additional_stack": 1, "_id": 0}).get("additional_stack")
            student_error_id, _ = additional_info.get('student_error_id'), additional_info.get('em_id')  # FIXME
            message = Message.objects.get_or_create(
                chat=chat,
                owner=chat.user,
                text=chat.state.fsmNode.title,
                student_error=StudentError.objects.filter(id=student_error_id).first(),
                input_type='custom',
                kind='message',
                is_additional=True)[0]
        if self.node_name_is_one_of(
            'END',
            'IF_RESOURCES',
            'NEED_HELP_MESSAGE',
            'ASSESS_QUESTION_MESSAGE',
            'ADDITIONAL_ASSESS_QUESTION_MESSAGE'
        ):
            if not self.help:
                text = self.get_help(chat.state, request=None)
            else:
                text = self.help
            message = Message.objects.create(
                response_to_check=message.response_to_check,
                chat=chat,
                owner=chat.user,
                text=text,
                input_type='custom',
                kind='message',
                is_additional=True)
        if self.node_name_is_one_of('GREAT_MESSAGE', 'HOPENESS_MESSAGE', 'ORCT_LETS_START_MESSAGE'):
            if not self.help:
                text = self.get_help(chat.state, request=None)
            else:
                text = self.help
            message = Message.objects.create(
                response_to_check=message.response_to_check,
                chat=chat,
                owner=chat.user,
                text=text,
                input_type='custom',
                kind='message',
                is_additional=True)
        if self.node_name_is_one_of('GET_RESOLVE'):
            message = Message.objects.create(
                contenttype='unitlesson',
                content_id=next_lesson.id,
                input_type='options',
                chat=chat,
                owner=chat.user,
                student_error=message.student_error,
                kind='response',
                userMessage=True,
                is_additional=is_additional)
        if self.node_name_is_one_of('ERRORS'):
            message = Message.objects.get_or_create(
                chat=chat,
                owner=chat.user,
                text=''''''
                '''Here are the most common blindspots people reported when comparing their answer vs. '''
                '''the correct answer. Check the box(es) that seem relevant to your answer (if any).''',
                kind='message',
                input_type='custom',
                is_additional=is_additional)[0]
        if self.node_name_is_one_of('GET_ERRORS'):
            uniterror = UnitError.get_by_message(message)
            message = Message.objects.get_or_create(
                contenttype='uniterror',
                content_id=uniterror.id,
                input_type='options',
                chat=chat,
                kind='uniterror',
                owner=chat.user,
                userMessage=False,
                is_additional=is_additional)[0]
        if self.node_name_is_one_of('SHOW_FAQS'):
            is_new = chat.state.get_data_attr('updates') if 'updates' in chat.state.load_json_data() else None
            message = Message.objects.create(
                contenttype='unitlesson',
                content_id=next_lesson.id,
                input_type='options',
                chat=chat,
                kind='faqs',
                owner=chat.user,
                userMessage=False,
                is_new=is_new,
                is_additional=is_additional)
        if self.node_name_is_one_of('TITLE'):
            divider = ChatDivider(text=next_lesson.lesson.title,
                                  unitlesson=next_lesson)
            divider.save()
            message = Message.objects.get_or_create(
                contenttype='chatdivider',
                content_id=divider.id,
                input_type='custom',
                type='breakpoint',
                chat=chat,
                owner=chat.user,
                kind='message',
                is_additional=is_additional)[0]
        if self.node_name_is_one_of('START_MESSAGE',):
            message = Message.objects.create(
                input_type='options',
                text=self.title,
                chat=chat,
                owner=chat.user,
                kind='button',
                is_additional=is_additional)
        if self.node_name_is_one_of('DIVIDER'):
            divider = ChatDivider(text=self.title)
            divider.save()
            message = Message.objects.get_or_create(
                contenttype='chatdivider',
                content_id=divider.id,
                input_type='custom',
                type='breakpoint',
                chat=chat,
                owner=chat.user,
                kind='message',
                is_additional=is_additional)[0]

        if self.node_name_is_one_of('START') and self.fsm.fsm_name_is_one_of('live_chat'):
            message = Message.objects.get_or_create(
                chat=chat,
                text=self.title,
                kind='button',
                is_additional=is_additional,
                owner=chat.user,
            )[0]

        if self.name in (
                'GET_UNIT_NAME_TITLE',
                'GET_UNIT_QUESTION',
                'GET_UNIT_ANSWER',
                'GET_HAS_UNIT_ANSWER',
        ):
            _data = dict(
                chat=chat,
                owner=chat.user,
                input_type='text',
                kind='response',
                userMessage=True,
                is_additional=is_additional
            )
            if isinstance(current, UnitLesson):
                _data['content_id'] = current.id
                # _data['text'] = current.lesson.title
                _data['contenttype'] = 'unitlesson'
            elif message and message.content:
                # _data['text'] = "current.lesson"
                _data['content_id'] = message.content_id
                _data['contenttype'] = message.contenttype

            # content_id = current.id if current else None
            message = Message.objects.create(**_data)

        if self.name in ('HAS_UNIT_ANSWER', 'WELL_DONE'):
            text = "**{}** \n\n{}".format(self.title, getattr(self, 'help', '') or '')
            _data = dict(
                chat=chat,
                text=text,
                input_type='options',
                kind='message',
                owner=chat.user,
                userMessage=False,
                is_additional=is_additional
            )
            if message and message.content_id:
                _data['content_id'] = message.content_id
                _data['contenttype'] = 'unitlesson'
            message = Message.objects.create(**_data)

        if self.name in ('WELL_DONE', 'ASK_NEW_FAQ', 'GET_NEW_FAQ'):
            text = "**{}** \n\n{}".format(self.title, getattr(self, 'help', '') or '')
            _data = dict(
                chat=chat,
                text=text,
                input_type='options',
                kind='button',
                owner=chat.user,
                userMessage=False,
                is_additional=is_additional
            )
            if message and message.content_id:
                _data['content_id'] = message.content_id
                _data['contenttype'] = 'unitlesson'
            message = Message.objects.create(**_data)

        if self.name in ('ADDING_FAQ',):
            ul_id = c_chat_context().find_one({"chat_id": chat.id}).get('actual_ul_id')
            unitLesson = UnitLesson.objects.filter(id=ul_id).first()
            faq_response = Response.objects.create(
                unitLesson=unitLesson,
                lesson=unitLesson.lesson,
                kind=Response.STUDENT_QUESTION,
                course=self.load_json_id_dict(chat.state.data).get('course'),
                is_preview=chat.enroll_code.isPreview,
                is_test=chat.enroll_code.isTest,
                author=chat.user,
                needsEval=True)
            c_chat_stack().update_one(
                {"stack_id": faq_response_pattern},
                {"$push": {"stack": faq_response.id}},
                upsert=True)
            _data = dict(
                chat=chat,
                text=self.title,
                input_type='options',
                kind='button',
                owner=chat.user,
                userMessage=False,
                is_additional=is_additional
            )
            message = Message.objects.create(**_data)

        if self.name in (
            'NEW_FAQ_TITLE',
        ):
            ul_id = c_chat_context().find_one({"chat_id": chat.id}).get('actual_ul_id')
            unitLesson = UnitLesson.objects.filter(id=ul_id).first()
            faq_response = Response.objects.create(
                unitLesson=unitLesson,
                lesson=unitLesson.lesson,
                kind=Response.STUDENT_QUESTION,
                course=self.load_json_id_dict(chat.state.data).get('course'),
                is_preview=chat.enroll_code.isPreview,
                is_test=chat.enroll_code.isTest,
                author=chat.user,
                needsEval=True)
            c_chat_stack().update_one(
                {"stack_id": faq_response_pattern},
                {"$push": {"stack": faq_response.id}},
                upsert=True)
            _data = dict(
                chat=chat,
                owner=chat.user,
                text=self.title,
                input_type='custom',
                kind='message',
                userMessage=False,
                is_additional=is_additional
            )
            message = Message.objects.create(**_data)

        if self.name in (
            'ASK_NEW_FAQ',
        ):
            _data = dict(
                chat=chat,
                owner=chat.user,
                text=self.title,
                input_type='custom',
                kind='message',
                userMessage=False,
                is_additional=is_additional
            )
            message = Message.objects.create(**_data)

        if self.name in ('WILL_TRY_MESSAGE', 'F**K', 'MSG_FOR_INQUIRY',
                         'WILL_TRY_MESSAGE_2', 'SELECT_NEXT_FAQ', 'WILL_TRY_MESSAGE_3',
                         'SHOW_FAQ_ANSWERS', 'INTRO_MSG'):
            text = None
            if self.name == 'SHOW_FAQ_ANSWERS':
                ul_id = c_chat_context().find_one({"chat_id": chat.id}).get('actual_ul_id')
                actual_faq_id = c_chat_context().find_one(
                    {"chat_id": chat.id}).get('actual_faq_id', None)
                faq_answers = c_faq_data().find_one(
                    {
                        "chat_id": chat.id,
                        "ul_id": ul_id,
                        "faqs.{}.answers.done".format(actual_faq_id): False}
                ).get('faqs').get(actual_faq_id).get('answers')
                for i in faq_answers:
                    if not i.get('done'):
                        answer = Response.objects.filter(id=i.get('answer_id')).first()
                        text = "<b>{}</b><br>{}".format(answer.title, answer.text) if answer else None
                        c_faq_data().update_one(
                            {
                                "chat_id": chat.id,
                                "ul_id": ul_id,
                                "faqs.{}.answers.answer_id".format(actual_faq_id): i.get('answer_id')

                            },
                            {"$set": {"faqs.{}.answers.$.done".format(actual_faq_id): True}}
                        )
                        break
            _data = dict(
                chat=chat,
                owner=chat.user,
                text=text or self.title,
                input_type='custom',
                kind='message',
                userMessage=False,
                is_additional=is_additional
            )
            message = Message.objects.create(**_data)


        if self.name in (
            'NEW_FAQ_DESCRIPTION', 'ASK_FOR_FAQ_ANSWER', 'ASK_UNDERSTANDING', 'FAQ'
        ):
            _data = dict(
                chat=chat,
                owner=chat.user,
                text=self.title,
                input_type='custom',
                kind='message',
                userMessage=False,
                is_additional=is_additional
            )
            message = Message.objects.create(**_data)


        if self.name in ('GET_NEW_FAQ_TITLE', 'GET_NEW_FAQ_DESCRIPTION'):
            if self.name == 'GET_NEW_FAQ_TITLE':
                try:
                    faq_response_id = c_chat_stack().find_one({"stack_id": faq_response_pattern}).get('stack')[-1]
                except IndexError:
                    faq_response_id = None
            else:
                document = c_chat_stack().find_and_modify(
                    query={"stack_id": faq_response_pattern},
                    update={"$pop": {"stack": 1}})
                stack = document.get('stack', [])
                faq_response_id = stack.pop() if stack else None

            _data = dict(
                contenttype='response',
                content_id=faq_response_id,
                chat=chat,
                input_type='text',
                kind='response',
                sub_kind='add_faq',
                owner=chat.user,
                userMessage=True,
                is_additional=is_additional
            )
            message = Message.objects.create(**_data)

        if self.name in ('GET_NEW_FAQ',):
            _data = dict(
                input_type='options',
                chat=chat,
                owner=chat.user,
                kind='add_faq',
                sub_kind='add_faq',
                userMessage=True,
                is_additional=is_additional,
            )
            message = Message.objects.create(**_data)

        if self.name in ('GET_FOR_FAQ_ANSWER',):
            _data = dict(
                input_type='options',
                chat=chat,
                owner=chat.user,
                kind='get_faq_answer',
                sub_kind='get_faq_answer',
                userMessage=True,
                is_additional=is_additional,
            )
            message = Message.objects.create(**_data)

        if self.name in ('SHOW_FAQ_BY_ONE',):
            ul_id = c_chat_context().find_one({"chat_id": chat.id}).get('actual_ul_id')
            try:
                # TODO change to the Assignment expressions in Python3.8
                faq_data = c_faq_data().find_one({"chat_id": chat.id, "ul_id": ul_id})
                faqs = faq_data.get('faqs', {}) if faq_data else {}
                faq_id = None
                for key, value in list(faqs.items()):
                    if not value.get('status').get('done', False):
                        faq_id = key
                        break
            except IndexError:
                faq_id = None

            if faq_id:
                c_faq_data().update_one(
                    {"chat_id": chat.id, "ul_id": ul_id},
                    {"$set": {"faqs.{}.status.done".format(faq_id): True}})
                c_chat_context().update_one(
                    {"chat_id": chat.id},
                    {"$set": {"actual_faq_id": faq_id}},
                    upsert=True
                )
            _data = dict(
                contenttype='response',
                content_id=int(faq_id),
                kind='response',
                sub_kind='faq',
                chat=chat,
                owner=chat.user,
                userMessage=False,
                is_additional=is_additional,
            )
            message = Message.objects.create(**_data)

        if self.name in ('GET_UNDERSTANDING',):
            _data = dict(
                kind='ask_faq_understanding',
                input_type='options',
                chat=chat,
                owner=chat.user,
                userMessage=True,
                is_additional=is_additional,
            )
            message = Message.objects.create(**_data)

        # wait for RECYCLE node and  any node starting from WAIT_ except WAIT_ASSESS
        if is_wait_node(self.name):
            lookup = dict(
                chat=chat,
                text=self.title,
                kind='button',
                is_additional=False,
                owner=chat.user
            )
            message = Message.objects.get_or_create(**lookup)[0]
        return message
예제 #7
0
파일: api.py 프로젝트: cjlee112/socraticqs2
    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()