Example #1
0
    def get_message(self, chat, next_lesson, is_additional, *args, **kwargs) -> Message:
        # TODO implement Stack-like interface
        answers_stack = chat.state.get_data_attr('answers_stack')
        if answers_stack:
            answer = answers_stack.pop()
            chat.state.set_data_attr('answers_stack', answers_stack)
            chat.state.save_json_data()

        if waffle.switch_is_active('compound_faq_answer'):
            text1 = md2html(f'Here\'s my answer to your question \"{answer.get("faq_title")}\"')
            text2 = md2html(answer.get('text'))
            text = text1 + text2
        else:
            text = answer.get('text')

        _data = {
            'chat': chat,
            'text': mark_safe(text),
            'owner': chat.user,
            'input_type': 'options',
            'kind': 'button',
            'is_additional': is_additional
        }
        message = Message(**_data)
        message.save()
        return message
Example #2
0
 def get_message(self, chat, next_lesson, is_additional, *args,
                 **kwargs) -> Message:
     threads = chat.enroll_code.courseUnit.unit.unitlesson_set.filter(
         order__isnull=False).order_by('order')
     has_updates = {'enabled': False, 'thread_id': None}
     for thread in threads:
         # TODO: move to a dedicated util
         response_msg = chat.message_set.filter(
             lesson_to_answer_id=thread.id,
             kind='response',
             contenttype='response',
             content_id__isnull=False).last()
         if not response_msg:
             continue
         response = response_msg.content
         is_need_help = response.status in (None, NEED_HELP_STATUS,
                                            NEED_REVIEW_STATUS)
         if is_need_help and thread.updates_count(chat) > 0:
             has_updates.update({'thread_id': thread.id})
             chat.state.set_data_attr('next_update', has_updates)
             chat.state.save_json_data()
             break
     if has_updates['thread_id']:
         unitStatus = chat.state.get_data_attr('unitStatus')
         next_lesson = unitStatus.get_next_lesson()
         if not next_lesson:
             text = f'You have completed this thread. I have posted new messages to help you in the thread "{thread.lesson.title}". Would you like to view these updates now?'
         else:
             text1 = f'You have completed this thread. I have posted new messages to help you in the thread "{thread.lesson.title}". Would you like to view these updates now?'
             text2 = "*If you don't want to view them now, I'll ask you again once you have completed your next thread.*"
             text = md2html(text1) + md2html(text2)
     else:
         text = md2html('Now you can move to the next lesson')
     _data = {
         'chat': chat,
         'text': mark_safe(text),
         'owner': chat.user,
         'input_type': 'options',
         'kind': 'button',
         'sub_kind': 'transition',
         'is_additional': is_additional
     }
     message = Message(**_data)
     message.save()
     return message
Example #3
0
    def get_sidebar_html(self):
        '''
        :return: Return stripped html text (ho html tags)
        '''
        # html = self.get_html()
        # return html.split("\n")[0]
        p = re.compile(r'<.*?>')

        raw_html = self.text or self.content.text

        raw_html = raw_html.split("\n")[0]
        html = mark_safe(md2html(raw_html))
        stripped_text = p.sub('', html)
        return stripped_text
Example #4
0
    def get_sidebar_html(self):
        '''
        :return: Return stripped html text (ho html tags)
        '''
        # html = self.get_html()
        # return html.split("\n")[0]
        p = re.compile(r'<.*?>')

        raw_html = self.text or self.content.text

        raw_html = raw_html.split("\n")[0]
        html = mark_safe(md2html(raw_html))
        stripped_text = p.sub('', html)
        return stripped_text
Example #5
0
    def get_html(self):
        html = None
        if self.content_id:
            if self.contenttype == 'chatdivider':
                html = self.content.text
            elif self.contenttype == 'response':
                if self.input_type == 'text':
                    html = self.content.text
                else:
                    html = EVAL_OPTIONS[self.content.selfeval]
            elif self.contenttype == 'unitlesson':
                if self.content.kind == UnitLesson.MISUNDERSTANDS:
                    html = mark_safe(md2html('**%s** \n %s' % (self.content.lesson.title, self.content.lesson.text)))
                elif self.input_type == 'options' and self.text:
                    html = STATUS_OPTIONS[self.text]
                else:
                    html = mark_safe(md2html(self.content.lesson.text))
            elif self.contenttype == 'uniterror':
                html = self.get_errors()

        else:
            html = self.text
        return html
Example #6
0
    def test_get_resources_message_by_id(self):
        """
        Test get resources message by id from /resources response.

        Checks that returned content fits resources API documentation.
        """
        enroll_code = EnrollUnitCode.get_code(self.courseunit)
        self.client.login(username='******', password='******')
        chat_id = self.client.get(
            reverse('chat:chat_enroll', args=(enroll_code,)), follow=True
        ).context['chat_id']
        response = self.client.get(reverse('chat:resources-list'), {'chat_id': chat_id}, follow=True)
        json_content = json.loads(response.content)
        resource_response = self.client.get(
            reverse('chat:resources-detail', args=(json_content['breakpoints'][0]['ul'],)),
            {'chat_id': chat_id}
        )
        self.assertEquals(resource_response.status_code, 200)
        resource_response = self.client.get(
            reverse('chat:resources-detail', args=(json_content['breakpoints'][1]['ul'],)),
            {'chat_id': chat_id}
        )
        self.assertEquals(resource_response.status_code, 200)
        json_content = json.loads(resource_response.content)
        self.assertIsInstance(json_content['input'], dict)
        self.assertIsInstance(json_content['addMessages'], list)
        self.assertEquals(len(json_content['addMessages']), 3)

        self.assertIn('nextMessagesUrl', json_content)
        self.assertIsNone(json_content['nextMessagesUrl'])
        self.assertIn('id', json_content)

        self.assertEquals(json_content['addMessages'][0]['name'], self.resource_unitlesson.addedBy.username)
        self.assertEquals(json_content['addMessages'][0]['type'], 'breakpoint')
        self.assertEquals(json_content['addMessages'][0]['html'], self.resource_unitlesson.lesson.title)
        self.assertEquals(json_content['addMessages'][1]['type'], 'message')
        self.assertEquals(
            json_content['addMessages'][1]['html'], md2html(self.resource_unitlesson.lesson.text)
        )
        self.assertEquals(json_content['addMessages'][2]['type'], 'message')
        self.assertEquals(json_content['addMessages'][2]['html'], END.help)

        self.assertIn('url', json_content['input'])
        self.assertIn('includeSelectedValuesFromMessages', json_content['input'])
        self.assertIn('html', json_content['input'])
        self.assertIn('type', json_content['input'])
        self.assertIn('options', json_content['input'])
Example #7
0
    def get_message(self, chat, next_lesson, is_additional, *args, **kwargs) -> Message:
        # TODO implement Stack-like interface
        resolutions_stack = chat.state.get_data_attr('resolutions_stack')
        if resolutions_stack:
            resolution = resolutions_stack.pop()
            chat.state.set_data_attr('resolutions_stack', resolutions_stack)
            chat.state.save_json_data()

        _data = {
            'chat': chat,
            'text': mark_safe(md2html(resolution.get('text'))),
            'owner': chat.user,
            'input_type': 'options',
            'kind': 'button',
            'is_additional': is_additional
        }
        message = Message(**_data)
        message.save()
        return message
Example #8
0
    def get_message(self, chat, next_lesson, is_additional, *args, **kwargs) -> Message:
        em_resolutions = chat.state.get_data_attr('em_resolutions')
        if em_resolutions:
            em = em_resolutions.pop()
            chat.state.set_data_attr('resolutions_stack', em['resolutions'])
            chat.state.set_data_attr('em_resolutions', em_resolutions)
            chat.state.save_json_data()

        _data = {
            'chat': chat,
            'text': mark_safe(md2html(f'**{em.get("em_title")}** \n {em.get("em_text")}')),
            'owner': chat.user,
            'input_type': 'options',
            'kind': 'button',
            'is_additional': is_additional
        }
        message = Message(**_data)
        message.save()
        return message
Example #9
0
 def test_content(self):
     """
     Check that history content fits API documentation.
     """
     enroll_code = EnrollUnitCode.get_code(self.courseunit)
     self.client.login(username='******', password='******')
     chat_id = self.client.get(
         reverse('chat:chat_enroll', args=(enroll_code,)), follow=True
     ).context['chat_id']
     response = self.client.get(reverse('chat:history'), {'chat_id': chat_id}, follow=True)
     json_content = json.loads(response.content)
     self.assertIsInstance(json_content['input'], dict)
     self.assertIsInstance(json_content['addMessages'], list)
     self.assertEquals(len(json_content['addMessages']), 4)
     self.assertEquals(json_content['addMessages'][0]['name'], self.unitlesson.addedBy.username)
     self.assertEquals(json_content['addMessages'][0]['html'], self.unitlesson.lesson.title)
     self.assertEquals(json_content['addMessages'][1]['type'], 'message')
     self.assertEquals(
         json_content['addMessages'][1]['html'], md2html(self.unitlesson.lesson.text)
     )
     self.assertEquals(json_content['addMessages'][2]['type'], 'message')
Example #10
0
    def get_message(self, chat, next_lesson, is_additional, *args, **kwargs) -> Message:
        faq_answers = chat.state.get_data_attr('faq_answers')
        if faq_answers:
            faq = faq_answers.pop()
            answers = faq['answers']

            for answer in answers:
                answer['faq_title'] = (faq.get('faq_title', ''))

            chat.state.set_data_attr('answers_stack', answers)
            chat.state.set_data_attr('faq_answers', faq_answers)
            chat.state.save_json_data()

        _data = {
            'chat': chat,
            'text': mark_safe(md2html(f'**{faq.get("faq_title")}** \n {faq.get("faq_text")}')),
            'owner': chat.user,
            'input_type': 'options',
            'kind': 'button',
            'is_additional': is_additional
        }
        message = Message(**_data)
        message.save()
        return message
Example #11
0
    def get_html(self):
        if self.kind in ('get_faq_answer', ) or (self.kind == 'add_faq' and
                                                 self.input_type == 'options'):
            return self.text.capitalize() if self.text else 'No'
        if self.kind in ('ask_faq_understanding', ):
            return STATUS_OPTIONS.get(self.text, 'Still confused, need help')
        if self.kind == 'abort':
            return self.get_aborts()
        html = self.text
        if self.content_id:
            if self.contenttype == 'chatdivider':
                html = self.content.text
            elif self.contenttype == 'response' and self.sub_kind == 'faq':
                html = '<b>' + self.content.title + '</b><br>' + self.content.text \
                    if self.content.title else self.content.text
            elif self.contenttype == 'response':
                sub_kind = self.content.sub_kind
                if sub_kind and sub_kind == Lesson.MULTIPLE_CHOICES and not self.content.confidence:
                    # no confidence and no selfeval
                    if sub_kind == Lesson.MULTIPLE_CHOICES:
                        html = self.render_my_choices()
                        return html

                if sub_kind and self.content.confidence and self.content.selfeval and not self.text:
                    # if look history - we already have confidence and selfeval so just return msg text
                    if self.input_type == 'options':
                        html = self.render_my_choices()
                        return html
                if self.input_type == 'text':
                    if self.sub_kind == 'add_faq':
                        html = self.text
                    else:
                        html = mark_safe(md2html(self.content.text))
                        if self.content.attachment:
                            # display svg inline
                            html += mark_safe(self.content.get_html())
                else:
                    CONF_CHOICES = dict(Response.CONF_CHOICES)
                    is_chat_fsm = (
                        self.chat and self.chat.state
                        and self.chat.state.fsmNode.fsm.fsm_name_is_one_of(
                            'chat')  # TODO: add livechat here
                    )
                    values = list(CONF_CHOICES.values()) + list(
                        EVAL_OPTIONS.values())
                    text_in_values = self.text in values
                    if is_chat_fsm and not self.text:
                        if self.content.selfeval:  # confidence is before selfeval
                            html = EVAL_OPTIONS.get(
                                self.content.selfeval,
                                'Self evaluation not completed yet')
                        elif self.content.confidence:
                            html = CONF_CHOICES.get(
                                self.content.confidence,
                                'Confidence not settled yet')
                    elif is_chat_fsm and self.text and not text_in_values:
                        html = EVAL_OPTIONS.get(
                            self.text,
                            dict(Response.CONF_CHOICES).get(
                                self.text, self.text))
            elif self.contenttype == 'unitlesson':
                # Get FAQ for the UnitLesson
                if self.kind == 'faqs':
                    html = self.get_faqs()
                elif self.content.kind == UnitLesson.MISUNDERSTANDS:
                    html = mark_safe(
                        md2html('**Re: %s** \n %s' %
                                (self.content.lesson.title,
                                 self.content.lesson.text)))
                elif self.input_type == 'options' and self.text:  # and not self.content.lesson.sub_kind:
                    html = STATUS_OPTIONS[self.text]
                elif self.content.lesson.sub_kind and self.content.lesson.sub_kind == Lesson.MULTIPLE_CHOICES:
                    # render unitlesson (question) - answer
                    if self.content.kind in ('part', 'resol'):
                        html = mark_safe(
                            md2html(
                                self.content.lesson.get_choices_wrap_text()))
                        html += self.get_choices()
                elif self.content.lesson.sub_kind == Lesson.CANVAS and self.content.lesson.kind == Lesson.ORCT_QUESTION:
                    # adds canvas to draw svg image
                    messages = self.chat.message_set.filter(id__gt=self.id)
                    lesson_kwargs = {}
                    try:
                        response = messages[0].content
                        lesson_kwargs['disabled'] = response.attachment.url
                    except (AttributeError, IndexError, ValueError):
                        pass
                    html = mark_safe(md2html(self.content.lesson.text))
                    html += self.content.lesson.get_html(**lesson_kwargs)
                elif (self.content.kind == 'answers'
                      and self.content.parent.sub_kind and
                      self.content.parent.sub_kind == Lesson.MULTIPLE_CHOICES):
                    if not self.response_to_check.selfeval:
                        correct = self.content.parent.lesson.get_correct_choices(
                        )
                        html = self.render_choices(correct, [])
                    elif self.response_to_check.selfeval and self.response_to_check.confidence:
                        correct = self.content.parent.lesson.get_correct_choices(
                        )
                        html = self.render_choices(correct, [])
                elif self.content.kind == 'answers' and self.content.parent.lesson.sub_kind == Lesson.NUMBERS:
                    # TODO add tests for this case
                    html = mark_safe(
                        md2html("Expected value {value}. \n\n{text}".format(
                            value=self.content.lesson.number_value,
                            text=self.content.lesson.text)))
                else:
                    if self.content.lesson.url:
                        raw_html = '`Read more <{0}>`_ \n\n{1}'.format(
                            self.content.lesson.url, self.content.lesson.text)
                    else:
                        raw_html = self.content.lesson.text
                    html = mark_safe(md2html(raw_html))

                    if (
                            self.content.lesson.sub_kind == Lesson.CANVAS or
                        (self.content.parent
                         and self.content.parent.sub_kind == Lesson.CANVAS)
                    ) and self.content.lesson.attachment and self.content.lesson.kind != Lesson.ORCT_QUESTION:
                        # append svg attachment to the message
                        html += mark_safe(self.content.lesson.get_html())

                if (self.kind != 'faqs' and self.content.lesson.attachment
                        and self.content.lesson.sub_kind != Lesson.CANVAS
                        and not (self.content.parent and
                                 self.content.parent.sub_kind == Lesson.CANVAS)
                    ) or (self.kind != 'faqs'
                          and self.content.lesson.kind == Lesson.ERROR_MODEL
                          and self.content.lesson.attachment):
                    html += '<img src="{}" alt=""/>'.format(
                        self.content.lesson.attachment.url)
            elif self.contenttype == 'uniterror':
                html = self.get_errors()
        if html is None:
            html = 'Answer please'

        return html
Example #12
0
    def get_message(self, chat, request, current=None, message=None):
        stack_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 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 = Message.objects.get_or_create(**_data)[0]
            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('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('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('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('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('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]
        if self.node_name_is_one_of('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()
        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'):
            message = Message.objects.get_or_create(
                            chat=chat,
                            owner=chat.user,
                            text=chat.state.fsmNode.title,
                            student_error=message.student_error,
                            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'):
            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('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 ('START', 'UNIT_NAME_TITLE', 'NOT_A_QUESTION') and self.fsm.fsm_name_is_one_of('chat_add_lesson'):
            text = "**{}** \n\n{}".format(self.title, getattr(self, 'help', '') or '')
            _data = dict(
                chat=chat,
                text=text,
                input_type='custom',
                kind='message',
                is_additional=is_additional,
                owner=chat.user,
            )
            message = Message.objects.create(**_data)

        if self.name in ('UNIT_QUESTION', 'UNIT_ANSWER') and self.fsm.fsm_name_is_one_of('chat_add_lesson'):
            text = "**{}** \n\n{}".format(self.title, getattr(self, 'help', '') or '')
            _data = dict(
                chat=chat,
                text=text,
                input_type='custom',
                kind='message',
                is_additional=is_additional,
                owner=chat.user,
            )
            if message and message.content_id:
                _data['content_id'] = message.content_id
                _data['contenttype'] = 'unitlesson'
            elif isinstance(current, UnitLesson):
                _data['content_id'] = current.id
                _data['contenttype'] = 'unitlesson'
            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',):
            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)

        # 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
Example #13
0
    def get_html(self):
        if self.kind == 'abort':
            return self.get_aborts()
        html = self.text
        if self.is_in_fsm_node('chat_add_lesson'):
            if self.contenttype == 'chatdivider':
                html = self.content.text
            else:
                return mark_safe(md2html(self.text or ''))
        if self.content_id:
            if self.contenttype == 'chatdivider':
                html = self.content.text
            elif self.contenttype == 'response':
                sub_kind = self.content.sub_kind
                if sub_kind and sub_kind == Lesson.MULTIPLE_CHOICES and not self.content.confidence:
                    # no confidence and no selfeval
                    if sub_kind == Lesson.MULTIPLE_CHOICES:
                        html = self.render_my_choices()
                        return html

                if sub_kind and self.content.confidence and self.content.selfeval and not self.text:
                    # if look history - we already have confidence and selfeval so just return msg text
                    if self.input_type == 'options':
                        html = self.render_my_choices()
                        return html

                if self.input_type == 'text':
                    html = mark_safe(md2html(self.content.text))
                    if self.content.attachment:
                        # display svg inline
                        html += mark_safe(self.content.get_html())
                else:
                    CONF_CHOICES = dict(Response.CONF_CHOICES)
                    is_chat_fsm = (
                        self.chat and
                        self.chat.state and
                        self.chat.state.fsmNode.fsm.fsm_name_is_one_of('chat')  # TODO: add livechat here
                    )
                    values = CONF_CHOICES.values() + EVAL_OPTIONS.values()
                    text_in_values = self.text in values
                    if is_chat_fsm and not self.text:
                        if self.content.selfeval:  # confidence is before selfeval
                            html = EVAL_OPTIONS.get(
                                self.content.selfeval, 'Self evaluation not completed yet'
                            )
                        elif self.content.confidence:
                            html = CONF_CHOICES.get(
                                self.content.confidence, 'Confidence not settled yet'
                            )
                    elif is_chat_fsm and self.text and not text_in_values:
                        html = EVAL_OPTIONS.get(
                            self.text,
                            dict(Response.CONF_CHOICES).get(
                                self.text,
                                self.text
                            )
                        )
            elif self.contenttype == 'unitlesson':
                if self.content.kind == UnitLesson.MISUNDERSTANDS:
                    html = mark_safe(
                        md2html(
                            '**Re: %s** \n %s' %
                            (self.content.lesson.title, self.content.lesson.text)
                        )
                    )
                elif self.input_type == 'options' and self.text:  # and not self.content.lesson.sub_kind:
                    html = STATUS_OPTIONS[self.text]
                elif self.content.lesson.sub_kind and self.content.lesson.sub_kind == Lesson.MULTIPLE_CHOICES:
                    # render unitlesson (question) - answer
                    if self.content.kind == 'part':
                        html = mark_safe(
                            md2html(
                                self.content.lesson.get_choices_wrap_text()
                            )
                        )
                        html += self.get_choices()
                elif self.content.lesson.sub_kind == Lesson.CANVAS and self.content.lesson.kind == Lesson.ORCT_QUESTION:
                    # adds canvas to draw svg image
                    messages = self.chat.message_set.filter(id__gt=self.id)
                    lesson_kwargs = {}
                    try:
                        response = messages[0].content
                        lesson_kwargs['disabled'] = response.attachment.url
                    except (AttributeError, IndexError, ValueError):
                        pass
                    html = mark_safe(md2html(self.content.lesson.text))
                    html += self.content.lesson.get_html(**lesson_kwargs)
                elif (self.content.kind == 'answers' and
                      self.content.parent.sub_kind and
                      self.content.parent.sub_kind == Lesson.MULTIPLE_CHOICES
                    ):
                    if not self.response_to_check.selfeval:
                        correct = self.content.parent.lesson.get_correct_choices()
                        html = self.render_choices(correct, [])
                    elif self.response_to_check.selfeval and self.response_to_check.confidence:
                        correct = self.content.parent.lesson.get_correct_choices()
                        html = self.render_choices(correct, [])
                elif self.content.kind == 'answers' and self.content.parent.lesson.sub_kind == Lesson.NUMBERS:
                    # TODO add tests for this case
                    html = mark_safe(
                        md2html(
                            u"Expected value {value}. \n\n{text}".format(
                                value=self.content.lesson.number_value,
                                text=self.content.lesson.text
                            )
                        )
                    )
                else:
                    if self.content.lesson.url:
                        raw_html = u'`Read more <{0}>`_ \n\n{1}'.format(
                            self.content.lesson.url,
                            self.content.lesson.text
                        )
                    else:
                        raw_html = self.content.lesson.text
                    html = mark_safe(md2html(raw_html))

                    if (self.content.lesson.sub_kind == Lesson.CANVAS
                            or (self.content.parent and self.content.parent.sub_kind == Lesson.CANVAS)
                        ) and self.content.lesson.attachment and self.content.lesson.kind != Lesson.ORCT_QUESTION:
                        # append svg attachment to the message
                        html += mark_safe(self.content.lesson.get_html())

                if (self.content.lesson.attachment and self.content.lesson.sub_kind != Lesson.CANVAS and
                    not (self.content.parent and self.content.parent.sub_kind == Lesson.CANVAS)):
                    html += u'<img src="{}" alt=""/>'.format(self.content.lesson.attachment.url)
            elif self.contenttype == 'uniterror':
                html = self.get_errors()
        if html is None:
            html = 'Answer please'

        return html
Example #14
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