I am trying to create a questionnaire form with a likert scale from 1-4. But after the first user input the form gets filled with the same answer for all the slots.
I am using Duckling to extract numbers and time. I know it works, since I am able to extract the time correctly.
Do I have to reset the numbers slot set by Duckling after each question? If so, how?
stories.md:
*ces
- ces_form
- form{“name”:“ces_form”}
- form{“name”: null}
- slot{“ces_results”: “Q1: 1 Q2: 2 Q3: 3 Q4: 4 Q5: 2 Q6: 3 Q7: 4 Q8: 1 Q9: 1 Q10: 4”}
domain.yml:
intents:
- ces
- choose_likert
entities:
- number
- time
slots:
ces_q1:
type: unfeaturized
auto_fill: false
ces_q2:…
number:
type: unfeaturized
responses:
utter_ask_ces_q1:
- text: “…”
utter_ask_ces_q2:
- text: “…”
etc. (10 questions)
actions:
- utter_ask_ces_q1
etc. (…q2 to q10)
forms:
- ces_form
nlu.md
## intent:choose_likert
- 1
- 2
- 3
- 4
actions.py
class CESForm(FormAction):
def name(self) -> Text:
"""Unique identifier of the form"""
return "ces_form"
@staticmethod
def required_slots(tracker: Tracker) -> List[Text]:
"""A list of required slots that the form has to fill"""
return ["ces_q1", "ces_q2", "ces_q3", "ces_q4", "ces_q5", "ces_q6", "ces_q7", "ces_q8", "ces_q9", "ces_q10"]
def slot_mappings(self) -> Dict[Text, Union[Dict, List[Dict]]]:
return {
"ces_q1": [
self.from_entity(
entity="number", intent=["choose_likert"]
),
],
"ces_q2": [
self.from_entity(
entity="number", intent=["choose_likert"]
),
], etc....
}
@staticmethod
def is_int(string: Text) -> bool:
"""Check if a string is an integer"""
try:
int(string)
return True
except ValueError:
return False
def validate_ces_q1(
self,
value: Text,
dispatcher: CollectingDispatcher,
tracker: Tracker,
domain: Dict[Text, Any],
) -> Dict[Text, Any]:
"""Validate Q1 value."""
if self.is_int(value) and int(value) > 0 and int(value) < 5:
return {"ces_q1": value}
else:
dispatcher.utter_message("Please write a number between 1 and 4")
# validation failed, set slot to None
return {"ces_q1": None}
def validate_ces_q2(
self,
value: Text,
dispatcher: CollectingDispatcher,
tracker: Tracker,
domain: Dict[Text, Any],
) -> Dict[Text, Any]:
"""Validate Q2 value."""
if self.is_int(value) and int(value) > 0 and int(value) < 5:
return {"ces_q2": value}
else:
dispatcher.utter_message("Please write a number between 1 and 4")
# validation failed, set slot to None
return {"ces_q2": None} etc... (All the validate functions are the same)
def submit(
self,
dispatcher: CollectingDispatcher,
tracker: Tracker,
domain: Dict[Text, Any],
) -> List[Dict]:
"""Define what the form has to do
after all required slots are filled"""
q1 = tracker.get_slot("ces_q1")
q2 = tracker.get_slot("ces_q2")
q3 = tracker.get_slot("ces_q3")
q4 = tracker.get_slot("ces_q4")
q5 = tracker.get_slot("ces_q5")
q6 = tracker.get_slot("ces_q6")
q7 = tracker.get_slot("ces_q7")
q8 = tracker.get_slot("ces_q8")
q9 = tracker.get_slot("ces_q9")
q10 = tracker.get_slot("ces_q10")
resultList = ["Q1: {}".format(q1), "Q2: {}".format(q2), "Q3: {}".format(q3), "Q4: {}".format(q4), "Q5: {}".format(q5), "Q6: {}".format(q6), "Q7: {}".format(q7), "Q8: {}".format(q8), "Q9: {}".format(q9), "Q10: {}".format(q10)]
return [SlotSet("ces_results", '\n'.join(resultList)), SlotSet("ces_q1", None), SlotSet("ces_q2", None), SlotSet("ces_q3", None), SlotSet("ces_q4", None), SlotSet("ces_q5", None), SlotSet("ces_q6", None), SlotSet("ces_q7", None), SlotSet("ces_q8", None), SlotSet("ces_q9", None), SlotSet("ces_q10", None)]
Output:
Bot loaded. Type a message and press enter (use ‘/stop’ to exit):
Your input -> CES
Please indicate how often in the past week have you felt this way (type a number between 1-4):
I was bothered by things that usually do not bother me
1: rarely (less than 1 day)
2: sometimes (1-2 days)
3: occasionally (3-4 days)
4: all the time (5-7 days)
Your input -> 2
(Here it should ask the next question but instead immediately jumps to the submit method and sets all the values to the first input)
Q1: 2
Q2: 2
Q3: 2
Q4: 2
Q5: 2
Q6: 2
Q7: 2
Q8: 2
Q9: 2
Q10: 2