2014-05-29 07:19:55 +00:00
|
|
|
module JamRuby
|
2014-07-26 03:58:58 +00:00
|
|
|
|
|
|
|
|
class ResultStub
|
|
|
|
|
extend ActiveModel::Naming
|
|
|
|
|
extend ActiveModel::Translation
|
|
|
|
|
include ActiveModel::Validations
|
|
|
|
|
include ActiveModel::Conversion
|
|
|
|
|
|
|
|
|
|
attr_accessor :vals
|
|
|
|
|
|
|
|
|
|
def initialize(vals)
|
|
|
|
|
@vals = vals
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
def self.stubs(sql)
|
|
|
|
|
ActiveRecord::Base.connection.execute(sql).collect { |rr| self.new(rr) }
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
def persisted?; false; end
|
|
|
|
|
end
|
|
|
|
|
|
2014-08-01 01:16:38 +00:00
|
|
|
# Temporary Tables created by this class:
|
|
|
|
|
|
|
|
|
|
# tmp_candidate_sessions
|
|
|
|
|
# ----------------------
|
|
|
|
|
#
|
|
|
|
|
# These are 'open' sessions that have any open slots left, and fall within a certain start time.
|
|
|
|
|
# The session creator must also have a locidispid.
|
|
|
|
|
#
|
|
|
|
|
# session_id - music_session.id
|
|
|
|
|
# creator_id - music_session.user_id
|
|
|
|
|
# creator_score_idx - this is the creator's users.last_jam_locidispid
|
|
|
|
|
# instrument_id - instruments that are open as gleamed from the RSVP. If this is NULL, it means 'ANY INSTRUMENT'
|
2014-08-01 02:06:06 +00:00
|
|
|
# invited_user_id - the ID of a user who was invited. Can be NULL.
|
2014-08-01 01:16:38 +00:00
|
|
|
#
|
|
|
|
|
# tmp_candidate_recipients
|
|
|
|
|
# ------------------------
|
|
|
|
|
#
|
|
|
|
|
# These are musicians, that allow email notifications, that have an instrument which matches the session's open RSVP slot's instrument.
|
|
|
|
|
# The musician must also have a locidispid.
|
|
|
|
|
#
|
|
|
|
|
# receiver_id - user ID that could be in the session
|
|
|
|
|
# receiver_score_idx - the user's last_jam_locidispid
|
|
|
|
|
# instrument_id - the user's matching instrument for a open session slot. If this is NULL, it means 'ANY INSTRUMENT'
|
2014-08-01 21:17:04 +00:00
|
|
|
# invited_user_id
|
2014-08-01 01:16:38 +00:00
|
|
|
#
|
|
|
|
|
# tmp_matches
|
|
|
|
|
# -----------
|
|
|
|
|
#
|
|
|
|
|
# These are 'candidate_recipients' that have a decent enough score with the creator of the music sessions in tmp_candidate_sessions
|
|
|
|
|
#
|
|
|
|
|
# receiver_id - the user.id that should receive an Daily Session email
|
|
|
|
|
# session_id - the music_session.id for the email
|
|
|
|
|
# latency - the score.score between the creator and the candidate (needs to be full score soon)
|
2014-05-29 07:19:55 +00:00
|
|
|
class EmailBatchScheduledSessions < EmailBatchPeriodic
|
|
|
|
|
|
|
|
|
|
BATCH_SIZE = 500
|
|
|
|
|
SINCE_DAYS = 2
|
|
|
|
|
MIN_HOURS_START = 2
|
|
|
|
|
|
2014-07-26 03:58:58 +00:00
|
|
|
ENV_MAX_LATENCY = 'env_max_latency'
|
2014-07-26 04:10:40 +00:00
|
|
|
ENV_QUERY_LIMIT = 'env_query_limit'
|
|
|
|
|
SNAPSHOT_QUERY_LIMIT = '500'
|
2014-07-26 03:58:58 +00:00
|
|
|
|
|
|
|
|
def self.refresh_snapshot!
|
|
|
|
|
self.where(:aasm_state => 'snapshot').limit(1).first.try(:destroy)
|
|
|
|
|
oo = self.create
|
|
|
|
|
oo.snapshoting!
|
|
|
|
|
oo
|
|
|
|
|
end
|
|
|
|
|
|
2014-05-29 07:19:55 +00:00
|
|
|
def self.subject
|
|
|
|
|
"New sessions have been scheduled that may be a good match for you!"
|
|
|
|
|
end
|
|
|
|
|
|
2014-07-26 03:58:58 +00:00
|
|
|
def earliest_session_create_time
|
|
|
|
|
time_since_last_batch(SINCE_DAYS)
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
def latest_session_create_time
|
|
|
|
|
self.created_at
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
def earliest_session_start_time
|
|
|
|
|
self.created_at + MIN_HOURS_START.hours
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
def snapshot_eligible_sessions
|
2014-08-01 01:16:38 +00:00
|
|
|
rr = ActiveRecord::Base.connection.execute("SELECT COUNT(*) AS num FROM tmp_candidate_sessions")
|
|
|
|
|
[0 < rr.count ? rr[0]['num'].to_i : 0, ResultStub.stubs("SELECT * FROM tmp_candidate_sessions")]
|
2014-07-26 03:58:58 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
|
|
def snapshot_eligible_recipients
|
2014-08-01 01:16:38 +00:00
|
|
|
rr = ActiveRecord::Base.connection.execute("SELECT COUNT(*) AS num FROM tmp_candidate_recipients")
|
|
|
|
|
[0 < rr.count ? rr[0]['num'].to_i : 0, ResultStub.stubs("SELECT * FROM tmp_candidate_recipients")]
|
2014-07-26 03:58:58 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
|
|
def snapshot_scored_recipients
|
2014-08-01 01:16:38 +00:00
|
|
|
rr = ActiveRecord::Base.connection.execute("SELECT COUNT(*) AS num FROM tmp_matches")
|
|
|
|
|
[0 < rr.count ? rr[0]['num'].to_i : 0, ResultStub.stubs("SELECT * FROM tmp_matches")]
|
2014-07-26 03:58:58 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
|
|
def take_snapshot
|
|
|
|
|
_load_recipients
|
|
|
|
|
_count_recipients
|
|
|
|
|
self.update_attribute(:test_emails, @counters.inspect)
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
def fetch_recipients(per_page=BATCH_SIZE)
|
|
|
|
|
objs = []
|
|
|
|
|
|
|
|
|
|
_load_recipients
|
|
|
|
|
|
|
|
|
|
@per_page = per_page
|
|
|
|
|
num_recip = _select_scored_recipients(-1)
|
|
|
|
|
loops = (num_recip / @per_page) + (num_recip % @per_page) - 1
|
|
|
|
|
|
|
|
|
|
0.upto(loops) do |nn|
|
|
|
|
|
offset = nn * @per_page
|
|
|
|
|
# now just get the sessions/latency for each distinct mail recipient
|
|
|
|
|
_select_scored_recipients(offset).each do |result|
|
|
|
|
|
receiver = User.find_by_id(result['receiver_id'])
|
2014-08-01 01:16:38 +00:00
|
|
|
sessions = MusicSession.select("music_sessions.*, tmp_matches.latency")
|
|
|
|
|
.joins("INNER JOIN tmp_matches ON tmp_matches.session_id = music_sessions.id")
|
|
|
|
|
.where(["tmp_matches.receiver_id = ?", receiver.id])
|
2014-08-01 21:17:04 +00:00
|
|
|
.order('tmp_matches.latency')
|
|
|
|
|
.limit(20)
|
2014-07-26 03:58:58 +00:00
|
|
|
.includes([:genre, :creator])
|
|
|
|
|
block_given? ? yield(receiver, sessions) : objs << [receiver, sessions]
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
objs
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
def deliver_batch_sets!
|
|
|
|
|
self.opt_in_count = 0
|
|
|
|
|
self.fetch_recipients do |receiver, sessions_and_latency|
|
|
|
|
|
self.opt_in_count += 1
|
|
|
|
|
bset = EmailBatchSet.scheduled_session_set(self, receiver, sessions_and_latency)
|
2016-07-17 15:16:27 +00:00
|
|
|
UserMailer.scheduled_session_daily(receiver, sessions_and_latency).deliver_now
|
2014-07-26 03:58:58 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
|
|
self.test_emails = _count_recipients.inspect
|
|
|
|
|
|
|
|
|
|
self.sent_count = self.opt_in_count
|
|
|
|
|
self.save
|
|
|
|
|
self.did_batch_run!
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
def self.send_daily_session_batch
|
|
|
|
|
oo = self.create
|
|
|
|
|
oo.deliver_batch
|
|
|
|
|
oo
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
private
|
|
|
|
|
|
2014-05-29 16:32:22 +00:00
|
|
|
# inserts eligible sessions to temp table
|
2014-05-31 19:09:18 +00:00
|
|
|
def _collect_eligible_sessions
|
2014-08-01 01:16:38 +00:00
|
|
|
ActiveRecord::Base.connection.execute("DROP TABLE IF EXISTS tmp_candidate_sessions")
|
2014-07-26 04:10:40 +00:00
|
|
|
limit_sql = (self.snapshot? && 0 < ENV[ENV_QUERY_LIMIT].to_i) ? "LIMIT #{ENV[ENV_QUERY_LIMIT]}" : ''
|
2014-05-29 07:19:55 +00:00
|
|
|
sql =<<SQL
|
|
|
|
|
SELECT
|
2014-06-02 06:01:55 +00:00
|
|
|
msess.id AS session_id,
|
|
|
|
|
msess.user_id AS creator_id,
|
|
|
|
|
users.last_jam_locidispid AS creator_score_idx,
|
2014-08-01 02:06:06 +00:00
|
|
|
rs.instrument_id,
|
2014-08-01 21:17:04 +00:00
|
|
|
invitations.receiver_id AS invited_user_id,
|
|
|
|
|
msess.is_unstructured_rsvp,
|
|
|
|
|
msess.open_rsvps
|
2014-08-01 01:16:38 +00:00
|
|
|
INTO TEMP TABLE tmp_candidate_sessions
|
2014-05-29 07:19:55 +00:00
|
|
|
FROM music_sessions msess
|
2014-06-02 06:01:55 +00:00
|
|
|
INNER JOIN users ON users.id = msess.user_id
|
2014-05-29 07:19:55 +00:00
|
|
|
INNER JOIN rsvp_slots AS rs ON rs.music_session_id = msess.id
|
|
|
|
|
LEFT JOIN rsvp_requests_rsvp_slots AS rrrs ON rrrs.rsvp_slot_id = rs.id
|
2014-08-01 21:17:04 +00:00
|
|
|
LEFT JOIN invitations ON open_rsvps = FALSE AND invitations.music_session_id = msess.id
|
2014-05-29 07:19:55 +00:00
|
|
|
WHERE
|
2014-08-01 21:17:04 +00:00
|
|
|
(msess.is_unstructured_rsvp = TRUE OR (rrrs.id IS NULL OR rrrs.chosen != TRUE)) AND
|
2014-07-21 05:12:07 +00:00
|
|
|
users.last_jam_locidispid IS NOT NULL AND
|
2014-07-26 03:58:58 +00:00
|
|
|
msess.created_at > '#{earliest_session_create_time}' AND
|
|
|
|
|
msess.created_at < '#{latest_session_create_time}' AND
|
2014-08-01 21:17:04 +00:00
|
|
|
scheduled_start >= '#{earliest_session_start_time}'
|
2014-07-26 04:10:40 +00:00
|
|
|
#{limit_sql}
|
2014-05-29 07:19:55 +00:00
|
|
|
SQL
|
|
|
|
|
ActiveRecord::Base.connection.execute(sql)
|
|
|
|
|
end
|
2014-07-26 03:58:58 +00:00
|
|
|
|
2014-05-31 19:09:18 +00:00
|
|
|
def _collect_eligible_recipients
|
2014-08-01 21:17:04 +00:00
|
|
|
ActiveRecord::Base.connection.execute("DROP TABLE IF EXISTS tmp_candidate_recipients").check
|
2014-07-26 04:10:40 +00:00
|
|
|
limit_sql = (self.snapshot? && 0 < ENV[ENV_QUERY_LIMIT].to_i) ? "LIMIT #{ENV[ENV_QUERY_LIMIT]}" : ''
|
2014-05-29 16:32:22 +00:00
|
|
|
# load eligible recipients into tmp table
|
2014-05-29 07:19:55 +00:00
|
|
|
sql =<<SQL
|
|
|
|
|
SELECT
|
2014-06-02 06:01:55 +00:00
|
|
|
users.id AS receiver_id,
|
2014-07-26 03:58:58 +00:00
|
|
|
users.last_jam_locidispid AS receiver_score_idx,
|
2014-08-01 21:17:04 +00:00
|
|
|
mi.instrument_id,
|
|
|
|
|
tmp_candidate_sessions.invited_user_id,
|
|
|
|
|
tmp_candidate_sessions.session_id AS session_id,
|
2014-08-20 22:06:57 +00:00
|
|
|
tmp_candidate_sessions.creator_id AS creator_id,
|
2014-08-01 21:17:04 +00:00
|
|
|
tmp_candidate_sessions.creator_score_idx AS creator_score_idx
|
2014-08-01 01:16:38 +00:00
|
|
|
INTO TEMP TABLE tmp_candidate_recipients
|
2014-05-29 07:19:55 +00:00
|
|
|
FROM users
|
2015-07-15 16:25:58 +00:00
|
|
|
INNER JOIN musicians_instruments AS mi ON mi.player_id = users.id
|
2014-08-01 21:17:04 +00:00
|
|
|
INNER JOIN tmp_candidate_sessions ON tmp_candidate_sessions.is_unstructured_rsvp = TRUE OR
|
|
|
|
|
(tmp_candidate_sessions.open_rsvps = TRUE AND tmp_candidate_sessions.instrument_id = mi.instrument_id) OR
|
|
|
|
|
tmp_candidate_sessions.invited_user_id = users.id
|
2014-05-29 07:19:55 +00:00
|
|
|
WHERE
|
2014-07-21 05:12:07 +00:00
|
|
|
users.last_jam_locidispid IS NOT NULL AND
|
2014-08-01 01:16:38 +00:00
|
|
|
users.musician = TRUE AND
|
2014-08-01 21:17:04 +00:00
|
|
|
users.subscribe_email = TRUE AND
|
|
|
|
|
users.id != tmp_candidate_sessions.creator_id
|
2014-07-26 04:10:40 +00:00
|
|
|
#{limit_sql}
|
2014-05-29 07:19:55 +00:00
|
|
|
SQL
|
|
|
|
|
ActiveRecord::Base.connection.execute(sql)
|
2014-05-31 19:09:18 +00:00
|
|
|
end
|
2014-05-29 07:19:55 +00:00
|
|
|
|
2014-06-02 06:01:55 +00:00
|
|
|
def _collect_scored_recipients
|
2014-08-01 01:16:38 +00:00
|
|
|
ActiveRecord::Base.connection.execute("DROP TABLE IF EXISTS tmp_matches")
|
2014-07-26 04:10:40 +00:00
|
|
|
if !self.snapshot? || 0 == (max_score = ENV[ENV_MAX_LATENCY].to_i)
|
2014-09-10 14:25:43 +00:00
|
|
|
max_score = APP_CONFIG.max_yellow_full_score
|
2014-07-26 03:58:58 +00:00
|
|
|
end
|
2014-07-26 04:10:40 +00:00
|
|
|
limit_sql = (self.snapshot? && 0 < ENV[ENV_QUERY_LIMIT].to_i) ? "LIMIT #{ENV[ENV_QUERY_LIMIT]}" : ''
|
2014-05-29 07:19:55 +00:00
|
|
|
sql =<<SQL
|
2014-08-01 21:17:04 +00:00
|
|
|
SELECT DISTINCT
|
|
|
|
|
tmp_candidate_recipients.receiver_id,
|
|
|
|
|
tmp_candidate_recipients.session_id,
|
2014-09-30 15:51:23 +00:00
|
|
|
current_scores.full_score AS latency
|
2014-08-01 01:16:38 +00:00
|
|
|
INTO TEMP TABLE tmp_matches
|
2014-09-30 15:51:23 +00:00
|
|
|
FROM current_scores
|
2014-08-01 21:17:04 +00:00
|
|
|
INNER JOIN tmp_candidate_recipients ON
|
2014-09-30 15:51:23 +00:00
|
|
|
tmp_candidate_recipients.creator_id = current_scores.a_userid AND
|
|
|
|
|
tmp_candidate_recipients.receiver_id = current_scores.b_userid
|
2014-05-29 07:19:55 +00:00
|
|
|
WHERE
|
2014-09-30 15:51:23 +00:00
|
|
|
current_scores.full_score < #{max_score}
|
2014-08-01 21:17:04 +00:00
|
|
|
GROUP BY
|
|
|
|
|
tmp_candidate_recipients.receiver_id,
|
|
|
|
|
tmp_candidate_recipients.session_id,
|
2014-06-02 06:17:31 +00:00
|
|
|
latency
|
2014-07-26 04:10:40 +00:00
|
|
|
#{limit_sql}
|
2014-06-02 06:01:55 +00:00
|
|
|
SQL
|
|
|
|
|
ActiveRecord::Base.connection.execute(sql)
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
# select recipients whose score is below minimum threshold
|
|
|
|
|
def _select_scored_recipients(offset=0)
|
|
|
|
|
if 0 > offset
|
2014-08-01 01:16:38 +00:00
|
|
|
sql = "SELECT COUNT(DISTINCT receiver_id) AS num FROM tmp_matches"
|
2014-06-02 06:01:55 +00:00
|
|
|
rr = ActiveRecord::Base.connection.execute(sql)
|
|
|
|
|
return 0 < rr.count ? rr[0]['num'].to_i : 0
|
|
|
|
|
else
|
|
|
|
|
sql =<<SQL
|
|
|
|
|
SELECT DISTINCT receiver_id
|
2014-08-01 01:16:38 +00:00
|
|
|
FROM tmp_matches
|
2014-06-02 06:01:55 +00:00
|
|
|
ORDER BY receiver_id ASC
|
|
|
|
|
LIMIT #{@per_page}
|
|
|
|
|
OFFSET #{offset}
|
|
|
|
|
SQL
|
|
|
|
|
return ActiveRecord::Base.connection.execute(sql)
|
|
|
|
|
end
|
2014-05-31 19:09:18 +00:00
|
|
|
end
|
|
|
|
|
|
2014-07-26 03:58:58 +00:00
|
|
|
def _load_recipients
|
2014-05-31 19:09:18 +00:00
|
|
|
# load eligible sessions into tmp table
|
2014-07-26 03:58:58 +00:00
|
|
|
_collect_eligible_sessions
|
2014-05-31 19:09:18 +00:00
|
|
|
|
|
|
|
|
# load eligible mail recipients into tmp table
|
2014-07-26 03:58:58 +00:00
|
|
|
_collect_eligible_recipients
|
2014-05-29 16:32:22 +00:00
|
|
|
|
2014-06-02 06:01:55 +00:00
|
|
|
# load mail recipients with minimum score into tmp table
|
2014-07-26 03:58:58 +00:00
|
|
|
_collect_scored_recipients
|
2014-09-09 02:38:35 +00:00
|
|
|
|
2014-05-29 07:19:55 +00:00
|
|
|
end
|
|
|
|
|
|
2014-07-26 03:58:58 +00:00
|
|
|
def _count_recipients(load_tmp_tables = false)
|
|
|
|
|
return @counters if @counters || !self.snapshot?
|
|
|
|
|
_load_recipients if load_tmp_tables
|
|
|
|
|
|
2014-08-01 01:16:38 +00:00
|
|
|
rr = ActiveRecord::Base.connection.execute("SELECT COUNT(*) AS num FROM tmp_candidate_sessions")
|
2014-07-26 03:58:58 +00:00
|
|
|
session_count = 0 < rr.count ? rr[0]['num'].to_i : 0
|
2014-08-01 01:16:38 +00:00
|
|
|
rr = ActiveRecord::Base.connection.execute("SELECT COUNT(*) AS num FROM tmp_candidate_recipients")
|
2014-07-26 03:58:58 +00:00
|
|
|
receiver_candidate_count = 0 < rr.count ? rr[0]['num'].to_i : 0
|
2014-08-01 01:16:38 +00:00
|
|
|
rr = ActiveRecord::Base.connection.execute("SELECT COUNT(*) AS num FROM tmp_matches")
|
2014-07-26 03:58:58 +00:00
|
|
|
receiver_match_count = 0 < rr.count ? rr[0]['num'].to_i : 0
|
|
|
|
|
|
|
|
|
|
@counters = {
|
|
|
|
|
:sessions => session_count,
|
|
|
|
|
:receiver_candidates => receiver_candidate_count,
|
|
|
|
|
:receiver_match => receiver_match_count
|
|
|
|
|
}
|
2014-05-29 07:19:55 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
|
|
end
|
|
|
|
|
end
|