Skip to content

Commit

Permalink
first pass at getting wit.ai to help us with times!
Browse files Browse the repository at this point in the history
  • Loading branch information
cromulus committed May 26, 2016
1 parent adf98fa commit 369c5f4
Show file tree
Hide file tree
Showing 32 changed files with 607 additions and 348 deletions.
10 changes: 9 additions & 1 deletion Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,9 @@ gem 'rails', '~> 4.2.0'
# must use this version of mysql2 for rails 4.0.0
gem 'mysql2', '~> 0.3.18'

gem 'validates_overlap'
gem 'redis' # ephemeral storage. used for expiring wit.ai contexts

gem 'validates_overlap' # to ensure we don't double book people

gem 'mail', '2.6.3'

Expand Down Expand Up @@ -136,6 +138,9 @@ gem 'aasm'
# cron jobs for backups and sending reminders
gem 'whenever', require: false

# natural language processing API
gem 'wit'

group :testing do
# mock tests w/mocha
gem 'mocha', require: false
Expand All @@ -162,6 +167,9 @@ group :testing do

# webrick is slow, capybara will use puma instead
gem 'puma'

# in memory redis for testing only
gem 'mock_redis'
end

group :development, :test do
Expand Down
8 changes: 7 additions & 1 deletion Gemfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -224,6 +224,7 @@ GEM
minitest (5.8.4)
mocha (1.1.0)
metaclass (~> 0.0.1)
mock_redis (0.16.1)
multi_json (1.11.2)
multi_xml (0.5.5)
multipart-post (2.0.0)
Expand Down Expand Up @@ -307,6 +308,7 @@ GEM
rb-fsevent (0.9.7)
rb-inotify (0.9.5)
ffi (>= 0.5.0)
redis (3.3.0)
ref (2.0.0)
responders (2.1.1)
railties (>= 4.2.0, < 5.1)
Expand Down Expand Up @@ -449,6 +451,7 @@ GEM
will_paginate (3.1.0)
will_paginate-bootstrap (0.2.5)
will_paginate (>= 3.0.3)
wit (3.3.1)
wuparty (1.2.6)
httparty (>= 0.6.1)
mime-types (~> 1.16)
Expand Down Expand Up @@ -497,6 +500,7 @@ DEPENDENCIES
memory_profiler
memory_test_fix
mocha
mock_redis
mysql2 (~> 0.3.18)
newrelic_rpm
phony_rails
Expand All @@ -507,6 +511,7 @@ DEPENDENCIES
quiet_assets
rack-mini-profiler
rails (~> 4.2.0)
redis
rspec-rails (~> 3.0)
rspec-retry
rubocop
Expand All @@ -533,7 +538,8 @@ DEPENDENCIES
whenever
will_paginate
will_paginate-bootstrap (~> 0.2.5)
wit
wuparty

BUNDLED WITH
1.12.2
1.12.3
31 changes: 15 additions & 16 deletions Vagrantfile
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ Vagrant.configure(2) do |config|

# Every Vagrant development environment requires a box. You can search for
# boxes at https://atlas.hashicorp.com/search.
config.vm.box = "ubuntu/trusty64"
config.vm.box = 'ubuntu/trusty64'

# config.vm.network "forwarded_port", guest: 80, host: 3000

Expand All @@ -23,7 +23,7 @@ Vagrant.configure(2) do |config|
config.cache.scope = :box
config.cache.enable :generic

if !Gem.win_platform?
unless Gem.win_platform?
# passwordless nfs
# https://gist.github.com/cromulus/5044b9558319769aaf0b
config.cache.synced_folder_opts = {
Expand All @@ -33,7 +33,7 @@ Vagrant.configure(2) do |config|
end
else
puts "please run 'vagrant plugin install vagrant-cachier'"
puts "It will make vagrant substantially faster"
puts 'It will make vagrant substantially faster'
end

if Vagrant.has_plugin?('vagrant-hostmanager')
Expand All @@ -52,15 +52,15 @@ Vagrant.configure(2) do |config|
config.hostmanager.manage_host = true
else
puts "run 'vagrant plugin install vagrant-hostmanager'"
puts "It will help you find your dev environment!"
puts 'It will help you find your dev environment!'
end

# Provider-specific configuration so you can fine-tune various
# backing providers for Vagrant. These expose provider-specific options.
# Example for VirtualBox:
#

config.vm.provider "virtualbox" do |vb, override|
config.vm.provider 'virtualbox' do |vb, override|
# Don't display the VirtualBox GUI when booting the machine
vb.gui = false

Expand All @@ -74,7 +74,7 @@ Vagrant.configure(2) do |config|
# https://gist.github.com/cromulus/5044b9558319769aaf0b
# also this one: https://gist.github.com/GUI/2864683
override.vm.synced_folder '.', '/vagrant', type: 'nfs'
override.vm.network "private_network", ip: "192.168.33.124"
override.vm.network 'private_network', ip: '192.168.33.124'
end

# View the documentation for the provider you are using for more
Expand All @@ -91,8 +91,8 @@ Vagrant.configure(2) do |config|
# Puppet, Chef, Ansible, Salt, and Docker are also available. Please see the
# documentation for more information about their specific syntax and use.

#splitting shell provisioning for caching benefits.
config.vm.provision "shell", privileged: false, inline: %[
# splitting shell provisioning for caching benefits.
config.vm.provision :shell, privileged: false, inline: %(
sudo debconf-set-selections <<< 'mysql-server mysql-server/root_password password password'
sudo debconf-set-selections <<< 'mysql-server mysql-server/root_password_again password password'
Expand Down Expand Up @@ -121,24 +121,24 @@ Vagrant.configure(2) do |config|
sudo service elasticsearch start;
# automatically cd to /vagrant/
echo 'if [ -d /vagrant/ ]; then cd /vagrant/; fi' >> /home/vagrant/.bashrc
]
)

config.vm.provision :shell, privileged: false, inline: %[
config.vm.provision :shell, privileged: false, inline: %(
echo 'gem: --no-rdoc --no-ri' | sudo tee /etc/gemrc;
# rvm install is idempotent
curl -sSL https://rvm.io/mpapis.asc | gpg --import -
curl -sSL https://get.rvm.io | bash -s stable --auto-dotfiles
source ~/.profile
]
)

config.vm.provision :shell, privileged: false, inline: %[
config.vm.provision :shell, privileged: false, inline: %(
# cleanup and install the appropriate ruby version
source ~/.profile
rvm reload
rvm use --default install `cat /vagrant/.ruby-version`
rvm cleanup all
]
config.vm.provision :shell, privileged: false, inline: %[
)
config.vm.provision :shell, privileged: false, inline: %(
# setup our particular rails app
source ~/.profile
cd /vagrant/
Expand All @@ -156,6 +156,5 @@ Vagrant.configure(2) do |config|
sudo mkdir -p /var/run/nginx/tmp
sudo chown -R www-data:www-data /var/run/nginx/
sudo service nginx restart
]

)
end
2 changes: 2 additions & 0 deletions app/controllers/twilio_messages_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -93,11 +93,13 @@ def new
@twilio_message = TwilioMessage.new
end

# this is the callback from twilio about the message and it's delivery
# POST /twilio_messages/updatestatus
def updatestatus
this_message = TwilioMessage.find_by message_sid: params['MessageSid']
this_message.status = params['MessageStatus']
this_message.error_code = params['ErrorCode']
this_message.error_message = params['ErrorMessage']
this_message.save
end

Expand Down
3 changes: 2 additions & 1 deletion app/controllers/v2/event_invitations_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,8 @@ def send_email(person, event)
end

def send_sms(person, event)
::EventInvitationSms.new(to: person, event: event).send
# we send a bunch at once, delay it. Plus this has extra logic
Delayed::Job.enqueue SendEventInvitationsSmsJob.new(person, event)
end

# TODO: add a nested :event
Expand Down
69 changes: 17 additions & 52 deletions app/controllers/v2/sms_reservations_controller.rb
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
# FIXME: Refactor and re-enable cop
# rubocop:disable ClassLength
# FIXME: Refactor
class V2::SmsReservationsController < ApplicationController
skip_before_action :verify_authenticity_token, only: [:create]
skip_before_action :authenticate_user!
Expand All @@ -13,18 +12,11 @@ def create
# should do sms verification here if unverified

# FIXME: this needs a refactor badly.
if letters_and_numbers_only? # they are trying to accept!
reservation = V2::Reservation.new(generate_reservation_params)
if reservation.save
send_new_reservation_notifications(person, reservation)
else
resend_available_slots(person, event)
end
elsif remove?
if remove?
# do the remove people thing.
person.deactivate!
elsif declined? # currently not used.
send_decline_notifications(person, event)
#send_decline_notifications(person, event)
elsif confirm? # confirmation for the days reservations
if person.v2_reservations.for_today_and_tomorrow.size > 0
person.v2_reservations.for_today_and_tomorrow.each(&:confirm!)
Expand All @@ -46,7 +38,14 @@ def create
elsif calendar?
::ReservationReminderSms.new(to: person, reservations: person.v2_reservations.for_today_and_tomorrow).send
else
send_error_notification && return
str_context = Redis.current.get("wit_context:#{person.id}")

# we don't know what event_id we're talking about here
send_error_notification && return if str_context.nil?
context = JSON.parse(str_context)
new_context = ::WitClient.run_actions "#{person.id}_#{context['event_id']}", message, context
Redis.current.set("wit_context:#{person.id}", new_context.to_json)
Redis.current.expire("wit_context:#{person.id}", 3600)
end
render text: 'OK'
end
Expand All @@ -62,21 +61,6 @@ def person
@person ||= Person.find_by(phone_number: sms_params[:From])
end

# TODO: need to handle more than 26 slots
def selection
slot_letter = message.downcase.delete('^a-z')
# "a".ord - ("A".ord + 32) == 0
# "b".ord - ("A".ord + 32) == 1
# (0 + 97).chr == a
# (1 + 97).chr == b
slot_letter.ord - ('A'.ord + 32)
end

def event
event_id = message.delete('^0-9').to_i
@event ||= V2::Event.includes(:event_invitation, :user, :time_slots).find_by(id: event_id)
end

def event_invitation
@event_invitation ||= event.event_invitation
end
Expand All @@ -85,18 +69,6 @@ def user
@user ||= event_invitation.user
end

def time_slot
@event.time_slots[selection]
end

def generate_reservation_params
{ user: user,
person: person,
event: event,
event_invitation: event_invitation,
time_slot: time_slot }
end

def send_new_reservation_notifications(person, reservation)
::ReservationSms.new(to: person, reservation: reservation).send
ReservationNotifier.notify(email_address: reservation.user.email, reservation: reservation).deliver_later
Expand All @@ -107,7 +79,9 @@ def send_decline_notifications(person, event)
end

def send_error_notification
::InvalidOptionSms.new(to: sms_params[:From]).send
# awkward, yes, but see application_sms to understand why
phone_struct = Struct.new(:phone_number).new(sms_params[:From])
::InvalidOptionSms.new(to: phone_struct).send

render text: 'OK'
end
Expand Down Expand Up @@ -142,30 +116,21 @@ def remove?
message.downcase.include?('remove')
end

def letters_and_numbers_only?
# this is for accepting only. many messages now won't pass.
# up to 10k events
message.downcase =~ /\b\d{1,5}[a-z]\b/
end

def sms_params
params.permit(:From, :Body)
end

def twilio_params
res = {}
params.permit(:From, :To, :Body, :MessageSid, :DateCreated, :DateUpdated, :DateSent, :AccountSid, :WufooFormid, :SmsStatus, :FromZip, :FromCity,
:FromState, :ErrorCode, :ErrorMessage).to_unsafe_hash.keys do |k, v|
# behold the horror
:FromState, :ErrorCode, :ErrorMessage, :Direction, :AccountSid).to_unsafe_hash.keys do |k, v|
# behold the horror of translating twilio params to rails attributes
res[k.gsub!(/(.)([A-Z])/, '\1_\2').downcase] = v
end
res
end

def save_twilio_message
tm = TwilioMessage.new(twilio_params)
tm.direction = 'twiml-incoming'
tm.save
TwilioMessage.create(twilio_params)
end
end
# rubocop:enable ClassLength
Loading

0 comments on commit 369c5f4

Please sign in to comment.