Skip to content

Commit

Permalink
Merge pull request #665 from praekeltfoundation/location-survey
Browse files Browse the repository at this point in the history
Initial location survey commit
  • Loading branch information
erikh360 authored Aug 16, 2023
2 parents cab3c66 + ae9d931 commit 9cacb4e
Show file tree
Hide file tree
Showing 2 changed files with 323 additions and 0 deletions.
238 changes: 238 additions & 0 deletions yal/surveys/location.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,238 @@
from vaccine.base_application import BaseApplication
from vaccine.states import (
Choice,
EndState,
FreeText,
WhatsAppButtonState,
WhatsAppListState,
)
from vaccine.validators import nonempty_validator
from yal import rapidpro
from yal.utils import get_generic_error, normalise_phonenumber


class Application(BaseApplication):
START_STATE = "state_location_introduction"

async def state_location_introduction(self):
survey_status = self.user.metadata.get(
"ejaf_location_survey_status", "not_invited"
)
if survey_status in ("completed", "airtime_sent"):
return await self.go_to_state("state_location_already_completed")
elif survey_status != "pending":
return await self.go_to_state("state_location_not_invited")

choices = [
Choice("yes", self._("Yes, I agree")),
Choice("no", self._("No, I don't agree")),
Choice("question", self._("I have a question")),
]

question = self._(
"\n".join(
[
"*Fantastic! 👏🏾 🎉 And thank you 🙏🏽*",
"",
"*Before we start, here are a few important notes.* 📈",
"",
"This survey is just to understand who may be interested in "
"joining a focus group discussion in September and where would be "
"convenient for those users to meet. You do not have to be "
"interested in participating in focus groups to complete this "
"survey. If you indicate that you`re interested, we may phone you "
"about being part of a focus group in the future, however you do "
"not need to agree to participate in any future discussion.",
"",
"*It should only take 3 mins and we'll give you R10 airtime at the "
"end.*",
"",
"👤 Your answers are anonymous and confidential. In order to "
"respect your privacy we only ask about which city or town you "
"live in. We won`t share data outside the BWise WhatsApp Chatbot "
"team.",
"",
"✅ This study is voluntary and you can leave at any time by "
"responding with the keyword *“menu”* however, if you exit before "
"completing the survey, you will *not* be able to receive the R10 "
"airtime voucher.",
"",
"🔒 You`ve seen and agreed to the BWise privacy policy. Just a "
"reminder that we promise to keep all your info private and "
"secure.",
"",
"Are you comfortable for us to continue? Otherwise you can leave "
"the survey at any time by responding with the keyword “menu”. If "
"you have any questions, please email [email protected]",
]
)
)
# TODO: no and question destinations
return WhatsAppButtonState(
self,
question=question,
choices=choices,
error=self._(get_generic_error()),
next={"yes": "state_location_province", "no": "TODO", "question": "TODO"},
)

async def state_location_not_invited(self):
return EndState(
self,
self._(
"Unfortunately it looks like we already have enough people answering "
"this survey, but thank you for your interest."
),
next=self.START_STATE,
)

async def state_location_already_completed(self):
return EndState(
self,
self._("This number has already completed the location survey."),
next=self.START_STATE,
)

async def state_location_province(self):
async def _next(choice: Choice):
if choice.value == "other":
return "state_location_not_recruiting"
return "state_location_name_city"

question = "*What province do you live in?*"

return WhatsAppListState(
self,
question=question,
button="Province",
choices=[
Choice("GT", "Gauteng"),
Choice("NL", "Kwazulu-Natal"),
Choice("WC", "Western Cape"),
Choice("other", "None of the above"),
],
next=_next,
error=self._(get_generic_error()),
)

async def state_location_not_recruiting(self):
return EndState(
self,
self._(
"\n".join(
[
"Sorry, we`re only recruiting people for group discussions in "
"Gauteng, KZN and the Western Cape."
"",
"Reply with “menu” to return to the main menu",
]
)
),
next=self.START_STATE,
)

async def state_location_name_city(self):
question = self._(
"\n".join(
[
"*What is the name of the city or town you live in or live closest "
"to?*",
"",
"Please *TYPE* in the name of the city or town.",
]
)
)
return FreeText(
self,
question=question,
next="state_location_area_type",
check=nonempty_validator(question),
)

async def state_location_area_type(self):
question = "What type of area are you living in?"

return WhatsAppListState(
self,
question=question,
button="Area type",
choices=[
Choice("traditional", "Traditional/chiefdom"),
Choice("urban", "Urban/town"),
Choice("farm", "Farm/rural"),
Choice("dont_understand", "I don't understand"),
],
next="state_location_group_invite",
error=self._(get_generic_error()),
)

async def state_location_group_invite(self):
choices = [
Choice("yes", self._("Yes, I am interested")),
Choice("no", self._("No, thank you")),
Choice("dont_understand", self._("I don’t understand")),
]
question = self._(
"\n".join(
[
"All good, thank you! 🙌🏾",
"",
"We are organising group discussions for BWise users in September. "
"The focus groups will be with other users aged 15-24 years.",
"",
"We'd ask about your experiences on the platform and how feasible, "
"usable and effective the BWise chatbot is as a mobile health "
"platform for young South Africans.",
"",
"*Remember that you do not have to be interested in joining the "
"focus groups to complete this survey. If you indicate you are "
"interested you can still reject any invitation if we do contact "
"you.*",
"",
"Are you interested in being invited to one of these discussions "
"in the future?",
]
)
)
return WhatsAppButtonState(
self,
question=question,
choices=choices,
error=self._(get_generic_error()),
next="state_location_update_status",
)

async def state_location_update_status(self):
msisdn = normalise_phonenumber(self.inbound.from_addr)
whatsapp_id = msisdn.lstrip(" + ")

error = await rapidpro.update_profile(
whatsapp_id,
{"ejaf_location_survey_status": "completed"},
self.user.metadata,
)
if error:
return await self.go_to_state("state_error")

return await self.go_to_state("state_location_end")

async def state_location_end(self):
msg = self._(
"\n".join(
[
"*And that's a wrap!*",
"",
"Thank you for taking part in our survey 🙏🏽",
"",
"*You will get your R10 airtime within 24 hours.*",
"",
"You can engage with the B-Wise chatbot at any time for some "
"helpful messages or to ask any questions.",
]
)
)
return EndState(
self,
msg,
next=self.START_STATE,
)
85 changes: 85 additions & 0 deletions yal/tests/surveys/test_location.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
import pytest

from vaccine.models import Message
from vaccine.testing import AppTester

# TODO: fix this import once this flow is hooked up in main application
from yal.surveys.location import Application


@pytest.fixture
def tester():
return AppTester(Application)


@pytest.mark.asyncio
async def test_state_location_introduction_already_completed(tester: AppTester):
tester.setup_state("state_location_introduction")
tester.user.metadata["ejaf_location_survey_status"] = "completed"

await tester.user_input(session=Message.SESSION_EVENT.NEW)

tester.assert_state("state_location_introduction")

tester.assert_message("This number has already completed the location survey.")


@pytest.mark.asyncio
async def test_state_location_introduction_not_invited(tester: AppTester):
tester.setup_state("state_location_introduction")
await tester.user_input(session=Message.SESSION_EVENT.NEW)

tester.assert_state("state_location_introduction")

tester.assert_message(
"Unfortunately it looks like we already have enough people answering this "
"survey, but thank you for your interest."
)


@pytest.mark.asyncio
async def test_state_location_introduction_pending(tester: AppTester):
tester.setup_state("state_location_introduction")
tester.user.metadata["ejaf_location_survey_status"] = "pending"
await tester.user_input(session=Message.SESSION_EVENT.NEW)

tester.assert_state("state_location_introduction")

tester.assert_message(
"\n".join(
[
"*Fantastic! 👏🏾 🎉 And thank you 🙏🏽*",
"",
"*Before we start, here are a few important notes.* 📈",
"",
"This survey is just to understand who may be interested in "
"joining a focus group discussion in September and where would be "
"convenient for those users to meet. You do not have to be "
"interested in participating in focus groups to complete this "
"survey. If you indicate that you`re interested, we may phone you "
"about being part of a focus group in the future, however you do "
"not need to agree to participate in any future discussion.",
"",
"*It should only take 3 mins and we'll give you R10 airtime at the "
"end.*",
"",
"👤 Your answers are anonymous and confidential. In order to "
"respect your privacy we only ask about which city or town you "
"live in. We won`t share data outside the BWise WhatsApp Chatbot "
"team.",
"",
"✅ This study is voluntary and you can leave at any time by "
"responding with the keyword *“menu”* however, if you exit before "
"completing the survey, you will *not* be able to receive the R10 "
"airtime voucher.",
"",
"🔒 You`ve seen and agreed to the BWise privacy policy. Just a "
"reminder that we promise to keep all your info private and "
"secure.",
"",
"Are you comfortable for us to continue? Otherwise you can leave "
"the survey at any time by responding with the keyword “menu”. If "
"you have any questions, please email [email protected]",
]
)
)

0 comments on commit 9cacb4e

Please sign in to comment.