コード例 #1
0
def next_lesson_after_title(self,
                            edge,
                            fsmStack,
                            request,
                            useCurrent=False,
                            **kwargs):
    """
    Edge method that moves us to right state for next lesson (or END).
    """
    fsm = edge.fromNode.fsm
    unitStatus = fsmStack.state.get_data_attr('unitStatus')
    nextUL = unitStatus.get_lesson()
    answer = nextUL.get_answers().first()
    # Update chat context
    # Don't ask
    chat = fsmStack
    c_chat_context().update_one({"chat_id": chat.id}, {
        "$set": {
            "actual_ul_id": answer.id if answer else nextUL.id,
            "thread_id": nextUL.id,
            f"activity.{nextUL.id}": timezone.now(),
            "need_faqs": False
        }
    },
                                upsert=True)
    if nextUL.is_question():
        return fsm.get_node(name='ASK')
    else:  # just a lesson to read
        return edge.toNode
コード例 #2
0
def check_selfassess_and_next_lesson(self,
                                     edge,
                                     fsmStack,
                                     request,
                                     useCurrent=False,
                                     **kwargs):
    fsm = edge.fromNode.fsm

    if fsmStack.state.unitLesson.lesson.enable_auto_grading and not fsmStack.state.fsmNode.name == 'GRADING':
        return fsm.get_node('GRADING')

    if fsmStack.next_point.content.selfeval != 'correct':
        if (fsmStack.next_point.content.unitLesson.get_errors()
                or fsmStack.next_point.content.lesson.add_unit_aborts
                and fsmStack.next_point.content.unitLesson.unit.get_aborts()):
            c_chat_context().update_one({"chat_id": fsmStack.id},
                                        {"$set": {
                                            "need_faqs": True
                                        }},
                                        upsert=True)
            return fsm.get_node('ERRORS')
        else:
            resp = fsmStack.next_point.content
            resp.status = 'help'
            resp.save()
            return fsm.get_node('FAQ')

    return edge.toNode
コード例 #3
0
ファイル: faq.py プロジェクト: cjlee112/socraticqs2
    def next_edge(self, edge, fsmStack, request, useCurrent=False, **kwargs):
        fsm = edge.fromNode.fsm
        inquiry_id = c_chat_context().find_one({
            "chat_id": fsmStack.id
        }).get('actual_inquiry_id')
        if inquiry_id:
            ob = InquiryCount.objects.filter(id=inquiry_id).first()
            ob.status = fsmStack.next_point.text.lower()
            ob.save()

        # Defaul value - go to asking new faq from Student
        next_node = edge.toNode

        if fsmStack.next_point.text.lower() == 'help':
            next_node = fsm.get_node('WILL_TRY_MESSAGE_3')
        else:
            ul_id = c_chat_context().find_one({
                "chat_id": fsmStack.id
            }).get('actual_ul_id')

            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
コード例 #4
0
 def _update_thread_id(self, chat, thread_id):
     c_chat_context().update_one(
         {"chat_id": chat.id},
         {"$set": {
             "thread_id": thread_id,
         }},
         upsert=True
     )
コード例 #5
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
コード例 #6
0
 def update_activity(self, chat_id: int, thread_id: int) -> None:
     c_chat_context().update_one(
         {"chat_id": chat_id},
         {"$set": {
             "thread_id": thread_id,
             f"activity.{thread_id}": timezone.now(),
             "need_faqs": False
         }},
         upsert=True
     )
コード例 #7
0
 def next_edge(self, edge, fsmStack, request, useCurrent=False, **kwargs):
     fsm = edge.fromNode.fsm
     if (fsmStack.next_point.content.unitLesson.get_errors()
             or fsmStack.next_point.content.lesson.add_unit_aborts
             and fsmStack.next_point.content.unitLesson.unit.get_aborts()):
         c_chat_context().update_one({"chat_id": fsmStack.id},
                                     {"$set": {
                                         "need_faqs": True
                                     }},
                                     upsert=True)
         return fsm.get_node('ERRORS')
     else:
         return fsm.get_node('FAQ')
コード例 #8
0
ファイル: utils.py プロジェクト: cjlee112/socraticqs2
def update_activity(chat_id: int) -> None:
    """
    Update chat context for a currently active thread.
    """
    context = c_chat_context().find_one({"chat_id": chat_id})
    if context and context.get('thread_id'):
        thread_id = context.get('thread_id')
        c_chat_context().update_one(
            {"chat_id": chat_id},
            {"$set": {
                f"activity.{thread_id}": timezone.now()
            }},
        )
コード例 #9
0
ファイル: api.py プロジェクト: cjlee112/socraticqs2
    def get(self, request, pk=None):
        chat_id = self.request.GET.get('chat_id')
        try:
            chat = self.validate_and_get_chat(chat_id)
        except ValidationError as e:
            return Response({'errors': str(e)})
        self.check_object_permissions(self.request, chat)

        if chat.state and chat.state.fsmNode.fsm.name == 'updates':
            return Response({
                'error':
                'You are in the middle of the another updates review process'
            })

        unitlesson = get_object_or_404(UnitLesson, pk=pk)

        if chat.state:
            saved_actual_ul = c_chat_context().find_one({
                "chat_id": chat.id
            }).get('actual_ul_id')
            thread = c_chat_context().find_one({
                "chat_id": chat.id
            }).get('thread_id')
            chat.state.set_data_attr('saved_next_point', chat.next_point.id)
            chat.state.set_data_attr('saved_actual_ul', saved_actual_ul)
            chat.state.set_data_attr('thread', thread)
            chat.state.save_json_data()

        divider = ChatDivider(text=unitlesson.lesson.title,
                              unitlesson=unitlesson)

        divider.save()
        m = Message.objects.get_or_create(contenttype='chatdivider',
                                          content_id=divider.id,
                                          input_type='custom',
                                          type='breakpoint',
                                          thread_id=pk,
                                          chat=chat,
                                          owner=chat.user,
                                          kind='message',
                                          is_additional=True)[0]
        chat.next_point = self.next_handler.next_point(current=unitlesson,
                                                       chat=chat,
                                                       message=m,
                                                       request=request,
                                                       updates=True)
        chat.save()
        serializer = MessageSerializer(m)
        return Response(serializer.data)
コード例 #10
0
ファイル: additional.py プロジェクト: cjlee112/socraticqs2
def next_additional_lesson(self,
                           edge,
                           fsmStack,
                           request,
                           useCurrent=False,
                           **kwargs):
    """
    Edge method that moves us to right state for next lesson (or END).
    """
    fsm = edge.fromNode.fsm
    _status = fsmStack.next_point.student_error.status
    if _status == NEED_HELP_STATUS:
        additionals = Message.objects.filter(is_additional=True,
                                             chat=fsmStack,
                                             timestamp__isnull=True)
    elif _status in [NEED_REVIEW_STATUS, DONE_STATUS]:
        if _status == DONE_STATUS:
            c_chat_context().update_one({"chat_id": fsmStack.id},
                                        {"$set": {
                                            "need_faqs": False
                                        }})
        Message.objects.filter(student_error=fsmStack.next_point.student_error,
                               is_additional=True,
                               chat=fsmStack,
                               timestamp__isnull=True).delete()
        additionals = Message.objects.filter(is_additional=True,
                                             chat=fsmStack,
                                             timestamp__isnull=True)
    if additionals:
        next_message = additionals.order_by('student_error').first()
        fsmStack.state.unitLesson = next_message.content
        if next_message.student_error != fsmStack.next_point.student_error:
            return fsm.get_node(
                'NEED_HELP_MESSAGE'
            ) if _status == NEED_HELP_STATUS else fsm.get_node('STUDENTERROR')
        if fsmStack.state.unitLesson.lesson.kind in ('orct', 'choices'):
            return fsm.get_node('ORCT_LETS_START_MESSAGE')
    else:
        return fsm.get_node('NEED_HELP_MESSAGE'
                            ) if _status == NEED_HELP_STATUS else fsm.get_node(
                                'END')
    return edge.toNode
コード例 #11
0
def next_lesson_after_errors(self,
                             edge,
                             fsmStack,
                             request,
                             useCurrent=False,
                             **kwargs):
    """
    Edge method that moves us to right state for next lesson (or END).
    """
    fsm = edge.fromNode.fsm
    if c_chat_context().find_one({"chat_id": fsmStack.id}).get('need_faqs'):
        return fsm.get_node('FAQ')

    return edge.toNode
コード例 #12
0
 def get_message(self, chat, next_lesson, is_additional, *args, **kwargs) -> Message:
     data = chat.state.load_json_data()
     if any(('em_resolutions' in data,
             'faq_answers' in data,
             'new_ems' in data,
             'new_faqs' in data)):
         text = 'There are new updates for a Thread you asked for a help.'
         c_chat_context().update_one(
             {"chat_id": chat.id},
             {"$set": {"actual_ul_id": chat.state.unitLesson.id}}
         )
     else:
         text = 'I can\'t find updates for you.'
     _data = {
         'chat': chat,
         'text': text,
         'owner': chat.user,
         'input_type': 'custom',
         'kind': 'message',
         'is_additional': is_additional
     }
     message = Message(**_data)
     message.save()
     return message
コード例 #13
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
コード例 #14
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')

        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
        return fsm.get_node(
            'SHOW_FAQ_BY_ONE') if show_another_faq else edge.toNode
コード例 #15
0
    def collect_updates(self, node, fsmStack, request, **kwargs):
        # TODO add unittests
        chat = kwargs.get('chat')
        unit_lesson = kwargs.get('unitlesson')
        response = chat.message_set.filter(
            lesson_to_answer_id=unit_lesson.id,
            kind='response',
            contenttype='response',
            content_id__isnull=False).first().content
        affected_ems = [i.errorModel for i in response.studenterror_set.all()]

        context = c_chat_context().find_one({"chat_id": chat.id})
        last_access_time = context.get('activity', {}).get(f"{unit_lesson.id}") if context else None
        tz_aware_datetime = (
            last_access_time.replace(tzinfo=tz.tzutc()) if last_access_time else
            chat.last_modify_timestamp.replace(tzinfo=tz.tzutc()))

        # Collect EMs resolutions. Don't filter by user
        em_resolutions = unit_lesson.em_resolutions(tz_aware_datetime, affected_ems)
        fsmStack.state.set_data_attr('em_resolutions', em_resolutions) if em_resolutions else None

        thread_answer = unit_lesson.get_answers().first()
        interested_faqs = thread_answer.response_set.filter(
            Q(
                kind=Response.STUDENT_QUESTION, inquirycount__addedBy=request.user
            ) | Q(author=request.user, kind=Response.STUDENT_QUESTION))
        # Collect FAQ answers. Do filter by user as well as by applied previously FAQs
        faq_answers = unit_lesson.faq_answers(tz_aware_datetime, request.user, interested_faqs)
        fsmStack.state.set_data_attr('faq_answers', faq_answers) if faq_answers else None

        # Collect new EMs. Don't filter by user
        new_ems = unit_lesson.new_ems(tz_aware_datetime)
        fsmStack.state.set_data_attr('new_ems', new_ems) if new_ems else None

        # Collect ne FAQs. Do filter by user
        new_faqs = unit_lesson.new_faqs(tz_aware_datetime, request.user)
        fsmStack.state.set_data_attr('new_faqs', new_faqs) if new_faqs else None
コード例 #16
0
ファイル: models.py プロジェクト: cjlee112/socraticqs2
 def save(self, *args, **kwargs):
     context = c_chat_context().find_one({"chat_id": self.chat_id
                                          }) or dict()
     if not (self.pk or self.thread_id):
         self.thread_id = context.get('thread_id')
     super().save(*args, **kwargs)
コード例 #17
0
ファイル: services.py プロジェクト: cjlee112/socraticqs2
    def next_point(self,
                   current,
                   chat,
                   message,
                   request,
                   resources=False,
                   updates=False):
        next_point = None
        additionals = Message.objects.filter(is_additional=True,
                                             chat=chat,
                                             student_error__isnull=False,
                                             timestamp__isnull=True)
        helps = Message.objects.filter(is_additional=True,
                                       chat=chat,
                                       kind='abort',
                                       timestamp__isnull=True)
        if chat.state and chat.state.fsmNode.node_name_is_one_of('END'):
            if chat.state.fsmNode.fsm.fsm_name_is_one_of('faq'):
                self.pop_state(chat)
                edge = chat.state.fsmNode.outgoing.get(name='next')
                chat.state.fsmNode = edge.transition(chat, request)
                chat.state.save()
                saved_actual_ul = (chat.state.get_data_attr('saved_actual_ul')
                                   if 'saved_actual_ul'
                                   in chat.state.load_json_data() else None)
                c_chat_context().update_one(
                    {"chat_id": chat.id},
                    {"$set": {
                        "actual_ul_id": saved_actual_ul
                    }}) if saved_actual_ul else None
                next_point = chat.state.fsmNode.get_message(chat,
                                                            request,
                                                            current=current,
                                                            message=message)
            elif chat.state.fsmNode.fsm.fsm_name_is_one_of('updates'):
                self.pop_state(chat)
                if chat.state:
                    edge = chat.state.fsmNode.outgoing.get(name='next')
                    chat.state.fsmNode = edge.transition(chat, request)
                    chat.state.save()
                    next_point = chat.state.fsmNode.get_message(
                        chat, request, current=current, message=message)
            else:
                self.pop_state(chat)

        if chat.state and chat.state.fsmNode.node_name_is_one_of('FAQ'):
            chat_context = c_chat_context().find_one({'chat_id': chat.id})
            self.push_state(
                chat, request, 'faq', {
                    'unitlesson':
                    UnitLesson.objects.filter(
                        id=chat_context.get('actual_ul_id')).first(),
                    'chat':
                    chat
                })
            next_point = chat.state.fsmNode.get_message(chat,
                                                        request,
                                                        current=current,
                                                        message=message)
        if chat.state and chat.state.fsmNode.node_name_is_one_of(
                'FAQ_UPDATES'):
            saved_actual_ul = c_chat_context().find_one({
                "chat_id": chat.id
            }).get('actual_ul_id')
            chat.state.set_data_attr('saved_actual_ul', saved_actual_ul)
            chat.state.save_json_data()
            thread_answer = chat.state.unitLesson.get_answers().first()
            self.push_state(
                chat, request, 'faq', {
                    'unitlesson':
                    thread_answer,
                    'chat':
                    chat,
                    'updates':
                    True,
                    'new_faqs':
                    (chat.state.get_data_attr('new_faqs')
                     if 'new_faqs' in chat.state.load_json_data() else None)
                })
            c_chat_context().update_one(
                {"chat_id": chat.id},
                {"$set": {
                    "actual_ul_id": thread_answer.id
                }})
            next_point = chat.state.fsmNode.get_message(chat,
                                                        request,
                                                        current=current,
                                                        message=message)
        elif helps and not chat.state.fsmNode.fsm.fsm_name_is_one_of('help'):
            unitlesson = helps.first().content
            self.push_state(chat, request, 'help', {'unitlesson': unitlesson})
            next_point = chat.state.fsmNode.get_message(chat,
                                                        request,
                                                        current=current,
                                                        message=message)
        elif additionals and not chat.state.fsmNode.fsm.fsm_name_is_one_of(
                'additional'):
            unitlesson = additionals.order_by('student_error').first().content
            self.push_state(chat, request, 'additional',
                            {'unitlesson': unitlesson})
            next_point = chat.state.fsmNode.get_message(chat,
                                                        request,
                                                        current=current,
                                                        message=message)
        elif resources:
            self.push_state(chat, request, 'resource', {
                'unitlesson': current,
                'chat': chat
            })
            next_point = chat.state.fsmNode.get_message(chat, request)
        elif updates:
            self.push_state(chat, request, 'updates', {
                'unitlesson': current,
                'chat': chat
            })
            next_point = self.next_point(current=current,
                                         chat=chat,
                                         message=message,
                                         request=request)
        elif chat.state and chat.state.fsmNode.node_name_is_one_of(
                'VIEWUPDATES') and 'next_update' in chat.state.load_json_data(
                ) and chat.state.get_data_attr(
                    'next_update') and chat.state.get_data_attr(
                        'next_update').get('enabled'):
            unit_lesson_id = chat.state.get_data_attr('next_update').get(
                'thread_id')
            chat.state.set_data_attr('next_update', None)
            chat.state.save_json_data()
            self.push_state(
                chat, request, 'updates', {
                    'unitlesson':
                    UnitLesson.objects.filter(id=unit_lesson_id).first(),
                    'chat':
                    chat
                })
            next_point = self.next_point(current=current,
                                         chat=chat,
                                         message=message,
                                         request=request)
        elif chat.state:
            if not next_point:
                if not chat.state.fsmNode.node_name_is_one_of('END'):
                    edge = chat.state.fsmNode.outgoing.get(name='next')
                    chat.state.fsmNode = edge.transition(chat, request)
                    chat.state.save()
                if not (chat.state.fsmNode.node_name_is_one_of(
                        'FAQ', 'VIEWUPDATES', 'UPDATES')
                        or is_last_main_transition_wo_updates(chat.state)
                        or is_update_transition_wo_updates_w_last_main(
                            chat.state, chat) or is_end_update_node(chat.state)
                        or
                        (waffle.switch_is_active('compound_faq_answer') and
                         chat.state.fsmNode.node_name_is_one_of('SHOW_FAQ'))):
                    next_point = chat.state.fsmNode.get_message(
                        chat, request, current=current, message=message)
                else:
                    next_point = self.next_point(current=current,
                                                 chat=chat,
                                                 message=message,
                                                 request=request)
        else:
            return None

        if not message.timestamp:
            message.timestamp = timezone.now()
            message.save()

        # TODO: move to external function
        group = True
        while group:
            if self.group_filter(message, next_point):
                if next_point.input_type in [
                        'text', 'options'
                ] and next_point.sub_kind != 'faq':
                    break
                next_point = self.next_point(current=next_point.content,
                                             chat=chat,
                                             message=next_point,
                                             request=request)
            else:
                group = False
        return next_point
コード例 #18
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