Skip to content

Instantly share code, notes, and snippets.

@Val
Created July 9, 2014 14:59
Show Gist options
  • Save Val/09e43b3c4bd16a66071d to your computer and use it in GitHub Desktop.
Save Val/09e43b3c4bd16a66071d to your computer and use it in GitHub Desktop.
#!/usr/bin/env ruby
# -*- mode:ruby;tab-width:2;indent-tabs-mode:nil;coding:utf-8 -*-
# vim: ft=ruby syn=ruby fileencoding=utf-8 sw=2 ts=2 ai eol et si
#
# next_train_for.rb: french trains station in motion service client script
# (c) 2010-2014 Laurent Vallar <[email protected]>, WTFPL license v2 see below.
#
# This program is free software. It comes without any warranty, to
# the extent permitted by applicable law. You can redistribute it
# and/or modify it under the terms of the Do What The Fuck You Want
# To Public License, Version 2, as published by Sam Hocevar. See
# http://www.wtfpl.net/ for more details.
DEFAULT = 'Epinal' # default station
BASE_URL = 'http://www.gares-en-mouvement.com/fr/' # station in motion base url
STATUS = 'dep' # (dep)artures / (arr)ivals
gem 'mechanize'
require 'mechanize'
# get station name from command line if any
station = ARGV.length >= 1 ? ARGV[0].downcase.capitalize : DEFAULT
# get status (departures/arrivals) from command line if any
status = ARGV.length == 2 ? ARGV[1].downcase[0..2] : STATUS
# usage / help if needed
if ARGV == %w[ --help ]
STDERR.puts <<-EOF
#{$0} [city] [status]
where:
city is the city name, default: #{DEFAULT}
status is the ((arr)ivals|(dep)artures) list, default #{STATUS}
examples:
#{$0} 'Paris Est' arr
EOF
station = 'Paris Est'
status = 'arr'
end
# start Mechanize agent on main page to get station key
station_key = station_name = nil
agent = Mechanize.new
agent.get(BASE_URL) do |page|
page.links.each do |link|
station_name = link.text
if station_name =~ /#{station}/i && link.href =~ %r{/fr/(\w+)/accueil/$}
station_key = $1; break
end
end
end
headers = nil # store fields names as keys, titles as values
schedules = [] # store each schedule item as array of hashtables with
# same fields keys
# process schedule page and store all schedule items
agent.get("#{BASE_URL}#{station_key}/horaires-temps-reel/#{status}/") do |page|
# find shedule's DOM tree
schedules_doc = page.parser/'div[@class=infostempsreel_tgda]'
headers = Hash[*(schedules_doc/'thead/tr/th').map do |element|
key = element.attributes['id'].value.gsub(/_id$/, '').to_sym
key = :type if key == :marque_transporteur
[ key, element.inner_text ]
end.flatten]
# find schedule's entries
(schedules_doc/'tbody/tr').each do |element| # process table headers
schedule = {}
(element/'td').each do |sub_element| # find fields names
key = sub_element.attributes['class'].value.gsub(/^tvs_td_/, '').to_sym
next if sub_element.search('img').size > 0
schedule[key] = sub_element.inner_text
end # then add schedule to schedule array (preserve order)
schedules << schedule unless schedule.values.all? { |v| v.length > 0 }
end
end # all done
print_array = [] # store schedules values in a flat array, YxX
ORDER = [ :type, :numero, :heure, :originedestination, :voie, :situation ]
ORDER.each do |x| # headers on first colon, then items values each colon.
print_array << headers[x]
schedules.each { |sched| print_array << (sched[x].nil? ? '' : sched[x]) }
end
b = print_array.each_slice(schedules.size + 1).to_a # sub arrays mark lines
def tabulate(a) # magic helper :)
a[0].zip(a.length > 2 ? tabulate(a[1..-1]) : a[-1])
end
vcols = tabulate(b).map{|x| x.flatten.map {|x| x ? x : ''}} # magic (2)
widths = b.map{|x|x.max_by(&:length).length} # array of colons widths
buffer = vcols.map do |x| # magic (3) create buffer
' ' + x.each_with_index.map {|y,i| y.ljust(widths[i] + 2)}.join('| ')
end # then create header raw
hr = '-' + widths.map { |w| '-' * w }.map { |s| '--' + s }.join('+-') + '-'
puts "#{station_name} - #{status == 'dep' ? 'departures' : 'arrivals'}\n#{hr}"
puts buffer.join("\n").sub("\n", "\n" + hr + "\n") + "\n#{hr}" # add header raw
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment