Merge pull request #368 from RickCarlino/better_generate_calendar
31 July Updates
This commit is contained in:
commit
2cc6e900d8
1
Gemfile
1
Gemfile
|
@ -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"
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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"
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -2,5 +2,4 @@ FactoryGirl.define do
|
|||
factory :regimen, :class => 'Regimen' do
|
||||
name { Faker::Pokemon.name + Faker::Pokemon.name}
|
||||
end
|
||||
|
||||
end
|
||||
|
|
|
@ -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
|
||||
|
|
24
spec/serializers/farm_event_serializer_spec.rb
Normal file
24
spec/serializers/farm_event_serializer_spec.rb
Normal 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
|
|
@ -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'
|
||||
|
|
Loading…
Reference in a new issue