api_object 0.0.1 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/LICENSE CHANGED
@@ -1,5 +1,7 @@
1
1
  Copyright (c) 2012 TJ Moskun
2
2
 
3
+ MIT License
4
+
3
5
  Permission is hereby granted, free of charge, to any person obtaining
4
6
  a copy of this software and associated documentation files (the
5
7
  "Software"), to deal in the Software without restriction, including
data/README.md CHANGED
@@ -22,14 +22,16 @@ end
22
22
  class Station < ActiveApi::ApiObject
23
23
  end
24
24
  ```
25
- 2) Specify the url to load the data from, optionally a command, an api key and parameters(options) for the url; such as the url would look like "http://\<api_url\>/\<command\>?key=\<api_key\>&\<parameter1=value1&parameter2=value2...\>".
25
+ 2) Specify the url to load the data from, optionally an action and a mode, an api key and parameters(options) for the url; such as the url would look like "http://\<api_url\>/\<action\>?<mode>&key=\<api_key\>&\<parameter1=value1&parameter2=value2...\>".
26
26
 
27
27
  This will be defined in the upper object over the function "initialize_from_api". Options for this function:
28
28
 
29
29
  ```
30
30
  :url - specify url
31
31
 
32
- :command - specify command
32
+ :action - specify action
33
+
34
+ :mode - specify mode (such as 'verbose', 'terse' etc.)
33
35
 
34
36
  :key - api key
35
37
 
@@ -42,7 +44,7 @@ The following is designed to generate real time departure estimates for BART sta
42
44
  ```
43
45
  class Station
44
46
 
45
- initialize_from_api :url => "http://api.bart.gov/api/", :command => 'etd.aspx', :key => 'MW9S-E7SL-26DU-VV8V', :url_options => {:cmd => 'etd'}
47
+ initialize_from_api :url => "http://api.bart.gov/api/", :action => 'etd.aspx', :key => 'MW9S-E7SL-26DU-VV8V', :url_options => {:cmd => 'etd'}
46
48
 
47
49
  end
48
50
  ```
@@ -57,7 +59,7 @@ In the following example, a simple attribute name is "abbreviation", but the nam
57
59
  ```
58
60
  class Station < ActiveApi::ApiObject
59
61
 
60
- initialize_from_api :url => "http://api.bart.gov/api/", :command => 'etd.aspx', :key => 'MW9S-E7SL-26DU-VV8V', :url_options => {:cmd => 'etd'}
62
+ initialize_from_api :url => "http://api.bart.gov/api/", :action => 'etd.aspx', :key => 'MW9S-E7SL-26DU-VV8V', :url_options => {:cmd => 'etd'}
61
63
 
62
64
  attr_reader :name, :abbreviation, :date, :time, :est
63
65
 
@@ -87,11 +89,16 @@ If the data is an array of hashes, then it might be used to create an array of o
87
89
  ```
88
90
  stations = data.map {|d| Station.new(d)}
89
91
  ```
92
+ 6) Testing
93
+
94
+ The gem has been tested on BART, Google Weather and NextBus APIs.
95
+
90
96
 
91
- 6) Limitations
97
+ 7) Limitations
92
98
 
93
99
  * Api data must be presented either in XML or in JSON format. The distinction between XML and JSON is determinted automatically.
94
- * This is still a very early version and needs more testing. If something is not working, feel free to submit bugs and or/contribute.
100
+ * When using this gem with external APIs, check Terms and Conditions of the API usage.
101
+ * If something is not working, feel free to submit bugs and or/contribute.
95
102
 
96
103
  ## Compatibility
97
104
 
@@ -6,15 +6,19 @@ require_relative 'config_params'
6
6
  module Query
7
7
  include ConfigParams
8
8
 
9
- def query_api url, command = nil, url_options = {}, options = {}
9
+ def query_api url, action = nil, mode = nil, url_options = {}, options = {}
10
10
  result = nil
11
- cmd_url = url + (command.nil? ? '':"#{command}?")
12
- request_url = cmd_url + url_options.to_a.map{|opt| opt[0].to_s + "=" + opt[1]}.join("&")
11
+ request_url = get_url(url, action, mode, url_options)
13
12
  Timeout.timeout(self.fallback_timeout) do
14
13
  result_raw = RestClient::Request.execute(:method => :get, :url => request_url, :timeout => self.timeout)
15
14
  result = result_raw.start_with?('<?xml') ? Nori.parse(result_raw) : JSON.parse(result_raw)
16
15
  end
17
- result
16
+ {:result => result, :success => !result.nil?, :url => request_url}
17
+ end
18
+
19
+ def get_url url, action = nil, mode = nil, url_options = {}
20
+ cmd_url = url.chomp("/") + "/" + (action.nil? ? '':"#{action}?") # url + action
21
+ cmd_url + (mode.nil? ? '' : "#{mode}&") + url_options.to_a.map{|opt| opt[0].to_s + "=" + opt[1]}.join("&") # add parameters
18
22
  end
19
23
 
20
24
  end
@@ -1,3 +1,3 @@
1
1
  module ApiObject
2
- VERSION = "0.0.1"
2
+ VERSION = "0.2.0"
3
3
  end
data/lib/api_object.rb CHANGED
@@ -3,59 +3,93 @@ require "api_object/query"
3
3
  require "active_support/all"
4
4
 
5
5
  module ActiveApi
6
-
7
- module ClassMethods
8
6
 
7
+ module ClassMethods
8
+
9
9
  def self.extended(base)
10
10
  base.send(:extend, Query)
11
11
  end
12
12
 
13
13
  def initialize_from_api options = {}
14
- class_attribute :url, :command, :key, :url_options, :object_name
15
- self.url, self.command, self.key, self.url_options, self.object_name = [options[:url], options[:command], options[:key], (options[:url_options] || {}), (options[:object_name] || self.to_s.downcase.gsub(/^(.+::)(.+)$/, '\2'))]
16
- end
17
-
18
- def get_results options = {}
19
- self.url_options.merge!(:key => self.key) unless self.key.nil?
20
- result = query_api(self.url, self.command, self.url_options.merge(options))
21
- other_keys = {}
22
- unless result.nil?
23
- until result.keys.include?(self.object_name.to_s)
24
- result = result[result.keys[0]]
25
- return nil if (result.nil? || result.empty? || result.instance_of?(Array))
26
- other_keys.merge!(result.keys.inject({}) { |h, k| (self.method_defined?(k) ? h.merge(k => result[k]) : h)})
27
- end
14
+ class_attribute :url, :action, :key, :mode, :url_options, :data_tags, :object_name
15
+ self.url, self.action, self.key, self.mode, self.url_options, self.data_tags, self.object_name = [options[:url], options[:action], options[:key], options[:mode], (options[:url_options] || {}), ([*options[:data_tags]] || []), (options[:object_name] || self.to_s.downcase.gsub(/^(.+::)(.+)$/, '\2'))]
16
+ instance_eval do
17
+
18
+ def get_results options = {}
19
+ self.url_options.merge!(:key => self.key) unless self.key.nil?
20
+ [:url, :action, :mode].each {|opt| eval("self.#{opt.to_s} = options.delete(opt)") if options[opt]}
21
+ result = query_api(self.url, self.action, self.mode, self.url_options.merge(options))
22
+ process_result result
23
+ rescue
24
+ puts "WARNING: The request returned no valid data. #{warning_invalid_url result[:url]}"
25
+ return {}
26
+ end
27
+
28
+ def warning_invalid_url url
29
+ "The request url is #{url}, please, check if it's invalid of there is no connectivity." unless url.nil?
30
+ end
31
+
32
+ private
33
+ def process_result result
34
+ raise unless result[:success]
35
+
36
+ obj = result[:result]
37
+ url = result[:url]
38
+ other_keys = {}
39
+ until obj.keys.include?(self.object_name.to_s)
40
+ obj = obj[obj.keys[0]]
41
+ other_keys.merge!(obj.keys.inject({}) { |h, k| (self.method_defined?(k) ? h.merge(k => obj[k]) : h)})
42
+ end
43
+ res = obj[self.object_name.to_s]
44
+ res.instance_of?(Array) ? res.map{|r| attach_url other_keys.merge(r), url} : (attach_url other_keys.merge(res), url)
45
+ end
46
+
47
+
48
+ def attach_url result, url
49
+ result.merge(:url => url)
50
+ end
51
+
28
52
  end
29
- other_keys.merge(result[self.object_name.to_s])
30
53
  end
31
-
54
+
32
55
  end
33
56
 
34
57
  module InstanceMethods
35
58
 
36
59
  def initialize(*args)
60
+ url = args.first.delete(:url)
61
+ tags = respond_to?('data_tags') ? self.data_tags : args.last[:tags]
37
62
  args.first.each do |k, v|
38
- unless defined?(k).nil? # check if it's included as a reader attribute
39
- k = self.columns[k.to_sym] unless self.columns[k.to_sym].nil?
63
+ k = k.gsub(/@/, '')
64
+ k = self.columns[k.to_sym] unless self.columns[k.to_sym].nil?
65
+ if self.respond_to?(k) # check if it's included as a class attribute
40
66
  klass = self.assoc[k.to_sym] || k
41
- result = v.instance_of?(Array) ? v.inject([]) {|arr, v1| arr << init_object(v1, klass)} : init_object(v, klass)
67
+ result = v.instance_of?(Array) ? v.map {|v1| init_object(v1, klass, tags)} : init_object(v, klass, tags)
42
68
  instance_variable_set("@#{k.to_s}", result)
43
69
  end
44
- end if (args.length == 1 && args.first.is_a?(Hash))
70
+ end if args.first.is_a?(Hash)
71
+ puts "WARNING: data passed for initialization was invalid. #{self.class.warning_invalid_url url}" if self.empty?
72
+ end
73
+
74
+ def empty?
75
+ self.instance_variables.empty?
45
76
  end
46
77
 
47
78
 
48
79
  def persisted?
49
80
  false
50
81
  end
51
-
52
-
82
+
53
83
  private
54
84
 
55
- def init_object value, klass
85
+ def init_object value, klass, tags = []
56
86
  if value.instance_of?(Hash)
57
- klass = (get_module_name + klass.to_s.capitalize).constantize unless klass.class == Class
58
- klass.new(value)
87
+ if value.size == 1 && tags.include?(value.keys[0].gsub(/@/,'').to_sym)
88
+ value.values[0]
89
+ else
90
+ klass = (get_module_name + klass.to_s.gsub(/@/,'').capitalize).constantize unless klass.class == Class
91
+ klass.new(value, {:tags => tags})
92
+ end
59
93
  else
60
94
  value
61
95
  end
@@ -76,7 +110,8 @@ module ActiveApi
76
110
  options = args.extract_options!
77
111
  self.columns.merge!(args[1] => args[0]) if args.length >= 2
78
112
  self.assoc.merge!(args[0] => options[:as]) unless options[:as].nil?
79
- end
113
+ end
114
+
80
115
 
81
116
  end
82
117
 
@@ -0,0 +1,126 @@
1
+ <body copyright="All data copyright San Francisco Muni 2012.">
2
+ <route tag="F" title="F-Market & Wharves" color="555555" oppositeColor="ffffff" latMin="37.7625199" latMax="37.8085899" lonMin="-122.43487" lonMax="-122.39345">
3
+ <stop tag="5184" title="Jones St & Beach St" lat="37.8072499" lon="-122.41737" stopId="15184"/>
4
+ <stop tag="3092" title="Beach St & Mason St" lat="37.80741" lon="-122.4141199" stopId="13092"/>
5
+ <stop tag="3095" title="Beach St & Stockton St" lat="37.8078399" lon="-122.41081" stopId="13095"/>
6
+ <stop tag="4502" title="The Embarcadero & Bay St" lat="37.8066299" lon="-122.4060299" stopId="14502"/>
7
+ <stop tag="4529" title="The Embarcadero & Sansome St" lat="37.8050199" lon="-122.4033099" stopId="14529"/>
8
+ <stop tag="4516" title="The Embarcadero & Greenwich St" lat="37.80296" lon="-122.40103" stopId="14516"/>
9
+ <stop tag="4518" title="The Embarcadero & Green St" lat="37.80061" lon="-122.39892" stopId="14518"/>
10
+ <stop tag="4504" title="The Embarcadero & Broadway" lat="37.7988999" lon="-122.3974299" stopId="14504"/>
11
+ <stop tag="4534" title="The Embarcadero & Washington St" lat="37.7963599" lon="-122.3951799" stopId="14534"/>
12
+ <stop tag="7283" title="The Embarcadero & Ferry Building" lat="37.7948299" lon="-122.3937699" stopId="17283"/>
13
+ <stop tag="4726" title="Ferry Plaza" lat="37.7941099" lon="-122.39387" stopId="14726"/>
14
+ <stop tag="5669" title="Market St & Drumm St" lat="37.79347" lon="-122.3961799" stopId="15669"/>
15
+ <stop tag="5657" title="Market St & Battery St" lat="37.7911099" lon="-122.39907" stopId="15657"/>
16
+ <stop tag="5639" title="Market St & 2nd St" lat="37.7893499" lon="-122.40131" stopId="15639"/>
17
+ <stop tag="5678" title="Market St & Kearny St" lat="37.78773" lon="-122.4033699" stopId="15678"/>
18
+ <stop tag="5694" title="Market St & Stockton St" lat="37.7858599" lon="-122.40574" stopId="15694"/>
19
+ <stop tag="5655" title="Market St & 5th St North" lat="37.7840799" lon="-122.40799" stopId="15655"/>
20
+ <stop tag="5695" title="Market St & Taylor St" lat="37.78232" lon="-122.4102299" stopId="15695"/>
21
+ <stop tag="5656" title="Market St & 7th St North" lat="37.7805699" lon="-122.41244" stopId="15656"/>
22
+ <stop tag="5676" title="Market St & Hyde St" lat="37.7791099" lon="-122.41438" stopId="15676"/>
23
+ <stop tag="5679" title="Market St & Larkin St" lat="37.77759" lon="-122.4162099" stopId="15679"/>
24
+ <stop tag="5696" title="Market St & Van Ness Ave" lat="37.7752399" lon="-122.41918" stopId="15696"/>
25
+ <stop tag="5672" title="Market St & Gough St" lat="37.77327" lon="-122.4217699" stopId="15672"/>
26
+ <stop tag="5681" title="Market St & Laguna St" lat="37.77095" lon="-122.4246699" stopId="15681"/>
27
+ <stop tag="5659" title="Market St & Buchanan St" lat="37.76979" lon="-122.4261499" stopId="15659"/>
28
+ <stop tag="5661" title="Market St & Church St" lat="37.7678299" lon="-122.42863" stopId="15661"/>
29
+ <stop tag="5690" title="Market St & Sanchez St" lat="37.7661899" lon="-122.43071" stopId="15690"/>
30
+ <stop tag="5686" title="Market St & Noe St" lat="37.7644899" lon="-122.43281" stopId="15686"/>
31
+ <stop tag="33311" title="17th St & Castro St" lat="37.7625199" lon="-122.43487" stopId="133311"/>
32
+ <stop tag="5662" title="Market St & Church St" lat="37.7672599" lon="-122.42915" stopId="15662"/>
33
+ <stop tag="5668" title="Market St & Dolores St" lat="37.76888" lon="-122.4270999" stopId="15668"/>
34
+ <stop tag="5675" title="Market St & Guerrero St" lat="37.7705699" lon="-122.42497" stopId="15675"/>
35
+ <stop tag="5673" title="Market St & Gough St" lat="37.7728799" lon="-122.42199" stopId="15673"/>
36
+ <stop tag="5692" title="Market St & South Van Ness Av" lat="37.7750599" lon="-122.41932" stopId="15692"/>
37
+ <stop tag="5652" title="Market St & 9th St" lat="37.7774099" lon="-122.41634" stopId="15652"/>
38
+ <stop tag="5651" title="Market St & 8th St" lat="37.7786099" lon="-122.41483" stopId="15651"/>
39
+ <stop tag="5650" title="Market St & 7th St" lat="37.7803599" lon="-122.41261" stopId="15650"/>
40
+ <stop tag="5647" title="Market St & 6th St" lat="37.7820999" lon="-122.4104" stopId="15647"/>
41
+ <stop tag="5645" title="Market St & 5th St" lat="37.7838899" lon="-122.40814" stopId="15645"/>
42
+ <stop tag="5643" title="Market St & 4th St" lat="37.7856499" lon="-122.40589" stopId="15643"/>
43
+ <stop tag="5640" title="Market St & 3rd St" lat="37.7875299" lon="-122.40352" stopId="15640"/>
44
+ <stop tag="5685" title="Market St & New Montgomery St" lat="37.7886099" lon="-122.40216" stopId="15685"/>
45
+ <stop tag="7264" title="Market St & 1st St" lat="37.7909399" lon="-122.39919" stopId="17264"/>
46
+ <stop tag="5682" title="Market St & Main St" lat="37.7929799" lon="-122.39663" stopId="15682"/>
47
+ <stop tag="4727" title="Ferry Plaza" lat="37.7938999" lon="-122.39345" stopId="14727"/>
48
+ <stop tag="4513" title="The Embarcadero & Ferry Term" lat="37.79511" lon="-122.39386" stopId="14513"/>
49
+ <stop tag="4532" title="The Embarcadero & Washington St" lat="37.7970899" lon="-122.3956699" stopId="14532"/>
50
+ <stop tag="4503" title="The Embarcadero & Broadway" lat="37.7995499" lon="-122.3978699" stopId="14503"/>
51
+ <stop tag="4517" title="The Embarcadero & Green St" lat="37.8012599" lon="-122.3993699" stopId="14517"/>
52
+ <stop tag="4515" title="The Embarcadero & Greenwich St" lat="37.8032599" lon="-122.4011099" stopId="14515"/>
53
+ <stop tag="7281" title="Embarcadero & Sansome St" lat="37.80515" lon="-122.40323" stopId="17281"/>
54
+ <stop tag="4501" title="The Embarcadero & Bay St" lat="37.80695" lon="-122.40628" stopId="14501"/>
55
+ <stop tag="4530" title="The Embarcadero & Stockton St" lat="37.8083499" lon="-122.41029" stopId="14530"/>
56
+ <stop tag="5174" title="Jefferson St & Powell St" lat="37.8085899" lon="-122.41336" stopId="15174"/>
57
+ <stop tag="5175" title="Jefferson St & Taylor St" lat="37.8083199" lon="-122.41551" stopId="15175"/>
58
+ <stop tag="35184" title="Jones St & Beach St" lat="37.8072499" lon="-122.41737" stopId="135184"/>
59
+ <stop tag="3311" title="17th St & Castro St" lat="37.7625199" lon="-122.43487" stopId="13311"/>
60
+ <stop tag="5687" title="Market St & Noe St" lat="37.7639599" lon="-122.43332" stopId="15687"/>
61
+ <stop tag="5691" title="Market St & Sanchez St" lat="37.7656899" lon="-122.43114" stopId="15691"/>
62
+ <direction tag="F__OBCTRO" title="Outbound to Castro Station via Downtown" name="Outbound" useForUI="true">
63
+ <stop tag="5184"/>
64
+ <stop tag="3092"/>
65
+ <stop tag="3095"/>
66
+ <stop tag="4502"/>
67
+ <stop tag="4529"/>
68
+ <stop tag="4516"/>
69
+ <stop tag="4518"/>
70
+ <stop tag="4504"/>
71
+ <stop tag="4534"/>
72
+ <stop tag="7283"/>
73
+ <stop tag="4726"/>
74
+ <stop tag="5669"/>
75
+ <stop tag="5657"/>
76
+ <stop tag="5639"/>
77
+ <stop tag="5678"/>
78
+ <stop tag="5694"/>
79
+ <stop tag="5655"/>
80
+ <stop tag="5695"/>
81
+ <stop tag="5656"/>
82
+ <stop tag="5676"/>
83
+ <stop tag="5679"/>
84
+ <stop tag="5696"/>
85
+ <stop tag="5672"/>
86
+ <stop tag="5681"/>
87
+ <stop tag="5659"/>
88
+ <stop tag="5661"/>
89
+ <stop tag="5690"/>
90
+ <stop tag="5686"/>
91
+ <stop tag="33311"/>
92
+ </direction>
93
+ <direction tag="F__IBCTRO" title="Inbound to Fisherman's Wharf via Downtown" name="Inbound" useForUI="true">
94
+ <stop tag="3311"/>
95
+ <stop tag="5687"/>
96
+ <stop tag="5691"/>
97
+ <stop tag="5662"/>
98
+ <stop tag="5668"/>
99
+ <stop tag="5675"/>
100
+ <stop tag="5673"/>
101
+ <stop tag="5692"/>
102
+ <stop tag="5652"/>
103
+ <stop tag="5651"/>
104
+ <stop tag="5650"/>
105
+ <stop tag="5647"/>
106
+ <stop tag="5645"/>
107
+ <stop tag="5643"/>
108
+ <stop tag="5640"/>
109
+ <stop tag="5685"/>
110
+ <stop tag="7264"/>
111
+ <stop tag="5682"/>
112
+ <stop tag="4727"/>
113
+ <stop tag="4513"/>
114
+ <stop tag="4532"/>
115
+ <stop tag="4503"/>
116
+ <stop tag="4517"/>
117
+ <stop tag="4515"/>
118
+ <stop tag="7281"/>
119
+ <stop tag="4501"/>
120
+ <stop tag="4530"/>
121
+ <stop tag="5174"/>
122
+ <stop tag="5175"/>
123
+ <stop tag="35184"/>
124
+ </direction>
125
+ </route>
126
+ </body>
@@ -0,0 +1,83 @@
1
+ <body copyright="All data copyright San Francisco Muni 2012.">
2
+ <route tag="F" title="F-Market & Wharves"/>
3
+ <route tag="J" title="J-Church"/>
4
+ <route tag="KT" title="KT-Ingleside/Third Street"/>
5
+ <route tag="L" title="L-Taraval"/>
6
+ <route tag="M" title="M-Ocean View"/>
7
+ <route tag="N" title="N-Judah"/>
8
+ <route tag="NX" title="NX-N Express"/>
9
+ <route tag="1" title="1-California"/>
10
+ <route tag="1AX" title="1AX-California A Express"/>
11
+ <route tag="1BX" title="1BX-California B Express"/>
12
+ <route tag="2" title="2-Clement"/>
13
+ <route tag="3" title="3-Jackson"/>
14
+ <route tag="5" title="5-Fulton"/>
15
+ <route tag="6" title="6-Parnassus"/>
16
+ <route tag="8" title="8-Shuttle"/>
17
+ <route tag="8X" title="8X-Bayshore Exp"/>
18
+ <route tag="8AX" title="8AX-Bayshore A Exp"/>
19
+ <route tag="8BX" title="8BX-Bayshore B Exp"/>
20
+ <route tag="9" title="9-San Bruno"/>
21
+ <route tag="9L" title="9L-San Bruno Limited"/>
22
+ <route tag="10" title="10-Townsend"/>
23
+ <route tag="12" title="12-Folsom/Pacific"/>
24
+ <route tag="14" title="14-Mission"/>
25
+ <route tag="14L" title="14L-Mission Limited"/>
26
+ <route tag="14X" title="14X-Mission Express"/>
27
+ <route tag="16X" title="16X-Noriega Express"/>
28
+ <route tag="17" title="17-Park Merced"/>
29
+ <route tag="18" title="18-46th Avenue"/>
30
+ <route tag="19" title="19-Polk"/>
31
+ <route tag="21" title="21-Hayes"/>
32
+ <route tag="22" title="22-Fillmore"/>
33
+ <route tag="23" title="23-Monterey"/>
34
+ <route tag="24" title="24-Divisadero"/>
35
+ <route tag="27" title="27-Bryant"/>
36
+ <route tag="28" title="28-19th Avenue"/>
37
+ <route tag="28L" title="28L-19th Avenue Limited"/>
38
+ <route tag="29" title="29-Sunset"/>
39
+ <route tag="30" title="30-Stockton"/>
40
+ <route tag="30X" title="30X-Marina Express"/>
41
+ <route tag="31" title="31-Balboa"/>
42
+ <route tag="31AX" title="31AX-Balboa A Express"/>
43
+ <route tag="31BX" title="31BX-Balboa B Express"/>
44
+ <route tag="33" title="33-Stanyan"/>
45
+ <route tag="35" title="35-Eureka"/>
46
+ <route tag="36" title="36-Teresita"/>
47
+ <route tag="37" title="37-Corbett"/>
48
+ <route tag="38" title="38-Geary"/>
49
+ <route tag="38AX" title="38AX-Geary A Express"/>
50
+ <route tag="38BX" title="38BX-Geary B Express"/>
51
+ <route tag="38L" title="38L-Geary Limited"/>
52
+ <route tag="39" title="39-Coit"/>
53
+ <route tag="41" title="41-Union"/>
54
+ <route tag="43" title="43-Masonic"/>
55
+ <route tag="44" title="44-O'Shaughnessy"/>
56
+ <route tag="45" title="45-Union/Stockton"/>
57
+ <route tag="47" title="47-Van Ness"/>
58
+ <route tag="48" title="48-Quintara - 24th Street"/>
59
+ <route tag="49" title="49-Mission-Van Ness"/>
60
+ <route tag="52" title="52-Excelsior"/>
61
+ <route tag="54" title="54-Felton"/>
62
+ <route tag="56" title="56-Rutland"/>
63
+ <route tag="66" title="66-Quintara"/>
64
+ <route tag="67" title="67-Bernal Heights"/>
65
+ <route tag="71" title="71-Haight-Noriega"/>
66
+ <route tag="71L" title="71L-Haight-Noriega Limited"/>
67
+ <route tag="76" title="76-Marin Headlands"/>
68
+ <route tag="80X" title="80X-Gateway Express"/>
69
+ <route tag="81X" title="81X-Caltrain Express"/>
70
+ <route tag="82X" title="82X-Levi Plaza Express"/>
71
+ <route tag="88" title="88-B.A.R.T. Shuttle"/>
72
+ <route tag="90" title="90-San Bruno Owl"/>
73
+ <route tag="91" title="91-Owl"/>
74
+ <route tag="108" title="108-Treasure Island"/>
75
+ <route tag="K OWL" title="K-Owl"/>
76
+ <route tag="L OWL" title="L-Owl"/>
77
+ <route tag="M OWL" title="M-Owl"/>
78
+ <route tag="N OWL" title="N-Owl"/>
79
+ <route tag="T OWL" title="T-Owl"/>
80
+ <route tag="59" title="Powell/Mason Cable Car"/>
81
+ <route tag="60" title="Powell/Hyde Cable Car"/>
82
+ <route tag="61" title="California Cable Car"/>
83
+ </body>
@@ -69,68 +69,6 @@
69
69
  <bikeflag>1</bikeflag>
70
70
  </estimate>
71
71
  </etd>
72
- <etd>
73
- <destination>Fremont</destination>
74
- <abbreviation>FRMT</abbreviation>
75
- <estimate>
76
- <minutes>4</minutes>
77
- <platform>2</platform>
78
- <direction>North</direction>
79
- <length>8</length>
80
- <color>GREEN</color>
81
- <hexcolor>#339933</hexcolor>
82
- <bikeflag>1</bikeflag>
83
- </estimate>
84
- <estimate>
85
- <minutes>18</minutes>
86
- <platform>2</platform>
87
- <direction>North</direction>
88
- <length>9</length>
89
- <color>GREEN</color>
90
- <hexcolor>#339933</hexcolor>
91
- <bikeflag>1</bikeflag>
92
- </estimate>
93
- <estimate>
94
- <minutes>33</minutes>
95
- <platform>2</platform>
96
- <direction>North</direction>
97
- <length>9</length>
98
- <color>GREEN</color>
99
- <hexcolor>#339933</hexcolor>
100
- <bikeflag>1</bikeflag>
101
- </estimate>
102
- </etd>
103
- <etd>
104
- <destination>Millbrae</destination>
105
- <abbreviation>MLBR</abbreviation>
106
- <estimate>
107
- <minutes>6</minutes>
108
- <platform>1</platform>
109
- <direction>South</direction>
110
- <length>9</length>
111
- <color>RED</color>
112
- <hexcolor>#ff0000</hexcolor>
113
- <bikeflag>0</bikeflag>
114
- </estimate>
115
- <estimate>
116
- <minutes>15</minutes>
117
- <platform>1</platform>
118
- <direction>South</direction>
119
- <length>8</length>
120
- <color>RED</color>
121
- <hexcolor>#ff0000</hexcolor>
122
- <bikeflag>1</bikeflag>
123
- </estimate>
124
- <estimate>
125
- <minutes>28</minutes>
126
- <platform>1</platform>
127
- <direction>South</direction>
128
- <length>10</length>
129
- <color>RED</color>
130
- <hexcolor>#ff0000</hexcolor>
131
- <bikeflag>1</bikeflag>
132
- </estimate>
133
- </etd>
134
72
  <etd>
135
73
  <destination>Pittsburg/Bay Point</destination>
136
74
  <abbreviation>PITT</abbreviation>
@@ -162,90 +100,6 @@
162
100
  <bikeflag>1</bikeflag>
163
101
  </estimate>
164
102
  </etd>
165
- <etd>
166
- <destination>Richmond</destination>
167
- <abbreviation>RICH</abbreviation>
168
- <estimate>
169
- <minutes>13</minutes>
170
- <platform>2</platform>
171
- <direction>North</direction>
172
- <length>10</length>
173
- <color>RED</color>
174
- <hexcolor>#ff0000</hexcolor>
175
- <bikeflag>1</bikeflag>
176
- </estimate>
177
- <estimate>
178
- <minutes>25</minutes>
179
- <platform>2</platform>
180
- <direction>North</direction>
181
- <length>10</length>
182
- <color>RED</color>
183
- <hexcolor>#ff0000</hexcolor>
184
- <bikeflag>1</bikeflag>
185
- </estimate>
186
- <estimate>
187
- <minutes>37</minutes>
188
- <platform>2</platform>
189
- <direction>North</direction>
190
- <length>10</length>
191
- <color>RED</color>
192
- <hexcolor>#ff0000</hexcolor>
193
- <bikeflag>1</bikeflag>
194
- </estimate>
195
- </etd>
196
- <etd>
197
- <destination>SF Airport</destination>
198
- <abbreviation>SFIA</abbreviation>
199
- <estimate>
200
- <minutes>8</minutes>
201
- <platform>1</platform>
202
- <direction>South</direction>
203
- <length>10</length>
204
- <color>YELLOW</color>
205
- <hexcolor>#ffff33</hexcolor>
206
- <bikeflag>1</bikeflag>
207
- </estimate>
208
- <estimate>
209
- <minutes>21</minutes>
210
- <platform>1</platform>
211
- <direction>South</direction>
212
- <length>10</length>
213
- <color>YELLOW</color>
214
- <hexcolor>#ffff33</hexcolor>
215
- <bikeflag>1</bikeflag>
216
- </estimate>
217
- </etd>
218
- <etd>
219
- <destination>SFO/Millbrae</destination>
220
- <abbreviation>MLBR</abbreviation>
221
- <estimate>
222
- <minutes>35</minutes>
223
- <platform>1</platform>
224
- <direction>South</direction>
225
- <length>10</length>
226
- <color>YELLOW</color>
227
- <hexcolor>#ffff33</hexcolor>
228
- <bikeflag>1</bikeflag>
229
- </estimate>
230
- <estimate>
231
- <minutes>50</minutes>
232
- <platform>1</platform>
233
- <direction>South</direction>
234
- <length>9</length>
235
- <color>YELLOW</color>
236
- <hexcolor>#ffff33</hexcolor>
237
- <bikeflag>1</bikeflag>
238
- </estimate>
239
- <estimate>
240
- <minutes>65</minutes>
241
- <platform>1</platform>
242
- <direction>South</direction>
243
- <length>9</length>
244
- <color>YELLOW</color>
245
- <hexcolor>#ffff33</hexcolor>
246
- <bikeflag>1</bikeflag>
247
- </estimate>
248
- </etd>
249
103
  </station>
250
104
  <message/>
251
105
  </root>