Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .ruby-version
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
2.7.2
12 changes: 11 additions & 1 deletion app/commands/send_grid_email.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
class SendGridEmail < ApplicationCommand
argument :from, default: "Greenlight <lucy@greenlightready.com>"
argument :to
argument :reply_to
argument :cc
argument :bcc
argument :subject
Expand All @@ -12,12 +13,17 @@ class SendGridEmail < ApplicationCommand
validates :to, presence: true
validates :subject, presence: true

def logger
@logger ||= Logger.new(Rails.root.join('log', 'email.log'))
end

def pony_payload
return @pony_payload if defined?(@pony_payload)

@pony_payload = {}
@pony_payload[:from] = self.from
@pony_payload[:to] = self.to
@pony_payload[:reply_to] = self.reply_to if self.reply_to
@pony_payload[:cc] = self.cc if self.cc
@pony_payload[:bcc] = self.bcc if self.bcc
@pony_payload[:subject] = self.subject
Expand All @@ -28,6 +34,10 @@ def pony_payload
end

def work
Pony.mail(pony_payload)
if Rails.env.development?
logger.info(pony_payload.inspect)
else
Pony.mail(pony_payload)
end
end
end
42 changes: 42 additions & 0 deletions app/commands/survey_response_register.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
# frozen_literal_string: true

class SurveyResponseRegister < ApplicationCommand
argument :medium
argument :response
argument :from
argument :permalink

def work
return false if respondant.nil?
return false if survey.nil?
return false unless survey.valid_response? response

survey_response = respondant.survey_responses.
not_answered.find_by(survey: survey)
return false if survey_response.nil?

survey_response.update(response: response, medium: medium, responded_at: DateTime.now)
end

private

def respondant
@respondant ||= User.find_by_email_or_mobile(from)
end

def survey
return @survey if @survey.present?

@survey = medium == :email ?
Survey.find_by_permalink(permalink) :
last_sent_survey(respondant)
end

def last_sent_survey(respondant)
last_survey = respondant.survey_responses.
not_answered.order(created_at: :desc).first
return nil if last_survey.nil?

last_survey.survey
end
end
61 changes: 61 additions & 0 deletions app/controllers/admin/surveys_controller.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
module Admin
class SurveysController < ApplicationController
before_action :set_survey, except: [:index, :new, :create]

def index
@pagy, @surveys = pagy(Survey.all.order(:id))
end

def show
@result = SurveyResult.new(@survey).result_presentable
end

def location
@location = Location.find(params[:location_id])
@result = SurveyResult.new(
@survey,
@survey.survey_responses.at_locations(@location.id)
).result_presentable
end

def new
@survey = Survey.new
end

def create
@survey = Survey.new(params.require(:survey).permit!)
if @survey.save
redirect_to admin_survey_path(@survey), notice: "#{@survey.question} created!"
else
render 'new'
end
end

def edit; end

def update
@survey.assign_attributes(params.require(:survey).permit!)
if @survey.save
redirect_to admin_survey_path(@survey), notice: "#{@survey.question} updated!"
else
render 'edit'
end
end

def destroy
if params[:confirmation] == "DELETE #{@survey.permalink[0..3].upcase}"
@survey.destroy
redirect_to admin_surveys_path, notice: "Congrats! You deleted Survey: #{@survey.question}"
else
flash[:alert] = 'Incorrect confirmation code.'
redirect_to [:admin, @survey]
end
end

private

def set_survey
@survey = Survey.find(params[:id])
end
end
end
20 changes: 20 additions & 0 deletions app/controllers/admin/targets_controller.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# frozen_literal_string: true
module Admin
class TargetsController < ApplicationController
before_action :set_survey

def index
@pagy, @locations = pagy(Location.q(params[:query]))
end

def update
@survey.send_to_locations([params[:id]])
end

private

def set_survey
@survey = Survey.find(params[:survey_id])
end
end
end
1 change: 1 addition & 0 deletions app/controllers/api_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ class APIController < ActionController::API
include UsersController
include CurrentUserController
include MailController
include InboundsController
include SmokeTestsController
include UtilController
end
50 changes: 50 additions & 0 deletions app/controllers/inbounds_controller.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
# frozen_string_literal: true
module InboundsController
extend ActiveSupport::Concern

included do
post '/v1/inbounds/email', auth: false do
survey_permalink = SurveyParser.permalink_from_email(params[:to])
content = params[:text]
from_mail = JSON.parse(params[:envelope])['from']

if survey_permalink.present? && SurveyParser.content_valid?(content)
SurveyResponseRegister.new(
medium: :email,
response: SurveyParser.clean_content(content),
from: from_mail,
permalink: survey_permalink,
).run
end

success_response
end

#
# {"From"=>"19199993085",
# "MessageIntent"=>"",
# "MessageUUID"=>"18db7417-5f38-11eb-8b58-0242ac110008",
# "PowerpackUUID"=>"",
# "Text"=>"Survey-5",
# "To"=>"19197285377",
# "TotalAmount"=>"0",
# "TotalRate"=>"0",
# "Type"=>"sms",
# "Units"=>"1"}
#
post '/v1/inbounds/sms', auth: false do
text = params['Text']
from = params['From']

if SurveyParser.content_valid?(text)
SurveyResponseRegister.new(
medium: :phone,
response: SurveyParser.clean_content(text),
from: from,
).run
end

success_response
end
end
end
1 change: 1 addition & 0 deletions app/models/location.rb
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ class Location < ApplicationRecord
has_many :location_accounts
has_many :cohorts
has_many :users, -> { distinct }, through: :location_accounts
has_many :surveys

LocationAccount::ROLES.each do |role|
has_many "#{role}_accounts".to_sym, -> { where(role: role) }, class_name: 'LocationAccount'
Expand Down
113 changes: 113 additions & 0 deletions app/models/survey.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
# frozen_string_literal: true

class Survey < ApplicationRecord
extend Enumerize

QUESTION_TYPES = [
CHOICES = 'choices',
PLAIN = 'plain',
].freeze

before_save :make_it_permalink, if: :new_record?

enumerize :question_type, in: QUESTION_TYPES, default: CHOICES

has_many :survey_responses
has_many :users, -> { distinct }, through: :survey_responses

validates :question, presence: true

# locations that the survey is sent to
def locations
Location.where(id: location_ids)
end

def send_to_locations(location_ids)
target_users = User.joined(location_ids).with_contact_method
.where.not(id: self.users.pluck(:id))
send_to_users(target_users.pluck(:id))

update(
location_ids: (self.location_ids + location_ids.map(&:to_i)).uniq,
last_sent_at: DateTime.now,
)
end

def send_to_users(user_ids)
user_ids.each do |user_id|
self.survey_responses.build(user_id: user_id)
# trigger sender job
SendSurveyWorker.perform_async(id, user_id)
end

save
end

def valid_response?(response)
choices.keys.include? response
end

def choices_str
choices.values.join(', ')
end

def choices_str=(str)
self.choices = choices_str_to_hash(str)
end

def choices_es_str
choices_es.values.join(', ')
end

def choices_es_str=(str)
self.choices_es = choices_str_to_hash(str)
end

def locale_question(locale)
if locale == 'es'
return question_es || question
end

question
end

def locale_choices(locale)
if locale == 'es'
return choices_es.keys.length == choices.keys.length ? choices_es : choices
end

choices
end

private

def choices_str_to_hash(str)
labels = str.split(',').map(&:strip)
labels.each.with_index.reduce({}) do |hash, (label, index)|
hash.merge({
"#{index+1}" => label,
})
end
end

def make_it_permalink
self.permalink = SecureRandom.urlsafe_base64(10)
end
end

# == Schema Information
#
# Table name: surveys
#
# id :bigint not null, primary key
# question :string not null
# question_es :string
# question_type :string default("choices"), not null
# choices :jsonb
# choices_es :jsonb
# location_ids :jsonb
# last_sent_at :datetime
# created_at :datetime not null
# updated_at :datetime not null
# permalink :string
#
43 changes: 43 additions & 0 deletions app/models/survey_response.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
# frozen_string_literal: true

class SurveyResponse < ApplicationRecord
extend Enumerize

MEDIUMS = [
EMAIL = 'email',
PHONE = 'phone',
].freeze

enumerize :medium, in: MEDIUMS

belongs_to :survey
belongs_to :user

validates :user_id, uniqueness: { scope: :survey_id }

scope :not_answered, -> { where(responded_at: nil) }
scope :answered, -> { where.not(responded_at: nil) }
scope :at_locations, -> (locations) {
joins(user: :locations).where(locations: { id: locations })
}
end

# == Schema Information
#
# Table name: survey_responses
#
# id :bigint not null, primary key
# user_id :bigint not null
# survey_id :bigint not null
# response :string
# medium :string
# responded_at :datetime
# created_at :datetime not null
# updated_at :datetime not null
#
# Indexes
#
# index_survey_responses_on_survey_id (survey_id)
# index_survey_responses_on_survey_id_and_user_id (survey_id,user_id) UNIQUE
# index_survey_responses_on_user_id (user_id)
#
Loading