metra_schedule 0.2.2 → 0.3.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/VERSION +1 -1
- data/lib/metra/cacher.rb +30 -1
- data/lib/metra/delay_parser.rb +60 -0
- data/lib/metra/extensions/time_extension.rb +5 -1
- data/lib/metra/line.rb +29 -2
- data/lib/metra/train.rb +10 -15
- data/lib/metra.rb +1 -0
- data/metra_schedule.gemspec +9 -2
- data/test/fixture/service_updates_alerts.html +26 -0
- data/test/fixture/service_updates_alerts_multiple_with_non_delay.html +39 -0
- data/test/fixture/service_updates_alerts_no_advisories.html +792 -0
- data/test/fixture/service_updates_alerts_no_range.html +25 -0
- data/test/functional/test_all_filters.rb +8 -8
- data/test/integration/test_line_integration.rb +16 -0
- data/test/test_helper.rb +1 -0
- data/test/unit/test_cacher.rb +29 -0
- data/test/unit/test_delay_parser.rb +76 -0
- data/test/unit/test_line.rb +26 -0
- data/test/unit/test_parser.rb +5 -24
- data/test/unit/test_time_extension.rb +7 -0
- data/test/unit/test_train.rb +17 -0
- metadata +9 -2
    
        data/VERSION
    CHANGED
    
    | @@ -1 +1 @@ | |
| 1 | 
            -
            0. | 
| 1 | 
            +
            0.3.0
         | 
    
        data/lib/metra/cacher.rb
    CHANGED
    
    | @@ -3,10 +3,11 @@ require 'fileutils' | |
| 3 3 | 
             
            module MetraSchedule
         | 
| 4 4 | 
             
              class Cacher
         | 
| 5 5 |  | 
| 6 | 
            -
                attr_accessor :cache_dir
         | 
| 6 | 
            +
                attr_accessor :cache_dir, :delays
         | 
| 7 7 |  | 
| 8 8 | 
             
                def initialize
         | 
| 9 9 | 
             
                  @cache_dir = "#{ENV['HOME']}/.metra_schedule"
         | 
| 10 | 
            +
                  @delay_file = 'delays'
         | 
| 10 11 | 
             
                end
         | 
| 11 12 |  | 
| 12 13 | 
             
                def self.load_from_cache(line)
         | 
| @@ -43,6 +44,20 @@ module MetraSchedule | |
| 43 44 | 
             
                  end
         | 
| 44 45 | 
             
                end
         | 
| 45 46 |  | 
| 47 | 
            +
                def delays_exist?
         | 
| 48 | 
            +
                  return true if File.exists?(File.join(@cache_dir, @delay_file))
         | 
| 49 | 
            +
                  false
         | 
| 50 | 
            +
                end
         | 
| 51 | 
            +
             | 
| 52 | 
            +
                def clear_delays
         | 
| 53 | 
            +
                  begin
         | 
| 54 | 
            +
                    FileUtils.rm(File.join(@cache_dir, @delay_file))
         | 
| 55 | 
            +
                    true
         | 
| 56 | 
            +
                  rescue
         | 
| 57 | 
            +
                    false
         | 
| 58 | 
            +
                  end
         | 
| 59 | 
            +
                end
         | 
| 60 | 
            +
             | 
| 46 61 | 
             
                def persist_line(line)
         | 
| 47 62 | 
             
                  create_cache_dir_if_not_exists
         | 
| 48 63 | 
             
                  return false unless line.engines
         | 
| @@ -56,6 +71,20 @@ module MetraSchedule | |
| 56 71 | 
             
                    nil
         | 
| 57 72 | 
             
                  end
         | 
| 58 73 | 
             
                end
         | 
| 74 | 
            +
             | 
| 75 | 
            +
                def persist_delays(delays)
         | 
| 76 | 
            +
                  create_cache_dir_if_not_exists
         | 
| 77 | 
            +
                  return false unless delays
         | 
| 78 | 
            +
                  true if File.open(File.join(@cache_dir, @delay_file), 'w') {|f| Marshal.dump(delays, f)}
         | 
| 79 | 
            +
                end
         | 
| 80 | 
            +
             | 
| 81 | 
            +
                def retrieve_delays
         | 
| 82 | 
            +
                  if delays_exist?
         | 
| 83 | 
            +
                    File.open(File.join(@cache_dir, @delay_file), 'r') {|f| Marshal.load(f)}
         | 
| 84 | 
            +
                  else
         | 
| 85 | 
            +
                    nil
         | 
| 86 | 
            +
                  end
         | 
| 87 | 
            +
                end
         | 
| 59 88 |  | 
| 60 89 | 
             
              end
         | 
| 61 90 | 
             
            end
         | 
| @@ -0,0 +1,60 @@ | |
| 1 | 
            +
            require 'nokogiri'
         | 
| 2 | 
            +
            require 'open-uri'
         | 
| 3 | 
            +
             | 
| 4 | 
            +
            module MetraSchedule
         | 
| 5 | 
            +
              class DelayParser
         | 
| 6 | 
            +
                attr_reader :html_doc, :url
         | 
| 7 | 
            +
                attr_accessor :line
         | 
| 8 | 
            +
             | 
| 9 | 
            +
                def initialize(html_doc=nil)
         | 
| 10 | 
            +
                  time = Time.now.to_i
         | 
| 11 | 
            +
                  @url = "http://metrarail.com/content/metra/en/home/service_updates/service_updates_alerts/jcr:content/serviceAdvisory.display.html?t=#{time}"
         | 
| 12 | 
            +
                  @html_doc = @url unless html_doc
         | 
| 13 | 
            +
                  @html_doc = html_doc if html_doc
         | 
| 14 | 
            +
                end
         | 
| 15 | 
            +
             | 
| 16 | 
            +
                def scrape
         | 
| 17 | 
            +
                  parse_nokogiri
         | 
| 18 | 
            +
                  make_train_delays
         | 
| 19 | 
            +
                end
         | 
| 20 | 
            +
             | 
| 21 | 
            +
                def parse_nokogiri
         | 
| 22 | 
            +
                  html_doc = open(@html_doc) if @html_doc.is_a?(String)
         | 
| 23 | 
            +
                  html_doc = @html_doc if @html_doc.is_a?(File)
         | 
| 24 | 
            +
                  @html_doc = Nokogiri::HTML(html_doc)
         | 
| 25 | 
            +
                end
         | 
| 26 | 
            +
             | 
| 27 | 
            +
                def make_train_delays
         | 
| 28 | 
            +
                  return unless has_delays?
         | 
| 29 | 
            +
                  all_advisories.inject([]) do |total, node|
         | 
| 30 | 
            +
                    total << {:train_num => find_train_num(node), :delay => find_delay(node)}
         | 
| 31 | 
            +
                  end
         | 
| 32 | 
            +
                end
         | 
| 33 | 
            +
             | 
| 34 | 
            +
                def find_train_num(node)
         | 
| 35 | 
            +
                  node.text.scan(/#([0-9]+)/).first.first.to_i
         | 
| 36 | 
            +
                end
         | 
| 37 | 
            +
             | 
| 38 | 
            +
                def find_delay(node)
         | 
| 39 | 
            +
                  text_array = node.text.delete("\r\n").split("\s")[3..-1]
         | 
| 40 | 
            +
                  text_array.delete_at(0) if text_array[0] == "-"
         | 
| 41 | 
            +
                  text_array.join("\s").strip
         | 
| 42 | 
            +
                end
         | 
| 43 | 
            +
                
         | 
| 44 | 
            +
                def has_delays?
         | 
| 45 | 
            +
                  @html_doc.css('#serviceAdvisory').count > 0
         | 
| 46 | 
            +
                end
         | 
| 47 | 
            +
             | 
| 48 | 
            +
                private
         | 
| 49 | 
            +
             | 
| 50 | 
            +
                def all_advisories
         | 
| 51 | 
            +
                  nodes = @html_doc.css('#serviceAdvisory dl dd')
         | 
| 52 | 
            +
                  advisories = nodes.inject([]) do |total, node|
         | 
| 53 | 
            +
                    id = node.attributes.has_key?("id")
         | 
| 54 | 
            +
                    total << node unless id
         | 
| 55 | 
            +
                    total
         | 
| 56 | 
            +
                  end
         | 
| 57 | 
            +
                end
         | 
| 58 | 
            +
             | 
| 59 | 
            +
              end
         | 
| 60 | 
            +
            end
         | 
| @@ -2,7 +2,11 @@ module MetraSchedule | |
| 2 2 | 
             
              module Extensions
         | 
| 3 3 | 
             
                module TimeExtension
         | 
| 4 4 | 
             
                  def to_today
         | 
| 5 | 
            -
                     | 
| 5 | 
            +
                    if self.hour >= 0 and self.hour <= 2 # Midnight to 2AM counts as the same day
         | 
| 6 | 
            +
                      Time.local(Time.now.year, Time.now.month, Time.now.day+1, self.hour, self.min, self.sec)
         | 
| 7 | 
            +
                    else
         | 
| 8 | 
            +
                      Time.local(Time.now.year, Time.now.month, Time.now.day, self.hour, self.min, self.sec)
         | 
| 9 | 
            +
                    end
         | 
| 6 10 | 
             
                  end
         | 
| 7 11 | 
             
                end
         | 
| 8 12 | 
             
              end
         | 
    
        data/lib/metra/line.rb
    CHANGED
    
    | @@ -5,7 +5,7 @@ module MetraSchedule | |
| 5 5 | 
             
                include MetraSchedule::TrainData
         | 
| 6 6 |  | 
| 7 7 | 
             
                attr_reader :cacher, :cache_dir 
         | 
| 8 | 
            -
                attr_reader :line_key, :name, :url, :dir, :sched, :start, :destination, :time
         | 
| 8 | 
            +
                attr_reader :line_key, :name, :url, :dir, :sched, :start, :destination, :time, :del_threshold
         | 
| 9 9 | 
             
                attr_accessor :engines
         | 
| 10 10 |  | 
| 11 11 | 
             
                def initialize(line_name)
         | 
| @@ -30,6 +30,7 @@ module MetraSchedule | |
| 30 30 | 
             
                  else
         | 
| 31 31 | 
             
                    @engines = cached_engines
         | 
| 32 32 | 
             
                  end
         | 
| 33 | 
            +
                  self
         | 
| 33 34 | 
             
                end
         | 
| 34 35 |  | 
| 35 36 | 
             
                def update_schedule
         | 
| @@ -155,8 +156,16 @@ module MetraSchedule | |
| 155 156 | 
             
                  @engines.find {|e| e.train_num == train_num}
         | 
| 156 157 | 
             
                end
         | 
| 157 158 |  | 
| 159 | 
            +
                def delay_threshold(minutes)
         | 
| 160 | 
            +
                  @del_threshold = minutes if minutes
         | 
| 161 | 
            +
                  self
         | 
| 162 | 
            +
                end
         | 
| 163 | 
            +
             | 
| 158 164 | 
             
                def trains
         | 
| 159 165 | 
             
                  return [] unless engines
         | 
| 166 | 
            +
                  if MetraSchedule::Cacher.new.delays_exist?
         | 
| 167 | 
            +
                    @filters.insert(0, inject_delays)
         | 
| 168 | 
            +
                  end
         | 
| 160 169 | 
             
                  @filters.inject(engines) { |e, fun| fun.call(e) }.sort
         | 
| 161 170 | 
             
                end
         | 
| 162 171 |  | 
| @@ -172,6 +181,20 @@ module MetraSchedule | |
| 172 181 | 
             
                  end
         | 
| 173 182 | 
             
                end
         | 
| 174 183 |  | 
| 184 | 
            +
                def inject_delays
         | 
| 185 | 
            +
                  lambda do |engines|
         | 
| 186 | 
            +
                    delays = MetraSchedule::Cacher.new.retrieve_delays
         | 
| 187 | 
            +
                    delays.each do |d|
         | 
| 188 | 
            +
                      if engines.any? {|e| e.train_num == d[:train_num] }
         | 
| 189 | 
            +
                        train = find_train_by_train_num(d[:train_num])
         | 
| 190 | 
            +
                        train.delay = d[:delay]
         | 
| 191 | 
            +
                        train.del_threshold = @del_threshold if @del_threshold
         | 
| 192 | 
            +
                      end
         | 
| 193 | 
            +
                    end
         | 
| 194 | 
            +
                    engines
         | 
| 195 | 
            +
                  end
         | 
| 196 | 
            +
                end
         | 
| 197 | 
            +
             | 
| 175 198 | 
             
                def filter_by_stop
         | 
| 176 199 | 
             
                  lambda do |engines|
         | 
| 177 200 | 
             
                    if @start and not @destination
         | 
| @@ -190,7 +213,11 @@ module MetraSchedule | |
| 190 213 | 
             
                  lambda do |engines|
         | 
| 191 214 | 
             
                    return engines unless @time and @start
         | 
| 192 215 | 
             
                    engines.find_all do |engine|
         | 
| 193 | 
            -
                      engine. | 
| 216 | 
            +
                      if @del_threshold and engine.delay
         | 
| 217 | 
            +
                        engine.in_time?(@start, @time - (60 * @del_threshold))
         | 
| 218 | 
            +
                      else
         | 
| 219 | 
            +
                        engine.in_time?(@start, @time)
         | 
| 220 | 
            +
                      end
         | 
| 194 221 | 
             
                    end
         | 
| 195 222 | 
             
                  end
         | 
| 196 223 | 
             
                end
         | 
    
        data/lib/metra/train.rb
    CHANGED
    
    | @@ -1,7 +1,7 @@ | |
| 1 1 | 
             
            module MetraSchedule
         | 
| 2 2 | 
             
              class Train
         | 
| 3 3 | 
             
                attr_reader :train_num, :schedule, :bike_limit, :direction, :stops
         | 
| 4 | 
            -
                attr_accessor :my_departure, :my_arrival # Injected when the line is filtered
         | 
| 4 | 
            +
                attr_accessor :my_departure, :my_arrival, :delay, :del_threshold # Injected when the line is filtered
         | 
| 5 5 |  | 
| 6 6 | 
             
                def initialize(options={})
         | 
| 7 7 | 
             
                  unless options.empty?
         | 
| @@ -20,17 +20,7 @@ module MetraSchedule | |
| 20 20 |  | 
| 21 21 | 
             
                def in_time?(station, time)
         | 
| 22 22 | 
             
                  stop_time = stops.find {|s| s.station == station}.time
         | 
| 23 | 
            -
                   | 
| 24 | 
            -
                    if (stop_time.min > time.min)
         | 
| 25 | 
            -
                      return true
         | 
| 26 | 
            -
                    else
         | 
| 27 | 
            -
                      return false
         | 
| 28 | 
            -
                    end
         | 
| 29 | 
            -
                  elsif (stop_time.hour > time.hour)
         | 
| 30 | 
            -
                    return true
         | 
| 31 | 
            -
                  else
         | 
| 32 | 
            -
                    return false
         | 
| 33 | 
            -
                  end
         | 
| 23 | 
            +
                  stop_time.to_today > time.to_today
         | 
| 34 24 | 
             
                end
         | 
| 35 25 |  | 
| 36 26 | 
             
                def departure_and_arrival(start, destination)
         | 
| @@ -39,11 +29,16 @@ module MetraSchedule | |
| 39 29 | 
             
                  {:departure => departure.to_today, :arrival => arrival.to_today}
         | 
| 40 30 | 
             
                end
         | 
| 41 31 |  | 
| 32 | 
            +
                def departure_with_delay
         | 
| 33 | 
            +
                  return @my_departure unless @del_threshold
         | 
| 34 | 
            +
                  if @my_departure
         | 
| 35 | 
            +
                    @my_departure + (60 * @del_threshold)
         | 
| 36 | 
            +
                  end
         | 
| 37 | 
            +
                end
         | 
| 38 | 
            +
             | 
| 42 39 | 
             
                def my_travel_time
         | 
| 43 40 | 
             
                  return nil unless @my_departure and @my_arrival
         | 
| 44 | 
            -
                  minutes = (@my_arrival.to_i - @my_departure.to_i) / 60
         | 
| 45 | 
            -
                  return minutes if minutes > 0
         | 
| 46 | 
            -
                  return minutes + 1440 if minutes < 0
         | 
| 41 | 
            +
                  minutes = (@my_arrival.to_today.to_i - @my_departure.to_today.to_i) / 60
         | 
| 47 42 | 
             
                end
         | 
| 48 43 |  | 
| 49 44 | 
             
                def print_my_travel_time
         | 
    
        data/lib/metra.rb
    CHANGED
    
    | @@ -3,6 +3,7 @@ require File.join(File.dirname(__FILE__), 'metra', 'line') | |
| 3 3 | 
             
            require File.join(File.dirname(__FILE__), 'metra', 'train')
         | 
| 4 4 | 
             
            require File.join(File.dirname(__FILE__), 'metra', 'stop')
         | 
| 5 5 | 
             
            require File.join(File.dirname(__FILE__), 'metra', 'parser')
         | 
| 6 | 
            +
            require File.join(File.dirname(__FILE__), 'metra', 'delay_parser')
         | 
| 6 7 | 
             
            require File.join(File.dirname(__FILE__), 'metra', 'cacher')
         | 
| 7 8 | 
             
            require File.join(File.dirname(__FILE__), 'metra', 'extensions', 'time_extension')
         | 
| 8 9 |  | 
    
        data/metra_schedule.gemspec
    CHANGED
    
    | @@ -5,11 +5,11 @@ | |
| 5 5 |  | 
| 6 6 | 
             
            Gem::Specification.new do |s|
         | 
| 7 7 | 
             
              s.name = %q{metra_schedule}
         | 
| 8 | 
            -
              s.version = "0. | 
| 8 | 
            +
              s.version = "0.3.0"
         | 
| 9 9 |  | 
| 10 10 | 
             
              s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
         | 
| 11 11 | 
             
              s.authors = ["Blake Smith"]
         | 
| 12 | 
            -
              s.date = %q{2010- | 
| 12 | 
            +
              s.date = %q{2010-02-03}
         | 
| 13 13 | 
             
              s.description = %q{metra_schedule provides a ruby object interface to the Chicago metra train schedule}
         | 
| 14 14 | 
             
              s.email = %q{blakesmith0@gmail.com}
         | 
| 15 15 | 
             
              s.extra_rdoc_files = [
         | 
| @@ -25,6 +25,7 @@ Gem::Specification.new do |s| | |
| 25 25 | 
             
                 "lib/metra.rb",
         | 
| 26 26 | 
             
                 "lib/metra/cacher.rb",
         | 
| 27 27 | 
             
                 "lib/metra/classmethods.rb",
         | 
| 28 | 
            +
                 "lib/metra/delay_parser.rb",
         | 
| 28 29 | 
             
                 "lib/metra/extensions/time_extension.rb",
         | 
| 29 30 | 
             
                 "lib/metra/line.rb",
         | 
| 30 31 | 
             
                 "lib/metra/parser.rb",
         | 
| @@ -33,6 +34,10 @@ Gem::Specification.new do |s| | |
| 33 34 | 
             
                 "lib/metra/train_data.rb",
         | 
| 34 35 | 
             
                 "metra_schedule.gemspec",
         | 
| 35 36 | 
             
                 "test/fixture/UP_NW.html",
         | 
| 37 | 
            +
                 "test/fixture/service_updates_alerts.html",
         | 
| 38 | 
            +
                 "test/fixture/service_updates_alerts_multiple_with_non_delay.html",
         | 
| 39 | 
            +
                 "test/fixture/service_updates_alerts_no_advisories.html",
         | 
| 40 | 
            +
                 "test/fixture/service_updates_alerts_no_range.html",
         | 
| 36 41 | 
             
                 "test/functional/test_all_filters.rb",
         | 
| 37 42 | 
             
                 "test/integration/test_line_integration.rb",
         | 
| 38 43 | 
             
                 "test/test_helper.rb",
         | 
| @@ -46,6 +51,7 @@ Gem::Specification.new do |s| | |
| 46 51 | 
             
                 "test/timecop/lib/timecop/time_stack_item.rb",
         | 
| 47 52 | 
             
                 "test/timecop/lib/timecop/timecop.rb",
         | 
| 48 53 | 
             
                 "test/unit/test_cacher.rb",
         | 
| 54 | 
            +
                 "test/unit/test_delay_parser.rb",
         | 
| 49 55 | 
             
                 "test/unit/test_line.rb",
         | 
| 50 56 | 
             
                 "test/unit/test_metra.rb",
         | 
| 51 57 | 
             
                 "test/unit/test_parser.rb",
         | 
| @@ -71,6 +77,7 @@ Gem::Specification.new do |s| | |
| 71 77 | 
             
                 "test/unit/test_metra.rb",
         | 
| 72 78 | 
             
                 "test/unit/test_stop.rb",
         | 
| 73 79 | 
             
                 "test/unit/test_parser.rb",
         | 
| 80 | 
            +
                 "test/unit/test_delay_parser.rb",
         | 
| 74 81 | 
             
                 "test/unit/test_cacher.rb",
         | 
| 75 82 | 
             
                 "test/test_helper.rb"
         | 
| 76 83 | 
             
              ]
         | 
| @@ -0,0 +1,26 @@ | |
| 1 | 
            +
                        	<div id="serviceAdvisory">
         | 
| 2 | 
            +
            	            	
         | 
| 3 | 
            +
            	                    
         | 
| 4 | 
            +
            	                    
         | 
| 5 | 
            +
            	                        <h2><span class="hidden">Service Advisories</span></h2>
         | 
| 6 | 
            +
            	                    
         | 
| 7 | 
            +
            	                
         | 
| 8 | 
            +
                                <dl>
         | 
| 9 | 
            +
                                    
         | 
| 10 | 
            +
                                        
         | 
| 11 | 
            +
                                            
         | 
| 12 | 
            +
                                                
         | 
| 13 | 
            +
             | 
| 14 | 
            +
                                                    <dt class="line md-n first">Milwaukee District / North Line</dt>
         | 
| 15 | 
            +
                                                
         | 
| 16 | 
            +
                                                
         | 
| 17 | 
            +
                                                
         | 
| 18 | 
            +
                                            
         | 
| 19 | 
            +
                                            <dd class="first"><a href="#serviceAdvisory1">Inbound Train #2156  18 - 20 minutes delayed</a></dd>
         | 
| 20 | 
            +
                                            <dd id="serviceAdvisory1" class="details">Inbound Train #2156 scheduled to arrive Chicago at 8:59 p.m., is operating approximately 20 minutes behind schedule due to freight train interference.</dd>
         | 
| 21 | 
            +
                                        
         | 
| 22 | 
            +
                                    
         | 
| 23 | 
            +
                                </dl>
         | 
| 24 | 
            +
                            </div>
         | 
| 25 | 
            +
             | 
| 26 | 
            +
                        
         | 
| @@ -0,0 +1,39 @@ | |
| 1 | 
            +
                        
         | 
| 2 | 
            +
                          <div id="serviceAdvisory">
         | 
| 3 | 
            +
                            
         | 
| 4 | 
            +
                                  
         | 
| 5 | 
            +
                                  
         | 
| 6 | 
            +
                                      <h2><span class="hidden">Service Advisories</span></h2>
         | 
| 7 | 
            +
                                  
         | 
| 8 | 
            +
                              
         | 
| 9 | 
            +
                                <dl>
         | 
| 10 | 
            +
                                    
         | 
| 11 | 
            +
                                        
         | 
| 12 | 
            +
                                            
         | 
| 13 | 
            +
                                                
         | 
| 14 | 
            +
             | 
| 15 | 
            +
                                                    <dt class="line sws first">SouthWest Service</dt>
         | 
| 16 | 
            +
                                                
         | 
| 17 | 
            +
                                                
         | 
| 18 | 
            +
                                                
         | 
| 19 | 
            +
                                            
         | 
| 20 | 
            +
                                            <dd class="first"><a href="#serviceAdvisory1">Outbound Train #807 - Stopped North of Ashburn</a></dd>
         | 
| 21 | 
            +
                                            <dd id="serviceAdvisory1" class="details">Train #807, scheduled to arrive Orland Park 179th at 9:39 a.m., is stopped north of Ashburn due to switch problems. The duration of this delay is unknown. Metra will update this site as information becomes available.</dd>
         | 
| 22 | 
            +
                                        
         | 
| 23 | 
            +
                                    
         | 
| 24 | 
            +
                                        
         | 
| 25 | 
            +
                                            
         | 
| 26 | 
            +
                                                
         | 
| 27 | 
            +
             | 
| 28 | 
            +
                                                    <dt class="line up-w ">Union Pacific / West Line</dt>
         | 
| 29 | 
            +
                                                
         | 
| 30 | 
            +
                                                
         | 
| 31 | 
            +
                                                
         | 
| 32 | 
            +
                                            
         | 
| 33 | 
            +
                                            <dd ><a href="#serviceAdvisory2">Outbound Train #25 - 15 Minute Delay</a></dd>
         | 
| 34 | 
            +
                                            <dd id="serviceAdvisory2" class="details">Train #25, scheduled to arrive Elburn at 10:08 a.m., is operating 15 minutes behind schedule due to switch problems.</dd>
         | 
| 35 | 
            +
                                        
         | 
| 36 | 
            +
                                    
         | 
| 37 | 
            +
                                </dl>
         | 
| 38 | 
            +
                            </div>
         | 
| 39 | 
            +
                        
         |