Merge pull request #368 from RickCarlino/better_generate_calendar

31 July Updates
This commit is contained in:
Rick Carlino 2017-07-31 13:47:17 -05:00 committed by GitHub
commit 2cc6e900d8
11 changed files with 315 additions and 231 deletions

View file

@ -23,7 +23,6 @@ gem "paperclip"
gem "figaro"
gem "fog-google", git: "https://github.com/fog/fog-google"
gem "pg"
gem "montrose"
gem "polymorphic_constraints"
gem "tzinfo" # For validation of user selected timezone names
gem "foreman"

View file

@ -153,8 +153,6 @@ GEM
mimemagic (0.3.2)
mini_portile2 (2.1.0)
minitest (5.10.2)
montrose (0.4.0)
activesupport
multi_json (1.12.1)
mutations (0.8.1)
activesupport
@ -290,7 +288,6 @@ DEPENDENCIES
foreman
jwt
letter_opener
montrose
mutations
paperclip
pg

View file

@ -4,10 +4,13 @@ module Api
skip_before_action :check_fbos_version, only: :create
CREDS = Auth::CreateTokenFromCredentials
NO_CREDS = Auth::CreateToken
NO_USER_ATTR = "API requets need a `user` attribute that is a JSON object."
def create
klass = (auth_params[:credentials]) ? CREDS : NO_CREDS
mutate klass.run(auth_params).tap{ |result| maybe_halt_login(result) }
if_properly_formatted do |auth_params|
klass = (auth_params[:credentials]) ? CREDS : NO_CREDS
mutate klass.run(auth_params).tap { |result| maybe_halt_login(result) }
end
end
private
@ -16,14 +19,19 @@ module Api
result.result[:user].try(:require_consent!) if result.success?
end
def auth_params
def if_properly_formatted
user = params.as_json.deep_symbolize_keys.fetch(:user, {})
{ email: user.fetch(:email, "").downcase,
password: user[:password],
credentials: user[:credentials],
agree_to_terms: !!user[:agree_to_terms],
host: $API_URL }
# If data handling for this method gets any more complicated,
# extract into a mutation.
if(user.is_a?(Hash))
yield({ email: user.fetch(:email, "").downcase,
password: user[:password],
credentials: user[:credentials],
agree_to_terms: !!user[:agree_to_terms],
host: $API_URL })
else
render json: {error: NO_USER_ATTR}, status: 422
end
end
end
end

View file

@ -2,13 +2,12 @@ module FarmEvents
# Used to calculate next 60ish occurrences or so of a FarmEvent.
class GenerateCalendar < Mutations::Command
NEVER = FarmEvent::NEVER.to_s
TIME = { "minutely" => 60,
"hourly" => 60 * 60,
"daily" => 60 * 60 * 24,
"weekly" => 60 * 60 * 24 * 7,
"monthly" => 60 * 60 * 24 * 30, # Not perfect...
"yearly" => 60 * 60 * 24 * 365 }
TIME = { "minutely" => 60,
"hourly" => 60 * 60,
"daily" => 60 * 60 * 24,
"weekly" => 60 * 60 * 24 * 7,
"monthly" => 60 * 60 * 24 * 30, # Not perfect...
"yearly" => 60 * 60 * 24 * 365 }
UNIT_TRANSLATION = { "minutely" => :minutes,
"hourly" => :hours,
"daily" => :days,
@ -18,11 +17,12 @@ module FarmEvents
required do
integer :repeat
string :time_unit, in: FarmEvent::UNITS_OF_TIME
time :start_time
time :origin
time :lower_limit
end
optional do
time :end_time
time :upper_limit
end
def execute
@ -30,35 +30,45 @@ module FarmEvents
# Is it in the future?
# Then generate a calendar.
# Otherwise, return a "partial calendar" that is either empty or (in the
# case of one-off events) has only one date in it (start_time).
(every ? full_calendar : partial_calendar)
# case of one-off events) has only one date in it (origin).
(is_repeating ? full_calendar : partial_calendar)
end
def full_calendar
throw "NO NO NO!!!" if start_time && end_time && (start_time > end_time)
options = { starts: start_time }
options[:until] = end_time if end_time
return Montrose
.every(every, options)
.take(60)
.reject { |x| end_time ? x > (end_time + 1.second) : false } # clear events beyond the end time
.reject { |x| x <= Time.now } # Clear past events
interval_sec = TIME[time_unit] * repeat
upper = compute_endtime
# How many items must we skip to get to the first occurence?
skip_intervals = ((lower_limit - origin) / interval_sec).ceil
# At what time does the first event occur?
first_item = origin + (skip_intervals * interval_sec).seconds
list = [first_item]
60.times do
item = list.last + interval_sec.seconds
list.push(item) unless (item >= upper) || (item <= Time.now)
end
return list
end
def partial_calendar
in_future? ? [start_time] : []
in_future? ? [origin] : []
end
def the_unit
UNIT_TRANSLATION[time_unit]
end
def every
def is_repeating
(the_unit != NEVER) && the_unit && repeat.send(the_unit)
end
def in_future?
start_time > Time.now
origin > Time.now
end
def compute_endtime
next_year = (Time.now + 1.year)
(upper_limit && (upper_limit < next_year)) ? upper_limit : next_year
end
end
end

View file

@ -5,20 +5,30 @@ class FarmEventSerializer < ActiveModel::Serializer
def calendar
case object.executable
when Sequence then sequence_calendar
# We don't make calendars for Regimens- compute it yourself using
# my_farm_event.executable.regimen_items - RC July 2017
else []
when Regimen then regimen_calendar
else throw "Dont know how to calendarize #{exe.class}"
end
end
private
def regimen_calendar
object
.executable
.regimen_items
.pluck(:time_offset)
.map { |x| x / 1000 }
.map { |x| object.start_time.midnight + x }
.select { |x| x > Time.now } || []
end
def sequence_calendar
FarmEvents::GenerateCalendar
.run!(start_time: object.start_time,
end_time: object.end_time,
repeat: object.repeat,
time_unit: object.time_unit)
.run!(origin: object.start_time,
lower_limit: instance_options[:upper_limit] || Time.now,
upper_limit: instance_options[:lower_limit] || object.end_time,
repeat: object.repeat,
time_unit: object.time_unit)
.map(&:utc)
.map(&:as_json)
end

View file

@ -1,160 +1,160 @@
module.exports = {
"ACCELERATE FOR (steps)": "BESCHLEUNIGEN FÜR (Schritte)",
"Account Settings": "Konto-Einstellungen",
"Add": "Hinzufügen",
"Add Farm Event": "Farm Event hinzufügen",
"Age": "Alter",
"Agree to Terms of Service": "Den Nutzungsbedingungen zustimmen",
"ALLOW NEGATIVES": "NEGATIVE WERTE ZULASSEN",
"BACK": "ZURÜCK",
"Bot ready": "Bot bereit",
"CALIBRATE {{axis}}": "{{axis}} KALIBRIEREN",
"CALIBRATION": "KALIBRIERUNG",
"calling FarmBot with credentials": "verbinde zu FarmBot mit Zugangsdaten",
"Camera": "Kamera",
"Choose a species": "Wähle eine Art",
"Confirm Password": "Passwort bestätigen",
"CONTROLLER": "CONTROLLER",
"Copy": "Kopieren",
"Could not download sync data": "Konnte Sync-Daten nicht herunterladen",
"Create Account": "Konto anlegen",
"Create An Account": "Ein Konto anlegen",
"Crop Info": "Pflanzen-Info",
"Data Label": "Daten Label",
"Day {{day}}": "Tag {{day}}",
"days old": "Tage alt",
"Delete": "Löschen",
"DELETE ACCOUNT": "KONTO LÖSCHEN",
"Delete this plant": "Diese Pflanze löschen",
"Designer": "Designer",
"DEVICE": "GERÄT",
"downloading device credentials": "Geräte-Zugangsdaten herunterladen",
"Drag and drop into map": "Auf Karte ziehen und fallen lassen",
"DRAG STEP HERE": "Schritt hier fallen lassen",
"Edit": "Bearbeiten",
"EDIT": "BEARBEITEN",
"Edit Farm Event": "Farm-Event bearbeiten",
"Email": "Email",
"ENABLE ENCODERS": "ENCODER AKTIVIEREN",
"Enter Email": "Email eingeben",
"Enter Password": "Passwort eingeben",
"Error establishing socket connection": "Fehler beim Einrichten der Socket-Verbindung",
"Execute Script": "Skript ausführen",
"EXECUTE SCRIPT": "SKRIPT AUSFÜHREN",
"Execute Sequence": "Sequenz ausführen",
"EXECUTE SEQUENCE": "SEQUENZ AUSFÜHREN",
"Factory Reset": "Fabrik-Einstellungen wiederherstellen",
"Farm Events": "Farm-Events",
"FIRMWARE": "FIRMWARE",
"Forgot Password": "Passwort vergessen",
"GO": "Los",
"I Agree to the Terms of Service": "Ich stimme den Nutzungsbedingungen zu",
"I agree to the terms of use": "Ich stimme den Nutzungsbedingungen zu",
"If Statement": "Wenn-Schleife",
"IF STATEMENT": "WENN-SCHLEIFE",
"Import coordinates from": "Koordination importieren von",
"initiating connection": "Verbindung initialisieren",
"INVERT ENDPOINTS": "ENDPUNKTE INVERTIEREN",
"INVERT MOTORS": "MOTOREN INVERTIEREN",
"LENGTH (m)": "LÄNGE (m)",
"Location": "Ort",
"Login": "Login",
"Logout": "Logout",
"Message": "Nachricht",
"Move Absolute": "absolut bewegen",
"MOVE ABSOLUTE": "ABSOLUT BEWEGEN",
"MOVE AMOUNT (mm)": "BETRAG BEWEGEN (mm)",
"Move Relative": "relativ bewegen",
"MOVE RELATIVE": "RELATIV BEWEGEN",
"NAME": "NAME",
"NETWORK": "NETZWERK",
"never connected to device": "noch nie mit Gerät verbunden",
"New Password": "Neues Passwort",
"no": "nein",
"Not Connected to bot": "Nicht mit Bot verbunden",
"Old Password": "Altes Passwort",
"Operator": "Betreiber",
"Package Name": "Packet-Name",
"Parameters": "Parameter",
"Password": "Passwort",
"Pin {{num}}": "Pin {{num}}",
"Pin Mode": "Pin-Modus",
"Pin Number": "Pin-Nummer",
"Plant Info": "Pflanzen-Info",
"Plants": "Pflanzen",
"Problem Loading Terms of Service": "Probleme beim Laden der Nutzungsbedingnungen",
"Read Pin": "Pin lesen",
"READ PIN": "PIN LESEN",
"Regimen Name": "Regimen Name",
"Regimens": "Regimen",
"Repeats Every": "Wiederholt alle",
"Request sent": "Anfrage gesendet",
"Reset": "Zurücksetzen",
"RESET": "ZURÜCKSETZEN",
"Reset Password": "Passwort zurücksetzen",
"Reset your password": "Setze dein Passwort zurück",
"RESTART": "NEUSTART",
"RESTART FARMBOT": "FARMBOT NEUSTARTEN",
"Save": "Seichern",
"SAVE": "SPEICHERN",
"Send Message": "Nachricht senden",
"SEND MESSAGE": "NACHRICHT SENDEN",
"Send Password reset": "Passwort-Zurücksetzen gesendet",
"Sequence": "Sequenz",
"Sequence Editor": "Sequenzen-Editor",
"Sequence or Regimen": "Sequenz oder Regimen",
"Sequences": "Sequenzen",
"Server Port": "Server Port",
"Server URL": "Server URL",
"SHUTDOWN": "Ausschalten",
"SHUTDOWN FARMBOT": "FARMBOT AUSSCHALTEN",
"SLOT": "SLOT",
"Socket Connection Established": "Socket-Verbindung hergestellt",
"Speed": "Geschwindigkeit",
"Started": "Gestartet",
"Starts": "Starts",
"STATUS": "STATUS",
"Steps per MM": "Schritte per MM",
"Sync Required": "Sync benötigt",
"Take a Photo": "Mache ein Foto",
"Take Photo": "Mache Foto",
"TAKE PHOTO": "MACHE FOTO",
"TEST": "TEST",
"Time": "Zeit",
"Time in milliseconds": "Zeit in Millisekunden",
"TIMEOUT AFTER (seconds)": "TIMEOUT NACH (Sekunden)",
"TOOL": "GERÄT",
"TOOL NAME": "GERÄTE-NAMEN",
"TOOLBAY NAME": "GERÄTEHALTER-NAMEN",
"Tried to delete Farm Event": "Versucht Farm-Event zu löschen",
"Tried to delete plant": "Versucht Pflanze zu löschen",
"Tried to save Farm Event": "Versucht Farm-Event zu speichern",
"Tried to save plant": "Versucht Pflanze zu speichern",
"Tried to update Farm Event": "Versucht Farm-Event zu aktualisieren",
"Unable to delete sequence": "Sequenz konnte nicht gelöscht werden",
"Unable to download device credentials": "Geräte-Zugangsdaten konnten nicht heruntergeladen werden",
"Until": "Bis",
"UP TO DATE": "Aktuell",
"UPDATE": "UPDATE",
"Value": "Wert",
"Variable": "Variable",
"Verify Password": "Passwort bestätigen",
"Version": "Version",
"Wait": "Warte",
"WAIT": "WARTE",
"Weed Detector": "Beikraut-Detektor",
"Week": "Wocke",
"Write Pin": "Schreibe Pin",
"WRITE PIN": "SCHREIBE PIN",
"X": "X",
"X (mm)": "X (mm)",
"X AXIS": "X-ACHSE",
"Y": "Y",
"Y (mm)": "Y (mm)",
"Y AXIS": "Y-ACHSE",
"yes": "ya",
"Your Name": "Dein Name",
"Z": "Z",
"Z (mm)": "Z (mm)",
"Z AXIS": "Z-ACHSE"
"ACCELERATE FOR (steps)": "BESCHLEUNIGEN FÜR (Schritte)",
"Account Settings": "Konto-Einstellungen",
"Add": "Hinzufügen",
"Add Farm Event": "Farm Event hinzufügen",
"Age": "Alter",
"Agree to Terms of Service": "Den Nutzungsbedingungen zustimmen",
"ALLOW NEGATIVES": "NEGATIVE WERTE ZULASSEN",
"BACK": "ZURÜCK",
"Bot ready": "Bot bereit",
"CALIBRATE {{axis}}": "{{axis}} KALIBRIEREN",
"CALIBRATION": "KALIBRIERUNG",
"calling FarmBot with credentials": "verbinde zu FarmBot mit Zugangsdaten",
"Camera": "Kamera",
"Choose a species": "Wähle eine Art",
"Confirm Password": "Passwort bestätigen",
"CONTROLLER": "CONTROLLER",
"Copy": "Kopieren",
"Could not download sync data": "Konnte Sync-Daten nicht herunterladen",
"Create Account": "Konto anlegen",
"Create An Account": "Ein Konto anlegen",
"Crop Info": "Pflanzen-Info",
"Data Label": "Daten Label",
"Day {{day}}": "Tag {{day}}",
"days old": "Tage alt",
"Delete": "Löschen",
"DELETE ACCOUNT": "KONTO LÖSCHEN",
"Delete this plant": "Diese Pflanze löschen",
"Designer": "Designer",
"DEVICE": "GERÄT",
"downloading device credentials": "Geräte-Zugangsdaten herunterladen",
"Drag and drop into map": "Auf Karte ziehen und fallen lassen",
"DRAG STEP HERE": "Schritt hier fallen lassen",
"Edit": "Bearbeiten",
"EDIT": "BEARBEITEN",
"Edit Farm Event": "Farm-Event bearbeiten",
"Email": "Email",
"ENABLE ENCODERS": "ENCODER AKTIVIEREN",
"Enter Email": "Email eingeben",
"Enter Password": "Passwort eingeben",
"Error establishing socket connection": "Fehler beim Einrichten der Socket-Verbindung",
"Execute Script": "Skript ausführen",
"EXECUTE SCRIPT": "SKRIPT AUSFÜHREN",
"Execute Sequence": "Sequenz ausführen",
"EXECUTE SEQUENCE": "SEQUENZ AUSFÜHREN",
"Factory Reset": "Fabrik-Einstellungen wiederherstellen",
"Farm Events": "Farm-Events",
"FIRMWARE": "FIRMWARE",
"Forgot Password": "Passwort vergessen",
"GO": "Los",
"I Agree to the Terms of Service": "Ich stimme den Nutzungsbedingungen zu",
"I agree to the terms of use": "Ich stimme den Nutzungsbedingungen zu",
"If Statement": "Wenn-Schleife",
"IF STATEMENT": "WENN-SCHLEIFE",
"Import coordinates from": "Koordination importieren von",
"initiating connection": "Verbindung initialisieren",
"INVERT ENDPOINTS": "ENDPUNKTE INVERTIEREN",
"INVERT MOTORS": "MOTOREN INVERTIEREN",
"LENGTH (m)": "LÄNGE (m)",
"Location": "Ort",
"Login": "Login",
"Logout": "Logout",
"Message": "Nachricht",
"Move Absolute": "absolut bewegen",
"MOVE ABSOLUTE": "ABSOLUT BEWEGEN",
"MOVE AMOUNT (mm)": "BETRAG BEWEGEN (mm)",
"Move Relative": "relativ bewegen",
"MOVE RELATIVE": "RELATIV BEWEGEN",
"NAME": "NAME",
"NETWORK": "NETZWERK",
"never connected to device": "noch nie mit Gerät verbunden",
"New Password": "Neues Passwort",
"no": "nein",
"Not Connected to bot": "Nicht mit Bot verbunden",
"Old Password": "Altes Passwort",
"Operator": "Betreiber",
"Package Name": "Packet-Name",
"Parameters": "Parameter",
"Password": "Passwort",
"Pin {{num}}": "Pin {{num}}",
"Pin Mode": "Pin-Modus",
"Pin Number": "Pin-Nummer",
"Plant Info": "Pflanzen-Info",
"Plants": "Pflanzen",
"Problem Loading Terms of Service": "Probleme beim Laden der Nutzungsbedingnungen",
"Read Pin": "Pin lesen",
"READ PIN": "PIN LESEN",
"Regimen Name": "Regimen Name",
"Regimens": "Regimen",
"Repeats Every": "Wiederholt alle",
"Request sent": "Anfrage gesendet",
"Reset": "Zurücksetzen",
"RESET": "ZURÜCKSETZEN",
"Reset Password": "Passwort zurücksetzen",
"Reset your password": "Setze dein Passwort zurück",
"RESTART": "NEUSTART",
"RESTART FARMBOT": "FARMBOT NEUSTARTEN",
"Save": "Seichern",
"SAVE": "SPEICHERN",
"Send Message": "Nachricht senden",
"SEND MESSAGE": "NACHRICHT SENDEN",
"Send Password reset": "Passwort-Zurücksetzen gesendet",
"Sequence": "Sequenz",
"Sequence Editor": "Sequenzen-Editor",
"Sequence or Regimen": "Sequenz oder Regimen",
"Sequences": "Sequenzen",
"Server Port": "Server Port",
"Server URL": "Server URL",
"SHUTDOWN": "Ausschalten",
"SHUTDOWN FARMBOT": "FARMBOT AUSSCHALTEN",
"SLOT": "SLOT",
"Socket Connection Established": "Socket-Verbindung hergestellt",
"Speed": "Geschwindigkeit",
"Started": "Gestartet",
"Starts": "Starts",
"STATUS": "STATUS",
"Steps per MM": "Schritte per MM",
"Sync Required": "Sync benötigt",
"Take a Photo": "Mache ein Foto",
"Take Photo": "Mache Foto",
"TAKE PHOTO": "MACHE FOTO",
"TEST": "TEST",
"Time": "Zeit",
"Time in milliseconds": "Zeit in Millisekunden",
"TIMEOUT AFTER (seconds)": "TIMEOUT NACH (Sekunden)",
"TOOL": "GERÄT",
"TOOL NAME": "GERÄTE-NAMEN",
"TOOLBAY NAME": "GERÄTEHALTER-NAMEN",
"Tried to delete Farm Event": "Versucht Farm-Event zu löschen",
"Tried to delete plant": "Versucht Pflanze zu löschen",
"Tried to save Farm Event": "Versucht Farm-Event zu speichern",
"Tried to save plant": "Versucht Pflanze zu speichern",
"Tried to update Farm Event": "Versucht Farm-Event zu aktualisieren",
"Unable to delete sequence": "Sequenz konnte nicht gelöscht werden",
"Unable to download device credentials": "Geräte-Zugangsdaten konnten nicht heruntergeladen werden",
"Until": "Bis",
"UP TO DATE": "Aktuell",
"UPDATE": "UPDATE",
"Value": "Wert",
"Variable": "Variable",
"Verify Password": "Passwort bestätigen",
"Version": "Version",
"Wait": "Warte",
"WAIT": "WARTE",
"Weed Detector": "Unkraut-Detektor",
"Week": "Woche",
"Write Pin": "Schreibe Pin",
"WRITE PIN": "SCHREIBE PIN",
"X": "X",
"X (mm)": "X (mm)",
"X AXIS": "X-ACHSE",
"Y": "Y",
"Y (mm)": "Y (mm)",
"Y AXIS": "Y-ACHSE",
"yes": "ya",
"Your Name": "Dein Name",
"Z": "Z",
"Z (mm)": "Z (mm)",
"Z AXIS": "Z-ACHSE"
}

View file

@ -13,5 +13,12 @@ describe Api::TokensController do
expect(token[:iss].last).not_to eq("/") # Trailing slashes are BAD!
expect(token[:iss]).to include($API_URL)
end
it 'handles bad params' do
err_msg = Api::TokensController::NO_USER_ATTR
payload = {user: "NOPE!"}
post :create, params: payload
expect(json[:error]).to include(err_msg)
end
end
end

View file

@ -2,5 +2,4 @@ FactoryGirl.define do
factory :regimen, :class => 'Regimen' do
name { Faker::Pokemon.name + Faker::Pokemon.name}
end
end

View file

@ -3,24 +3,26 @@ require 'spec_helper'
describe FarmEvents::GenerateCalendar do
it 'Builds a list of dates' do
start = Time.now + 1.minute
params = { start_time: start,
end_time: start + 1.hours,
repeat: 5,
time_unit: "minutely" }
params = { origin: start,
lower_limit: start,
upper_limit: start + 1.hours,
repeat: 5,
time_unit: "minutely" }
calendar = FarmEvents::GenerateCalendar.run!(params)
expect(calendar.first).to eq(params[:start_time])
calendar.map { |date| expect(date).to be >= params[:start_time] }
calendar.map { |date| expect(date).to be <= params[:end_time] }
expect(calendar.first).to eq(params[:origin])
calendar.map { |date| expect(date).to be >= params[:origin] }
calendar.map { |date| expect(date).to be <= params[:upper_limit] }
expect(calendar.length).to eq(12)
end
it 'hit a bug in production' do
start = Time.now + 1.minute
finish = start + 5.days
params = { start_time: start,
end_time: finish,
repeat: 1,
time_unit: "daily" }
params = { origin: start,
lower_limit: start,
upper_limit: finish,
repeat: 1,
time_unit: "daily" }
calendar = FarmEvents::GenerateCalendar.run!(params)
expect(calendar.first.day).to eq(start.day)
expect(calendar.length).to be > 4
@ -28,36 +30,61 @@ describe FarmEvents::GenerateCalendar do
end
it 'has a known calendar bug' do
tomorrow = Time.now + 1.day
calendar = FarmEvents::GenerateCalendar.run!(
"start_time" => tomorrow,
"end_time" => tomorrow + 10.hours,
"repeat" => 2,
"time_unit" => "hourly")
tomorrow = (Time.now + 1.day).midnight
ten_am = tomorrow + 10.hours
calendar = FarmEvents::GenerateCalendar.run!("origin" => tomorrow,
"lower_limit" => tomorrow,
"upper_limit" => ten_am,
"repeat" => 2,
"time_unit" => "hourly")
expect(calendar.length).to be > 3
expect(calendar.length).to be < 7
end
it 'hit more bugs' do
tomorrow = Time.now + 1.day
calendar = FarmEvents::GenerateCalendar.run!("start_time" => tomorrow,
"end_time" => tomorrow + 5.minutes,
"repeat" => 1,
"time_unit" => "minutely")
calendar = FarmEvents::GenerateCalendar.run!("origin" => tomorrow,
"lower_limit" => tomorrow,
"upper_limit" => tomorrow + 5.minutes,
"repeat" => 1,
"time_unit" => "minutely")
expect(calendar.length).to be > 3
expect(calendar.length).to be < 7
end
it 'schedules one-off events' do
tomorrow = Time.now + 1.day
params = { start_time: tomorrow,
end_time: nil,
repeat: 1,
time_unit: FarmEvent::NEVER }
params = { origin: tomorrow,
lower_limit: tomorrow,
upper_limit: nil,
repeat: 1,
time_unit: FarmEvent::NEVER }
calendar = FarmEvents::GenerateCalendar.run!(params)
expect(calendar.length).to eq(1)
expect(calendar.first).to eq(params[:start_time])
expect(calendar.first).to eq(params[:origin])
end
idea = ->(start, interval_sec, lower, upper = (Time.now + 1.year)) {
# How many items must we skip to get to the first occurence?
skip_intervals = ((lower - start) / interval_sec).ceil
# At what time does the first event occur?
first_item = start + (skip_intervals * interval_sec).seconds
list = [first_item]
60.times do
item = list.last + interval_sec.seconds
list.push(item) unless item > upper
end
return list
}
it 'trys new idea' do
monday = (Time.now - 14.days).monday.midnight + 8.hours # 8am Monday
tuesday = monday + 19.hours # 3am Tuesday
thursday = (monday + 3.days) + 10.hours # 18pm Thursday
interval = 4 * FarmEvents::GenerateCalendar::TIME["hourly"]
result1 = idea[monday, interval, tuesday, thursday]
expect(result1[0].tuesday?).to be(true)
expect(result1[0].hour).to be(4)
expect(result1.length).to be(16)
end
end

View file

@ -0,0 +1,24 @@
require "spec_helper"
describe FarmEventSerializer do
let(:farm_event) do
fe = FactoryGirl.build(:farm_event, start_time: Time.now + 5.days)
fe.executable = FactoryGirl.build(:regimen, device: fe.device)
fe.save!
FactoryGirl.create(:regimen_item, regimen: fe.executable,
time_offset: 7000)
fe
end
it "renders a regimen" do
result = FarmEventSerializer.new(farm_event).as_json
cal = result[:calendar]
expect(cal.length).to be(1)
expect(cal.first).to eq(farm_event.start_time.midnight + 7.seconds)
end
it "does not render `nil` and friends" do
farm_event.executable = nil
expect{ FarmEventSerializer.new(farm_event).as_json }.to raise_error
end
end

View file

@ -9,9 +9,12 @@ SimpleCov.start do
end
require 'codecov'
SimpleCov.formatter = SimpleCov::Formatter::Codecov
SimpleCov.formatters = SimpleCov::Formatter::MultiFormatter.new([
SimpleCov::Formatter::HTMLFormatter,
SimpleCov::Formatter::Codecov,
])
require 'pry'
# This file is copied to spec/ when you run 'rails generate rspec:install'
ENV['RAILS_ENV'] ||= 'test'
require File.expand_path('../../config/environment', __FILE__)
require 'rspec/rails'