event.rb 3.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126
  1. require 'location'
  2. # Events are how Huginn Agents communicate and log information about the world. Events can be emitted and received by
  3. # Agents. They contain a serialized `payload` of arbitrary JSON data, as well as optional `lat`, `lng`, and `expires_at`
  4. # fields.
  5. class Event < ActiveRecord::Base
  6. include JSONSerializedField
  7. include LiquidDroppable
  8. acts_as_mappable
  9. json_serialize :payload
  10. belongs_to :user, optional: true
  11. belongs_to :agent, :counter_cache => true
  12. has_many :agent_logs_as_inbound_event, :class_name => "AgentLog", :foreign_key => :inbound_event_id, :dependent => :nullify
  13. has_many :agent_logs_as_outbound_event, :class_name => "AgentLog", :foreign_key => :outbound_event_id, :dependent => :nullify
  14. scope :recent, lambda { |timespan = 12.hours.ago|
  15. where("events.created_at > ?", timespan)
  16. }
  17. after_create :update_agent_last_event_at
  18. after_create :possibly_propagate
  19. scope :expired, lambda {
  20. where("expires_at IS NOT NULL AND expires_at < ?", Time.now)
  21. }
  22. scope :with_location, -> {
  23. where.not(lat: nil).where.not(lng: nil)
  24. }
  25. def location
  26. @location ||= Location.new(
  27. # lat and lng are BigDecimal, but converted to Float by the Location class
  28. lat: lat,
  29. lng: lng,
  30. radius:
  31. begin
  32. h = payload[:horizontal_accuracy].presence
  33. v = payload[:vertical_accuracy].presence
  34. if h && v
  35. (h.to_f + v.to_f) / 2
  36. else
  37. (h || v || payload[:accuracy]).to_f
  38. end
  39. end,
  40. course: payload[:course],
  41. speed: payload[:speed].presence)
  42. end
  43. def location=(location)
  44. case location
  45. when nil
  46. self.lat = self.lng = nil
  47. return
  48. when Location
  49. else
  50. location = Location.new(location)
  51. end
  52. self.lat, self.lng = location.lat, location.lng
  53. location
  54. end
  55. # Emit this event again, as a new Event.
  56. def reemit!
  57. agent.create_event :payload => payload, :lat => lat, :lng => lng
  58. end
  59. # Look for Events whose `expires_at` is present and in the past. Remove those events and then update affected Agents'
  60. # `events_counts` cache columns. This method is called by bin/schedule.rb periodically.
  61. def self.cleanup_expired!
  62. affected_agents = Event.expired.group("agent_id").pluck(:agent_id)
  63. Event.expired.delete_all
  64. Agent.where(:id => affected_agents).update_all "events_count = (select count(*) from events where agent_id = agents.id)"
  65. end
  66. protected
  67. def update_agent_last_event_at
  68. agent.touch :last_event_at
  69. end
  70. def possibly_propagate
  71. #immediately schedule agents that want immediate updates
  72. propagate_ids = agent.receivers.where(:propagate_immediately => true).pluck(:id)
  73. Agent.receive!(:only_receivers => propagate_ids) unless propagate_ids.empty?
  74. end
  75. end
  76. class EventDrop
  77. def initialize(object)
  78. @payload = object.payload
  79. super
  80. end
  81. def before_method(key)
  82. @payload[key]
  83. end
  84. def each(&block)
  85. @payload.each(&block)
  86. end
  87. def agent
  88. @payload.fetch(__method__) {
  89. @object.agent
  90. }
  91. end
  92. def created_at
  93. @payload.fetch(__method__) {
  94. @object.created_at
  95. }
  96. end
  97. def _location_
  98. @object.location
  99. end
  100. def as_json
  101. {location: _location_.as_json, agent: @object.agent.to_liquid.as_json, payload: @payload.as_json, created_at: created_at.as_json}
  102. end
  103. end