Skip to content

Commit

Permalink
Merge branch 'master' of https://github.com/sara/garfield
Browse files Browse the repository at this point in the history
  • Loading branch information
sara committed Jul 25, 2018
2 parents 048d953 + 8ce0ce3 commit 761fbdb
Show file tree
Hide file tree
Showing 35 changed files with 801 additions and 53 deletions.
1 change: 1 addition & 0 deletions .gitignore
Expand Up @@ -24,3 +24,4 @@ static/
.tox/
coverage.xml
.pytest_cache/
celerybeat*
Empty file added garfield/bots/__init__.py
Empty file.
8 changes: 8 additions & 0 deletions garfield/bots/admin.py
@@ -0,0 +1,8 @@
from django.contrib import admin

from bots.models import Bot


@admin.register(Bot)
class BotAdmin(admin.ModelAdmin):
pass
5 changes: 5 additions & 0 deletions garfield/bots/apps.py
@@ -0,0 +1,5 @@
from django.apps import AppConfig


class BotConfig(AppConfig):
name = 'bot'
29 changes: 29 additions & 0 deletions garfield/bots/migrations/0001_initial.py
@@ -0,0 +1,29 @@
# Generated by Django 2.0.4 on 2018-07-23 17:07

from django.db import migrations, models


class Migration(migrations.Migration):

initial = True

dependencies = [
]

operations = [
migrations.CreateModel(
name='Bot',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('date_created', models.DateTimeField(auto_now_add=True, db_index=True)),
('date_modified', models.DateTimeField(auto_now=True)),
('deleted', models.BooleanField(default=False)),
('alias', models.CharField(blank=True, max_length=255, null=True)),
('neighborhood', models.CharField(blank=True, max_length=255, null=True)),
('location', models.CharField(blank=True, max_length=255, null=True)),
('rates', models.CharField(blank=True, max_length=255, null=True)),
('model', models.CharField(blank=True, max_length=255, null=True)),
('threshold', models.FloatField(default=0.7)),
],
),
]
28 changes: 28 additions & 0 deletions garfield/bots/migrations/0002_auto_20180723_2008.py
@@ -0,0 +1,28 @@
# Generated by Django 2.0.4 on 2018-07-23 20:08

from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
('bots', '0001_initial'),
]

operations = [
migrations.AddField(
model_name='bot',
name='debug',
field=models.BooleanField(default=False),
),
migrations.AddField(
model_name='bot',
name='human_delay_max',
field=models.IntegerField(default=120),
),
migrations.AddField(
model_name='bot',
name='human_delay_min',
field=models.IntegerField(default=30),
),
]
18 changes: 18 additions & 0 deletions garfield/bots/migrations/0003_bot_answers.py
@@ -0,0 +1,18 @@
# Generated by Django 2.0.4 on 2018-07-24 14:46

from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
('bots', '0002_auto_20180723_2008'),
]

operations = [
migrations.AddField(
model_name='bot',
name='answers',
field=models.CharField(blank=True, max_length=255, null=True),
),
]
Empty file.
27 changes: 27 additions & 0 deletions garfield/bots/models.py
@@ -0,0 +1,27 @@
from django.db import models


class Bot(models.Model):
date_created = models.DateTimeField(auto_now_add=True, db_index=True)
date_modified = models.DateTimeField(auto_now=True)
deleted = models.BooleanField(default=False)

alias = models.CharField(max_length=255, null=True, blank=True)
neighborhood = models.CharField(max_length=255, null=True, blank=True)
location = models.CharField(max_length=255, null=True, blank=True)
rates = models.CharField(max_length=255, null=True, blank=True)

model = models.CharField(max_length=255, null=True, blank=True)
answers = models.CharField(max_length=255, null=True, blank=True)
threshold = models.FloatField(default=0.7)

human_delay_min = models.IntegerField(default=30)
human_delay_max = models.IntegerField(default=120)

debug = models.BooleanField(default=False)

def __str__(self):
return "{0} - {1}: {2}, {3}".format(self.alias,
self.model,
self.neighborhood,
self.location)
167 changes: 167 additions & 0 deletions garfield/bots/tasks.py
@@ -0,0 +1,167 @@
import os
import operator
from random import choice
import json

from celery import shared_task

from django.conf import settings
from django.template import Context
from django.template import Template

import spacy

from sms.tasks import send_sms_message
from sms.tasks import save_sms_message

from .models import Bot

phrase_order = {'SALUTATION': 10,
'ACKNOWLEDGEMENT': 20,
'FLIRT': 30,
'AVAILABILITY': 40,
'HAGGLE': 50,
'PRICE': 60,
'IN-OR-OUT': 70,
'LOCATION': 80,
'EXTRAS': 90,
'PICS': 100,
'ETHNICITY': 110,
'LE-CHECK': 120,
'REAL': 130,
'CONFUSION': 140,
'PHYSICAL': 150,
'AGE': 160,
'SERVICES': 170,
'SOURCE': 180,
'VALEDICTION': 190,
'TIME': 200,
'RECRUITING': 210}


@shared_task
def process_bot_response(message, bot_id):
bot = Bot.objects.get(id=bot_id)

if not bot.debug:
save_sms_message(message)

classify_message_intent.apply_async(args=[message, bot_id])


@shared_task
def classify_message_intent(message, bot_id):
bot = Bot.objects.get(id=bot_id)

nlp = spacy.load(os.path.join(settings.ARBUCKLE_DIR,
'models',
bot.model))

doc = nlp(message['Body'])

compose_response.apply_async(args=[doc.cats,
message,
bot_id])


@shared_task
def compose_response(cats, message, bot_id):
bot = Bot.objects.get(id=bot_id)

intents = process_intents(cats,
bot.threshold)

order = order_intents(intents)

response = retrieve_answer(order, bot_id)

send_bot_response.apply_async(args=[response,
intents,
message,
bot_id])


@shared_task
def send_bot_response(response,
intents,
message,
bot_id):

bot = Bot.objects.get(id=bot_id)

countdown = choice(range(bot.human_delay_min,
bot.human_delay_max))

if bot.debug:
countdown = 0

if intents:
deliver_bot_response.apply_async(args=[response,
message,
bot_id],
countdown=countdown)

if bot.debug:
send_sms_message(to=message['From'],
from_=message['To'],
body="[Debug]\n{0}".format(intents))


@shared_task
def deliver_bot_response(response, message, bot_id):
bot = Bot.objects.get(id=bot_id)

msg = send_sms_message(to=message['From'],
from_=message['To'],
body=response)

if not bot.debug:
save_sms_message.apply_async(args=[msg])


def process_intents(cats, threshold):
cats = sorted(cats.items(),
key=operator.itemgetter(1))

intents = {}

for cat in cats:
if cat[1] >= threshold:
intents[cat[0]] = cat[1]

return intents


def order_intents(intents):
order = {}

for key, value in intents.items():
order[key] = phrase_order[key]

return sorted(order.items(),
key=operator.itemgetter(1))


def retrieve_answer(order, bot_id):
bot = Bot.objects.get(id=bot_id)

answers_file = open(os.path.join(settings.ARBUCKLE_DIR,
'answers',
bot.answers),
'r')
answers = json.load(answers_file)
answers_file.close()

response = ""

for key, value in order:
response = "{0} {1}".format(response,
choice(answers[key])).strip()

template = Template(response)
context = Context({'alias': bot.alias,
'neighborhood': bot.neighborhood,
'location': bot.location,
'price': bot.rates})

return template.render(context)
Empty file added garfield/bots/tests/__init__.py
Empty file.
8 changes: 8 additions & 0 deletions garfield/bots/tests/assets/answers/answers.json
@@ -0,0 +1,8 @@
{
"LOCATION": [
"{{ location }}"
],
"SALUTATION": [
"Hi"
]
}
17 changes: 17 additions & 0 deletions garfield/bots/tests/test_models.py
@@ -0,0 +1,17 @@
from django.test import TestCase

from bots.models import Bot


class BotModelTestCase(TestCase):
def setUp(self):
self.bot = Bot.objects.create(alias="Rosa",
neighborhood="Times Square",
location="42nd and Broadway",
rates="100hr 50hh 40 ss",
model="buyer_intent_full")

def test_string_representation(self):
self.assertEquals(str(self.bot),
"Rosa - buyer_intent_full: Times Square,"
" 42nd and Broadway")

0 comments on commit 761fbdb

Please sign in to comment.