diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index 391787e..f4d3ba8 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -11,7 +11,7 @@ jobs: needs: test strategy: matrix: - bot: ['base', 'dbe', 'hh'] + bot: ['base', 'hh'] lang: ['eng'] steps: - uses: actions/checkout@v2 @@ -38,7 +38,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - bot: ['base', 'dbe', 'hh'] + bot: ['base', 'hh'] lang: ['eng'] steps: - uses: actions/checkout@v2 diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml index 5ffb79a..4a86b64 100644 --- a/.github/workflows/test.yaml +++ b/.github/workflows/test.yaml @@ -7,7 +7,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - bot: ['base', 'dbe', 'hh'] + bot: ['base', 'hh'] lang: ['eng'] steps: - uses: actions/checkout@v2 diff --git a/Dockerfile b/Dockerfile index 9de3647..251f631 100644 --- a/Dockerfile +++ b/Dockerfile @@ -8,9 +8,7 @@ RUN pip install -r requirements-actions.txt USER 1001 COPY ./base/actions /app/base/actions -COPY ./dbe/actions /app/dbe/actions COPY ./hh/actions /app/hh/actions COPY ./base/data /app/base/data -COPY ./dbe/data /app/dbe/data COPY ./hh/data /app/hh/data CMD ["start", "--actions", "base.actions.actions"] diff --git a/README.md b/README.md index 30be828..0036684 100644 --- a/README.md +++ b/README.md @@ -24,16 +24,6 @@ It has the following languages: `eng`: English -### dbe -This is the HealthCheck for the Department of Basic Education / E3. -It has an import of the actions, a copy of the domain, and a symlink for all the data -files. -It has the following languages: - -`eng`: English - -See [this readme](dbe/actions/README.md) for details on how to update the EMIS search index - ## hh This is the HealthCheck for Higher Health. diff --git a/dbe/__init__.py b/dbe/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/dbe/actions/README.md b/dbe/actions/README.md deleted file mode 100644 index e2c3337..0000000 --- a/dbe/actions/README.md +++ /dev/null @@ -1,20 +0,0 @@ -Importing school/EMIS data --------------------------- -The source for the EMIS information was downloaded from https://www.education.gov.za/Programmes/EMIS/EMISDownloads.aspx - -Current info is from "Quarter 4 of 2019: March 2020", except for Northern Cape, which is from "Quarter 3 of 2019: December 2019", as there's an error trying to download that file. - -That data is in Excel xlsx format, so use something like `ssconvert` to convert it to CSV: -```bash -~ for f in *.xlsx; do ssconvert "$f" "${f%.xlsx}.csv"; done -``` - -For the current data, the "Special Needs Education Centres.xlsx" file had different headings to the rest, so the headings were manually edited. The important headings are `NatEMIS`, `Province`, and `Official_Institution_Name`. - -Then to import, use the `import_school_data.py` script, which accepts a list of CSV files as arguments, processes them and deduplicates entries, builds the search index, and outputs the index to the `emis_index` folder. This script will always overwrite all the data in `emis_index`. - -```bash -~ python import_school_data.py Eastern\ Cape.csv Free\ State.csv Gauteng.csv KwaZulu\ Natal.csv Limpopo.csv Mpumalanga.csv National.csv North\ West.csv Northern\ Cape.csv Special\ Needs\ Education\ Centres.csv Western\ Cape.csv -``` - -Then commit the files in the `emis_index` folder, to update the search index. diff --git a/dbe/actions/__init__.py b/dbe/actions/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/dbe/actions/actions.py b/dbe/actions/actions.py deleted file mode 100644 index 2a2f6af..0000000 --- a/dbe/actions/actions.py +++ /dev/null @@ -1,1118 +0,0 @@ -import uuid -from datetime import datetime, timedelta, timezone -from inspect import iscoroutinefunction -from typing import Any, Dict, List, Optional, Text, Tuple, Union - -from rasa_sdk import Action, Tracker -from rasa_sdk.events import ActionExecuted, SessionStarted, SlotSet -from rasa_sdk.executor import CollectingDispatcher -from whoosh.index import open_dir -from whoosh.qparser import MultifieldParser, OrGroup, QueryParser -from whoosh.query import FuzzyTerm, Term - -from base.actions.actions import YES_NO_DATA -from base.actions.actions import HealthCheckForm as BaseHealthCheckForm -from base.actions.actions import HealthCheckProfileForm as BaseHealthCheckProfileForm -from base.actions.actions import HealthCheckTermsForm as BaseHealthCheckTermsForm -from dbe.actions import utils - -REQUESTED_SLOT = "requested_slot" - -PROVINCE_DISPLAY = { - "ec": "EASTERN CAPE", - "fs": "FREE STATE", - "gt": "GAUTENG", - "nl": "KWAZULU NATAL", - "lp": "LIMPOPO", - "mp": "MPUMALANGA", - "nw": "NORTH WEST", - "nc": "NORTHERN CAPE", - "wc": "WESTERN CAPE", -} - -PROFILE_DISPLAY = { - "educator": "Educator", - "learner": "Learner", - "parent": "Parents / Guardian on behalf of learner", - "actual_parent": "Parent", - "support": "School Support or Admin", - "marker": "Marker or Moderator", - "exam_assistant": "Exam Assistant (EA)", - "exam_official": "Exam Official", - "dbe_staff": "National DBE Staff", - "dhet_staff": "National DHET Staff", - "district_provincial_official": "District or Provincial Official", -} - - -def obo_validator(function): - async def call(*args, **kwargs): - if iscoroutinefunction(function): - result = await function(*args, **kwargs) - else: - result = function(*args, **kwargs) - return {f"obo_{k}": v for k, v in result.items()} - - return call - - -def generic_validator(slot_name, data): - def validator( - self, - value: Text, - dispatcher: CollectingDispatcher, - tracker: Tracker, - domain: Dict[Text, Any], - ) -> Dict[Text, Optional[Text]]: - return self.validate_generic(slot_name, dispatcher, value, data) - - return validator - - -class DBEHealthCheckTermsForm(BaseHealthCheckTermsForm): - SLOTS = [ - "terms", - ] - - def name(self) -> Text: - """Unique identifier of the form""" - - return "healthcheck_terms_form_dbe" - - def request_next_slot(self, dispatcher, tracker, domain): - next_slot = super().request_next_slot(dispatcher, tracker, domain) - if not next_slot: - return next_slot - slot_name = next_slot[0]["value"] - if slot_name == "terms" and not tracker.get_slot("terms"): - dispatcher.utter_message(template="utter_ask_terms_doc") - return next_slot - - async def submit( # type: ignore - self, - dispatcher: CollectingDispatcher, - tracker: Tracker, - domain: Dict[Text, Any], - ) -> Any: - """Define what the form has to do - after all required slots are filled""" - if tracker.get_slot("terms") == "no": - tracker.slots["terms"] = None - return await ActionExit().run( - dispatcher, tracker, domain, template="utter_no_consent_parent" - ) - return [] - - def validate_terms( - self, - value: Text, - dispatcher: CollectingDispatcher, - tracker: Tracker, - domain: Dict[Text, Any], - ) -> Dict[Text, Optional[Text]]: - if value == "more": - dispatcher.utter_message(template="utter_more_terms") - return {"terms": None} - if value.lower() in ["2", "no"]: - return {"terms": "no"} - return self.validate_generic("terms", dispatcher, value, {1: "yes"}) - - -class HealthCheckProfileForm(BaseHealthCheckProfileForm): - SLOTS = ["profile", "age"] - - PERSISTED_SLOTS = [ - "gender", - "province", - "location", - "location_confirm", - "school", - "school_confirm", - "medical_condition", - ] - MINOR_SKIP_SLOTS = ["first_name", "last_name", "location", "location_confirm"] - - def request_next_slot(self, dispatcher, tracker, domain): - for slot in self.required_slots(tracker): - if slot == "consent_parent" and tracker.get_slot("consent_parent") == "": - return [] - if self._should_request_slot(tracker, slot): - if ( - slot == "consent_parent" - and tracker.get_slot("consent_parent") == "" - ): - return [] - if slot in [ - "school", - "school_confirm", - "confirm_details", - "change_details", - ] and tracker.get_slot("profile") in [ - "marker", - "exam_assistant", - "exam_official", - ]: - kwargs = { - "school": tracker.get_slot("school"), - "province_display": tracker.get_slot("province_display"), - "profile_display": tracker.get_slot("profile_display"), - } - dispatcher.utter_message( - template=f"utter_ask_{slot}_marker", **kwargs - ) - return [SlotSet(REQUESTED_SLOT, slot)] - - return super().request_next_slot(dispatcher, tracker, domain) - - def slot_mappings(self) -> Dict[Text, Union[Dict, List[Dict]]]: - mappings = super().slot_mappings() - mappings["school"] = [self.from_text()] - for field in [ - "school_confirm", - "confirm_details", - "medical_condition_asthma", - "medical_condition_tb", - "medical_condition_pregnant", - "medical_condition_respiratory", - "medical_condition_cardiac", - "medical_condition_immuno", - "confirm_details_parent", - ]: - mappings[field] = [ - self.from_entity(entity="number"), - self.from_intent(intent="affirm", value="yes"), - self.from_intent(intent="deny", value="no"), - self.from_text(), - ] - mappings["consent_parent"] = [ - self.from_entity(entity="number"), - self.from_intent(intent="affirm", value="yes"), - self.from_intent(intent="deny", value="no"), - self.from_intent(intent="more", value="more"), - self.from_text(), - ] - mappings["change_details"] = [ - self.from_entity(entity="number"), - self.from_text(), - ] - mappings["select_learner_profile"] = [ - self.from_entity(entity="number"), - self.from_text(), - ] - mappings.update({f"obo_{m}": v for m, v in mappings.items()}) - mappings["profile"] = [self.from_entity(entity="number"), self.from_text()] - mappings["obo_name"] = [self.from_text()] - return mappings - - @classmethod - def get_all_slots(cls, tracker: Tracker) -> List[Text]: - slots = cls.SLOTS + cls.PERSISTED_SLOTS - if ( - tracker.get_slot("medical_condition") != "no" - and tracker.get_slot("obo_medical_condition") != "no" - ): - slots += cls.CONDITIONS - if ( - tracker.get_slot("profile") == "learner" - or tracker.get_slot("profile") == "parent" - ): - slots += ["medical_condition_asthma", "medical_condition_tb"] - try: - if ( - int(tracker.get_slot("age")) >= 12 - and tracker.get_slot("gender") == "FEMALE" - ): - slots += ["medical_condition_pregnant"] - except (TypeError, ValueError): - pass - try: - if int(tracker.get_slot("age")) < 18: - for slot in cls.MINOR_SKIP_SLOTS: - slots.remove(slot) - except (TypeError, ValueError): - pass - try: - if ( - int(tracker.get_slot("obo_age")) >= 12 - and tracker.get_slot("obo_gender") == "FEMALE" - ): - slots += ["medical_condition_pregnant"] - except (TypeError, ValueError): - pass - try: - if int(tracker.get_slot("obo_age")) < 18: - for slot in cls.MINOR_SKIP_SLOTS: - slots.remove(slot) - except (TypeError, ValueError): - pass - - slots += [ - "medical_condition_respiratory", - "medical_condition_cardiac", - "medical_condition_immuno", - ] - - # Use on behalf of slots for parent profile - if tracker.get_slot("profile") == "parent": - if tracker.get_slot("consent_parent") == "no": - tracker.slots["consent_parent"] = "" - slots = [ - "select_learner_profile", - "consent_parent", - "profile", - "obo_name", - ] + [f"obo_{s}" for s in slots[1:]] - if tracker.get_slot("returning_user") == "yes": - if tracker.get_slot("profile") == "parent": - slots = ["confirm_details_parent"] + slots - elif tracker.get_slot("change_details"): - slots = [ - "province", - "school", - "school_confirm", - "confirm_details", - ] + slots - elif tracker.get_slot("confirm_details") == "no": - slots = ["change_details"] + slots - elif not tracker.get_slot("confirm_details"): - slots = ["confirm_details"] + slots - return slots - - @classmethod - def required_slots(cls, tracker: Tracker) -> List[Text]: - for slot in cls.get_all_slots(tracker): - if not tracker.get_slot(slot): - return [slot] - return [] - - def validate_select_learner_profile( - self, - value: Text, - dispatcher: CollectingDispatcher, - tracker: Tracker, - domain: Dict[Text, Any], - ) -> Dict[Text, Optional[Text]]: - profiles = tracker.get_slot("learner_profiles") - data = { - i + 1: profile["name"].lower().strip() for i, profile in enumerate(profiles) - } - data[len(data) + 1] = new_learner = object() - results = self.validate_generic( - "select_learner_profile", dispatcher, value, data - ) - user_answer = results["select_learner_profile"] or "" - if user_answer == new_learner: - return { - "select_learner_profile": "new", - "obo_name": None, - "obo_age": None, - "obo_gender": None, - "obo_province": None, - "obo_location": None, - "obo_location_confirm": None, - "obo_location_coords": None, - "obo_city_location_coords": None, - "obo_school": None, - "obo_school_confirm": None, - "obo_school_emis": None, - "obo_medical_condition": None, - "obo_medical_condition_obesity": None, - "obo_medical_condition_diabetes": None, - "obo_medical_condition_hypertension": None, - "obo_medical_condition_cardio": None, - "obo_medical_condition_asthma": None, - "obo_medical_condition_tb": None, - "obo_medical_condition_pregnant": None, - "obo_medical_condition_respiratory": None, - "obo_medical_condition_cardiac": None, - "obo_medical_condition_immuno": None, - } - if user_answer: - [profile] = filter( - lambda p: p.get("name", "").lower().strip() - == user_answer.lower().strip(), - profiles, - ) - gender_mappings = {v: k for k, v in HealthCheckForm.GENDER_MAPPING.items()} - yes_no_mappings = {v: k for k, v in HealthCheckForm.YES_NO_MAPPING.items()} - yes_no_maybe_mappings = { - v: k for k, v in HealthCheckForm.YES_NO_MAYBE_MAPPING.items() - } - results.update( - { - "obo_name": profile["name"], - "obo_age": profile["age"], - "obo_gender": gender_mappings[profile["gender"]], - "obo_province": profile["province"][3:].lower(), - "obo_location": profile["city"], - "obo_location_confirm": "yes", - "obo_location_coords": profile["location"], - "obo_city_location_coords": profile["city_location"], - "obo_school": profile["school"], - "obo_school_confirm": "yes", - "obo_school_emis": profile["school_emis"], - "obo_medical_condition": yes_no_maybe_mappings[ - profile["preexisting_condition"] - ], - "obo_medical_condition_obesity": yes_no_mappings.get( - profile["obesity"] - ), - "obo_medical_condition_diabetes": yes_no_mappings.get( - profile["diabetes"] - ), - "obo_medical_condition_hypertension": yes_no_mappings.get( - profile["hypertension"] - ), - "obo_medical_condition_cardio": yes_no_mappings.get( - profile["cardio"] - ), - "obo_medical_condition_asthma": yes_no_mappings.get( - profile["asthma"] - ), - "obo_medical_condition_tb": yes_no_mappings.get(profile["tb"]), - "obo_medical_condition_pregnant": yes_no_mappings.get( - profile["pregnant"] - ), - "obo_medical_condition_respiratory": yes_no_mappings.get( - profile["respiratory"] - ), - "obo_medical_condition_cardiac": yes_no_mappings.get( - profile["cardiac"] - ), - "obo_medical_condition_immuno": yes_no_mappings.get( - profile["immuno"] - ), - } - ) - return results - - def validate_confirm_details( - self, - value: Text, - dispatcher: CollectingDispatcher, - tracker: Tracker, - domain: Dict[Text, Any], - ) -> Dict[Text, Optional[Text]]: - result = self.validate_generic( - "confirm_details", dispatcher, value, self.yes_no_data - ) - if result["confirm_details"] == "no": - result["change_details"] = None - return result - - def validate_change_details( - self, - value: Text, - dispatcher: CollectingDispatcher, - tracker: Tracker, - domain: Dict[Text, Any], - ) -> Dict[Text, Optional[Text]]: - result = self.validate_generic( - "change_details", - dispatcher, - value, - {1: "school name", 2: "province", 3: "role"}, - ) - if result["change_details"] == "school name": - result["school"] = None - result["school_confirm"] = None - elif result["change_details"] == "province": - result["province"] = None - elif result["change_details"] == "role": - return { - "confirm_details_parent": None, - "confirm_details": None, - "change_details": None, - "returning_user": None, - "profile": None, - "profile_display": None, - "age": None, - "gender": None, - "province": None, - "province_display": None, - "location": None, - "location_confirm": None, - "location_coords": None, - "city_location_coords": None, - "school": None, - "school_confirm": None, - "school_emis": None, - "medical_condition": None, - "medical_condition_obesity": None, - "medical_condition_diabetes": None, - "medical_condition_hypertension": None, - "medical_condition_cardio": None, - "medical_condition_asthma": None, - "medical_condition_tb": None, - "medical_condition_pregnant": None, - "medical_condition_respiratory": None, - "medical_condition_cardiac": None, - "medical_condition_immuno": None, - "symptoms_fever": None, - "symptoms_cough": None, - "symptoms_sore_throat": None, - "symptoms_difficulty_breathing": None, - "symptoms_taste_smell": None, - "exposure": None, - "tracing": None, - "learner_profiles": None, - "select_learner_profile": None, - "display_learner_profiles": None, - "obo_name": None, - "obo_age": None, - "obo_gender": None, - "obo_province": None, - "obo_location": None, - "obo_location_confirm": None, - "obo_location_coords": None, - "obo_city_location_coords": None, - "obo_school": None, - "obo_school_confirm": None, - "obo_school_emis": None, - "obo_medical_condition": None, - "obo_medical_condition_obesity": None, - "obo_medical_condition_diabetes": None, - "obo_medical_condition_hypertension": None, - "obo_medical_condition_cardio": None, - "obo_medical_condition_asthma": None, - "obo_medical_condition_tb": None, - "obo_medical_condition_pregnant": None, - "obo_medical_condition_respiratory": None, - "obo_medical_condition_cardiac": None, - "obo_medical_condition_immuno": None, - "obo_symptoms_fever": None, - "obo_symptoms_cough": None, - "obo_symptoms_sore_throat": None, - "obo_symptoms_difficulty_breathing": None, - "obo_symptoms_taste_smell": None, - "obo_exposure": None, - "obo_tracing": None, - } - result["confirm_details"] = None - return result - - def validate_confirm_details_parent( - self, - value: Text, - dispatcher: CollectingDispatcher, - tracker: Tracker, - domain: Dict[Text, Any], - ) -> Dict[Text, Optional[Text]]: - result = self.validate_generic( - "confirm_details_parent", dispatcher, value, {1: "yes", 2: "no"}, - ) - if result["confirm_details_parent"] == "no": - return { - "confirm_details_parent": None, - "confirm_details": None, - "change_details": None, - "returning_user": None, - "profile": None, - "profile_display": None, - "age": None, - "gender": None, - "province": None, - "province_display": None, - "location": None, - "location_confirm": None, - "location_coords": None, - "city_location_coords": None, - "school": None, - "school_confirm": None, - "school_emis": None, - "medical_condition": None, - "medical_condition_obesity": None, - "medical_condition_diabetes": None, - "medical_condition_hypertension": None, - "medical_condition_cardio": None, - "medical_condition_asthma": None, - "medical_condition_tb": None, - "medical_condition_pregnant": None, - "medical_condition_respiratory": None, - "medical_condition_cardiac": None, - "medical_condition_immuno": None, - "symptoms_fever": None, - "symptoms_cough": None, - "symptoms_sore_throat": None, - "symptoms_difficulty_breathing": None, - "symptoms_taste_smell": None, - "exposure": None, - "tracing": None, - "learner_profiles": None, - "select_learner_profile": None, - "display_learner_profiles": None, - "obo_name": None, - "obo_age": None, - "obo_gender": None, - "obo_province": None, - "obo_location": None, - "obo_location_confirm": None, - "obo_location_coords": None, - "obo_city_location_coords": None, - "obo_school": None, - "obo_school_confirm": None, - "obo_school_emis": None, - "obo_medical_condition": None, - "obo_medical_condition_obesity": None, - "obo_medical_condition_diabetes": None, - "obo_medical_condition_hypertension": None, - "obo_medical_condition_cardio": None, - "obo_medical_condition_asthma": None, - "obo_medical_condition_tb": None, - "obo_medical_condition_pregnant": None, - "obo_medical_condition_respiratory": None, - "obo_medical_condition_cardiac": None, - "obo_medical_condition_immuno": None, - "obo_symptoms_fever": None, - "obo_symptoms_cough": None, - "obo_symptoms_sore_throat": None, - "obo_symptoms_difficulty_breathing": None, - "obo_symptoms_taste_smell": None, - "obo_exposure": None, - "obo_tracing": None, - } - return result - - def validate_consent_parent( - self, - value: Text, - dispatcher: CollectingDispatcher, - tracker: Tracker, - domain: Dict[Text, Any], - ) -> Dict[Text, Optional[Text]]: - if value == "more": - dispatcher.utter_message(template="utter_more_terms") - return {"consent_parent": None} - result = self.validate_generic( - "consent_parent", dispatcher, value, {1: "yes", 2: "no"}, - ) - if result["consent_parent"] == "no": - dispatcher.utter_message(template="utter_no_consent_parent") - return result - - def validate_age( - self, - value: Text, - dispatcher: CollectingDispatcher, - tracker: Tracker, - domain: Dict[Text, Any], - ) -> Dict[Text, Optional[Text]]: - if self.is_int(value) and int(value) > 0 and int(value) < 150: - return {"age": value} - return {"age": None} - - def validate_school( - self, - value: Text, - dispatcher: CollectingDispatcher, - tracker: Tracker, - domain: Dict[Text, Any], - ) -> Dict[Text, Optional[Text]]: - if value and isinstance(value, str) and value.strip().lower() == "other": - return {"school": "OTHER", "school_emis": None, "school_confirm": "yes"} - - province = tracker.get_slot("obo_province") or tracker.get_slot("province") - - if tracker.get_slot("profile") in ["marker", "exam_assistant", "exam_official"]: - ix = open_dir("dbe/actions/marking_centre_index") - - parser = QueryParser("name", ix.schema, termclass=FuzzyTerm) - elif tracker.get_slot("profile") in [ - "dbe_staff", - "dhet_staff", - "district_provincial_official", - ]: - schools: List[Tuple[float, Text, Optional[Text]]] = [] - ix = open_dir("dbe/actions/marking_centre_index") - parser = QueryParser("name", ix.schema, termclass=FuzzyTerm, group=OrGroup) - query = parser.parse(value) - with ix.searcher() as s: - results = s.search(query, limit=1, filter=Term("province", province)) - for result in results: - schools.append((result.score, result["name"], None)) - ix = open_dir("dbe/actions/emis_index") - parser = MultifieldParser( - ["name", "emis"], ix.schema, termclass=FuzzyTerm, group=OrGroup - ) - query = parser.parse(value) - with ix.searcher() as s: - results = s.search(query, limit=1, filter=Term("province", province)) - for result in results: - schools.append((result.score, result["name"], result["emis"])) - schools.sort(key=lambda r: r[0], reverse=True) - if schools: - school = schools[0] - return { - "school": school[1], - "school_emis": school[2], - } - else: - dispatcher.utter_message(template="utter_incorrect_school") - return {"school": None, "province": None} - else: - ix = open_dir("dbe/actions/emis_index") - - parser = MultifieldParser( - ["name", "emis"], ix.schema, termclass=FuzzyTerm, group=OrGroup - ) - - query = parser.parse(value) - - with ix.searcher() as s: - results = s.search(query, limit=1, filter=Term("province", province)) - if results: - result = results[0] - return { - "school": result["name"], - "school_emis": result.get("emis"), - } - else: - if tracker.get_slot("profile") in [ - "marker", - "exam_assistant", - "exam_official", - ]: - dispatcher.utter_message(template="utter_incorrect_school_marker") - else: - dispatcher.utter_message(template="utter_incorrect_school") - return {"school": None, "province": None} - - def validate_school_confirm( - self, - value: Text, - dispatcher: CollectingDispatcher, - tracker: Tracker, - domain: Dict[Text, Any], - ) -> Dict[Text, Optional[Text]]: - school_confirm = self.validate_generic( - "school_confirm", dispatcher, value, self.yes_no_data - ) - if ( - school_confirm["school_confirm"] - and school_confirm["school_confirm"] == "no" - ): - return {"school_confirm": None, "school": None} - return school_confirm - - def validate_province( - self, - value: Text, - dispatcher: CollectingDispatcher, - tracker: Tracker, - domain: Dict[Text, Any], - ) -> Dict[Text, Optional[Text]]: - result = self.validate_generic( - "province", dispatcher, value, self.province_data - ) - if ( - isinstance(result["province"], str) - and result["province"] in PROVINCE_DISPLAY.keys() - ): - result["province_display"] = PROVINCE_DISPLAY[result["province"]] - return result - - @property - def profile_data(self) -> Dict[int, Text]: - with open("dbe/data/lookup_tables/profiles.txt") as f: - return dict(enumerate(f.read().splitlines(), start=1)) - - async def validate_profile( - self, - value: Text, - dispatcher: CollectingDispatcher, - tracker: Tracker, - domain: Dict[Text, Any], - ) -> Dict[Text, Optional[Text]]: - results = self.validate_generic( - "profile", dispatcher, value, self.profile_data, accept_labels=False - ) - if ( - isinstance(results["profile"], str) - and results["profile"] in PROFILE_DISPLAY.keys() - ): - results["profile_display"] = PROFILE_DISPLAY[results["profile"]] - if results.get("profile") == "parent": - results.update(await utils.get_learner_profile_slots_dict(tracker)) - return results - - def validate_medical_condition_pregnant( - self, - value: Text, - dispatcher: CollectingDispatcher, - tracker: Tracker, - domain: Dict[Text, Any], - ) -> Dict[Text, Optional[Text]]: - result = self.validate_generic( - "medical_condition_pregnant", dispatcher, value, YES_NO_DATA - ) - if result["medical_condition_pregnant"] == "yes": - dispatcher.utter_message(template="utter_pregnant_yes") - return result - - def validate_obo_medical_condition_pregnant( - self, - value: Text, - dispatcher: CollectingDispatcher, - tracker: Tracker, - domain: Dict[Text, Any], - ) -> Dict[Text, Optional[Text]]: - result = self.validate_generic( - "obo_medical_condition_pregnant", dispatcher, value, YES_NO_DATA - ) - if result["obo_medical_condition_pregnant"] == "yes": - dispatcher.utter_message(template="utter_obo_pregnant_yes") - return result - - def get_province(self, tracker): - if tracker.get_slot("profile") == "parent": - return tracker.get_slot("obo_province") - return tracker.get_slot("province") - - validate_medical_condition_asthma = generic_validator( - "medical_condition_asthma", YES_NO_DATA - ) - validate_medical_condition_tb = generic_validator( - "medical_condition_tb", YES_NO_DATA - ) - validate_medical_condition_respiratory = generic_validator( - "medical_condition_respiratory", YES_NO_DATA - ) - validate_medical_condition_cardiac = generic_validator( - "medical_condition_cardiac", YES_NO_DATA - ) - validate_medical_condition_immuno = generic_validator( - "medical_condition_immuno", YES_NO_DATA - ) - - validate_obo_age = obo_validator(validate_age) - validate_obo_gender = obo_validator(BaseHealthCheckProfileForm.validate_gender) - validate_obo_province = obo_validator(BaseHealthCheckProfileForm.validate_province) - validate_obo_location = obo_validator(BaseHealthCheckProfileForm.validate_location) - validate_obo_location_confirm = obo_validator( - BaseHealthCheckProfileForm.validate_location_confirm - ) - validate_obo_school = obo_validator(validate_school) - validate_obo_school_confirm = obo_validator(validate_school_confirm) - validate_obo_medical_condition = obo_validator( - BaseHealthCheckProfileForm.validate_medical_condition - ) - validate_obo_medical_condition_obesity = obo_validator( - BaseHealthCheckProfileForm.validate_medical_condition_obesity - ) - validate_obo_medical_condition_diabetes = obo_validator( - BaseHealthCheckProfileForm.validate_medical_condition_diabetes - ) - validate_obo_medical_condition_hypertension = obo_validator( - BaseHealthCheckProfileForm.validate_medical_condition_hypertension - ) - validate_obo_medical_condition_cardio = obo_validator( - BaseHealthCheckProfileForm.validate_medical_condition_cardio - ) - validate_obo_medical_condition_asthma = obo_validator( - validate_medical_condition_asthma - ) - validate_obo_medical_condition_tb = obo_validator(validate_medical_condition_tb) - validate_obo_medical_condition_respiratory = obo_validator( - validate_medical_condition_respiratory - ) - validate_obo_medical_condition_cardiac = obo_validator( - validate_medical_condition_cardiac - ) - validate_obo_medical_condition_immuno = obo_validator( - validate_medical_condition_immuno - ) - - -class HealthCheckForm(BaseHealthCheckForm): - @classmethod - def required_slots(cls, tracker: Tracker) -> List[Text]: - slots = super().required_slots(tracker) - if tracker.get_slot("profile") == "parent": - for slot in cls.SLOTS: - slot = f"obo_{slot}" - if not tracker.get_slot(slot): - return [slot] - return [] - if tracker.get_slot("age") == "<18": - for slot in cls.MINOR_SKIP_SLOTS: - slots.remove(slot) - return slots - - def slot_mappings(self) -> Dict[Text, Union[Dict, List[Dict]]]: - mappings = super().slot_mappings() - mappings.update({f"obo_{m}": v for m, v in mappings.items()}) - return mappings - - def map_age(self, value: Text): - age = int(value) - if age < 18: - return "<18" - if age < 40: - return "18-40" - if age <= 65: - return "40-65" - return ">65" - - def get_eventstore_data(self, tracker: Tracker, risk: Text) -> Dict[Text, Any]: - if tracker.get_slot("profile") == "parent": - return { - "deduplication_id": uuid.uuid4().hex, - "msisdn": f'+{tracker.sender_id.lstrip("+")}', - "source": "WhatsApp", - "province": f'ZA-{tracker.get_slot("obo_province").upper()}', - "city": tracker.get_slot("obo_location"), - "age": self.map_age(tracker.get_slot("obo_age")), - "fever": self.YES_NO_MAPPING[tracker.get_slot("obo_symptoms_fever")], - "cough": self.YES_NO_MAPPING[tracker.get_slot("obo_symptoms_cough")], - "sore_throat": self.YES_NO_MAPPING[ - tracker.get_slot("obo_symptoms_sore_throat") - ], - "difficulty_breathing": self.YES_NO_MAPPING[ - tracker.get_slot("obo_symptoms_difficulty_breathing") - ], - "exposure": self.YES_NO_MAYBE_MAPPING[tracker.get_slot("obo_exposure")], - "tracing": self.YES_NO_MAPPING[tracker.get_slot("obo_tracing")], - "risk": risk, - "gender": self.GENDER_MAPPING[tracker.get_slot("obo_gender")], - "location": self.fix_location_format( - tracker.get_slot("obo_location_coords") - ), - "city_location": self.fix_location_format( - tracker.get_slot("obo_city_location_coords") - ), - "smell": self.YES_NO_MAPPING[ - tracker.get_slot("obo_symptoms_taste_smell") - ], - "preexisting_condition": self.YES_NO_MAYBE_MAPPING[ - tracker.get_slot("obo_medical_condition") - ], - # TODO: Put these 4 fields as columns on the table for a v4 API - "data": { - "obesity": self.YES_NO_MAPPING.get( - tracker.get_slot("obo_medical_condition_obesity") - ), - "diabetes": self.YES_NO_MAPPING.get( - tracker.get_slot("obo_medical_condition_diabetes") - ), - "hypertension": self.YES_NO_MAPPING.get( - tracker.get_slot("obo_medical_condition_hypertension") - ), - "cardio": self.YES_NO_MAPPING.get( - tracker.get_slot("obo_medical_condition_cardio") - ), - "age": tracker.get_slot("obo_age"), - "school_name": tracker.get_slot("obo_school"), - "school_emis": tracker.get_slot("obo_school_emis"), - "profile": tracker.get_slot("profile"), - "name": tracker.get_slot("obo_name"), - "asthma": self.YES_NO_MAPPING.get( - tracker.get_slot("obo_medical_condition_asthma") - ), - "tb": self.YES_NO_MAPPING.get( - tracker.get_slot("obo_medical_condition_tb") - ), - "pregnant": self.YES_NO_MAPPING.get( - tracker.get_slot("obo_medical_condition_pregnant") - ), - "respiratory": self.YES_NO_MAPPING.get( - tracker.get_slot("obo_medical_condition_respiratory") - ), - "cardiac": self.YES_NO_MAPPING.get( - tracker.get_slot("obo_medical_condition_cardiac") - ), - "immuno": self.YES_NO_MAPPING.get( - tracker.get_slot("obo_medical_condition_immuno") - ), - }, - } - # Add the original value for `age` to `data` - data = super().get_eventstore_data(tracker, risk) - data["data"]["age"] = tracker.get_slot("age") - data["data"]["school_name"] = tracker.get_slot("school") - data["data"]["school_emis"] = tracker.get_slot("school_emis") - data["data"]["profile"] = tracker.get_slot("profile") - data["data"]["asthma"] = self.YES_NO_MAPPING.get( - tracker.get_slot("medical_condition_asthma") - ) - data["data"]["tb"] = self.YES_NO_MAPPING.get( - tracker.get_slot("medical_condition_tb") - ) - data["data"]["pregnant"] = self.YES_NO_MAPPING.get( - tracker.get_slot("medical_condition_pregnant") - ) - data["data"]["respiratory"] = self.YES_NO_MAPPING.get( - tracker.get_slot("medical_condition_respiratory") - ) - data["data"]["cardiac"] = self.YES_NO_MAPPING.get( - tracker.get_slot("medical_condition_cardiac") - ) - data["data"]["immuno"] = self.YES_NO_MAPPING.get( - tracker.get_slot("medical_condition_immuno") - ) - return data - - def get_risk_data(self, tracker: Tracker) -> Dict: - if tracker.get_slot("profile") == "parent": - data = { - slot: tracker.get_slot(f"obo_{slot}") - for slot in self.SLOTS - if slot.startswith("symptoms_") - } - data.update( - { - "exposure": tracker.get_slot("obo_exposure"), - "age": self.map_age(tracker.get_slot("obo_age")), - } - ) - return data - return super().get_risk_data(tracker) - - def send_risk_to_user(self, dispatcher, risk, tracker): - template = f"utter_risk_{risk}" - if tracker.get_slot("profile") == "parent": - template = f"utter_obo_risk_{risk}" - elif tracker.get_slot("profile") == "actual_parent": - template = f"utter_risk_{risk}_parent" - elif tracker.get_slot("profile") in [ - "support", - "marker", - "exam_assistant", - "exam_official", - "educator", - ]: - template = f"utter_risk_{risk}_support" - # ZA timezone - issued = datetime.now(tz=timezone(timedelta(hours=2))) - expired = issued + timedelta(days=1) - date_format = "%B %-d, %Y, %-I:%M %p" - dispatcher.utter_message( - template=template, - issued=issued.strftime(date_format), - expired=expired.strftime(date_format), - ) - - validate_obo_symptoms_fever = obo_validator( - BaseHealthCheckForm.validate_symptoms_fever - ) - validate_obo_symptoms_cough = obo_validator( - BaseHealthCheckForm.validate_symptoms_cough - ) - validate_obo_symptoms_sore_throat = obo_validator( - BaseHealthCheckForm.validate_symptoms_sore_throat - ) - validate_obo_symptoms_difficulty_breathing = obo_validator( - BaseHealthCheckForm.validate_symptoms_difficulty_breathing - ) - validate_obo_symptoms_taste_smell = obo_validator( - BaseHealthCheckForm.validate_symptoms_taste_smell - ) - validate_obo_exposure = obo_validator(BaseHealthCheckForm.validate_exposure) - validate_obo_tracing = obo_validator(BaseHealthCheckForm.validate_tracing) - - def send_post_risk_prompts( - self, dispatcher: CollectingDispatcher, risk: Text, tracker: Tracker - ): - pass - - -class ActionSessionStart(Action): - def name(self) -> Text: - return "action_session_start" - - async def get_carry_over_slots(self, tracker: Tracker) -> List[Dict[Text, Any]]: - actions = [SessionStarted()] - carry_over_slots = ( - DBEHealthCheckTermsForm.SLOTS - + HealthCheckProfileForm.PERSISTED_SLOTS - + HealthCheckProfileForm.CONDITIONS - + ["location_coords", "city_location_coords"] - + ["school", "school_confirm", "school_emis", "profile"] - + [ - "medical_condition_asthma", - "medical_condition_tb", - "medical_condition_pregnant", - "medical_condition_respiratory", - "medical_condition_cardiac", - "medical_condition_immuno", - ] - ) - for slot in carry_over_slots: - actions.append(SlotSet(slot, tracker.get_slot(slot))) - if tracker.get_slot("profile") in PROFILE_DISPLAY.keys(): - actions.append(SlotSet("returning_user", "yes")) - actions.append( - SlotSet("profile_display", PROFILE_DISPLAY[tracker.get_slot("profile")]) - ) - if tracker.get_slot("province") in PROVINCE_DISPLAY.keys(): - actions.append( - SlotSet( - "province_display", PROVINCE_DISPLAY[tracker.get_slot("province")] - ) - ) - if tracker.get_slot("profile") == "parent": - actions.extend(await utils.get_learner_profile_slots(tracker)) - return actions - - async def run( - self, - dispatcher: CollectingDispatcher, - tracker: Tracker, - domain: Dict[Text, Any], - ) -> List[Dict[Text, Any]]: - actions = await self.get_carry_over_slots(tracker) - actions.append(ActionExecuted("action_listen")) - return actions - - -class ActionExit(Action): - def name(self) -> Text: - return "action_exit" - - async def run( - self, - dispatcher: CollectingDispatcher, - tracker: Tracker, - domain: Dict[Text, Any], - template: Text = "utter_exit", - ) -> List[Dict[Text, Any]]: - dispatcher.utter_message(template=template) - return await ActionSessionStart().get_carry_over_slots(tracker) - - -class ActionSetProfileObo(Action): - def name(self) -> Text: - return "action_set_profile_obo" - - async def run( - self, - dispatcher: CollectingDispatcher, - tracker: Tracker, - domain: Dict[Text, Any], - ) -> List[Dict[Text, Any]]: - actions = [SlotSet("profile", "parent")] - actions.extend(await utils.get_learner_profile_slots(tracker)) - return actions - - -class ActionSendStudyMessages(Action): - def name(self) -> Text: - return "action_send_study_messages" - - def run( - self, - dispatcher: CollectingDispatcher, - tracker: Tracker, - domain: Dict[Text, Any], - ) -> List[Dict[Text, Any]]: - return [] - - -__all__ = [ - "DBEHealthCheckTermsForm", - "HealthCheckProfileForm", - "HealthCheckForm", - "ActionSendStudyMessages", - "ActionSessionStart", - "ActionExit", - "ActionSessionStart", -] diff --git a/dbe/actions/emis_index/MAIN_WRITELOCK b/dbe/actions/emis_index/MAIN_WRITELOCK deleted file mode 100755 index e69de29..0000000 diff --git a/dbe/actions/emis_index/MAIN_r9ntc5gfkpnl3lbc.seg b/dbe/actions/emis_index/MAIN_r9ntc5gfkpnl3lbc.seg deleted file mode 100644 index 0c1d988..0000000 Binary files a/dbe/actions/emis_index/MAIN_r9ntc5gfkpnl3lbc.seg and /dev/null differ diff --git a/dbe/actions/emis_index/_MAIN_1.toc b/dbe/actions/emis_index/_MAIN_1.toc deleted file mode 100644 index d245304..0000000 Binary files a/dbe/actions/emis_index/_MAIN_1.toc and /dev/null differ diff --git a/dbe/actions/import_marking_centres.py b/dbe/actions/import_marking_centres.py deleted file mode 100644 index cca5607..0000000 --- a/dbe/actions/import_marking_centres.py +++ /dev/null @@ -1,32 +0,0 @@ -import csv - -from whoosh import fields -from whoosh.index import create_in - - -def read_file(filename): - centres = set() - with open(filename) as f: - print(f"Reading {filename}...") - for row in csv.DictReader(f): - centres.add((row["PROVINCE"], row["NAME"])) - return centres - - -def overwrite_index(centres): - schema = fields.Schema(province=fields.ID, name=fields.TEXT(stored=True)) - ix = create_in("marking_centre_index", schema) - writer = ix.writer() - for centre in centres: - writer.add_document( - province=centre[0], name=centre[1], - ) - writer.commit(optimize=True) - - -if __name__ == "__main__": - centres = set() - centres.update(read_file("marking_centres.csv")) - print(f"Read {len(centres)} centres") - print("Writing index...") - overwrite_index(centres) diff --git a/dbe/actions/import_school_data.py b/dbe/actions/import_school_data.py deleted file mode 100644 index c2fe45f..0000000 --- a/dbe/actions/import_school_data.py +++ /dev/null @@ -1,57 +0,0 @@ -import argparse -import csv - -from whoosh import fields -from whoosh.index import create_in - -PROVINCE_MAPPING = { - "MP": "mp", - "EC": "ec", - "KZN": "nl", - "WC": "wc", - "NW": "nw", - "NC": "nc", - "FS": "fs", - "LP": "lp", - "GT": "gt", -} - - -def read_file(filename): - schools = set() - with open(filename) as f: - print(f"Reading {filename}...") - for row in csv.DictReader(f): - schools.add( - ( - row["NatEMIS"], - PROVINCE_MAPPING[row["Province"]], - row["Official_Institution_Name"], - ) - ) - return schools - - -def overwrite_index(schools): - schema = fields.Schema( - emis=fields.ID(stored=True), province=fields.ID, name=fields.TEXT(stored=True), - ) - ix = create_in("emis_index", schema) - writer = ix.writer() - for school in schools: - writer.add_document( - emis=school[0], province=school[1], name=school[2], - ) - writer.commit(optimize=True) - - -if __name__ == "__main__": - parser = argparse.ArgumentParser(description="Import school data into search index") - parser.add_argument("files", nargs="+") - args = parser.parse_args() - schools = set() - for filename in args.files: - schools.update(read_file(filename)) - print(f"Read {len(schools)} schools") - print("Writing index...") - overwrite_index(schools) diff --git a/dbe/actions/marking_centre_index/MAIN_WRITELOCK b/dbe/actions/marking_centre_index/MAIN_WRITELOCK deleted file mode 100755 index e69de29..0000000 diff --git a/dbe/actions/marking_centre_index/MAIN_d5j9d21h5bwdsqwe.seg b/dbe/actions/marking_centre_index/MAIN_d5j9d21h5bwdsqwe.seg deleted file mode 100644 index 0fba67d..0000000 Binary files a/dbe/actions/marking_centre_index/MAIN_d5j9d21h5bwdsqwe.seg and /dev/null differ diff --git a/dbe/actions/marking_centre_index/_MAIN_1.toc b/dbe/actions/marking_centre_index/_MAIN_1.toc deleted file mode 100644 index bfb4684..0000000 Binary files a/dbe/actions/marking_centre_index/_MAIN_1.toc and /dev/null differ diff --git a/dbe/actions/marking_centres.csv b/dbe/actions/marking_centres.csv deleted file mode 100644 index bc71ad4..0000000 --- a/dbe/actions/marking_centres.csv +++ /dev/null @@ -1,197 +0,0 @@ -PROVINCE,NAME -ec,ADELAIDE GYMNASIUM -ec,ALIWAL NORTH HIGH -ec,BURGERSDORP HS -ec,BYLETTS COMBINED -ec,CLARENDON GHS -ec,CRADOCK HIGH -ec,COLLEGIATE GHS -ec,DANIEL PIENAAR THS -ec,GILL COLLEGE -ec,GRENS HIGH -ec,KHANYISA HIGH -ec,MTHATHA HIGH -ec,MVENYANE -ec,NYANGA HIGH -ec,NICO MALAN HIGH -ec,PAUL SAUER HIGH -ec,PHANDULWAZI AGRIC. HIGH -ec,QUEENSTOWN GIRLS HIGH -ec,SIVE SPECIAL SCHOOL -ec,ST JOHNS COLLEGE -ec,STRELITZIA HIGH -ec,STUTTERHEIM HIGH -ec,VOLSKOOL HIGH -fs,Landboudal Jacobsdal High School -fs,Boshof High School -fs,Unitas High School -fs,Welkom Gimnasium High School -fs,Welkom High School -fs,Brentpark High School -fs,Kroonstad High School -fs,Parys High School -fs,Albert Moroka High School -fs,Moroka High School -fs,Unicom High School -fs,Bainsvlei Combined School -fs,Brebner High School -fs,Eunice High School -fs,Grey College High School -fs,H.T.S Louis Botha -fs,Marti Du Plessis School -fs,Oranje Meisies School -fs,Rosenhof High School -fs,Sentraal High School -fs,Bethlehem Voortrekker High School -fs,Ficksburg High School -fs,Seotlong High School -fs,Kroonstad High School Landbou Terrein -gt,Allen Glen High School -gt,Hoērskool President -gt,Hoērskool Garsfonten -gt,Mondeor High School -gt,Queens High School -gt,Nokuthula LSEN School -gt,Westridge High School -gt,Alberton High School -gt,Allen Glen High School -gt,Bracken High School -gt,Ferndale High School -gt,Florida Park High School -gt,Greenside High School -gt,Hoërskool Alberton -gt,Hoërskool Dinamika -gt,Hoërskool Elspark -gt,Hoërskool Florida -gt,Hoërskool Linden -gt,Hoërskool President -gt,Hoërskool Roodepoort -gt,Jeppe High School for Boys -gt,Krugersdorp High School -gt,Mondeor High School -gt,Parklands High School -gt,Parktown Boys’ High School -gt,Pretoria High School for Girls -gt,Queens High School -gt,Rand Girls High School -gt,Rand Park High School -gt,Roosevelt High School -gt,St. Barnabas High School -gt,Westridge High School -nl,ADAMS COLLEGE -nl,AM MOOLLA SPES NOVA -nl,DLANEZWA HIGH SCHOOL -nl,DURBAN HIGH SCHOOL -nl,EMPANGENI HIGH -nl,ESAYIDI TVET COLLEGE GAMALAKHE CAMPUS -nl,ESHOWE HIGH SCHOOL -nl,ESTCOURT HIGH SCHOOL -nl,GLENWOOD HIGH SCHOOL -nl,HAYTHORNE HIGH SCHOOL -nl,INANDA SEMINARY SCHOOL -nl,KOKSTAD COLLEGE -nl,LADYSMITH HIGH SCHOOL -nl,MARITZBURG COLLEGE -nl,NORTHWOOD -nl,PIONIER HIGH SCHOOL -nl,PIETERMARITZBURG GIRLS’ HIGH -nl,PORT SHEPSTONE SEN. PRIMARY -nl,PORT NATAL HIGH SCHOOL -nl,SAREL CILLIERS HIGH -nl,SIYAMUKELA HIGH -nl,SUID NATAL HIGH SCHOOL -nl,UMFOLOZI TVET COLLEGE ESIKHAWINI CAMPUS -nl,UMLAZI COMP. TECH. HIGH -nl,V.N. NAIK SCHOOL -nl,VRYHEID COMPREHENSIVE -nl,VRYHEID HIGH SCHOOL -nl,VUKILE HIGH SCHOOL -lp,Makhado CPTD 1 -lp,Makhado CPTD2 -lp,Mastec CPTD -lp,Tivumbeni CPTD 1 -lp,Tivumbeni CPTD2 -lp,Lord Milner 1 -lp,Capricorn High School -lp,General Piet Joubert -lp,Noorderland High -lp,Tom Naude Technical -lp,Pietersburg High School -lp,Northern Academy -lp,SJ van der Merwe Tech -lp,Nylstroom High School -lp,Hans Strydom High -lp,Warmbad High School -lp,Ben Viljoen High School -lp,Ben Voster High School -lp,Merensky High School -lp,Louis Trichardt High -lp,Lord Milner 2 School -lp,Settlers Agricultural High -lp,New Horizon School -lp,Ellisras High School -lp,Northern Academy Secondary School -lp,Frikkie Meyer High School -lp,Mokopane EMPC -mp,Izimbali Boarding School -mp,Hoërskool Volksrust -mp,Hoërskool Ermelo -mp,Ligbron Academy of Technology -mp,Hoërskool Nelspruit -mp,Lowveld High School -mp,Hoërskool Lydenburg -mp,Emakhazeni Boarding School -mp,Hoërskool Delmas -mp,Hoërskool Middelburg -mp,HTS Middelburg -mp,Hoërskool Piet Retief -mp,Hoërskool Rob Ferreira -mp,Steve Tshwete Boarding School -mp,Witbank High School -mp,Hoërskool Sybrand van Niekerk -mp,Hoërskool Generaal Hertzog -mp,Hoërskool Hoogenhout -mp,Jim van Tonder School -nc,Diamantveldt High School -nc,Kimberley Girl’s High School -nc,Northern Cape High School -nc,Kimberley Technical High School -nw,Brits High -nw,Hartbeespoort HS -nw,Rustenburg High -nw,Wagpos HS -nw,Schweizer Reneke High -nw,Vryburg High -nw,Hoër Volkskool Potch -nw,Hoërskool Ferdinand Postma (Potchefstroom) -nw,HTS Klerksdorp -nw,HTS Potchefstroom -nw,Klerksdorp High -nw,Potch Gimnasium -nw,Potch Girls High -nw,Lichtenburg High -nw,Sannieshof HS -nw,De Wilge -nw,Wolmaransstad HoëR -wc,BRACKENFELL HOËRSKOOL -wc,CAPE TEACHING AND LEADERSHIP INST. -wc,WESTERN CAPE SPORT SCHOOL -wc,DE KUILEN HOËRSKOOL -wc,DURBANVILLE HOËRSKOOL -wc,GROOTE SCHUUR HIGH SCHOOL -wc,JAN KRIEL-SKOOL -wc,RONDEBOSCH BOYS' HIGH SCHOOL -wc,S.A. COLLEGE HIGH SCHOOL -wc,WYNBERG BOYS' HIGH SCHOOL -wc,WYNBERG GIRLS' HIGH SCHOOL -wc,BRACKENFELL HOËRSKOOL -wc,CAPE TEACHING AND LEADERSHIP INST. -wc,WESTERN CAPE SPORT SCHOOL -wc,DE KUILEN HOËRSKOOL -wc,DURBANVILLE HOËRSKOOL -wc,GROOTE SCHUUR HIGH SCHOOL -wc,JAN KRIEL-SKOOL -wc,RONDEBOSCH BOYS' HIGH SCHOOL -wc,S.A. COLLEGE HIGH SCHOOL -wc,WYNBERG BOYS' HIGH SCHOOL -wc,WYNBERG GIRLS' HIGH SCHOOL diff --git a/dbe/actions/utils.py b/dbe/actions/utils.py deleted file mode 100644 index 952ff6f..0000000 --- a/dbe/actions/utils.py +++ /dev/null @@ -1,66 +0,0 @@ -from typing import Any, Dict, List, Text -from urllib.parse import urlencode, urljoin - -import httpx -import phonenumbers -from rasa_sdk import Tracker -from rasa_sdk.events import SlotSet - -from base.actions import config - -if hasattr(httpx, "AsyncClient"): - # from httpx>=0.11.0, the async client is a different class - HTTPXClient = getattr(httpx, "AsyncClient") -else: - HTTPXClient = getattr(httpx, "Client") - - -async def get_learner_profiles(msisdn: Text) -> List[Dict[Text, Any]]: - """ - Gets the learner profiles for `msisdn` from the data store. Returns an iterator. - """ - if not (config.EVENTSTORE_URL and config.EVENTSTORE_TOKEN): - return [] - async with HTTPXClient() as client: - url = urljoin(config.EVENTSTORE_URL, "/api/v2/dbeonbehalfofprofile/") - querystring = urlencode({"msisdn": msisdn}) - headers = { - "Authorization": f"Token {config.EVENTSTORE_TOKEN}", - "User-Agent": "rasa/dbe-healthcheckbot", - } - response = await client.get(f"{url}?{querystring}", headers=headers) - # Ignore pagination here. Sending more than 1000 options in a message is crazy - return response.json()["results"] - - -def option_list_from_profiles(profiles: List[Dict[Text, Any]]) -> Text: - """ - Given a list of user profiles, returns an option list of the profile names - """ - profiles = profiles + [{"name": "New HealthCheck"}] - return "\n".join( - f"*{i + 1}.* {profile['name']}" for i, profile in enumerate(profiles) - ) - - -def normalise_msisdn(msisdn: Text) -> Text: - m = phonenumbers.parse(msisdn, "ZA") - return phonenumbers.format_number(m, phonenumbers.PhoneNumberFormat.E164) - - -async def get_learner_profile_slots_dict(tracker: Tracker) -> Dict[Text, Any]: - msisdn = normalise_msisdn(tracker.sender_id) - profiles = await get_learner_profiles(msisdn) - display_profiles = option_list_from_profiles(profiles) - slots = { - "learner_profiles": profiles, - "display_learner_profiles": display_profiles, - } - if len(profiles) == 0: - slots["select_learner_profile"] = "new" - return slots - - -async def get_learner_profile_slots(tracker: Tracker) -> List[Dict[Text, Any]]: - slots = await get_learner_profile_slots_dict(tracker) - return [SlotSet(k, v) for k, v in slots.items()] diff --git a/dbe/data/lookup_tables/gender.txt b/dbe/data/lookup_tables/gender.txt deleted file mode 120000 index a53aac1..0000000 --- a/dbe/data/lookup_tables/gender.txt +++ /dev/null @@ -1 +0,0 @@ -../../../base/data/lookup_tables/gender.txt \ No newline at end of file diff --git a/dbe/data/lookup_tables/profiles.txt b/dbe/data/lookup_tables/profiles.txt deleted file mode 100644 index bed57f9..0000000 --- a/dbe/data/lookup_tables/profiles.txt +++ /dev/null @@ -1,11 +0,0 @@ -educator -learner -parent -actual_parent -support -marker -exam_assistant -exam_official -dbe_staff -dhet_staff -district_provincial_official diff --git a/dbe/data/lookup_tables/provinces.txt b/dbe/data/lookup_tables/provinces.txt deleted file mode 120000 index 7ab6f11..0000000 --- a/dbe/data/lookup_tables/provinces.txt +++ /dev/null @@ -1 +0,0 @@ -../../../base/data/lookup_tables/provinces.txt \ No newline at end of file diff --git a/dbe/data/nlu/address.md b/dbe/data/nlu/address.md deleted file mode 120000 index abca5cd..0000000 --- a/dbe/data/nlu/address.md +++ /dev/null @@ -1 +0,0 @@ -../../../base/data/nlu/address.md \ No newline at end of file diff --git a/dbe/data/nlu/affirm.md b/dbe/data/nlu/affirm.md deleted file mode 120000 index 5c60b0c..0000000 --- a/dbe/data/nlu/affirm.md +++ /dev/null @@ -1 +0,0 @@ -../../../base/data/nlu/affirm.md \ No newline at end of file diff --git a/dbe/data/nlu/child.md b/dbe/data/nlu/child.md deleted file mode 100644 index d02cdfe..0000000 --- a/dbe/data/nlu/child.md +++ /dev/null @@ -1,3 +0,0 @@ -## intent:child -- child -- next diff --git a/dbe/data/nlu/chitchat.md b/dbe/data/nlu/chitchat.md deleted file mode 120000 index 7c46c7f..0000000 --- a/dbe/data/nlu/chitchat.md +++ /dev/null @@ -1 +0,0 @@ -../../../base/data/nlu/chitchat.md \ No newline at end of file diff --git a/dbe/data/nlu/deny.md b/dbe/data/nlu/deny.md deleted file mode 120000 index 5322ac9..0000000 --- a/dbe/data/nlu/deny.md +++ /dev/null @@ -1 +0,0 @@ -../../../base/data/nlu/deny.md \ No newline at end of file diff --git a/dbe/data/nlu/exit.md b/dbe/data/nlu/exit.md deleted file mode 120000 index 7dea2bb..0000000 --- a/dbe/data/nlu/exit.md +++ /dev/null @@ -1 +0,0 @@ -../../../base/data/nlu/exit.md \ No newline at end of file diff --git a/dbe/data/nlu/healthcheck.md b/dbe/data/nlu/healthcheck.md deleted file mode 120000 index 9ee5406..0000000 --- a/dbe/data/nlu/healthcheck.md +++ /dev/null @@ -1 +0,0 @@ -../../../base/data/nlu/healthcheck.md \ No newline at end of file diff --git a/dbe/data/nlu/inform.md b/dbe/data/nlu/inform.md deleted file mode 120000 index 32eaaf9..0000000 --- a/dbe/data/nlu/inform.md +++ /dev/null @@ -1 +0,0 @@ -../../../base/data/nlu/inform.md \ No newline at end of file diff --git a/dbe/data/nlu/maybe.md b/dbe/data/nlu/maybe.md deleted file mode 120000 index d86d82f..0000000 --- a/dbe/data/nlu/maybe.md +++ /dev/null @@ -1 +0,0 @@ -../../../base/data/nlu/maybe.md \ No newline at end of file diff --git a/dbe/data/nlu/more.md b/dbe/data/nlu/more.md deleted file mode 120000 index 345adf4..0000000 --- a/dbe/data/nlu/more.md +++ /dev/null @@ -1 +0,0 @@ -../../../base/data/nlu/more.md \ No newline at end of file diff --git a/dbe/data/stories.md b/dbe/data/stories.md deleted file mode 100644 index abc7e02..0000000 --- a/dbe/data/stories.md +++ /dev/null @@ -1,79 +0,0 @@ -## happy path -* request_healthcheck - - slot{"terms": null} - - utter_welcome - - healthcheck_terms_form_dbe - - form{"name": "healthcheck_terms_form_dbe"} - - slot{"terms": null} - - slot{"terms": "yes"} - - form{"name": null} - - healthcheck_profile_form - - form{"name": "healthcheck_profile_form"} - - form{"name": null} - - utter_start_health_check - - healthcheck_form - - form{"name": "healthcheck_form"} - - form{"name": null} - - action_send_study_messages - - action_session_start - -## happy path returning user -* request_healthcheck - - slot{"terms": "yes"} - - utter_welcome_back - - healthcheck_profile_form - - form{"name": "healthcheck_profile_form"} - - form{"name": null} - - utter_start_health_check - - healthcheck_form - - form{"name": "healthcheck_form"} - - form{"name": null} - - action_send_study_messages - - action_session_start - -## session start new - - action_session_start - - slot{"terms": null} - -## session start returning - - action_session_start - - slot{"terms": "yes"} - -## child keyword new -* child - - action_set_profile_obo - - slot{"terms": null} - - utter_welcome - - healthcheck_terms_form_dbe - - form{"name": "healthcheck_terms_form_dbe"} - - slot{"terms": null} - - slot{"terms": "yes"} - - form{"name": null} - - healthcheck_profile_form - - form{"name": "healthcheck_profile_form"} - - form{"name": null} - - utter_start_health_check - - healthcheck_form - - form{"name": "healthcheck_form"} - - form{"name": null} - - action_session_start - -## child keyword returning -* child - - action_set_profile_obo - - slot{"terms": "yes"} - - utter_welcome_back - - healthcheck_profile_form - - form{"name": "healthcheck_profile_form"} - - form{"name": null} - - utter_start_health_check - - healthcheck_form - - form{"name": "healthcheck_form"} - - form{"name": null} - - action_session_start - -## exit -* exit - - action_exit - - slot{"terms": null} - - action_listen diff --git a/dbe/domain-eng.yml b/dbe/domain-eng.yml deleted file mode 100644 index ceaf7c4..0000000 --- a/dbe/domain-eng.yml +++ /dev/null @@ -1,1166 +0,0 @@ -intents: - - request_healthcheck - - affirm - - deny - - maybe - - inform - - more - - chitchat - - address - - child - - exit: - triggers: action_exit - -entities: - - province - - number - -actions: - - action_session_start - - action_exit - - action_set_profile_obo - - action_send_study_messages - -slots: - terms: - type: categorical - values: - - yes - confirm_details: - type: unfeaturized - auto_fill: false - confirm_details_parent: - type: unfeaturized - auto_fill: false - consent_parent: - type: unfeaturized - auto_fill: false - no_consent_parent: - type: unfeaturized - auto_fill: false - change_details: - type: unfeaturized - auto_fill: false - returning_user: - type: unfeaturized - auto_fill: false - profile: - type: unfeaturized - auto_fill: false - profile_display: - type: unfeaturized - auto_fill: false - age: - type: unfeaturized - auto_fill: false - gender: - type: unfeaturized - auto_fill: false - province: - type: unfeaturized - auto_fill: false - province_display: - type: unfeaturized - auto_fill: false - location: - type: unfeaturized - auto_fill: false - location_confirm: - type: unfeaturized - auto_fill: false - location_coords: - type: unfeaturized - auto_fill: false - city_location_coords: - type: unfeaturized - auto_fill: false - school: - type: unfeaturized - auto_fill: false - school_confirm: - type: unfeaturized - auto_fill: false - school_emis: - type: unfeaturized - auto_fill: false - medical_condition: - type: unfeaturized - auto_fill: false - medical_condition_obesity: - type: unfeaturized - auto_fill: false - medical_condition_diabetes: - type: unfeaturized - auto_fill: false - medical_condition_hypertension: - type: unfeaturized - auto_fill: false - medical_condition_cardio: - type: unfeaturized - auto_fill: false - medical_condition_asthma: - type: unfeaturized - auto_fill: false - medical_condition_tb: - type: unfeaturized - auto_fill: false - medical_condition_pregnant: - type: unfeaturized - auto_fill: false - medical_condition_respiratory: - type: unfeaturized - auto_fill: false - medical_condition_cardiac: - type: unfeaturized - auto_fill: false - medical_condition_immuno: - type: unfeaturized - auto_fill: false - symptoms_fever: - type: unfeaturized - auto_fill: false - symptoms_cough: - type: unfeaturized - auto_fill: false - symptoms_sore_throat: - type: unfeaturized - auto_fill: false - symptoms_difficulty_breathing: - type: unfeaturized - auto_fill: false - symptoms_taste_smell: - type: unfeaturized - auto_fill: false - exposure: - type: unfeaturized - auto_fill: false - tracing: - type: unfeaturized - auto_fill: false - learner_profiles: - type: unfeaturized - auto_fill: false - select_learner_profile: - type: unfeaturized - auto_fill: false - display_learner_profiles: - type: unfeaturized - auto_fill: false - requested_slot: - type: unfeaturized - obo_name: - type: unfeaturized - auto_fill: false - obo_age: - type: unfeaturized - auto_fill: false - obo_gender: - type: unfeaturized - auto_fill: false - obo_province: - type: unfeaturized - auto_fill: false - obo_location: - type: unfeaturized - auto_fill: false - obo_location_confirm: - type: unfeaturized - auto_fill: false - obo_location_coords: - type: unfeaturized - auto_fill: false - obo_city_location_coords: - type: unfeaturized - auto_fill: false - obo_school: - type: unfeaturized - auto_fill: false - obo_school_confirm: - type: unfeaturized - auto_fill: false - obo_school_emis: - type: unfeaturized - auto_fill: false - obo_medical_condition: - type: unfeaturized - auto_fill: false - obo_medical_condition_obesity: - type: unfeaturized - auto_fill: false - obo_medical_condition_diabetes: - type: unfeaturized - auto_fill: false - obo_medical_condition_hypertension: - type: unfeaturized - auto_fill: false - obo_medical_condition_cardio: - type: unfeaturized - auto_fill: false - obo_medical_condition_asthma: - type: unfeaturized - auto_fill: false - obo_medical_condition_tb: - type: unfeaturized - auto_fill: false - obo_medical_condition_pregnant: - type: unfeaturized - auto_fill: false - obo_medical_condition_respiratory: - type: unfeaturized - auto_fill: false - obo_medical_condition_cardiac: - type: unfeaturized - auto_fill: false - obo_medical_condition_immuno: - type: unfeaturized - auto_fill: false - obo_symptoms_fever: - type: unfeaturized - auto_fill: false - obo_symptoms_cough: - type: unfeaturized - auto_fill: false - obo_symptoms_sore_throat: - type: unfeaturized - auto_fill: false - obo_symptoms_difficulty_breathing: - type: unfeaturized - auto_fill: false - obo_symptoms_taste_smell: - type: unfeaturized - auto_fill: false - obo_exposure: - type: unfeaturized - auto_fill: false - obo_tracing: - type: unfeaturized - auto_fill: false - study_a_arm: - type: unfeaturized - -forms: - - healthcheck_form - - obo_healthcheck_form - - healthcheck_terms_form_dbe - - healthcheck_profile_form - - obo_healthcheck_profile_form - -responses: - utter_welcome: - - text: | - *HealthCheck* is your daily risk assessment tool, brought to you by the DBE to ensure the continued health and safety of learners, educators, markers, exam assistants and school support staff. Please help us by correctly answering a few questions about you and your health. The information you provide will assist us to guide and advise on your health and actions to take based on national guidelines. The data you enter will also help us plan and execute our national response to COVID-19. Thank you for coming forward and for contributing to the health of all citizens AND stopping the spread of the virus in our schools and work environment. - - utter_welcome_back: - - text: | - Welcome back to *HealthCheck*, your daily risk assessment tool, brought to you by the DBE to ensure the continued health and safety of learners, educators, markers, exam assistants and school support staff. - - Please help us by correctly answering a few questions about you and your health today. - - 📌Reply with *MENU* to return to the main menu - - utter_ask_terms: - - text: | - Do you agree to the attached PRIVACY POLICY that explains how your data is used and protected? - - 1. Yes, I accept - 2. No - - Or Reply: - MORE 📄 - _for more information about the terms and conditions_ - MENU 📌 _to return to the main menu_ - - utter_ask_terms_doc: - - document: "https://healthcheck-rasa-images.s3.af-south-1.amazonaws.com/E%C2%B3-Privacy-Policy-2021.pdf" - text: Privacy Policy - - utter_ask_confirm_details: - - text: | - Please confirm that the information below is correct, based on the information you shared previously: - School Name: {school} - Province: {province_display} - Role: {profile_display} - - Reply - *1.* YES - *2.* NO - - utter_ask_confirm_details_parent: - - text: | - Please confirm that the information below is correct, based on the information you shared previously: - Role: {profile_display} - - Reply - *1.* YES - *2.* NO - - utter_ask_confirm_details_marker: - - text: | - Please confirm that the information below is correct, based on the information you shared previously: - School / Facility Name: {school} - Province: {province_display} - Role: {profile_display} - - Reply - *1.* YES - *2.* NO - - utter_ask_change_details: - - text: | - What information would you like to change? - *1.* School Name - *2.* Province - *3.* Role - - utter_ask_change_details_marker: - - text: | - What information would you like to change? - *1.* School Name / Facility - *2.* Province - *3.* Role - - utter_ask_profile: - - text: | - Which describes you best? - - Reply - *1.* Educator - *2.* Learner - *3.* Parents / Guardian on behalf of learner - *4.* Parent - *5.* School Support or Admin - *6.* Marker or Moderator - *7.* Exam Assistant (EA) - *8.* Exam Official - *9.* National DBE Staff - *10.* National DHET Staff - *11.* District or Provincial Official - - utter_ask_consent_parent: - - text: | - If you are using this service on behalf of a child as a competent person to complete this *Covid-screening*, you need to confirm that you are sharing the child's information in line with the Health Check Privacy Policy. - - The POPI Act says that a competent person is someone who is legally competent to make decisions on behalf of a child. - - Please confirm that you consent to the Health Check Privacy Policy on behalf of the child. - - 1. Yes, I consent - 2. No - - Or Reply: - *MORE* 📄 for more information about the terms and conditions - *MENU* 📌 to return to the main menu - - utter_more_terms: - - text: | - You confirm that you are responsible for your medical care & treatment. This service only provides info. - - This service is not a substitute for professional medical advice/diagnosis/treatment. Get a qualified health provider's advice about your medical condition/care, especially if you develop severe symptoms. - - You confirm that you shouldn't disregard/delay seeking medical advice about treatment/care because of this service. Rely on info at your own risk. - - utter_more_terms_doc: - - document: "https://healthcheck-rasa-images.s3.af-south-1.amazonaws.com/E%C2%B3-Privacy-Policy-2021.pdf" - text: Terms and Conditions - - utter_ask_province: - - text: | - In which Province are you currently residing? - - Reply: - *1.* EASTERN CAPE - *2.* FREE STATE - *3.* GAUTENG - *4.* KWAZULU NATAL - *5.* LIMPOPO - *6.* MPUMALANGA - *7.* NORTH WEST - *8.* NORTHERN CAPE - *9.* WESTERN CAPE - - utter_incorrect_selection: - - text: | - This service works best when you use the options provided in *BOLD*. - 📌Reply *MENU* to return to the main menu - - utter_incorrect_location: - - text: | - If you have typed your address incorrectly, please try again. If you are unable to provide your address, please TYPE the name of your Suburb, Township, Town or Village (or nearest) - - utter_no_consent_parent: - - claim: release - text: | - Thank you. If you change your mind, type CHECK to restart your registration session - - utter_incorrect_school: - - text: | - You may have typed the school name incorrectly, please TYPE the name of your school again and check you've selected the right province. - - utter_incorrect_school_marker: - - text: | - You may have typed the facility or school name incorrectly, please TYPE the name of your school again and check you've selected the right province. - - utter_ask_age: - - text: "Please TYPE your age in years (eg. 35)" - - utter_ask_gender: - - text: | - Please provide us with the gender you identify as: - - Reply - *1.* MALE - *2.* FEMALE - *3.* OTHER - *4.* RATHER NOT SAY - - utter_ask_location: - - text: | - We need to collect your location. - - Please share your location 📍 using WhatsApp by following these steps: - - Tap the attach icon (+ or 📎) on the bottom of WhatsApp. - - Select location - - This will bring up a map with a pin showing your current location. Tap *Send Your Current Location* if this is your address. - - You can also choose from various other nearby locations if you think that the GPS is a bit off. - - Or TYPE the name of your Suburb, Township, Town or Village (or nearest)? - - utter_ask_location_confirm: - - text: | - We use Google Location Services to verify locations. Please confirm that the address below is correct based on the information you have shared: - {location} - - Reply - *1.* YES - *2.* NO - - utter_ask_school: - - text: | - Please TYPE the name of your school OR your school's EMIS number. (Type OTHER if you are not visiting a school) - - utter_ask_school_marker: - - text: | - Please TYPE the name of the facility, school OR school's EMIS number. - - utter_ask_school_confirm: - - text: | - Please confirm that the identified school below is correct, based on the information you shared: - {school} - - Reply - *1.* YES - *2.* NO - - utter_ask_school_confirm_marker: - - text: | - Please confirm that the identified facility or school below is correct, based on the information you shared: - {school} - - Reply - *1.* YES - *2.* NO - - utter_ask_medical_condition: - - text: | - Do you have any pre-existing medical conditions that we should be aware of? - - Reply - *1.* YES - *2.* NO - *3.* NOT SURE - - utter_ask_medical_condition_obesity: - - text: | - Has a doctor or other health professional diagnosed you with Obesity? - - Reply - *1.* YES - *2.* NO - - utter_ask_medical_condition_diabetes: - - text: | - Has a doctor or other health professional diagnosed you with Diabetes (Sugar Diabetes)? - - Reply - *1.* YES - *2.* NO - - utter_ask_medical_condition_hypertension: - - text: | - Has a doctor or other health professional diagnosed you with Hypertension (high blood pressure)? - - Reply - *1.* YES - *2.* NO - - utter_ask_medical_condition_cardio: - - text: | - Has a doctor or other health professional diagnosed you with Cardiovascular (heart) Disease? - - Reply - *1.* YES - *2.* NO - - utter_ask_medical_condition_asthma: - - text: | - Has a doctor or other health professional prescribed chronic medication for Asthma? - - Reply - *1.* YES - *2.* NO - - utter_ask_medical_condition_tb: - - text: | - Has a doctor or any other health professional initiated you on treatment for TB in the past 14 days? - - Reply - *1.* YES - *2.* NO - - utter_ask_medical_condition_pregnant: - - text: | - Has a doctor or other health professional recently confirmed that you are pregnant? - - Reply - *1.* YES - *2.* NO - - utter_pregnant_yes: - - text: | - If you are 27 weeks or less, please consult a healthcare professional for more information regarding how to protect yourself from COVID-19. - - If you are 27 weeks or more, notify your school and consult a healthcare professional for guidance regarding your health. - - utter_ask_medical_condition_respiratory: - - text: | - Has a doctor or other health professional diagnosed you with a severe chronic respiratory disease (Inherited conditions, e.g. cystic fibrosis, Chronic lung diseases)? - - Reply - *1.* YES - *2.* NO - - utter_ask_medical_condition_cardiac: - - text: | - Has a doctor or other health professional diagnosed you with severe hereditary Cardiac Disease (not corrected by surgery)? - - Reply - *1.* YES - *2.* NO - - utter_ask_medical_condition_immuno: - - text: | - Has a doctor or other health professional diagnosed you with severe immunodeficiency? - _This includes HIV infection, cancer treatment or if you are on chronic immuno-suppressive medication such as after an organ transplant?_ - - Reply - *1.* YES - *2.* NO - - utter_start_health_check: - - text: "Tell us how you are feeling today:" - - utter_ask_symptoms_fever: - - text: | - ⬛⬜⬜⬜⬜⬜⬜ - - Do you feel very hot or cold? Are you sweating or shivering? When you touch your forehead, does it feel hot? - - Reply - *1.* YES - *2.* NO - - utter_ask_symptoms_cough: - - text: | - ⬛⬛⬜⬜⬜⬜⬜ - - Do you have a cough that recently started? - - Reply - *1.* YES - *2.* NO - - utter_ask_symptoms_sore_throat: - - text: | - ⬛⬛⬛⬜⬜⬜⬜ - - Do you have a sore throat or pain when swallowing? - - Reply - *1.* YES - *2.* NO - - utter_ask_symptoms_difficulty_breathing: - - text: | - ⬛⬛⬛⬛⬜⬜⬜ - - Do you have breathlessness or difficulty in breathing, that you've noticed recently? - - Reply - *1.* YES - *2.* NO - - utter_ask_symptoms_taste_smell: - - text: | - ⬛⬛⬛⬛⬛⬜⬜ - - Have you noticed any recent changes in your ability to taste or smell things? - - Reply - *1.* YES - *2.* NO - - utter_ask_exposure: - - text: | - ⬛⬛⬛⬛⬛⬛⬜ - - Have you been in close contact to someone confirmed to be infected with COVID-19? - - Reply - *1.* YES - *2.* NO - *3.* NOT SURE - - utter_ask_tracing: - - text: | - ⬛⬛⬛⬛⬛⬛⬛ - - Finally, please confirm that the information you shared is *accurate* to the best of your knowledge and that you give the National Department of Health permission to contact you if necessary? - - Reply - *1.* YES - *2.* NO - - utter_risk_low: - - image: "https://healthcheck-rasa-images.s3.af-south-1.amazonaws.com/dbe-receipt-low.png" - claim: release - text: | - *HealthCheck Self-assessment Result* - - Risk Level: Low - Date Issued: {issued} - Expiry date: {expired} - - Well done for staying healthy. Stay safe, protect your school from COVID-19 by always wearing a mask and remember to always keep a spare mask in your school bag. - - Thank you for answering all questions. - - Based on your responses, you are at low risk of having COVID-19. Please continue to practice social distancing and proper hand sanitising. - - *If* you start to feel ill or if you come into contact with someone infected with COVID-19 please take this risk assessment again. This clearance is available for the next 24hours so please check in again tomorrow, with HealthCheck. The power to defeat COVID-19 is in your hands, play your part and keep South Africa safe. - - 🤒 Reply with *SYMPTOMS* for more information - 📌 Reply with *MENU* to return to the main menu - - utter_risk_low_parent: - - image: "https://healthcheck-rasa-images.s3.af-south-1.amazonaws.com/dbe-receipt-low.png" - claim: release - text: | - *HealthCheck Self-assessment Result* - - Risk Level: Low - Date Issued: {issued} - Expiry date: {expired} - - Well done for staying healthy. Stay safe and protect yourself and others from COVID-19. - - Thank you for answering all questions. - - Based on your responses, you are at low risk of having COVID-19. Please continue to practice social distancing and proper hand sanitising. - - *If* you start to feel ill or if you come into contact with someone infected with COVID-19 please take this risk assessment again. This clearance is available for the next 24hours so please check in again tomorrow, with HealthCheck. The power to defeat COVID-19 is in your hands, play your part and keep South Africa safe. - - Reply with *CHILD* to do a HealthCheck for your child(ren) - 🤒 Reply with *SYMPTOMS* for more information - 📌 Reply with *MENU* to return to the main menu - - utter_risk_low_support: - - image: "https://healthcheck-rasa-images.s3.af-south-1.amazonaws.com/dbe-receipt-low.png" - claim: release - text: | - *HealthCheck Self-assessment Result* - - Risk Level: Low - Date Issued: {issued} - Expiry date: {expired} - - Well done for staying healthy. - - Thank you for answering all questions. - - Based on your responses, you are at low risk of having COVID-19. Please continue to practice social distancing and proper hand sanitising. - - *If* you start to feel ill or if you come into contact with someone infected with COVID-19 please take this risk assessment again. This clearance is available for the next 24hours so please check in again tomorrow, with HealthCheck. The power to defeat COVID-19 is in your hands, play your part and keep South Africa safe. - - 🤒 Reply with *SYMPTOMS* for more information - 📌 Reply with *MENU* to return to the main menu - - utter_risk_moderate: - - image: "https://healthcheck-rasa-images.s3.af-south-1.amazonaws.com/dbe-receipt-mod.png" - claim: release - text: | - *HealthCheck Self-assessment Result* - - Risk Level: Moderate - Date Issued: {issued} - Expiry date: {expired} - - Well done for taking the risk assessment. Stay safe, protect your school from COVID-19 by always wearing a mask and remember to always keep a spare mask in your school bag. - - *Based on your responses, we recommend that you self-quarantine for the next 10 days and complete this HealthCheck every day to monitor your symptoms.* - - If possible, stay and sleep alone in a room that has a window with good air flowing through. - - You can talk to other family members and go outside, but you should stay at least 2 meters away from everyone at all times. - - You should not have visitors at your house during this time. - - One family member should be assigned as your caregiver. This exposes less of your family and also ensures that you and this caregiver can develop good habits so that they do not catch the virus from you. - - If these procedures are not possible, you should contact your health care worker or clinic for alternative options. - - Reply with *QUARANTINE* for more information on how to self-quarantine - 📌Reply with *TESTING* for more information or *MENU* to return to the main menu - - utter_risk_moderate_parent: - - image: "https://healthcheck-rasa-images.s3.af-south-1.amazonaws.com/dbe-receipt-mod.png" - claim: release - text: | - *HealthCheck Self-assessment Result* - - Risk Level: Moderate - Date Issued: {issued} - Expiry date: {expired} - - Well done for taking the risk assessment. Stay safe and continue to monitor your health. - - *Based on your responses, we recommend that you self-quarantine for the next 10 days and complete this HealthCheck every day to monitor your symptoms.* - - If possible, stay and sleep alone in a room that has a window with good air flowing through. - - You can talk to other family members and go outside, but you should stay at least 2 meters away from everyone at all times. - - You should not have visitors at your house during this time. - - One family member should be assigned as your caregiver. This exposes less of your family and also ensures that you and this caregiver can develop good habits so that they do not catch the virus from you. - - If these procedures are not possible, you should contact your health care worker or clinic for alternative options. - - Reply with *CHILD* to do a HealthCheck for your child(ren) - Reply with *QUARANTINE* for more information on how to self-quarantine - 📌Reply with *TESTING* for more information or *MENU* to return to the main menu - - utter_risk_moderate_support: - - image: "https://healthcheck-rasa-images.s3.af-south-1.amazonaws.com/dbe-receipt-mod.png" - claim: release - text: | - *HealthCheck Self-assessment Result* - - Risk Level: Moderate - Date Issued: {issued} - Expiry date: {expired} - - Well done for taking the risk assessment. - - *Based on your responses, we recommend that you self-quarantine for the next 10 days and complete this HealthCheck every day to monitor your symptoms.* - - If possible, stay and sleep alone in a room that has a window with good air flowing through. - - You can talk to other family members and go outside, but you should stay at least 2 meters away from everyone at all times. - - You should not have visitors at your house during this time. - - One family member should be assigned as your caregiver. This exposes less of your family and also ensures that you and this caregiver can develop good habits so that they do not catch the virus from you. - - If these procedures are not possible, you should contact your health care worker or clinic for alternative options. - - Reply with *QUARANTINE* for more information on how to self-quarantine - 📌Reply with *TESTING* for more information or *MENU* to return to the main menu - - utter_risk_high: - - image: "https://healthcheck-rasa-images.s3.af-south-1.amazonaws.com/dbe-receipt-high.png" - claim: release - text: | - *HealthCheck Self-assessment Result* - - Risk Level: High - Date Issued: {issued} - Expiry date: {expired} - - Stay safe, protect your school from COVID-19 by always wearing a mask and remember to always keep a spare mask in your school bag. - - *Based on your responses, you may be eligible for testing to find out if you are infected with COVID-19.* - - Call the COVID-19 Hotline: 0800029999, your healthcare practitioner or your local health facility, for further information on what to do next, how and where to get tested if advised to do so. - - Follow these important steps when seeking care: - - Avoid contact with other people in your household and your community, if possible, stay in a separate room. - - Ensure you put on a face mask before you enter a healthcare facility. - - Wash your hands frequently with soap and water and or sanitise with a 70% alcohol hand sanitiser - - If you have or start getting severe symptoms, seek medical care urgently. - - Reply with *QUARANTINE* for more information on how to self-quarantine - 📌Reply with *TESTING* for more information or *MENU* to return to the main menu - - utter_risk_high_parent: - - image: "https://healthcheck-rasa-images.s3.af-south-1.amazonaws.com/dbe-receipt-high.png" - claim: release - text: | - *HealthCheck Self-assessment Result* - - Risk Level: High - Date Issued: {issued} - Expiry date: {expired} - - *Based on your responses, you may be eligible for testing to find out if you are infected with COVID-19.* - - Call the COVID-19 Hotline: 0800029999, your healthcare practitioner or your local health facility, for further information on what to do next, how and where to get tested if advised to do so. - - Follow these important steps when seeking care: - - Avoid contact with other people in your household and your community, if possible, stay in a separate room. - - Ensure you put on a face mask before you enter a healthcare facility. - - Wash your hands frequently with soap and water and or sanitise with a 70% alcohol hand sanitiser - - If you have or start getting severe symptoms, seek medical care urgently. - - Reply with *CHILD* to do a HealthCheck for your child(ren) - Reply with *QUARANTINE* for more information on how to self-quarantine - 📌Reply with *TESTING* for more information or *MENU* to return to the main menu - - utter_risk_high_support: - - image: "https://healthcheck-rasa-images.s3.af-south-1.amazonaws.com/dbe-receipt-high.png" - claim: release - text: | - *HealthCheck Self-assessment Result* - - Risk Level: High - Date Issued: {issued} - Expiry date: {expired} - - *Based on your responses, you may be eligible for testing to find out if you are infected with COVID-19.* - - Call the COVID-19 Hotline: 0800029999, your healthcare practitioner or your local health facility, for further information on what to do next, how and where to get tested if advised to do so. - - Follow these important steps when seeking care: - - Avoid contact with other people in your household and your community, if possible, stay in a separate room. - - Ensure you put on a face mask before you enter a healthcare facility. - - Wash your hands frequently with soap and water and or sanitise with a 70% alcohol hand sanitiser - - If you have or start getting severe symptoms, seek medical care urgently. - - Reply with *QUARANTINE* for more information on how to self-quarantine - 📌Reply with *TESTING* for more information or *MENU* to return to the main menu - -# claim: revert will ignore the message text, and reevaluate this message using -# turn automation - utter_exit: - - text: "Thank you. You can do your HealthCheck anytime by typing the word *CHECK*." - claim: revert - - utter_ask_select_learner_profile: - - text: | - Whose HealthCheck would you like to do? - - {display_learner_profiles} - - utter_ask_obo_name: - - text: "Please TYPE your child's name (ie. Thato)" - - utter_ask_obo_age: - - text: "Please TYPE how old {obo_name} is in years (eg. 35)" - - utter_ask_obo_gender: - - text: | - Please provide us with the gender of {obo_name}: - - Reply - *1.* MALE - *2.* FEMALE - *3.* OTHER - *4.* RATHER NOT SAY - - utter_ask_obo_province: - - text: | - In which Province is {obo_name} currently residing? - - Reply: - *1.* EASTERN CAPE - *2.* FREE STATE - *3.* GAUTENG - *4.* KWAZULU NATAL - *5.* LIMPOPO - *6.* MPUMALANGA - *7.* NORTH WEST - *8.* NORTHERN CAPE - *9.* WESTERN CAPE - - utter_ask_obo_location: - - text: | - We need to collect {obo_name} location. - - Please share your location 📍 using WhatsApp by following these steps: - - Tap the attach icon (+ or 📎) on the bottom of WhatsApp. - - Select location - - This will bring up a map with a pin showing your current location. Tap *Send Your Current Location* if this is your address. - - You can also choose from various other nearby locations if you think that the GPS is a bit off. - - Or TYPE the name of your Suburb, Township, Town or Village (or nearest)? - - utter_ask_obo_location_confirm: - - text: | - We use Google Location Services to verify locations. Please confirm that the address below is correct based on the information you have shared: - {obo_location} - - Reply - *1.* YES - *2.* NO - - utter_ask_obo_school: - - text: "Please TYPE the name of {obo_name}'s school." - - utter_ask_obo_school_confirm: - - text: | - Please confirm that the school below is correct based on the information you have shared: - {obo_school} - - Reply - *1.* YES - *2.* NO - - utter_ask_obo_medical_condition: - - text: | - Does {obo_name} have any pre-existing medical conditions that we should be aware of? - - Reply - *1.* YES - *2.* NO - *3.* NOT SURE - - utter_ask_obo_medical_condition_obesity: - - text: | - Has a doctor or other health professional diagnosed {obo_name} with Obesity? - - Reply - *1.* YES - *2.* NO - - utter_ask_obo_medical_condition_diabetes: - - text: | - Has a doctor or other health professional diagnosed {obo_name} with Diabetes (Sugar Diabetes)? - - Reply - *1.* YES - *2.* NO - - utter_ask_obo_medical_condition_hypertension: - - text: | - Has a doctor or other health professional diagnosed {obo_name} with Hypertension (High blood pressure)? - - Reply - *1.* YES - *2.* NO - - utter_ask_obo_medical_condition_cardio: - - text: | - Has a doctor or other health professional diagnosed {obo_name} with Cardiovascular (heart) Disease? - - Reply - *1.* YES - *2.* NO - - utter_ask_obo_medical_condition_asthma: - - text: | - Has a doctor or other health professional prescribed {obo_name} chronic medication for Asthma? - - Reply - *1.* YES - *2.* NO - - utter_ask_obo_medical_condition_tb: - - text: | - Has a doctor or any other health professional initiated {obo_name} on treatment for TB in the past 14 days? - - Reply - *1.* YES - *2.* NO - - utter_ask_obo_medical_condition_pregnant: - - text: | - Has a doctor or other health professional recently confirmed that {obo_name} is pregnant? - - Reply - *1.* YES - *2.* NO - - utter_obo_pregnant_yes: - - text: | - If {obo_name} is 27 weeks or less, please consult a healthcare professional for more information regarding how to protect {obo_name} from COVID-19. - - If {obo_name} is 27 weeks or more, notify your school and consult a healthcare professional for guidance regarding {obo_name}'s health. - - utter_ask_obo_medical_condition_respiratory: - - text: | - Has a doctor or other health professional diagnosed {obo_name} with a severe chronic respiratory disease (Inherited conditions, e.g. cystic fibrosis, Chronic lung diseases)? - - Reply - *1.* YES - *2.* NO - - utter_ask_obo_medical_condition_cardiac: - - text: | - Has a doctor or other health professional diagnosed {obo_name} with severe hereditary Cardiac Disease (not corrected by surgery)? - - Reply - *1.* YES - *2.* NO - - utter_ask_obo_medical_condition_immuno: - - text: | - Has a doctor or other health professional diagnosed {obo_name} with severe immunodeficiency? - _This includes HIV infection, cancer treatment or if {obo_name} is on chronic immuno-suppressive medication such as after an organ transplant?_ - - Reply - *1.* YES - *2.* NO - - utter_obo_start_healthcheck: - - text: | - Tell us how {obo_name} is feeling today: - - utter_ask_obo_symptoms_fever: - - text: | - ⬛⬜⬜⬜⬜⬜⬜ - - Does {obo_name} feel very hot or cold? Are they sweating or shivering? When they touch their forehead, does it feel hot? - - Reply - *1.* YES - *2.* NO - - utter_ask_obo_symptoms_cough: - - text: | - ⬛⬛⬜⬜⬜⬜⬜ - - Does {obo_name} have a cough that recently started? - - Reply - *1.* YES - *2.* NO - - utter_ask_obo_symptoms_sore_throat: - - text: | - ⬛⬛⬛⬜⬜⬜⬜ - - Does {obo_name} have a sore throat or pain when swallowing? - - Reply - *1.* YES - *2.* NO - - utter_ask_obo_symptoms_difficulty_breathing: - - text: | - ⬛⬛⬛⬛⬜⬜⬜ - - Does {obo_name} have breathlessness or difficulty in breathing, that you've noticed recently? - - Reply - *1.* YES - *2.* NO - - utter_ask_obo_symptoms_taste_smell: - - text: | - ⬛⬛⬛⬛⬛⬜⬜ - - Has {obo_name} noticed any recent changes in their ability to taste or smell things? - - Reply - *1.* YES - *2.* NO - - utter_ask_obo_exposure: - - text: | - ⬛⬛⬛⬛⬛⬛⬜ - - Has {obo_name} been in close contact to someone confirmed to be infected with COVID-19? - - Reply - *1.* YES - *2.* NO - *3.* NOT SURE - - utter_ask_obo_tracing: - - text: | - ⬛⬛⬛⬛⬛⬛⬛ - - Finally, please confirm that the information you shared is *accurate* to the best of your knowledge and that you give the National Department of Health permission to contact you if necessary? - - Reply - *1.* YES - *2.* NO - - utter_obo_risk_low: - - image: "https://healthcheck-rasa-images.s3.af-south-1.amazonaws.com/dbe-receipt-low.png" - claim: release - text: | - *HealthCheck Self-assessment Result* - - {obo_name} - Risk Level: Low - Date Issued: {issued} - Expiry date: {expired} - - Well done for staying healthy. Stay safe, protect their school from COVID-19 by always reminding them to wear a mask and to always keep a spare mask in their school bag. - - Thank you for answering all questions. - - Based on {obo_name}'s responses, they are at low risk of having COVID-19. Please continue to practice social distancing and proper hand sanitising. - - *If* they start to feel ill or come into contact with someone infected with COVID-19 please take this risk assessment again. This clearance is available for the next 24hours so please check in again tomorrow, with HealthCheck. The power to defeat COVID-19 is in your hands, play your part and keep South Africa safe. - - Reply with *CHECK* to do a HealthCheck for another child. - 🤒 Reply with *SYMPTOMS* for more information - 📌 Reply with *MENU* to return to the main menu - - utter_obo_risk_moderate: - - image: "https://healthcheck-rasa-images.s3.af-south-1.amazonaws.com/dbe-receipt-mod.png" - claim: release - text: | - *HealthCheck Self-assessment Result* - - {obo_name} - Risk Level: Medium - Date Issued: {issued} - Expiry date: {expired} - - Well done for taking the risk assessment on behalf of {obo_name}. Stay safe, protect their school from COVID-19 by always reminding them to wear a mask and to always keep a spare mask in their school bag. - - *Based on {obo_name}'s responses, we recommend that they self-quarantine for the next 10 days and complete this HealthCheck daily to self monitor their symptoms.* - - If possible, they should stay and sleep alone in a room that has a window with good air flowing through. - - They can talk to other family members and go outside, but should stay at least 2 meters away from everyone at all times. - - They should not have visitors at the house during this time. - - One family member should be assigned as their caregiver. This exposes less of your family and also ensures that [child's name] and the caregiver can develop good habits so that they do not catch the virus from them. - - If these procedures are not possible, please contact your health care worker or clinic for alternative options. - - Reply with *CHECK* to do a HealthCheck for another child. - Reply with *QUARANTINE* for more information on how to self-quarantine - 📌Reply with *TESTING* for more information or *MENU* to return to the main menu - - utter_obo_risk_high: - - image: "https://healthcheck-rasa-images.s3.af-south-1.amazonaws.com/dbe-receipt-high.png" - claim: release - text: | - *HealthCheck Self-assessment Result* - - {obo_name} - Risk Level: High - Date Issued: {issued} - Expiry date: {expired} - - Stay safe, protect their school from COVID-19 by always reminding them to wear a mask and to always keep a spare mask in their school bag. - - *Based on your responses, {obo_name} may be eligible for testing to find out if they are infected with COVID-19.* - - Call the COVID-19 Hotline: 0800029999, or your healthcare practitioner, for further information on what to do next, how and where to get tested if advised to do so. - - Follow these important steps when seeking care: - - Avoid contact with other people in your household and your community, if possible, stay in a separate room. - - Ensure you and {obo_name} put on a face mask before entering a healthcare facility. - - Wash your hands frequently with soap and water and or sanitise with a 70% alcohol hand sanitiser. - - If {obo_name} has or starts getting severe symptoms, seek medical care urgently. - - Reply with *CHECK* to do a HealthCheck for another child. - Reply with *QUARANTINE* for more information on how to self-quarantine - 📌Reply with *TESTING* for more information or *MENU* to return to the main menu - -session_config: - session_expiration_time: 5 - carry_over_slots_to_new_session: false diff --git a/dbe/tests/__init__.py b/dbe/tests/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/dbe/tests/conversation_tests.md b/dbe/tests/conversation_tests.md deleted file mode 100644 index fd895cf..0000000 --- a/dbe/tests/conversation_tests.md +++ /dev/null @@ -1,35 +0,0 @@ -#### This file contains tests to evaluate that your bot behaves as expected. -#### If you want to learn more, please see the docs: https://legacy-docs-v1.rasa.com/user-guide/testing-your-assistant/ - -## happy path healthcheck -* request_healthcheck: check - - slot{"terms": null} - - utter_welcome - - healthcheck_terms_form_dbe - - form{"name": "healthcheck_terms_form_dbe"} - - slot{"terms": null} - - slot{"terms": "yes"} - - form{"name": null} - - healthcheck_profile_form - - form{"name": "healthcheck_profile_form"} - - form{"name": null} - - utter_start_health_check - - healthcheck_form - - form{"name": "healthcheck_form"} - - form{"name": null} - - action_send_study_messages - - action_session_start - -## happy path healthcheck returning user -* request_healthcheck: check - - slot{"terms": "yes"} - - utter_welcome_back - - healthcheck_profile_form - - form{"name": "healthcheck_profile_form"} - - form{"name": null} - - utter_start_health_check - - healthcheck_form - - form{"name": "healthcheck_form"} - - form{"name": null} - - action_send_study_messages - - action_session_start diff --git a/dbe/tests/test_actions.py b/dbe/tests/test_actions.py deleted file mode 100644 index b99258a..0000000 --- a/dbe/tests/test_actions.py +++ /dev/null @@ -1,1446 +0,0 @@ -from datetime import datetime, timedelta, timezone -from unittest import TestCase, mock - -import pytest -from rasa_sdk import Tracker -from rasa_sdk.events import SlotSet -from rasa_sdk.executor import CollectingDispatcher - -from dbe.actions.actions import ( - ActionExit, - ActionSessionStart, - ActionSetProfileObo, - DBEHealthCheckTermsForm, - HealthCheckForm, - HealthCheckProfileForm, -) - - -class DBEHealthCheckTermsFormTests(TestCase): - def test_validate_terms(self): - form = DBEHealthCheckTermsForm() - tracker = Tracker( - "27820001001", {"terms": None}, {}, [], False, None, {}, "action_listen" - ) - response = form.validate_terms("1", CollectingDispatcher(), tracker, {}) - self.assertEqual(response, {"terms": "yes"}) - response = form.validate_terms("more", CollectingDispatcher(), tracker, {}) - self.assertEqual(response, {"terms": None}) - response = form.validate_terms("2", CollectingDispatcher(), tracker, {}) - self.assertEqual(response, {"terms": "no"}) - - def test_terms_doc_sent(self): - form = DBEHealthCheckTermsForm() - tracker = Tracker( - "27820001001", {"terms": None}, {}, [], False, None, {}, "action_listen" - ) - dispatcher = CollectingDispatcher() - form.request_next_slot(dispatcher, tracker, "dbe") - assert len(dispatcher.messages) == 2 - message = dispatcher.messages[1] - assert message["template"] == "utter_ask_terms_doc" - - def test_terms_doc_sent_on_more(self): - form = DBEHealthCheckTermsForm() - tracker = Tracker( - "27820001001", {"terms": None}, {}, [], False, None, {}, "action_listen" - ) - dispatcher = CollectingDispatcher() - form.validate_terms("more", dispatcher, tracker, {}) - form.request_next_slot(dispatcher, tracker, "dbe") - assert len(dispatcher.messages) == 3 - message = dispatcher.messages[2] - assert message["template"] == "utter_ask_terms_doc" - - def test_terms_doc_sent_on_invalid_input(self): - form = DBEHealthCheckTermsForm() - tracker = Tracker( - "27820001001", {"terms": None}, {}, [], False, None, {}, "action_listen" - ) - dispatcher = CollectingDispatcher() - form.validate_terms("invalid", dispatcher, tracker, {}) - form.request_next_slot(dispatcher, tracker, "dbe") - assert len(dispatcher.messages) == 3 - message = dispatcher.messages[2] - assert message["template"] == "utter_ask_terms_doc" - - def test_terms_doc_not_sent_on_no(self): - form = DBEHealthCheckTermsForm() - tracker = Tracker( - "27820001001", {"terms": "None"}, {}, [], False, None, {}, "action_listen" - ) - dispatcher = CollectingDispatcher() - form.validate_terms("no", dispatcher, tracker, {}) - form.request_next_slot(dispatcher, tracker, "dbe") - assert len(dispatcher.messages) == 0 - - -@pytest.mark.asyncio -async def test_deny_terms(): - action = DBEHealthCheckTermsForm() - dispatcher = CollectingDispatcher() - events = await action.submit( - dispatcher, - Tracker( - "27820001001", {"terms": "no"}, {}, [], False, None, {}, "action_listen" - ), - {}, - ) - assert SlotSet("terms", None) in events - [message1] = dispatcher.messages - assert message1["template"] == "utter_no_consent_parent" - - -@pytest.mark.asyncio -async def test_affirm_terms(): - action = DBEHealthCheckTermsForm() - dispatcher = CollectingDispatcher() - events = await action.submit( - dispatcher, - Tracker( - "27820001001", {"terms": "yes"}, {}, [], False, None, {}, "action_listen" - ), - {}, - ) - assert events == [] - message = dispatcher.messages - assert message == [] - - -class HealthCheckProfileFormTests(TestCase): - def test_validate_age(self): - """ - Should be an age between 0 and 150 - """ - form = HealthCheckProfileForm() - tracker = Tracker( - "27820001001", {"province": "wc"}, {}, [], False, None, {}, "action_listen" - ) - response = form.validate_age("1", CollectingDispatcher(), tracker, {}) - self.assertEqual(response, {"age": "1"}) - response = form.validate_age("0", CollectingDispatcher(), tracker, {}) - self.assertEqual(response, {"age": None}) - response = form.validate_age("150", CollectingDispatcher(), tracker, {}) - self.assertEqual(response, {"age": None}) - response = form.validate_age("abc", CollectingDispatcher(), tracker, {}) - self.assertEqual(response, {"age": None}) - - def test_validate_consent_parent(self): - """ - Should be yes no - """ - form = HealthCheckProfileForm() - tracker = Tracker( - "27820001001", {"province": "wc"}, {}, [], False, None, {}, "action_listen" - ) - response = form.validate_consent_parent( - "1", CollectingDispatcher(), tracker, {} - ) - self.assertEqual(response, {"consent_parent": "yes"}) - response = form.validate_consent_parent( - "0", CollectingDispatcher(), tracker, {} - ) - self.assertEqual(response, {"consent_parent": None}) - response = form.validate_consent_parent( - "2", CollectingDispatcher(), tracker, {} - ) - self.assertEqual(response, {"consent_parent": "no"}) - response = form.validate_consent_parent( - "more", CollectingDispatcher(), tracker, {} - ) - self.assertEqual(response, {"consent_parent": None}) - response = form.validate_consent_parent( - "menu", CollectingDispatcher(), tracker, {} - ) - self.assertEqual(response, {"consent_parent": None}) - - def test_validate_school(self): - """ - Stores first search result - """ - form = HealthCheckProfileForm() - tracker = Tracker( - "27820001001", {"province": "wc"}, {}, [], False, None, {}, "action_listen" - ) - response = form.validate_school( - "bergvleet", CollectingDispatcher(), tracker, {} - ) - self.assertEqual( - response, {"school": "BERGVLIET HIGH SCHOOL", "school_emis": "105310201"} - ) - - def test_validate_school_dbe_staff(self): - """ - Should search and sort against both school and marking centre indexes - """ - form = HealthCheckProfileForm() - tracker = Tracker( - "27820001001", - {"province": "wc", "profile": "dbe_staff"}, - {}, - [], - False, - None, - {}, - "action_listen", - ) - response = form.validate_school( - "cape teaching and leadershup", CollectingDispatcher(), tracker, {} - ) - self.assertEqual( - response, - {"school": "CAPE TEACHING AND LEADERSHIP INST.", "school_emis": None}, - ) - response = form.validate_school( - "carpe diem", CollectingDispatcher(), tracker, {} - ) - self.assertEqual( - response, {"school": "CARPE DIEM SKOOL", "school_emis": "118456233"} - ) - - def test_validate_school_dhet_staff(self): - """ - Should search and sort against both school and marking centre indexes - """ - form = HealthCheckProfileForm() - tracker = Tracker( - "27820001001", - {"province": "wc", "profile": "dhet_staff"}, - {}, - [], - False, - None, - {}, - "action_listen", - ) - response = form.validate_school( - "cape teaching and leadershup", CollectingDispatcher(), tracker, {} - ) - self.assertEqual( - response, - {"school": "CAPE TEACHING AND LEADERSHIP INST.", "school_emis": None}, - ) - response = form.validate_school( - "carpe diem", CollectingDispatcher(), tracker, {} - ) - self.assertEqual( - response, {"school": "CARPE DIEM SKOOL", "school_emis": "118456233"} - ) - - def test_validate_school_district_provincial_official(self): - """ - Should search and sort against both school and marking centre indexes - """ - form = HealthCheckProfileForm() - tracker = Tracker( - "27820001001", - {"province": "wc", "profile": "district_provincial_official"}, - {}, - [], - False, - None, - {}, - "action_listen", - ) - response = form.validate_school( - "cape teaching and leadershup", CollectingDispatcher(), tracker, {} - ) - self.assertEqual( - response, - {"school": "CAPE TEACHING AND LEADERSHIP INST.", "school_emis": None}, - ) - response = form.validate_school( - "carpe diem", CollectingDispatcher(), tracker, {} - ) - self.assertEqual( - response, {"school": "CARPE DIEM SKOOL", "school_emis": "118456233"} - ) - - def test_validate_school_or_clause(self): - """ - The search should be an OR between terms - """ - form = HealthCheckProfileForm() - tracker = Tracker( - "27820001001", {"province": "fs"}, {}, [], False, None, {}, "action_listen" - ) - response = form.validate_school( - "bedelia primary", CollectingDispatcher(), tracker, {} - ) - self.assertEqual( - response, {"school": "BEDELIA P/S", "school_emis": "444712090"} - ) - - def test_validate_school_other(self): - """ - Stores the school as other and moves on - """ - form = HealthCheckProfileForm() - tracker = Tracker( - "27820001001", {"province": "wc"}, {}, [], False, None, {}, "action_listen" - ) - response = form.validate_school("OthER ", CollectingDispatcher(), tracker, {}) - self.assertEqual( - response, {"school": "OTHER", "school_emis": None, "school_confirm": "yes"} - ) - - def test_validate_school_no_results(self): - """ - Returns error message and clears value for province - """ - form = HealthCheckProfileForm() - tracker = Tracker( - "27820001001", {"province": "gt"}, {}, [], False, None, {}, "action_listen" - ) - dispatcher = CollectingDispatcher() - response = form.validate_school("bergvleet", dispatcher, tracker, {}) - self.assertEqual(response, {"school": None, "province": None}) - [message] = dispatcher.messages - self.assertEqual(message["template"], "utter_incorrect_school") - - def test_validate_school_dbe_staff_no_results(self): - """ - Returns error message and clears value for province - """ - form = HealthCheckProfileForm() - tracker = Tracker( - "27820001001", - {"province": "gt", "profile": "dbe_staff"}, - {}, - [], - False, - None, - {}, - "action_listen", - ) - dispatcher = CollectingDispatcher() - response = form.validate_school("bergvleet", dispatcher, tracker, {}) - self.assertEqual(response, {"school": None, "province": None}) - [message] = dispatcher.messages - self.assertEqual(message["template"], "utter_incorrect_school") - - def test_validate_school_dhet_staff_no_results(self): - """ - Returns error message and clears value for province - """ - form = HealthCheckProfileForm() - tracker = Tracker( - "27820001001", - {"province": "gt", "profile": "dhet_staff"}, - {}, - [], - False, - None, - {}, - "action_listen", - ) - dispatcher = CollectingDispatcher() - response = form.validate_school("bergvleet", dispatcher, tracker, {}) - self.assertEqual(response, {"school": None, "province": None}) - [message] = dispatcher.messages - self.assertEqual(message["template"], "utter_incorrect_school") - - def test_validate_school_no_results_marker(self): - """ - Returns error message and clears value for province - """ - form = HealthCheckProfileForm() - tracker = Tracker( - "27820001001", - {"province": "gt", "profile": "marker"}, - {}, - [], - False, - None, - {}, - "action_listen", - ) - dispatcher = CollectingDispatcher() - response = form.validate_school("bergvleet", dispatcher, tracker, {}) - self.assertEqual(response, {"school": None, "province": None}) - [message] = dispatcher.messages - self.assertEqual(message["template"], "utter_incorrect_school_marker") - - def test_validate_school_confirm_no(self): - """ - Try again getting the name of the school - """ - form = HealthCheckProfileForm() - tracker = Tracker("27820001001", {}, {}, [], False, None, {}, "action_listen") - dispatcher = CollectingDispatcher() - response = form.validate_school_confirm("no", dispatcher, tracker, {}) - self.assertEqual(response, {"school": None, "school_confirm": None}) - - def test_validate_school_confirm_yes(self): - """ - Confirms the name of the school - """ - form = HealthCheckProfileForm() - tracker = Tracker("27820001001", {}, {}, [], False, None, {}, "action_listen") - dispatcher = CollectingDispatcher() - response = form.validate_school_confirm("yes", dispatcher, tracker, {}) - self.assertEqual(response, {"school_confirm": "yes"}) - - def test_validate_confirm_details(self): - """ - Confirms the returning user details are correct - """ - form = HealthCheckProfileForm() - tracker = Tracker("27820001001", {}, {}, [], False, None, {}, "action_listen") - dispatcher = CollectingDispatcher() - response = form.validate_confirm_details("yes", dispatcher, tracker, {}) - self.assertEqual(response, {"confirm_details": "yes"}) - - def test_validate_confirm_details_parent(self): - """ - Confirms the returning user details are correct - """ - form = HealthCheckProfileForm() - tracker = Tracker("27820001001", {}, {}, [], False, None, {}, "action_listen") - dispatcher = CollectingDispatcher() - response = form.validate_confirm_details_parent("yes", dispatcher, tracker, {}) - self.assertEqual(response, {"confirm_details_parent": "yes"}) - - def test_validate_province(self): - """ - Should also update the province_display slot - """ - form = HealthCheckProfileForm() - tracker = Tracker("27820001001", {}, {}, [], False, None, {}, "action_listen") - dispatcher = CollectingDispatcher() - response = form.validate_province("wc", dispatcher, tracker, {}) - self.assertEqual( - response, {"province": "wc", "province_display": "WESTERN CAPE"} - ) - - def test_generic_validator(self): - """ - Should use the validate_generic with default data - """ - # Use the validate_medical_condition_asthma to test generic validator - form = HealthCheckProfileForm() - tracker = Tracker("27820001001", {}, {}, [], False, None, {}, "action_listen") - dispatcher = CollectingDispatcher() - response = form.validate_medical_condition_asthma("2", dispatcher, tracker, {}) - self.assertEqual(response, {"medical_condition_asthma": "no"}) - - def test_validate_select_learner_profile(self): - """ - Should update the slots with the ones in the selected learner profile - """ - form = HealthCheckProfileForm() - tracker = Tracker( - "27820001001", - { - "learner_profiles": [ - { - "name": "thabo", - "age": 12, - "gender": "not_say", - "province": "ZA-WC", - "city": "Cape Town", - "location": "", - "city_location": "+12-34/", - "school": "Bergvliet High School", - "school_emis": "123456", - "preexisting_condition": "not_sure", - "obesity": None, - "diabetes": False, - "hypertension": True, - "cardio": False, - "asthma": None, - "tb": None, - "pregnant": None, - "respiratory": None, - "cardiac": None, - "immuno": None, - } - ] - }, - {}, - [], - False, - None, - None, - None, - ) - dispatcher = CollectingDispatcher() - response = form.validate_select_learner_profile( - "Thabo", dispatcher, tracker, {} - ) - self.assertEqual( - response, - { - "obo_name": "thabo", - "obo_age": 12, - "obo_gender": "RATHER NOT SAY", - "obo_province": "wc", - "obo_location": "Cape Town", - "obo_location_confirm": "yes", - "obo_location_coords": "", - "obo_city_location_coords": "+12-34/", - "obo_school": "Bergvliet High School", - "obo_school_confirm": "yes", - "obo_school_emis": "123456", - "obo_medical_condition": "not sure", - "obo_medical_condition_obesity": None, - "obo_medical_condition_diabetes": "no", - "obo_medical_condition_hypertension": "yes", - "obo_medical_condition_cardio": "no", - "obo_medical_condition_asthma": None, - "obo_medical_condition_tb": None, - "obo_medical_condition_pregnant": None, - "obo_medical_condition_respiratory": None, - "obo_medical_condition_cardiac": None, - "obo_medical_condition_immuno": None, - "select_learner_profile": "Thabo", - }, - ) - - def test_validate_change_details_school(self): - form = HealthCheckProfileForm() - tracker = Tracker("27820001001", {}, {}, [], False, None, {}, "action_listen") - dispatcher = CollectingDispatcher() - response = form.validate_change_details("1", dispatcher, tracker, {}) - self.assertEqual( - response, - { - "change_details": "school name", - "confirm_details": None, - "school": None, - "school_confirm": None, - }, - ) - - def test_validate_change_details_province(self): - form = HealthCheckProfileForm() - tracker = Tracker("27820001001", {}, {}, [], False, None, {}, "action_listen") - dispatcher = CollectingDispatcher() - response = form.validate_change_details("2", dispatcher, tracker, {}) - self.assertEqual( - response, - {"change_details": "province", "confirm_details": None, "province": None}, - ) - - def test_validate_change_details_role(self): - form = HealthCheckProfileForm() - tracker = Tracker("27820001001", {}, {}, [], False, None, {}, "action_listen") - dispatcher = CollectingDispatcher() - response = form.validate_change_details("3", dispatcher, tracker, {}) - self.assertEqual( - response, - { - "confirm_details": None, - "confirm_details_parent": None, - "change_details": None, - "returning_user": None, - "profile": None, - "profile_display": None, - "age": None, - "gender": None, - "province": None, - "province_display": None, - "location": None, - "location_confirm": None, - "location_coords": None, - "city_location_coords": None, - "school": None, - "school_confirm": None, - "school_emis": None, - "medical_condition": None, - "medical_condition_obesity": None, - "medical_condition_diabetes": None, - "medical_condition_hypertension": None, - "medical_condition_cardio": None, - "medical_condition_asthma": None, - "medical_condition_tb": None, - "medical_condition_pregnant": None, - "medical_condition_respiratory": None, - "medical_condition_cardiac": None, - "medical_condition_immuno": None, - "symptoms_fever": None, - "symptoms_cough": None, - "symptoms_sore_throat": None, - "symptoms_difficulty_breathing": None, - "symptoms_taste_smell": None, - "exposure": None, - "tracing": None, - "learner_profiles": None, - "select_learner_profile": None, - "display_learner_profiles": None, - "obo_name": None, - "obo_age": None, - "obo_gender": None, - "obo_province": None, - "obo_location": None, - "obo_location_confirm": None, - "obo_location_coords": None, - "obo_city_location_coords": None, - "obo_school": None, - "obo_school_confirm": None, - "obo_school_emis": None, - "obo_medical_condition": None, - "obo_medical_condition_obesity": None, - "obo_medical_condition_diabetes": None, - "obo_medical_condition_hypertension": None, - "obo_medical_condition_cardio": None, - "obo_medical_condition_asthma": None, - "obo_medical_condition_tb": None, - "obo_medical_condition_pregnant": None, - "obo_medical_condition_respiratory": None, - "obo_medical_condition_cardiac": None, - "obo_medical_condition_immuno": None, - "obo_symptoms_fever": None, - "obo_symptoms_cough": None, - "obo_symptoms_sore_throat": None, - "obo_symptoms_difficulty_breathing": None, - "obo_symptoms_taste_smell": None, - "obo_exposure": None, - "obo_tracing": None, - }, - ) - - def test_validate_change_details_role_parent(self): - form = HealthCheckProfileForm() - tracker = Tracker("27820001001", {}, {}, [], False, None, {}, "action_listen") - dispatcher = CollectingDispatcher() - response = form.validate_confirm_details_parent("2", dispatcher, tracker, {}) - self.assertEqual( - response, - { - "confirm_details_parent": None, - "confirm_details": None, - "change_details": None, - "returning_user": None, - "profile": None, - "profile_display": None, - "age": None, - "gender": None, - "province": None, - "province_display": None, - "location": None, - "location_confirm": None, - "location_coords": None, - "city_location_coords": None, - "school": None, - "school_confirm": None, - "school_emis": None, - "medical_condition": None, - "medical_condition_obesity": None, - "medical_condition_diabetes": None, - "medical_condition_hypertension": None, - "medical_condition_cardio": None, - "medical_condition_asthma": None, - "medical_condition_tb": None, - "medical_condition_pregnant": None, - "medical_condition_respiratory": None, - "medical_condition_cardiac": None, - "medical_condition_immuno": None, - "symptoms_fever": None, - "symptoms_cough": None, - "symptoms_sore_throat": None, - "symptoms_difficulty_breathing": None, - "symptoms_taste_smell": None, - "exposure": None, - "tracing": None, - "learner_profiles": None, - "select_learner_profile": None, - "display_learner_profiles": None, - "obo_name": None, - "obo_age": None, - "obo_gender": None, - "obo_province": None, - "obo_location": None, - "obo_location_confirm": None, - "obo_location_coords": None, - "obo_city_location_coords": None, - "obo_school": None, - "obo_school_confirm": None, - "obo_school_emis": None, - "obo_medical_condition": None, - "obo_medical_condition_obesity": None, - "obo_medical_condition_diabetes": None, - "obo_medical_condition_hypertension": None, - "obo_medical_condition_cardio": None, - "obo_medical_condition_asthma": None, - "obo_medical_condition_tb": None, - "obo_medical_condition_pregnant": None, - "obo_medical_condition_respiratory": None, - "obo_medical_condition_cardiac": None, - "obo_medical_condition_immuno": None, - "obo_symptoms_fever": None, - "obo_symptoms_cough": None, - "obo_symptoms_sore_throat": None, - "obo_symptoms_difficulty_breathing": None, - "obo_symptoms_taste_smell": None, - "obo_exposure": None, - "obo_tracing": None, - }, - ) - - def test_slot_mappings(self): - """ - Ensures that the additional fields are in the slot mappings - """ - form = HealthCheckProfileForm() - mappings = form.slot_mappings() - self.assertIn("school", mappings) - self.assertIn("school_confirm", mappings) - self.assertIn("profile", mappings) - self.assertIn("consent_parent", mappings) - self.assertIn("obo_name", mappings) - self.assertIn("obo_age", mappings) - self.assertIn("obo_gender", mappings) - self.assertIn("obo_location", mappings) - self.assertIn("obo_location_confirm", mappings) - self.assertIn("obo_medical_condition", mappings) - self.assertIn("obo_medical_condition_cardio", mappings) - self.assertIn("obo_medical_condition_diabetes", mappings) - self.assertIn("obo_medical_condition_hypertension", mappings) - self.assertIn("obo_medical_condition_obesity", mappings) - self.assertIn("obo_province", mappings) - self.assertIn("obo_school", mappings) - self.assertIn("obo_school_confirm", mappings) - self.assertIn("select_learner_profile", mappings) - - def test_required_slots_not_parent(self): - """ - If no profile selected, or a profile other than parent is selected, then we - should return the usual slots - """ - tracker = Tracker("27820001001", {}, {}, [], False, None, {}, "action_listen") - slots = HealthCheckProfileForm.required_slots(tracker) - self.assertEqual(slots, ["profile"]) - - tracker.slots["profile"] = "educator" - slots = HealthCheckProfileForm.required_slots(tracker) - self.assertEqual(slots, ["age"]) - - def test_required_slots_parent(self): - """ - For the parent profile, we should use the on behalf of slots - """ - tracker = Tracker("27820001001", {}, {}, [], False, None, {}, "action_listen") - tracker.slots["profile"] = "parent" - tracker.slots["select_learner_profile"] = "new" - tracker.slots["consent_parent"] = "yes" - slots = HealthCheckProfileForm.required_slots(tracker) - self.assertEqual(slots, ["obo_name"]) - - def test_required_slots_returning_parent(self): - """ - For the returning parent profile, we want to see the confirm details slot - """ - tracker = Tracker("27820001001", {}, {}, [], False, None, {}, "action_listen") - tracker.slots["profile"] = "parent" - tracker.slots["returning_user"] = "yes" - slots = HealthCheckProfileForm.required_slots(tracker) - self.assertEqual(slots, ["confirm_details_parent"]) - - def test_required_slots_returning_user(self): - """ - For returning users, we should confirm details - """ - tracker = Tracker("27820001001", {}, {}, [], False, None, {}, "action_listen") - tracker.slots["province_display"] = "WESTERN CAPE" - tracker.slots["school"] = "BERGVLIET HIGH SCHOOL" - tracker.slots["returning_user"] = "yes" - slots = HealthCheckProfileForm.required_slots(tracker) - self.assertEqual(slots, ["confirm_details"]) - - def test_required_slots_returning_user_additional(self): - """ - For returning users, after confirming information, if there are any additional - slots that we still need to fill, we should ask them - """ - tracker = Tracker("27820001001", {}, {}, [], False, None, {}, "action_listen") - tracker.slots["province_display"] = "WESTERN CAPE" - tracker.slots["school"] = "BERGVLIET HIGH SCHOOL" - tracker.slots["returning_user"] = "yes" - tracker.slots["confirm_details"] = "yes" - tracker.slots["profile"] = "learner" - slots = HealthCheckProfileForm.required_slots(tracker) - self.assertEqual(slots, ["age"]) - - def test_end_of_form_parent(self): - """ - For the parent profile, if all the fields are filled, then we should return - an empty list - """ - tracker = Tracker("27820001001", {}, {}, [], False, None, {}, "action_listen") - tracker.slots["select_learner_profile"] = "Thabo" - tracker.slots["consent_parent"] = "yes" - tracker.slots["profile"] = "parent" - tracker.slots["obo_name"] = "Thabo" - tracker.slots["obo_age"] = "23" - tracker.slots["obo_gender"] = "male" - tracker.slots["obo_province"] = "wc" - tracker.slots["obo_location"] = "cape town" - tracker.slots["obo_location_confirm"] = "yes" - tracker.slots["obo_school"] = "BERGVLIET HIGH SCHOOL" - tracker.slots["obo_school_confirm"] = "yes" - tracker.slots["obo_medical_condition"] = "no" - tracker.slots["obo_medical_condition_asthma"] = "no" - tracker.slots["obo_medical_condition_tb"] = "no" - tracker.slots["obo_medical_condition_respiratory"] = "no" - tracker.slots["obo_medical_condition_cardiac"] = "no" - tracker.slots["obo_medical_condition_immuno"] = "no" - slots = HealthCheckProfileForm.required_slots(tracker) - self.assertEqual(slots, []) - - def test_get_all_slots(self): - """ - Should return all the slots that the user needs to complete - """ - tracker = Tracker("27820001001", {}, {}, [], False, None, {}, "action_listen") - slots = HealthCheckProfileForm.get_all_slots(tracker) - self.assertEqual( - slots, - [ - "profile", - "age", - "gender", - "province", - "location", - "location_confirm", - "school", - "school_confirm", - "medical_condition", - "medical_condition_obesity", - "medical_condition_diabetes", - "medical_condition_hypertension", - "medical_condition_cardio", - ], - ) - - def test_get_all_slots_expanded_comorbidities(self): - """ - Learner and parent on-behalf-of profiles should get asked the additional - comorbidity questions - """ - tracker = Tracker("27820001001", {}, {}, [], False, None, {}, "action_listen") - tracker.slots["profile"] = "learner" - slots = HealthCheckProfileForm.get_all_slots(tracker) - self.assertIn("medical_condition_asthma", slots) - self.assertIn("medical_condition_tb", slots) - self.assertIn("medical_condition_respiratory", slots) - self.assertIn("medical_condition_cardiac", slots) - self.assertIn("medical_condition_immuno", slots) - - tracker.slots["profile"] = "parent" - slots = HealthCheckProfileForm.get_all_slots(tracker) - self.assertIn("obo_medical_condition_asthma", slots) - self.assertIn("obo_medical_condition_tb", slots) - self.assertIn("obo_medical_condition_respiratory", slots) - self.assertIn("obo_medical_condition_cardiac", slots) - self.assertIn("obo_medical_condition_immuno", slots) - - tracker.slots["profile"] = "educator" - slots = HealthCheckProfileForm.get_all_slots(tracker) - self.assertNotIn("medical_condition_asthma", slots) - self.assertNotIn("medical_condition_tb", slots) - self.assertNotIn("medical_condition_respiratory", slots) - self.assertNotIn("medical_condition_cardiac", slots) - self.assertNotIn("medical_condition_immuno", slots) - - def test_get_all_slots_pregnant(self): - """ - Pregnancy questions should only be asked for female users over 12 for learner - and on-behalf-of - """ - tracker = Tracker("27820001001", {}, {}, [], False, None, {}, "action_listen") - tracker.slots["profile"] = "learner" - tracker.slots["gender"] = "FEMALE" - tracker.slots["age"] = "13" - slots = HealthCheckProfileForm.get_all_slots(tracker) - self.assertIn("medical_condition_pregnant", slots) - - tracker = Tracker("27820001001", {}, {}, [], False, None, {}, "action_listen") - tracker.slots["profile"] = "parent" - tracker.slots["obo_gender"] = "FEMALE" - tracker.slots["obo_age"] = "13" - slots = HealthCheckProfileForm.get_all_slots(tracker) - self.assertIn("obo_medical_condition_pregnant", slots) - - tracker = Tracker("27820001001", {}, {}, [], False, None, {}, "action_listen") - tracker.slots["profile"] = "parent" - tracker.slots["obo_gender"] = "FEMALE" - tracker.slots["age"] = "9" - slots = HealthCheckProfileForm.get_all_slots(tracker) - self.assertNotIn("obo_medical_condition_pregnant", slots) - - def test_validate_medical_condition_pregnant(self): - """ - Should send the additional message if yes - """ - form = HealthCheckProfileForm() - tracker = Tracker("27820001001", {}, {}, [], False, None, {}, "action_listen") - dispatcher = CollectingDispatcher() - form.validate_medical_condition_pregnant("no", dispatcher, tracker, {}) - self.assertEqual(dispatcher.messages, []) - - dispatcher = CollectingDispatcher() - form.validate_medical_condition_pregnant("yes", dispatcher, tracker, {}) - [msg] = dispatcher.messages - self.assertEqual(msg["template"], "utter_pregnant_yes") - - def test_validate_obo_medical_condition_pregnant(self): - """ - Should send the additional message if yes - """ - form = HealthCheckProfileForm() - tracker = Tracker("27820001001", {}, {}, [], False, None, {}, "action_listen") - dispatcher = CollectingDispatcher() - form.validate_obo_medical_condition_pregnant("no", dispatcher, tracker, {}) - self.assertEqual(dispatcher.messages, []) - - form.validate_obo_medical_condition_pregnant("yes", dispatcher, tracker, {}) - [msg] = dispatcher.messages - self.assertEqual(msg["template"], "utter_obo_pregnant_yes") - - -@pytest.mark.asyncio -async def test_validate_profile(): - """ - Get the profile of the user. Should not accept labels. - """ - form = HealthCheckProfileForm() - tracker = Tracker("27820001001", {}, {}, [], False, None, {}, "action_listen") - dispatcher = CollectingDispatcher() - response = await form.validate_profile("4", dispatcher, tracker, {}) - assert response == { - "profile": "actual_parent", - "profile_display": "Parent", - } - - response = await form.validate_profile("parent", dispatcher, tracker, {}) - assert response == { - "profile": None, - } - - -@pytest.mark.asyncio -async def test_validate_profile_parent(): - """ - If it's a parent profile, check for existing users - """ - form = HealthCheckProfileForm() - tracker = Tracker("27820001001", {}, {}, [], False, None, {}, "action_listen") - dispatcher = CollectingDispatcher() - response = await form.validate_profile("3", dispatcher, tracker, {}) - assert response == { - "profile": "parent", - "profile_display": "Parents / Guardian on behalf of learner", - "display_learner_profiles": "*1.* New HealthCheck", - "learner_profiles": [], - "select_learner_profile": "new", - } - - -@pytest.mark.asyncio -class TestHealthCheckProfileFormAsync: - async def test_obo_validator_sync(self): - """ - Should change a validator from a normal slot to an obo slot, for sync functions - """ - form = HealthCheckProfileForm() - tracker = Tracker("27820001001", {}, {}, [], False, None, {}, "action_listen") - result = await form.validate_obo_age("22", CollectingDispatcher(), tracker, {}) - assert result == {"obo_age": "22"} - - async def test_obo_validator_async(self): - """ - Should change a validator from a normal slot to an obo slot, for sync functions - """ - form = HealthCheckProfileForm() - tracker = Tracker( - "27820001001", {}, {}, [{"event": "user"}], False, None, {}, "action_listen" - ) - result = await form.validate_obo_location( - "cape town", CollectingDispatcher(), tracker, {} - ) - assert result == {"obo_location": "cape town"} - - -class TestHealthCheckProfile(TestCase): - def test_request_next_slot(self): - form = HealthCheckProfileForm() - dispatcher = CollectingDispatcher() - tracker = Tracker("27820001001", {}, {}, [], False, None, {}, "action_listen") - tracker.slots["profile"] = "educator" - tracker.slots["age"] = "34" - tracker.slots["gender"] = "OTHER" - tracker.slots["province"] = "wc" - tracker.slots["location"] = "Long Street, Cape Town" - tracker.slots["location_confirm"] = "yes" - tracker.slots["province_display"] = "Western Cape" - - form.request_next_slot(dispatcher, tracker, {}) - [msg] = dispatcher.messages - self.assertEqual(msg["template"], "utter_ask_school") - - tracker.slots["profile"] = "marker" - dispatcher.messages = [] - - form.request_next_slot(dispatcher, tracker, {}) - [msg] = dispatcher.messages - self.assertEqual(msg["template"], "utter_ask_school_marker") - self.assertEqual(msg["province_display"], "Western Cape") - - -class HealthCheckFormTests(TestCase): - def test_eventstore_data(self): - """ - The data is transformed from the tracker store into the event store format - """ - form = HealthCheckForm() - tracker = Tracker( - "27820001001", - { - "province": "wc", - "age": "43", - "symptoms_fever": "no", - "symptoms_cough": "yes", - "symptoms_sore_throat": "no", - "symptoms_difficulty_breathing": "no", - "symptoms_taste_smell": "no", - "medical_condition": "not sure", - "exposure": "not sure", - "tracing": "yes", - "gender": "OTHER", - "location": "Long Street, Cape Town", - "medical_condition_obesity": "no", - "medical_condition_diabetes": "no", - "medical_condition_hypertension": "yes", - "medical_condition_cardio": "no", - "school": "BERGVLIET HIGH SCHOOL", - "school_emis": "105310201", - "profile": "learner", - }, - {}, - [], - False, - None, - {}, - "action_listen", - ) - data = form.get_eventstore_data(tracker, "low") - self.assertTrue(data.pop("deduplication_id")) - self.assertEqual( - data, - { - "province": "ZA-WC", - "age": "40-65", - "fever": False, - "cough": True, - "sore_throat": False, - "difficulty_breathing": False, - "smell": False, - "preexisting_condition": "not_sure", - "exposure": "not_sure", - "tracing": True, - "gender": "other", - "city": "Long Street, Cape Town", - "city_location": "", - "location": "", - "msisdn": "+27820001001", - "risk": "low", - "source": "WhatsApp", - "data": { - "age": "43", - "cardio": False, - "diabetes": False, - "hypertension": True, - "obesity": False, - "school_name": "BERGVLIET HIGH SCHOOL", - "school_emis": "105310201", - "profile": "learner", - "asthma": None, - "tb": None, - "pregnant": None, - "respiratory": None, - "cardiac": None, - "immuno": None, - }, - }, - ) - - def test_eventstore_data_parent(self): - """ - The data is transformed from the tracker store into the event store format - from the on behalf of fields - """ - form = HealthCheckForm() - tracker = Tracker( - "27820001001", - { - "obo_name": "Thabo", - "obo_province": "wc", - "obo_age": "43", - "obo_symptoms_fever": "no", - "obo_symptoms_cough": "yes", - "obo_symptoms_sore_throat": "no", - "obo_symptoms_difficulty_breathing": "no", - "obo_symptoms_taste_smell": "no", - "obo_medical_condition": "not sure", - "obo_exposure": "not sure", - "obo_tracing": "yes", - "obo_gender": "OTHER", - "obo_location": "Long Street, Cape Town", - "obo_medical_condition_obesity": "no", - "obo_medical_condition_diabetes": "no", - "obo_medical_condition_hypertension": "yes", - "obo_medical_condition_cardio": "no", - "obo_school": "BERGVLIET HIGH SCHOOL", - "obo_school_emis": "105310201", - "profile": "parent", - }, - {}, - [], - False, - None, - {}, - "action_listen", - ) - data = form.get_eventstore_data(tracker, "low") - self.assertTrue(data.pop("deduplication_id")) - self.assertEqual( - data, - { - "province": "ZA-WC", - "age": "40-65", - "fever": False, - "cough": True, - "sore_throat": False, - "difficulty_breathing": False, - "smell": False, - "preexisting_condition": "not_sure", - "exposure": "not_sure", - "tracing": True, - "gender": "other", - "city": "Long Street, Cape Town", - "city_location": "", - "location": "", - "msisdn": "+27820001001", - "risk": "low", - "source": "WhatsApp", - "data": { - "age": "43", - "name": "Thabo", - "cardio": False, - "diabetes": False, - "hypertension": True, - "obesity": False, - "school_name": "BERGVLIET HIGH SCHOOL", - "school_emis": "105310201", - "asthma": None, - "tb": None, - "pregnant": None, - "respiratory": None, - "cardiac": None, - "immuno": None, - "profile": "parent", - }, - }, - ) - - @mock.patch("dbe.actions.actions.datetime") - def test_send_risk_to_user(self, dt): - """ - The message to the user has the relevant variables filled - """ - form = HealthCheckForm() - dispatcher = CollectingDispatcher() - dt.now.return_value = datetime( - 2020, 1, 2, 3, 4, 5, tzinfo=timezone(timedelta(hours=2)) - ) - tracker = Tracker("27820001001", {}, {}, [], False, None, {}, "action_listen") - form.send_risk_to_user(dispatcher, "low", tracker) - [msg] = dispatcher.messages - self.assertEqual(msg["template"], "utter_risk_low") - self.assertEqual(msg["issued"], "January 2, 2020, 3:04 AM") - self.assertEqual(msg["expired"], "January 3, 2020, 3:04 AM") - - @mock.patch("dbe.actions.actions.datetime") - def test_send_risk_to_user_parent_profile(self, dt): - """ - The message to the user has the relevant variables filled, and use the on behalf - of template - """ - form = HealthCheckForm() - dispatcher = CollectingDispatcher() - dt.now.return_value = datetime( - 2020, 1, 2, 3, 4, 5, tzinfo=timezone(timedelta(hours=2)) - ) - tracker = Tracker("27820001001", {}, {}, [], False, None, {}, "action_listen") - tracker.slots["profile"] = "parent" - form.send_risk_to_user(dispatcher, "low", tracker) - [msg] = dispatcher.messages - self.assertEqual(msg["template"], "utter_obo_risk_low") - self.assertEqual(msg["issued"], "January 2, 2020, 3:04 AM") - self.assertEqual(msg["expired"], "January 3, 2020, 3:04 AM") - - @mock.patch("dbe.actions.actions.datetime") - def test_send_risk_to_user_actual_parent_profile(self, dt): - """ - The message to the user has the relevant variables filled, and use the parent - template - """ - form = HealthCheckForm() - dispatcher = CollectingDispatcher() - dt.now.return_value = datetime( - 2020, 1, 2, 3, 4, 5, tzinfo=timezone(timedelta(hours=2)) - ) - tracker = Tracker("27820001001", {}, {}, [], False, None, {}, "action_listen") - tracker.slots["profile"] = "actual_parent" - form.send_risk_to_user(dispatcher, "low", tracker) - [msg] = dispatcher.messages - self.assertEqual(msg["template"], "utter_risk_low_parent") - self.assertEqual(msg["issued"], "January 2, 2020, 3:04 AM") - self.assertEqual(msg["expired"], "January 3, 2020, 3:04 AM") - - @mock.patch("dbe.actions.actions.datetime") - def test_send_risk_to_user_support_profile(self, dt): - """ - The message to the user has the relevant variables filled, and use the support - template - """ - form = HealthCheckForm() - dispatcher = CollectingDispatcher() - dt.now.return_value = datetime( - 2020, 1, 2, 3, 4, 5, tzinfo=timezone(timedelta(hours=2)) - ) - tracker = Tracker("27820001001", {}, {}, [], False, None, {}, "action_listen") - tracker.slots["profile"] = "support" - form.send_risk_to_user(dispatcher, "low", tracker) - [msg] = dispatcher.messages - self.assertEqual(msg["template"], "utter_risk_low_support") - self.assertEqual(msg["issued"], "January 2, 2020, 3:04 AM") - self.assertEqual(msg["expired"], "January 3, 2020, 3:04 AM") - - @mock.patch("dbe.actions.actions.datetime") - def test_send_risk_to_user_marker_profile(self, dt): - """ - The message to the user has the relevant variables filled, and use the support - template - """ - form = HealthCheckForm() - dispatcher = CollectingDispatcher() - dt.now.return_value = datetime( - 2020, 1, 2, 3, 4, 5, tzinfo=timezone(timedelta(hours=2)) - ) - tracker = Tracker("27820001001", {}, {}, [], False, None, {}, "action_listen") - tracker.slots["profile"] = "marker" - form.send_risk_to_user(dispatcher, "low", tracker) - [msg] = dispatcher.messages - self.assertEqual(msg["template"], "utter_risk_low_support") - self.assertEqual(msg["issued"], "January 2, 2020, 3:04 AM") - self.assertEqual(msg["expired"], "January 3, 2020, 3:04 AM") - - @mock.patch("dbe.actions.actions.datetime") - def test_send_risk_to_user_exam_assistant_profile(self, dt): - """ - The message to the user has the relevant variables filled, and use the support - template - """ - form = HealthCheckForm() - dispatcher = CollectingDispatcher() - dt.now.return_value = datetime( - 2020, 1, 2, 3, 4, 5, tzinfo=timezone(timedelta(hours=2)) - ) - tracker = Tracker("27820001001", {}, {}, [], False, None, {}, "action_listen") - tracker.slots["profile"] = "exam_assistant" - form.send_risk_to_user(dispatcher, "low", tracker) - [msg] = dispatcher.messages - self.assertEqual(msg["template"], "utter_risk_low_support") - self.assertEqual(msg["issued"], "January 2, 2020, 3:04 AM") - self.assertEqual(msg["expired"], "January 3, 2020, 3:04 AM") - - @mock.patch("dbe.actions.actions.datetime") - def test_send_risk_to_user_educator_profile(self, dt): - """ - The message to the user has the relevant variables filled, and use the support - template - """ - form = HealthCheckForm() - dispatcher = CollectingDispatcher() - dt.now.return_value = datetime( - 2020, 1, 2, 3, 4, 5, tzinfo=timezone(timedelta(hours=2)) - ) - tracker = Tracker("27820001001", {}, {}, [], False, None, {}, "action_listen") - tracker.slots["profile"] = "educator" - form.send_risk_to_user(dispatcher, "low", tracker) - [msg] = dispatcher.messages - self.assertEqual(msg["template"], "utter_risk_low_support") - self.assertEqual(msg["issued"], "January 2, 2020, 3:04 AM") - self.assertEqual(msg["expired"], "January 3, 2020, 3:04 AM") - - def test_map_age(self): - """ - Should map to one of the age buckets - """ - form = HealthCheckForm() - self.assertEqual(form.map_age("3"), "<18") - self.assertEqual(form.map_age("18"), "18-40") - self.assertEqual(form.map_age("65"), "40-65") - self.assertEqual(form.map_age("66"), ">65") - - def test_required_slots_not_parent(self): - """ - Should return the normal slots for profiles that are not parent - """ - tracker = Tracker("27820001001", {}, {}, [], False, None, {}, "action_listen") - tracker.slots["profile"] = "educator" - slots = HealthCheckForm.required_slots(tracker) - self.assertEqual(slots, ["symptoms_fever"]) - - def test_required_slots_parent(self): - """ - Should return the normal slots for profiles that are not parent - """ - tracker = Tracker("27820001001", {}, {}, [], False, None, {}, "action_listen") - tracker.slots["profile"] = "parent" - slots = HealthCheckForm.required_slots(tracker) - self.assertEqual(slots, ["obo_symptoms_fever"]) - tracker.slots["obo_symptoms_fever"] = "no" - slots = HealthCheckForm.required_slots(tracker) - self.assertEqual(slots, ["obo_symptoms_cough"]) - - def test_required_slots_parent_complete(self): - """ - If there are no more slots to fulfill, should return an empty list - """ - tracker = Tracker("27820001001", {}, {}, [], False, None, {}, "action_listen") - tracker.slots["profile"] = "parent" - tracker.slots["obo_symptoms_fever"] = "no" - tracker.slots["obo_symptoms_cough"] = "no" - tracker.slots["obo_symptoms_sore_throat"] = "no" - tracker.slots["obo_symptoms_difficulty_breathing"] = "no" - tracker.slots["obo_symptoms_taste_smell"] = "no" - tracker.slots["obo_exposure"] = "no" - tracker.slots["obo_tracing"] = "no" - slots = HealthCheckForm.required_slots(tracker) - self.assertEqual(slots, []) - - def test_slot_mappings(self): - """ - Should have the on behalf of mappings - """ - mappings = HealthCheckForm().slot_mappings() - self.assertIn("obo_exposure", mappings) - self.assertIn("obo_symptoms_cough", mappings) - self.assertIn("obo_symptoms_difficulty_breathing", mappings) - self.assertIn("obo_symptoms_fever", mappings) - self.assertIn("obo_symptoms_sore_throat", mappings) - self.assertIn("obo_symptoms_taste_smell", mappings) - self.assertIn("obo_tracing", mappings) - - -@pytest.mark.asyncio -class TestActionSessionStart: - async def test_school_details_copied(self): - """ - Should copy over the school details to the new session - """ - action = ActionSessionStart() - events = await action.get_carry_over_slots( - Tracker( - "27820001001", - { - "school": "BERGVLIET HIGH SCHOOL", - "school_emis": "105310201", - "school_confirm": "yes", - }, - {}, - [], - False, - None, - {}, - "action_listen", - ) - ) - assert SlotSet("school", "BERGVLIET HIGH SCHOOL") in events - assert SlotSet("school_emis", "105310201") in events - assert SlotSet("school_confirm", "yes") in events - - async def test_province_display(self): - """ - Should set province_display if returning user - """ - action = ActionSessionStart() - events = await action.get_carry_over_slots( - Tracker( - "27820001001", - {"province": "wc"}, - {}, - [], - False, - None, - {}, - "action_listen", - ) - ) - assert SlotSet("province_display", "WESTERN CAPE") in events - - async def test_parent_profile(self): - """ - Should do a learner profile lookup if parent on behalf of - """ - action = ActionSessionStart() - events = await action.get_carry_over_slots( - Tracker( - "27820001001", - {"profile": "parent"}, - {}, - [], - False, - None, - {}, - "action_listen", - ) - ) - assert SlotSet("learner_profiles", []) in events - - -@pytest.mark.asyncio -class TestActionExit: - async def test_school_details_copied(self): - """ - Should copy over the school details to the new session - """ - action = ActionExit() - events = await action.run( - CollectingDispatcher(), - Tracker( - "27820001001", - { - "school": "BERGVLIET HIGH SCHOOL", - "school_emis": "105310201", - "school_confirm": "yes", - }, - {}, - [], - False, - None, - {}, - "action_listen", - ), - {}, - ) - assert SlotSet("school", "BERGVLIET HIGH SCHOOL") in events - assert SlotSet("school_emis", "105310201") in events - assert SlotSet("school_confirm", "yes") in events - - -@pytest.mark.asyncio -async def test_action_set_profile_obo(): - """ - Should set the profile to "parent" - """ - action = ActionSetProfileObo() - events = await action.run( - CollectingDispatcher(), - Tracker("27820001001", {}, {}, [], False, None, {}, "action_listen"), - {}, - ) - assert SlotSet("profile", "parent") in events diff --git a/dbe/tests/test_forms.py b/dbe/tests/test_forms.py deleted file mode 100644 index 64d8f22..0000000 --- a/dbe/tests/test_forms.py +++ /dev/null @@ -1,420 +0,0 @@ -import json - -import pytest -import respx -from rasa_sdk import Tracker -from rasa_sdk.events import Form, SlotSet -from rasa_sdk.executor import CollectingDispatcher - -import base.actions.actions -from base.tests import utils -from dbe.actions.actions import ( - DBEHealthCheckTermsForm, - HealthCheckForm, - HealthCheckProfileForm, -) - - -class TestDBEHealthCheckTermsForm: - @pytest.mark.asyncio - async def test_validate_terms(self): - form = DBEHealthCheckTermsForm() - dispatcher = CollectingDispatcher() - - tracker = utils.get_tracker_for_slot_from_intent(form, "terms", "affirm") - events = await form.run(dispatcher=dispatcher, tracker=tracker, domain=None) - assert form.required_slots(tracker) == ["terms"] - assert events == [ - SlotSet("terms", "yes"), - Form(None), - SlotSet("requested_slot", None), - ] - - tracker = utils.get_tracker_for_slot_from_intent(form, "terms", "more") - events = await form.run(dispatcher=dispatcher, tracker=tracker, domain=None) - assert events == [SlotSet("terms", None), SlotSet("requested_slot", "terms")] - - -class TestHealthCheckForm: - @respx.mock - @pytest.mark.asyncio - async def test_submit_to_eventstore_low(self): - """ - Submits the data to the eventstore in the correct format - """ - base.actions.actions.config.EVENTSTORE_URL = "https://eventstore" - base.actions.actions.config.EVENTSTORE_TOKEN = "token" - - request = respx.post("https://eventstore/api/v5/covid19triage/", content={}) - - form = HealthCheckForm() - dispatcher = CollectingDispatcher() - tracker = utils.get_tracker_for_slot_from_intent( - form, - "tracing", - "affirm", - { - "obo_name": "Thabo", - "obo_province": "wc", - "obo_age": "43", - "obo_symptoms_fever": "no", - "obo_symptoms_cough": "no", - "obo_symptoms_sore_throat": "no", - "obo_symptoms_difficulty_breathing": "no", - "obo_symptoms_taste_smell": "no", - "obo_medical_condition": "not sure", - "obo_exposure": "not sure", - "obo_tracing": "no", - "obo_gender": "OTHER", - "obo_location": "Long Street, Cape Town", - "obo_location_coords": "+03.4-001.2/", - "obo_city_location_coords": "+01.2-003.4/", - "obo_medical_condition_obesity": "no", - "obo_medical_condition_diabetes": "no", - "obo_medical_condition_hypertension": "no", - "obo_medical_condition_cardio": "no", - "obo_school": "BERGVLIET HIGH SCHOOL", - "obo_school_emis": "105310201", - "profile": "parent", - }, - ) - await form.submit(dispatcher, tracker, {}) - - assert request.called - [(request, response)] = request.calls - data = json.loads(request.stream.body) - assert data.pop("deduplication_id") - assert data == { - "province": "ZA-WC", - "age": "40-65", - "fever": False, - "cough": False, - "sore_throat": False, - "difficulty_breathing": False, - "smell": False, - "exposure": "not_sure", - "tracing": False, - "gender": "other", - "preexisting_condition": "not_sure", - "city_location": "+01.2-003.4/", - "location": "+03.4-001.2/", - "city": "Long Street, Cape Town", - "msisdn": "+default", - "risk": "low", - "source": "WhatsApp", - "data": { - "age": "43", - "name": "Thabo", - "cardio": False, - "diabetes": False, - "hypertension": False, - "obesity": False, - "school_name": "BERGVLIET HIGH SCHOOL", - "school_emis": "105310201", - "asthma": None, - "tb": None, - "pregnant": None, - "respiratory": None, - "cardiac": None, - "immuno": None, - "profile": "parent", - }, - } - - base.actions.actions.config.EVENTSTORE_URL = None - base.actions.actions.config.EVENTSTORE_TOKEN = None - - @respx.mock - @pytest.mark.asyncio - async def test_submit_to_eventstore_moderate(self): - """ - Submits the data to the eventstore in the correct format - """ - base.actions.actions.config.EVENTSTORE_URL = "https://eventstore" - base.actions.actions.config.EVENTSTORE_TOKEN = "token" - - request = respx.post("https://eventstore/api/v5/covid19triage/", content={}) - - form = HealthCheckForm() - dispatcher = CollectingDispatcher() - tracker = utils.get_tracker_for_slot_from_intent( - form, - "tracing", - "affirm", - { - "obo_name": "Thabo", - "obo_province": "wc", - "obo_age": "43", - "obo_symptoms_fever": "no", - "obo_symptoms_cough": "no", - "obo_symptoms_sore_throat": "yes", - "obo_symptoms_difficulty_breathing": "no", - "obo_symptoms_taste_smell": "no", - "obo_medical_condition": "not sure", - "obo_exposure": "not sure", - "obo_tracing": "yes", - "obo_gender": "OTHER", - "obo_location": "Long Street, Cape Town", - "obo_location_coords": "+03.4-001.2/", - "obo_city_location_coords": "+01.2-003.4/", - "obo_medical_condition_obesity": "no", - "obo_medical_condition_diabetes": "no", - "obo_medical_condition_hypertension": "yes", - "obo_medical_condition_cardio": "no", - "obo_school": "BERGVLIET HIGH SCHOOL", - "obo_school_emis": "105310201", - "profile": "parent", - }, - ) - await form.submit(dispatcher, tracker, {}) - - assert request.called - [(request, response)] = request.calls - data = json.loads(request.stream.body) - assert data.pop("deduplication_id") - assert data == { - "province": "ZA-WC", - "age": "40-65", - "fever": False, - "cough": False, - "sore_throat": True, - "difficulty_breathing": False, - "smell": False, - "exposure": "not_sure", - "tracing": True, - "gender": "other", - "preexisting_condition": "not_sure", - "city_location": "+01.2-003.4/", - "location": "+03.4-001.2/", - "city": "Long Street, Cape Town", - "msisdn": "+default", - "risk": "moderate", - "source": "WhatsApp", - "data": { - "age": "43", - "name": "Thabo", - "cardio": False, - "diabetes": False, - "hypertension": True, - "obesity": False, - "school_name": "BERGVLIET HIGH SCHOOL", - "school_emis": "105310201", - "asthma": None, - "tb": None, - "pregnant": None, - "respiratory": None, - "cardiac": None, - "immuno": None, - "profile": "parent", - }, - } - - base.actions.actions.config.EVENTSTORE_URL = None - base.actions.actions.config.EVENTSTORE_TOKEN = None - - @respx.mock - @pytest.mark.asyncio - async def test_submit_to_eventstore_high(self): - """ - Submits the data to the eventstore in the correct format - """ - base.actions.actions.config.EVENTSTORE_URL = "https://eventstore" - base.actions.actions.config.EVENTSTORE_TOKEN = "token" - - request = respx.post("https://eventstore/api/v5/covid19triage/", content={}) - - form = HealthCheckForm() - dispatcher = CollectingDispatcher() - tracker = utils.get_tracker_for_slot_from_intent( - form, - "tracing", - "affirm", - { - "obo_name": "Thabo", - "obo_province": "wc", - "obo_age": "43", - "obo_symptoms_fever": "yes", - "obo_symptoms_cough": "yes", - "obo_symptoms_sore_throat": "yes", - "obo_symptoms_difficulty_breathing": "no", - "obo_symptoms_taste_smell": "no", - "obo_medical_condition": "not sure", - "obo_exposure": "not sure", - "obo_tracing": "yes", - "obo_gender": "OTHER", - "obo_location": "Long Street, Cape Town", - "obo_location_coords": "+03.4-001.2/", - "obo_city_location_coords": "+01.2-003.4/", - "obo_medical_condition_obesity": "no", - "obo_medical_condition_diabetes": "no", - "obo_medical_condition_hypertension": "yes", - "obo_medical_condition_cardio": "no", - "obo_school": "BERGVLIET HIGH SCHOOL", - "obo_school_emis": "105310201", - "profile": "parent", - }, - ) - await form.submit(dispatcher, tracker, {}) - - assert request.called - [(request, response)] = request.calls - data = json.loads(request.stream.body) - assert data.pop("deduplication_id") - assert data == { - "province": "ZA-WC", - "age": "40-65", - "fever": True, - "cough": True, - "sore_throat": True, - "difficulty_breathing": False, - "smell": False, - "exposure": "not_sure", - "tracing": True, - "gender": "other", - "preexisting_condition": "not_sure", - "city_location": "+01.2-003.4/", - "location": "+03.4-001.2/", - "city": "Long Street, Cape Town", - "msisdn": "+default", - "risk": "high", - "source": "WhatsApp", - "data": { - "age": "43", - "name": "Thabo", - "cardio": False, - "diabetes": False, - "hypertension": True, - "obesity": False, - "school_name": "BERGVLIET HIGH SCHOOL", - "school_emis": "105310201", - "profile": "parent", - "asthma": None, - "tb": None, - "pregnant": None, - "respiratory": None, - "cardiac": None, - "immuno": None, - }, - } - - base.actions.actions.config.EVENTSTORE_URL = None - base.actions.actions.config.EVENTSTORE_TOKEN = None - - @respx.mock - @pytest.mark.asyncio - async def test_submit_to_eventstore_moderate_age(self): - """ - Submits the data to the eventstore in the correct format - """ - base.actions.actions.config.EVENTSTORE_URL = "https://eventstore" - base.actions.actions.config.EVENTSTORE_TOKEN = "token" - - request = respx.post("https://eventstore/api/v5/covid19triage/", content={}) - - form = HealthCheckForm() - dispatcher = CollectingDispatcher() - tracker = utils.get_tracker_for_slot_from_intent( - form, - "tracing", - "affirm", - { - "obo_name": "Thabo", - "obo_province": "wc", - "obo_age": "67", - "obo_symptoms_fever": "yes", - "obo_symptoms_cough": "yes", - "obo_symptoms_sore_throat": "no", - "obo_symptoms_difficulty_breathing": "no", - "obo_symptoms_taste_smell": "no", - "obo_medical_condition": "not sure", - "obo_exposure": "no", - "obo_tracing": "yes", - "obo_gender": "OTHER", - "obo_location": "Long Street, Cape Town", - "obo_location_coords": "+03.4-001.2/", - "obo_city_location_coords": "+01.2-003.4/", - "obo_medical_condition_obesity": "no", - "obo_medical_condition_diabetes": "no", - "obo_medical_condition_hypertension": "no", - "obo_medical_condition_cardio": "no", - "obo_school": "BERGVLIET HIGH SCHOOL", - "obo_school_emis": "105310201", - "profile": "parent", - }, - ) - await form.submit(dispatcher, tracker, {}) - - assert request.called - [(request, response)] = request.calls - data = json.loads(request.stream.body) - assert data.pop("deduplication_id") - assert data == { - "province": "ZA-WC", - "age": ">65", - "fever": True, - "cough": True, - "sore_throat": False, - "difficulty_breathing": False, - "smell": False, - "exposure": "no", - "tracing": True, - "gender": "other", - "preexisting_condition": "not_sure", - "city_location": "+01.2-003.4/", - "location": "+03.4-001.2/", - "city": "Long Street, Cape Town", - "msisdn": "+default", - "risk": "high", - "source": "WhatsApp", - "data": { - "age": "67", - "name": "Thabo", - "cardio": False, - "diabetes": False, - "hypertension": False, - "obesity": False, - "school_name": "BERGVLIET HIGH SCHOOL", - "school_emis": "105310201", - "profile": "parent", - "asthma": None, - "tb": None, - "pregnant": None, - "respiratory": None, - "cardiac": None, - "immuno": None, - }, - } - - base.actions.actions.config.EVENTSTORE_URL = None - base.actions.actions.config.EVENTSTORE_TOKEN = None - - -class TestHealthCheckProfileForm: - def test_get_province(self): - """ - Should return obo province if obo profile, otherwise province - """ - form = HealthCheckProfileForm() - tracker = Tracker( - "27820001001", - {"profile": "parent", "obo_province": "wc"}, - None, - [], - False, - None, - None, - None, - ) - assert form.get_province(tracker) == "wc" - - tracker = Tracker( - "27820001001", - {"profile": "learner", "province": "wc"}, - None, - [], - False, - None, - None, - None, - ) - assert form.get_province(tracker) == "wc" diff --git a/dbe/tests/test_import_marking_centres.py b/dbe/tests/test_import_marking_centres.py deleted file mode 100644 index 9e3bbbd..0000000 --- a/dbe/tests/test_import_marking_centres.py +++ /dev/null @@ -1,15 +0,0 @@ -import tempfile - -from dbe.actions.import_marking_centres import read_file - - -class TestImportMarkingCentres: - def test_read_file(self): - """ - Should return the relevant marking centre data from the CSV - """ - (_, filename) = tempfile.mkstemp() - with open(filename, "w") as f: - f.write("PROVINCE,NAME\n" "nl,school 1\n" "mp,school 2\n") - centres = read_file(filename) - assert centres == set((("nl", "school 1"), ("mp", "school 2"))) diff --git a/dbe/tests/test_import_school_data.py b/dbe/tests/test_import_school_data.py deleted file mode 100644 index 602b997..0000000 --- a/dbe/tests/test_import_school_data.py +++ /dev/null @@ -1,21 +0,0 @@ -import tempfile - -from dbe.actions.import_school_data import read_file - - -class TestImportSchoolData: - def test_read_file(self): - """ - Should return the relevant school data from the CSV - """ - (_, filename) = tempfile.mkstemp() - with open(filename, "w") as f: - f.write( - "Unrelated,NatEMIS,Province,Official_Institution_Name\n" - "1,123456,KZN,school 1\n" - "2,654321,MP,school 2\n" - ) - schools = read_file(filename) - assert schools == set( - (("123456", "nl", "school 1"), ("654321", "mp", "school 2")) - ) diff --git a/dbe/tests/test_utils.py b/dbe/tests/test_utils.py deleted file mode 100644 index c7f0266..0000000 --- a/dbe/tests/test_utils.py +++ /dev/null @@ -1,92 +0,0 @@ -import json -from urllib.parse import urlencode - -import pytest -import respx -from rasa_sdk import Tracker - -from dbe.actions import utils -from dbe.actions.utils import config - - -class TestOptionListFromProfiles: - def test_result(self): - """ - Should return a human readable string of the list of options of profile names - """ - options = utils.option_list_from_profiles( - [{"name": "name 1"}, {"name": "name 2"}] - ) - result = "\n".join(["*1.* name 1", "*2.* name 2", "*3.* New HealthCheck"]) - assert options == result - - -class TestNormalizeMsisdn: - def test_no_plus(self): - """ - If there's no plus, it should be added - """ - assert utils.normalise_msisdn("27820001001") == "+27820001001" - - -@pytest.mark.asyncio -class TestGetLearnerProfiles: - @respx.mock - async def test_get_learner_profiles(self): - """ - Should return the list of learner profiles - """ - config.EVENTSTORE_URL = "https://eventstore" - config.EVENTSTORE_TOKEN = "testtoken" - querystring = urlencode({"msisdn": "+27820001001"}) - respx.get( - url=f"https://eventstore/api/v2/dbeonbehalfofprofile/?{querystring}", - content=json.dumps({"results": [{"name": "name1"}]}), - content_type="application/json", - ) - profiles = await utils.get_learner_profiles("+27820001001") - assert profiles == [{"name": "name1"}] - - -@pytest.mark.asyncio -class TestGetLearnerProfilesSlotsDict: - @respx.mock - async def test_get_learner_profiles_dict(self): - """ - Should return a dictionary of slots that should be filled with profile data - """ - config.EVENTSTORE_URL = "https://eventstore" - config.EVENTSTORE_TOKEN = "testtoken" - querystring = urlencode({"msisdn": "+27820001001"}) - respx.get( - url=f"https://eventstore/api/v2/dbeonbehalfofprofile/?{querystring}", - content=json.dumps({"results": [{"name": "name1"}]}), - content_type="application/json", - ) - tracker = Tracker("27820001001", {}, {}, [], False, None, None, None) - slots = await utils.get_learner_profile_slots_dict(tracker) - assert slots == { - "learner_profiles": [{"name": "name1"}], - "display_learner_profiles": "*1.* name1\n*2.* New HealthCheck", - } - - @respx.mock - async def test_get_learner_profiles_dict_no_results(self): - """ - Sets the select_learner_profile so that question gets skipped - """ - config.EVENTSTORE_URL = "https://eventstore" - config.EVENTSTORE_TOKEN = "testtoken" - querystring = urlencode({"msisdn": "+27820001001"}) - respx.get( - url=f"https://eventstore/api/v2/dbeonbehalfofprofile/?{querystring}", - content=json.dumps({"results": []}), - content_type="application/json", - ) - tracker = Tracker("27820001001", {}, {}, [], False, None, None, None) - slots = await utils.get_learner_profile_slots_dict(tracker) - assert slots == { - "learner_profiles": [], - "display_learner_profiles": "*1.* New HealthCheck", - "select_learner_profile": "new", - } diff --git a/endpoints.yml b/endpoints.yml index 4c72cdb..ef828cc 100644 --- a/endpoints.yml +++ b/endpoints.yml @@ -17,13 +17,11 @@ action_endpoint: # By default the conversations are stored in memory. # https://rasa.com/docs/rasa/api/tracker-stores/ - # tracker_store: - # type: redis -# url: -# port: -# db: -# password: -# use_ssl: +tracker_store: + type: "redis" + url: "localhost" + port: "6379" + db: "0" # tracker_store: # store_type: mongod @@ -32,12 +30,12 @@ action_endpoint: # username: admin # password: password # collection: conversations -tracker_store: - type: SQL - dialect: "postgresql" - url: "localhost" - db: "rasa" - username: "rasa" +# tracker_store: +# type: SQL +# dialect: "postgresql" +# url: "localhost" +# db: "rasa" +# username: "rasa" # Event broker which all conversation events should be streamed to. # https://rasa.com/docs/rasa/api/event-brokers/ diff --git a/setup.cfg b/setup.cfg index 5d6acd4..66d92df 100644 --- a/setup.cfg +++ b/setup.cfg @@ -1,5 +1,5 @@ [tool:pytest] -addopts = --verbose --cov=dbe.actions --cov=base.actions --cov=hh.actions --no-cov-on-fail +addopts = --verbose --cov=base.actions --cov=hh.actions --no-cov-on-fail [flake8] max-line-length = 88