Skip to content

Commit

Permalink
Release 7.7.0
Browse files Browse the repository at this point in the history
  • Loading branch information
il1tvinov committed Sep 4, 2020
1 parent f381758 commit 7d4825f
Show file tree
Hide file tree
Showing 108 changed files with 5,299 additions and 1,024 deletions.
2 changes: 1 addition & 1 deletion chatbot/Dockerfile-actions
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
FROM rasa/rasa:1.10.3-full
FROM rasa/rasa:1.10.11-full

WORKDIR /app

Expand Down
2 changes: 1 addition & 1 deletion chatbot/Dockerfile-core
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
FROM rasa/rasa:1.10.3-full
FROM rasa/rasa:1.10.11-full

WORKDIR /app

Expand Down
Empty file added chatbot/__init__.py
Empty file.
208 changes: 150 additions & 58 deletions chatbot/actions/actions.py
Original file line number Diff line number Diff line change
@@ -1,104 +1,196 @@
from typing import Dict, Text, Any, List, Union
from datetime import datetime
from typing import Dict, Text, Any, List, Union, Optional
from json import loads, JSONDecodeError

from pytz import utc
from rasa_sdk import Tracker, Action
from rasa_sdk.forms import FormAction
from rasa_sdk.events import EventType, SlotSet
from rasa_sdk.forms import FormAction, REQUESTED_SLOT, Form
from rasa_sdk.executor import CollectingDispatcher
from rasa_sdk.events import AllSlotsReset

from parsedatetime import Calendar

import requests


def convert_date(raw_date: str) -> tuple:
def _get_datetime_range(date: str) -> tuple:
""" Makes one day period by received date.
Parameters
----------
date:
Date for report generating.
from .responses import RESPONSES, WORKFLOW

Returns
-------
Datetime range.
"""
date = datetime.strptime(date, "%Y-%m-%d")
from_date = date.astimezone(utc)
from_date = from_date.strftime("%Y-%m-%dT%H:%M:%S.%f%z")

to_date = (
date.replace(hour=23, minute=59, second=59)
.astimezone(utc)
.strftime("%Y-%m-%dT%H:%M:%S.%f%z")
)

return from_date, to_date
from rasa_sdk import Tracker, Action

parser = Calendar()
date, status = parser.parse(raw_date)
date = datetime(*date[:3]).strftime("%Y-%m-%d")
return _get_datetime_range(date)
from .api.report_generator import (
generate_report,
request_values,
generate_report_payload,
check_issues,
)


def generate_report(date: str):
""" Sends request for report generating.
def get_action_with_help_intent(latest_intent: str) -> list:
""" Get action name for intent name.
Parameters
----------
date:
Date for which report will be generated.
latest_intent:
Bot intent.
Returns
-------
Report data generator.
----------
Actions name list.
"""
payload = {"date": date}
url = "http://nostradamus-core:8000/virtual_assistant/generate_report/"

request = requests.post(url, json=payload)
yield request.json()
actions = []
for action, intents in WORKFLOW.items():
for intent in intents:
if intent == latest_intent:
actions.append(action)
return actions


class ReportForm(FormAction):
"""Collects data for report"""
"""Collects data for report."""

def name(self) -> Text:
return "report_form"

def validate_period(
self,
value: Text,
dispatcher: CollectingDispatcher,
tracker: Tracker,
domain: Dict[Text, Any],
) -> Dict[Text, Any]:
"""Validate period value."""
try:
if isinstance(loads(value), list):
return {"period": value}
else:
dispatcher.utter_message("Incorrect date. Please try again")
return {"period": None}
except (JSONDecodeError, TypeError):
dispatcher.utter_message("Incorrect date. Please try again")
return {"period": None}

@staticmethod
def required_slots(tracker):
return ["period"]
required_slots = ["project", "period"]

if tracker.get_slot("project") == "Pick a project":
required_slots.insert(1, "project_selection")

return required_slots

def slot_mappings(self) -> Dict[Text, Union[Dict, List[Dict]]]:
return {"period": self.from_entity(entity="period")}
return {
"project": self.from_entity(entity="project"),
"project_selection": self.from_text(),
"period": self.from_text(),
}

def request_next_slot(
self,
dispatcher: "CollectingDispatcher",
tracker: "Tracker",
domain: Dict[Text, Any],
) -> Optional[List[EventType]]:
for slot in self.required_slots(tracker):

if self._should_request_slot(tracker, slot):

if not check_issues():
dispatcher.utter_message(
text="Oops! Bugs haven't been uploaded yet. Please try again later"
)

return [Form(None), AllSlotsReset()]

if slot == "project_selection":
response = request_values("Project")
response["operation"] = "filtration"

dispatcher.utter_message(
json_message=response, timeout=100,
)
elif slot == "period":
response = {
"operation": "calendar",
"title": "Please choose a date",
}
dispatcher.utter_message(json_message=response)
else:
dispatcher.utter_message(
template=f"utter_ask_{slot}", **tracker.slots
)
return [SlotSet(REQUESTED_SLOT, slot)]

# no more required slots to fill
return

def submit(
self,
dispatcher: CollectingDispatcher,
tracker: Tracker,
domain: Dict[Text, Any],
) -> List[Dict]:
period = convert_date(tracker.get_slot("period"))
message = generate_report(period)
dispatcher.utter_message(json_message=next(message), timeout=100)
payload = generate_report_payload(tracker)
message = generate_report(payload)

if not message.get("filename"):
message = "Oops! There is no data you’re looking for 😔"
dispatcher.utter_message(text=message, timeout=100)
else:
message["operation"] = "report"
message["filters"] = payload
dispatcher.utter_message(json_message=message, timeout=100)

return [AllSlotsReset()]


class ActionDefaultAskAffirmation(Action):
"""Override action default ask affirmation"""
class ActionFAQSelector(Action):
"""Basic FAQ response selector."""

def name(self) -> Text:
return "action_default_ask_affirmation"
return "action_faq_selector"

def run(
self,
dispatcher: CollectingDispatcher,
tracker: Tracker,
domain: Dict[Text, Any],
) -> List[Dict]:
dispatcher.utter_message(template="utter_cannot_help")
intent = tracker.latest_message["intent"].get("name")
messages = RESPONSES.get("faq").get(intent)
for message in messages:
dispatcher.utter_message(text=message)

return []


class ActionCustomFallback(Action):
"""Action custom fallback."""

def name(self) -> Text:
return "action_custom_fallback"

def run(
self,
dispatcher: CollectingDispatcher,
tracker: Tracker,
domain: Dict[Text, Any],
) -> List[Dict]:

latest_intent = tracker.latest_message["intent"].get("name")
actions = get_action_with_help_intent(latest_intent)
if actions:
for action in actions:
if action != tracker.latest_action_name:
if action == "action_faq_selector":
ActionFAQSelector().run(
dispatcher=dispatcher,
tracker=tracker,
domain=domain,
)
else:
dispatcher.utter_message(template=action)
elif (
latest_intent == "affirm"
and tracker.events[-4].get("text") == "Do you want to learn more?"
):
dispatcher.utter_message(
template="utter_more_details_analysis_and_training"
)
else:
dispatcher.utter_message(template="utter_cannot_help")

return [Form(None), AllSlotsReset()]
Empty file added chatbot/actions/api/__init__.py
Empty file.
113 changes: 113 additions & 0 deletions chatbot/actions/api/report_generator.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
import requests

from json import loads, JSONDecodeError
from parsedatetime import Calendar
from datetime import datetime
from pytz import utc

from rasa_sdk import Tracker

API_URL = "http://nostradamus-core:8000/virtual_assistant"


def convert_date(raw_date: str) -> tuple:
def _get_datetime_range(date: str) -> tuple:
""" Makes one day period by received date.
Parameters
----------
date:
Date for report generating.
Returns
-------
Datetime range.
"""
date = datetime.strptime(date, "%Y-%m-%d")
from_date = date.astimezone(utc)
from_date = from_date.strftime("%Y-%m-%dT%H:%M:%S.%f%z")

to_date = (
date.replace(hour=23, minute=59, second=59)
.astimezone(utc)
.strftime("%Y-%m-%dT%H:%M:%S.%f%z")
)

return from_date, to_date

parser = Calendar()
date, status = parser.parse(raw_date)
date = datetime(*date[:3]).strftime("%Y-%m-%d")
return _get_datetime_range(date)


def generate_report(payload: dict):
""" Sends request for report generating.
Parameters
----------
payload:
Additional filtration info.
Returns
-------
Report data generator.
"""
url = f"{API_URL}/generate_report/"

request = requests.post(url, json=payload)
return request.json()


def generate_report_payload(tracker: Tracker) -> dict:
""" Creates a filtration payload from report form slots.
Parameters
----------
tracker:
The state of conversation.
Returns
-------
Filtration payload.
"""
slots = tracker.current_slot_values()
payload = dict()

for slot in slots:
if f"{slot}_selection" in slots:
try:
payload[slot] = loads(slots[f"{slot}_selection"])
except (JSONDecodeError, TypeError):
payload[slot] = slots[f"{slot}_selection"]
elif slot == "period":
payload[slot] = loads(slots[slot])

return payload


def request_values(field: str) -> dict:
""" Query unique values by field.
Parameters
----------
field:
Issue field.
Returns
-------
Unique values.
"""
params = {"field": field}
url = f"{API_URL}/request_values/"

request = requests.get(url, params=params)
return request.json()


def check_issues() -> bool:
url = f"{API_URL}/issues_checker/"

request = requests.get(url)

return request.json().get("issues_loaded")
Loading

0 comments on commit 7d4825f

Please sign in to comment.