public_transport_agent.rb 5.1KB

    require 'date' require 'cgi' module Agents class PublicTransportAgent < Agent cannot_receive_events! description <<-MD Specify the following user settings: * stops (array) * agency (string) * alert_window_in_minutes (integer) This Agent generates Events based on NextBus GPS transit predictions. First, select an agency by visiting [http://www.nextbus.com/predictor/agencySelector.jsp](http://www.nextbus.com/predictor/agencySelector.jsp) and finding your transit system. Once you find it, copy the part of the URL after `?a=`. For example, for the San Francisco MUNI system, you would end up on [http://www.nextbus.com/predictor/stopSelector.jsp?a=**sf-muni**](http://www.nextbus.com/predictor/stopSelector.jsp?a=sf-muni) and copy "sf-muni". Put that into this Agent's agency setting. Next, find the stop tags that you care about. To find the tags for the sf-muni system, for the N route, visit this URL: [http://webservices.nextbus.com/service/publicXMLFeed?command=routeConfig&a=sf-muni&r=**N**](http://webservices.nextbus.com/service/publicXMLFeed?command=routeConfig&a=sf-muni&r=N) The tags are listed as tag="1234". Copy that number and add the route before it, separated by a pipe '&#124;' symbol. Once you have one or more tags from that page, add them to this Agent's stop list. E.g, agency: "sf-muni" stops: ["N|5221", "N|5215"] This Agent will generate predictions by requesting a URL similar to the following: [http://webservices.nextbus.com/service/publicXMLFeed?command=predictionsForMultiStops&a=sf-muni&stops=N&#124;5221&stops=N&#124;5215](http://webservices.nextbus.com/service/publicXMLFeed?command=predictionsForMultiStops&a=sf-muni&stops=N&#124;5221&stops=N&#124;5215) Finally, set the arrival window that you're interested in. E.g., 5 minutes. Events will be created by the agent anytime a new train or bus comes into that time window. alert_window_in_minutes: 5 This memory should get cleaned up when timestamp is older than an hour (or something) so that it doesn't fill up all of the Agent's memory. MD default_schedule "every_2m" event_description <<-MD Events look like this: { "routeTitle":"N-Judah", "stopTag":"5215", "prediction": {"epochTime":"1389622846689", "seconds":"3454","minutes":"57","isDeparture":"false", "affectedByLayover":"true","dirTag":"N__OB4KJU","vehicle":"1489", "block":"9709","tripTag":"5840086" } } MD def check_url stop_query = URI.encode(options["stops"].collect{|a| "&stops=#{a}"}.join) "http://webservices.nextbus.com/service/publicXMLFeed?command=predictionsForMultiStops&a=#{options["agency"]}#{stop_query}" end def stops options["stops"].collect{|a| a.split("|").last} end def check hydra = Typhoeus::Hydra.new request = Typhoeus::Request.new(check_url, :followlocation => true) request.on_success do |response| page = Nokogiri::XML response.body predictions = page.css("//prediction") predictions.each do |pr| parent = pr.parent.parent vals = {"routeTitle" => parent["routeTitle"], "stopTag" => parent["stopTag"]} if pr["minutes"] && pr["minutes"].to_i < options["alert_window_in_minutes"].to_i vals = vals.merge Hash.from_xml(pr.to_xml) if not_already_in_memory?(vals) create_event(:payload => vals) log "creating event..." update_memory(vals) else log "not creating event since already in memory" end end end end hydra.queue request hydra.run end def update_memory(vals) add_to_memory(vals) cleanup_old_memory end def cleanup_old_memory self.memory["existing_routes"] ||= [] self.memory["existing_routes"].reject!{|h| h["currentTime"].to_time <= (Time.now - 2.hours)} end def add_to_memory(vals) self.memory["existing_routes"] ||= [] self.memory["existing_routes"] << {"stopTag" => vals["stopTag"], "tripTag" => vals["prediction"]["tripTag"], "epochTime" => vals["prediction"]["epochTime"], "currentTime" => Time.now} end def not_already_in_memory?(vals) m = self.memory["existing_routes"] || [] m.select{|h| h['stopTag'] == vals["stopTag"] && h['tripTag'] == vals["prediction"]["tripTag"] && h['epochTime'] == vals["prediction"]["epochTime"] }.count == 0 end def default_options { agency: "sf-muni", stops: ["N|5221", "N|5215"], alert_window_in_minutes: 5 } end def validate_options errors.add(:base, 'agency is required') unless options['agency'].present? errors.add(:base, 'alert_window_in_minutes is required') unless options['alert_window_in_minutes'].present? errors.add(:base, 'stops are required') unless options['stops'].present? end def working? event_created_within?(2) && !recent_error_logs? end end end