Skip to content

Commit

Permalink
Merge branch 'master' into fb-add-better-admin-interface-15446845
Browse files Browse the repository at this point in the history
  • Loading branch information
emerencia committed Jun 14, 2018
2 parents f4e8563 + 4c08416 commit d3e45c8
Show file tree
Hide file tree
Showing 19 changed files with 408 additions and 134 deletions.
19 changes: 19 additions & 0 deletions app/jobs/rescheduling_job.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# frozen_string_literal: true

class ReschedulingJob < ApplicationJob
queue_as :default

def perform
ProtocolSubscription.active.find_each do |protocol_subscription|
RescheduleResponses.run!(protocol_subscription: protocol_subscription)
end
end

def max_attempts
2
end

def reschedule_at(current_time, _attempts)
current_time + 5.minutes
end
end
64 changes: 63 additions & 1 deletion app/models/measurement.rb
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,75 @@ class Measurement < ApplicationRecord
validates :period, numericality: { only_integer: true, allow_nil: true, greater_than: 0 }
# open_duration can be nil, in which case the questionnaire can be filled out until the end of the protocol
validates :open_duration, numericality: { only_integer: true, allow_nil: true, greater_than_or_equal_to: 0 }
validates :open_from_offset, numericality: { only_integer: true, greater_than_or_equal_to: 0 }
# either open_from_offset or offset_till_end has to be specified
validates :open_from_offset, numericality: { only_integer: true, allow_nil: true, greater_than_or_equal_to: 0 }
validates :offset_till_end, numericality: { only_integer: true, allow_nil: true, greater_than_or_equal_to: 0 }
validates :reward_points, numericality: { only_integer: true, greater_than_or_equal_to: 0 }
validates_associated :protocol
validate :either_open_from_or_offset_till_end

has_many :responses, dependent: :destroy

def periodical?
period.present?
end

def response_times(start_date, end_date)
unless periodical?
return [open_till(end_date)] if offset_till_end.present?
return [open_from(start_date)]
end
response_times = []
temp_open_from = open_from(start_date)
temp_open_till = open_till(end_date)
while temp_open_from < temp_open_till
response_times << temp_open_from
temp_open_from = TimeTools.increase_by_duration(temp_open_from, period)
end
response_times
end

private

def open_till(end_date)
if offset_till_end.present?
TimeTools.increase_by_duration(end_date, -offset_till_end)
else
end_date
end
end

def open_from(start_date)
if open_from_offset.present?
TimeTools.increase_by_duration(start_date, open_from_offset)
else
start_date
end
end

def either_open_from_or_offset_till_end
if periodical?
open_from_offset_cannot_be_blank
else
offsets_cannot_both_be_blank
offsets_cannot_both_be_present
end
end

def open_from_offset_cannot_be_blank
return unless open_from_offset.blank?
errors.add(:open_from_offset, 'cannot be blank')
end

def offsets_cannot_both_be_blank
return unless open_from_offset.blank? && offset_till_end.blank?
errors.add(:open_from_offset, 'cannot be blank if offset_till_end is blank')
errors.add(:offset_till_end, 'cannot be blank if open_from_offset is blank')
end

def offsets_cannot_both_be_present
return unless open_from_offset.present? && offset_till_end.present?
errors.add(:open_from_offset, 'cannot be present if offset_till_end is present')
errors.add(:offset_till_end, 'cannot be present if open_from_offset is present')
end
end
26 changes: 1 addition & 25 deletions app/models/protocol_subscription.rb
Original file line number Diff line number Diff line change
Expand Up @@ -135,30 +135,6 @@ def initialize_end_date
end

def schedule_responses
ActiveRecord::Base.transaction do
protocol.measurements.each do |measurement|
schedule_responses_for_measurement(measurement)
end
end
end

def schedule_responses_for_measurement(measurement)
open_from = TimeTools.increase_by_duration(start_date, measurement.open_from_offset)
open_till = measurement_open_till(measurement)
while open_from < open_till
Response.create!(protocol_subscription_id: id,
measurement_id: measurement.id,
open_from: open_from)
break unless measurement.period
open_from = TimeTools.increase_by_duration(open_from, measurement.period)
end
end

def measurement_open_till(measurement)
if measurement.offset_till_end.present?
TimeTools.increase_by_duration(end_date, - measurement.offset_till_end)
else
end_date
end
RescheduleResponses.run!(protocol_subscription: self, future: 1.second.ago)
end
end
4 changes: 4 additions & 0 deletions app/models/response.rb
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,10 @@ def self.between_dates(from_date, to_date)
)
end

def self.after_date(date)
where('open_from > :date', date: date)
end

def future?
open_from > Time.zone.now
end
Expand Down
34 changes: 34 additions & 0 deletions app/use_cases/reschedule_responses.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
# frozen_string_literal: true

class RescheduleResponses < ActiveInteraction::Base
object :protocol_subscription
time :future, default: TimeTools.increase_by_duration(Time.zone.now, 3.hours)

# Reschedules future responses for a protocol subscription
#
# Params:
# - protocol_subscription: the current protocol subscription
def execute
ActiveRecord::Base.transaction do
protocol_subscription.responses.after_date(future).destroy_all
schedule_responses
end
end

private

def schedule_responses
protocol_subscription.protocol.measurements.each do |measurement|
schedule_responses_for_measurement(measurement)
end
end

def schedule_responses_for_measurement(measurement)
measurement.response_times(protocol_subscription.start_date, protocol_subscription.end_date).each do |time|
next if time <= future
Response.create!(protocol_subscription_id: protocol_subscription.id,
measurement_id: measurement.id,
open_from: time)
end
end
end
5 changes: 5 additions & 0 deletions db/migrate/20180613123125_allow_open_from_offset_to_be_nil.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
class AllowOpenFromOffsetToBeNil < ActiveRecord::Migration[5.0]
def change
change_column_null :measurements, :open_from_offset, true
end
end
4 changes: 2 additions & 2 deletions db/schema.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
#
# It's strongly recommended that you check this file into your version control system.

ActiveRecord::Schema.define(version: 20180611144737) do
ActiveRecord::Schema.define(version: 20180613123125) do

# These are extensions that must be enabled in order to support this database
enable_extension "plpgsql"
Expand Down Expand Up @@ -68,7 +68,7 @@
t.integer "questionnaire_id", null: false
t.integer "protocol_id", null: false
t.integer "period"
t.integer "open_from_offset", null: false
t.integer "open_from_offset"
t.integer "open_duration"
t.integer "reward_points", default: 0, null: false
t.datetime "created_at", null: false
Expand Down
3 changes: 2 additions & 1 deletion db/seeds/protocols/mentor.rb
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,8 @@
raise "Cannot find questionnaire: #{nm_name}" unless nameting_id
nm_measurement = mentor_protocol.measurements.find_by_questionnaire_id(nameting_id)
nm_measurement ||= mentor_protocol.measurements.build(questionnaire_id: nameting_id)
nm_measurement.open_from_offset = 30.weeks + 3.days + 13.hours # Thursday 1pm last week
nm_measurement.open_from_offset = nil
nm_measurement.offset_till_end = 2.days + 12.hours
nm_measurement.period = nil
nm_measurement.open_duration = default_posttest_open_duration
nm_measurement.reward_points = default_posttest_reward_points
Expand Down
3 changes: 2 additions & 1 deletion db/seeds/protocols/student.rb
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,8 @@
raise "Cannot find questionnaire: #{nm_name}" unless nameting_id
nm_measurement = protocol.measurements.find_by_questionnaire_id(nameting_id)
nm_measurement ||= protocol.measurements.build(questionnaire_id: nameting_id)
nm_measurement.open_from_offset = 30.weeks + 3.days + 13.hours # Thursday 1pm last week
nm_measurement.open_from_offset = nil
nm_measurement.offset_till_end = 2.days + 12.hours
nm_measurement.period = nil
nm_measurement.open_duration = default_posttest_open_duration
nm_measurement.reward_points = 0
Expand Down
3 changes: 2 additions & 1 deletion db/seeds/protocols/student_control_group.rb
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,8 @@
raise "Cannot find questionnaire: #{nm_name}" unless nameting_id
nm_measurement = protocol.measurements.find_by_questionnaire_id(nameting_id)
nm_measurement ||= protocol.measurements.build(questionnaire_id: nameting_id)
nm_measurement.open_from_offset = 30.weeks + 3.days + 13.hours # Thursday 1pm last week
nm_measurement.open_from_offset = nil
nm_measurement.offset_till_end = 2.days + 12.hours
nm_measurement.period = nil
nm_measurement.open_duration = default_posttest_open_duration
nm_measurement.reward_points = 0
Expand Down
6 changes: 6 additions & 0 deletions lib/tasks/scheduler.rake
Original file line number Diff line number Diff line change
Expand Up @@ -38,4 +38,10 @@ namespace :scheduler do
SnitchJob.perform_later
puts 'Monitoring - done'
end

task rescheduling: :environment do
puts 'Rescheduling - started'
ReschedulingJob.perform_later
puts 'Rescheduling - done'
end
end
37 changes: 37 additions & 0 deletions spec/jobs/rescheduling_job_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
# frozen_string_literal: true

require 'rails_helper'

describe ReschedulingJob do
describe '#perform_later' do
it 'performs something later' do
ActiveJob::Base.queue_adapter = :test
expect do
described_class.perform_later
end.to have_enqueued_job(described_class)
end
end

describe 'perform' do
it 'should call the reschedule responses use case with the correct parameters' do
FactoryBot.create(:protocol_subscription, :canceled)
protsub = FactoryBot.create(:protocol_subscription)
expect(RescheduleResponses).to receive(:run!).with(protocol_subscription: protsub)
described_class.perform_now
end
end

describe 'max_attempts' do
it 'should be two' do
expect(subject.max_attempts).to eq 2
end
end

describe 'reschedule_at' do
it 'should be in one hour' do
time_now = Time.zone.now
expect(subject.reschedule_at(time_now, 1)).to be_within(1.minute)
.of(TimeTools.increase_by_duration(time_now, 5.minutes))
end
end
end
2 changes: 1 addition & 1 deletion spec/jobs/snitch_job_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
end

describe 'perform' do
it 'should call the Teamoverview function with the correct parameters' do
it 'should call the snitch function with the correct parameters' do
expect(ENV['SNITCH_KEY']).to_not be_blank
expect(Snitcher).to receive(:snitch).with(ENV['SNITCH_KEY'])
described_class.perform_now
Expand Down
19 changes: 19 additions & 0 deletions spec/lib/tasks/scheduler_rake_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -94,3 +94,22 @@
"Monitoring - done\n").to_stdout
end
end

describe 'rake scheduler:rescheduling', type: :task do
it 'should preload the Rails environment' do
expect(task.prerequisites).to include 'environment'
end

it 'should run gracefully' do
expect do
expect { task.execute }.to output("Rescheduling - started\n" \
"Rescheduling - done\n").to_stdout
end.not_to(raise_error)
end

it 'should call the rescheduling job' do
expect(ReschedulingJob).to receive(:perform_later)
expect { task.execute }.to output("Rescheduling - started\n" \
"Rescheduling - done\n").to_stdout
end
end

0 comments on commit d3e45c8

Please sign in to comment.