Esempio n. 1
0
    def test_revalidate_demo_user(self):
        validator = MagicMock()
        validation_payload = CodeValidationPayload(valid=True, is_demo=True)
        validator.validate_code = MagicMock(return_value=validation_payload)
        self.assertFalse(self.dialog_state.user_profile.validated)
        command = ProcessSMSMessage(self.phone_number, "hey", registration_validator=validator)

        batch = self._process_command(command)
        self._assert_event_types(batch, DialogEventType.USER_VALIDATED)

        command = StartDrill(self.phone_number, self.drill.slug, self.drill.dict(), uuid.uuid4())
        self._process_command(command)

        validation_payload = CodeValidationPayload(
            valid=True,
            account_info={
                "employer_id": 1,
                "unit_id": 1,
                "employer_name": "employer_name",
                "unit_name": "unit_name",
            },
        )
        validator.validate_code = MagicMock(return_value=validation_payload)
        command = ProcessSMSMessage(self.phone_number, "hey", registration_validator=validator)

        batch = self._process_command(command)
        self._assert_event_types(batch, DialogEventType.USER_VALIDATED)
Esempio n. 2
0
    def test_start_drill_opted_out(self):
        self.dialog_state.user_profile.validated = True
        self.dialog_state.user_profile.opted_out = True
        command = StartDrill(self.phone_number, self.drill.slug, self.drill.dict(), uuid.uuid4())

        batch = self._process_command(command)
        self.assertEqual(0, len(batch.events))
Esempio n. 3
0
    def test_start_drill_not_validated(self, get_drill_mock):
        self.dialog_state.user_profile.validated = False
        self.dialog_state.user_profile.opted_out = False
        command = StartDrill(self.phone_number, self.drill.slug)

        batch = self._process_command(command)
        self.assertEqual(0, len(batch.events))
Esempio n. 4
0
    def test_start_drill_not_validated(self):
        self.dialog_state.user_profile.validated = False
        self.dialog_state.user_profile.opted_out = False
        command = StartDrill(self.phone_number, self.drill.slug, self.drill.dict(), uuid.uuid4())

        batch = self._process_command(command)
        self._assert_event_types(batch, DialogEventType.DRILL_STARTED)
Esempio n. 5
0
def handle_inbound_commands(commands: List[InboundCommand]):

    for command in commands:
        if command.command_type == InboundCommandType.INBOUND_SMS:
            process_command(
                ProcessSMSMessage(phone_number=command.payload["From"],
                                  content=command.payload["Body"]),
                command.sequence_number,
            )
        elif command.command_type == InboundCommandType.START_DRILL:
            process_command(
                StartDrill(
                    phone_number=command.payload["phone_number"],
                    drill_slug=command.payload["drill_slug"],
                ),
                command.sequence_number,
            )
        elif command.command_type == InboundCommandType.TRIGGER_REMINDER:
            process_command(
                TriggerReminder(
                    phone_number=command.payload["phone_number"],
                    drill_instance_id=uuid.UUID(
                        command.payload["drill_instance_id"]),
                    prompt_slug=command.payload["prompt_slug"],
                ),
                command.sequence_number,
            )
        else:
            raise RuntimeError(f"Unknown command: {command.command_type}")

    return {"statusCode": 200}
Esempio n. 6
0
    def test_advance_demo_user(self, get_drill_mock):
        validator = MagicMock()
        validation_payload = CodeValidationPayload(valid=True, is_demo=True)
        validator.validate_code = MagicMock(return_value=validation_payload)
        self.assertFalse(self.dialog_state.user_profile.validated)
        command = ProcessSMSMessage(self.phone_number,
                                    "hey",
                                    registration_validator=validator)

        batch = self._process_command(command)
        self._assert_event_types(batch, DialogEventType.USER_VALIDATED)

        command = StartDrill(self.phone_number, self.drill.slug)
        self._process_command(command)

        # the user's next message isn't a validation code - so we just keep going
        validation_payload = CodeValidationPayload(valid=False)
        validator.validate_code = MagicMock(return_value=validation_payload)
        command = ProcessSMSMessage(self.phone_number,
                                    "hey",
                                    registration_validator=validator)

        batch = self._process_command(command)
        self._assert_event_types(batch, DialogEventType.COMPLETED_PROMPT,
                                 DialogEventType.ADVANCED_TO_NEXT_PROMPT)
Esempio n. 7
0
    def test_start_drill(self):
        self.dialog_state.user_profile.validated = True
        command = StartDrill(self.phone_number, self.drill.slug, self.drill.dict(), uuid.uuid4())

        batch = self._process_command(command)

        self._assert_event_types(batch, DialogEventType.DRILL_STARTED)
        event: DrillStarted = batch.events[0]  # type: ignore
        self.assertEqual(self.drill.dict(), event.drill.dict())
        self.assertEqual(self.drill.first_prompt().slug, event.first_prompt.slug)
        self.assertIsNotNone(event.drill_instance_id)
Esempio n. 8
0
def handle_inbound_commands(commands: List[InboundCommand]) -> dict:
    for command in commands:
        if command.command_type is InboundCommandType.INBOUND_SMS:
            process_command(
                ProcessSMSMessage(
                    phone_number=command.payload["From"],
                    content=command.payload["Body"],
                ),
                command.sequence_number,
            )
        elif command.command_type is InboundCommandType.START_DRILL:
            process_command(
                StartDrill(
                    phone_number=command.payload["phone_number"],
                    drill_slug=command.payload["drill_slug"],
                    drill_body=command.payload["drill_body"],
                    drill_instance_id=command.payload["drill_instance_id"],
                ),
                command.sequence_number,
            )
        elif command.command_type is InboundCommandType.SEND_AD_HOC_MESSAGE:
            process_command(
                SendAdHocMessage(
                    phone_number=command.payload["phone_number"],
                    message=command.payload["message"],
                    media_url=command.payload["media_url"],
                ),
                command.sequence_number,
            )
        elif command.command_type is InboundCommandType.UPDATE_USER:
            process_command(
                UpdateUser(
                    phone_number=command.payload["phone_number"],
                    user_profile_data=command.payload["user_profile_data"],
                    purge_drill_state=command.payload.get("purge_drill_state")
                    or False,
                ),
                command.sequence_number,
            )
        else:
            raise RuntimeError(f"Unknown command: {command.command_type}")

    return {"statusCode": 200}
Esempio n. 9
0
    def test_revalidate_demo_user(self, get_drill_mock):
        validator = MagicMock()
        validation_payload = CodeValidationPayload(valid=True, is_demo=True)
        validator.validate_code = MagicMock(return_value=validation_payload)
        self.assertFalse(self.dialog_state.user_profile.validated)
        command = ProcessSMSMessage(self.phone_number,
                                    "hey",
                                    registration_validator=validator)

        batch = self._process_command(command)
        self._assert_event_types(batch, DialogEventType.USER_VALIDATED)

        command = StartDrill(self.phone_number, self.drill.slug)
        self._process_command(command)

        validation_payload = CodeValidationPayload(
            valid=True, account_info={"company": "WeWork"})
        validator.validate_code = MagicMock(return_value=validation_payload)
        command = ProcessSMSMessage(self.phone_number,
                                    "hey",
                                    registration_validator=validator)

        batch = self._process_command(command)
        self._assert_event_types(batch, DialogEventType.USER_VALIDATED)
Esempio n. 10
0
    def persist_dialog_state(  # noqa: C901
            self, event_batch: DialogEventBatch, dialog_state: DialogState):
        self.repo[dialog_state.phone_number] = DialogStateSchema().dumps(
            dialog_state)

        drill_to_start = None
        for event in event_batch.events:
            if isinstance(event, AdvancedToNextPrompt):
                fake_sms(
                    event.phone_number,
                    dialog_state.user_profile,
                    [
                        message.text for message in event.prompt.messages
                        if message.text is not None
                    ],
                    with_initial_pause=True,
                )
            elif isinstance(event, FailedPrompt):
                if not event.abandoned:
                    fake_sms(event.phone_number, dialog_state.user_profile,
                             [TRY_AGAIN])
                else:
                    fake_sms(
                        event.phone_number,
                        dialog_state.user_profile,
                        ["{{corrected_answer}}"],
                        correct_answer=localize(
                            event.prompt.correct_response,  # type: ignore
                            dialog_state.user_profile.language,
                        ),
                    )
            elif isinstance(event, CompletedPrompt):
                if event.prompt.correct_response is not None:
                    fake_sms(event.phone_number, dialog_state.user_profile,
                             ["{{match_correct_answer}}"])
            elif isinstance(event, UserValidated):
                drill_to_start = dialog_state.user_profile.account_info["code"]
            elif isinstance(event, OptedOut):
                print("(You've been opted out.)")
                if event.drill_instance_id:
                    del STARTED_DRILLS[event.drill_instance_id]
            elif isinstance(event, NextDrillRequested):
                unstarted_drills = [
                    code for code in DRILLS.keys()
                    if DRILLS[code].slug not in STARTED_DRILLS.values()
                ]
                if unstarted_drills:
                    drill_to_start = unstarted_drills[0]
                else:
                    print("(You're all out of drills.)")
            elif isinstance(event, UserValidationFailed):
                print(f"(try {', '.join(DRILLS.keys())})")
            elif isinstance(event, DrillStarted):
                STARTED_DRILLS[
                    event.drill_instance_id] = dialog_state.current_drill.slug
                fake_sms(
                    event.phone_number,
                    dialog_state.user_profile,
                    [
                        message.text for message in event.first_prompt.messages
                        if message.text is not None
                    ],
                )
            elif isinstance(event, DrillCompleted):
                print(
                    "(The drill is complete. Type 'more' for another drill or crtl-D to exit.)"
                )
        if drill_to_start:
            global SEQ
            SEQ += 1
            process_command(StartDrill(PHONE_NUMBER,
                                       DRILLS[drill_to_start].slug),
                            str(SEQ),
                            repo=self)
Esempio n. 11
0
 def persist_dialog_state(  # noqa: C901
         self, event_batch: DialogEventBatch,
         dialog_state: DialogState) -> None:
     self.repo[dialog_state.phone_number] = dialog_state.json()
     assert dialog_state.user_profile.language
     drill_to_start = None
     for event in event_batch.events:
         if isinstance(event, AdvancedToNextPrompt):
             fake_sms(
                 event.phone_number,
                 dialog_state.user_profile,
                 [
                     message.text for message in event.prompt.messages
                     if message.text is not None
                 ],
                 with_initial_pause=True,
             )
         elif isinstance(event, FailedPrompt):
             if not event.abandoned:
                 fake_sms(
                     event.phone_number,
                     dialog_state.user_profile,
                     [
                         translate(
                             dialog_state.user_profile.language,
                             SupportedTranslation.INCORRECT_ANSWER,
                         )
                     ],
                 )
             else:
                 fake_sms(
                     event.phone_number,
                     dialog_state.user_profile,
                     [
                         translate(
                             dialog_state.user_profile.language,
                             SupportedTranslation.CORRECTED_ANSWER,
                             correct_answer=event.prompt.correct_response,
                         )
                     ],
                 )
         elif isinstance(event, CompletedPrompt):
             if event.prompt.correct_response is not None:
                 fake_sms(
                     event.phone_number,
                     dialog_state.user_profile,
                     [
                         correct_answer_response(
                             dialog_state.user_profile.language, )
                     ],
                 )
         elif isinstance(event, UserValidated):
             assert dialog_state.user_profile.account_info
             drill_to_start = dialog_state.user_profile.account_info.employer_name
         elif isinstance(event, OptedOut):
             print("(You've been opted out.)")
             if event.drill_instance_id:
                 del STARTED_DRILLS[event.drill_instance_id]
         elif isinstance(event, NextDrillRequested):
             drill_to_start = self.get_next_unstarted_drill()
             if not drill_to_start:
                 print("(You're all out of drills.)")
         elif isinstance(event, UserValidationFailed):
             print(f"(try {', '.join(DRILLS.keys())})")
         elif isinstance(event, DrillStarted):
             assert dialog_state.current_drill
             STARTED_DRILLS[
                 event.drill_instance_id] = dialog_state.current_drill.slug
             fake_sms(
                 event.phone_number,
                 dialog_state.user_profile,
                 [
                     message.text for message in event.first_prompt.messages
                     if message.text is not None
                 ],
             )
         elif isinstance(event, DrillCompleted):
             print(
                 "(The drill is complete. Type 'more' for another drill or crtl-D to exit.)"
             )
         elif isinstance(event, SchedulingDrillRequested):
             print("Scheduling drill requested")
     if drill_to_start:
         global SEQ
         SEQ += 1
         drill = DRILLS[drill_to_start]
         process_command(
             StartDrill(PHONE_NUMBER, drill.slug, drill.dict(),
                        uuid.uuid4()),
             str(SEQ),
             repo=self,
         )