viaggiatreno 1.0.2 → 1.0.5

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.
Files changed (42) hide show
  1. checksums.yaml +7 -0
  2. data/.codeclimate.yml +25 -0
  3. data/.gitignore +5 -1
  4. data/.rubocop.yml +46 -0
  5. data/.travis.yml +17 -0
  6. data/Gemfile +10 -1
  7. data/Guardfile +12 -0
  8. data/LICENSE +21 -0
  9. data/README.md +8 -2
  10. data/Rakefile +2 -1
  11. data/lib/viaggia_treno.rb +1 -0
  12. data/lib/viaggiatreno.rb +2 -3
  13. data/lib/viaggiatreno/regex_match_info.rb +11 -0
  14. data/lib/viaggiatreno/scraper.rb +94 -0
  15. data/lib/viaggiatreno/string_utils.rb +6 -0
  16. data/lib/viaggiatreno/train.rb +93 -0
  17. data/lib/viaggiatreno/train_state.rb +6 -0
  18. data/lib/viaggiatreno/train_stop.rb +26 -0
  19. data/lib/viaggiatreno/train_stop_state.rb +5 -0
  20. data/lib/viaggiatreno/version.rb +1 -2
  21. data/lib/viaggiatreno/viaggiatreno_urls.rb +14 -0
  22. data/lib/viaggiatreno/xpath_match_info.rb +9 -0
  23. data/spec/spec_helper.rb +17 -0
  24. data/spec/vcr_cassettes/Arrived_train_delay_0_.yml +538 -0
  25. data/spec/vcr_cassettes/Arrived_train_delay_negative_.yml +621 -0
  26. data/spec/vcr_cassettes/Arrived_train_delay_positive_.yml +680 -0
  27. data/spec/vcr_cassettes/Not_departed_train.yml +438 -0
  28. data/spec/vcr_cassettes/Running_train_delay_positive_.yml +468 -0
  29. data/spec/viaggiatreno/train_spec.rb +190 -0
  30. data/spec/viaggiatreno_spec.rb +9 -0
  31. data/viaggiatreno.gemspec +9 -8
  32. metadata +57 -25
  33. data/LICENSE.txt +0 -22
  34. data/lib/viaggiatreno/RegExpMatchInfo.rb +0 -23
  35. data/lib/viaggiatreno/Scraper.rb +0 -87
  36. data/lib/viaggiatreno/StopState.rb +0 -8
  37. data/lib/viaggiatreno/StringUtils.rb +0 -7
  38. data/lib/viaggiatreno/Train.rb +0 -92
  39. data/lib/viaggiatreno/TrainState.rb +0 -11
  40. data/lib/viaggiatreno/TrainStop.rb +0 -24
  41. data/lib/viaggiatreno/ViaggiatrenoURLs.rb +0 -17
  42. data/lib/viaggiatreno/XPathMatchInfo.rb +0 -19
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 5cf3f766e8e7a062b46e264817834fe98d21f02c
4
+ data.tar.gz: 91de43e8d50a9b01e92e59984995e01467f73149
5
+ SHA512:
6
+ metadata.gz: b8cc778203d61d1c5ced80dd05759b07c81a2278f2831160b0495b258636678ce90741a1d9fedb6acb926998879802a731d4378d6da6e2767283ac398b943d61
7
+ data.tar.gz: a0a8181f56b43948d01953abf2b2c1306e392013b7bf0790e46f51761f0c863dfc1ab06c19083de010527b4f078380f9e0417079f2e1003ef920f87e68268d09
@@ -0,0 +1,25 @@
1
+ ---
2
+ engines:
3
+ duplication:
4
+ enabled: true
5
+ config:
6
+ languages:
7
+ - ruby
8
+ - javascript
9
+ - python
10
+ - php
11
+ fixme:
12
+ enabled: true
13
+ rubocop:
14
+ enabled: true
15
+ ratings:
16
+ paths:
17
+ - "**.inc"
18
+ - "**.js"
19
+ - "**.jsx"
20
+ - "**.module"
21
+ - "**.php"
22
+ - "**.py"
23
+ - "**.rb"
24
+ exclude_paths:
25
+ - spec/**/*
data/.gitignore CHANGED
@@ -1,6 +1,5 @@
1
1
  *.gem
2
2
  *.rbc
3
- .bundle
4
3
  .config
5
4
  .yardoc
6
5
  Gemfile.lock
@@ -15,3 +14,8 @@ spec/reports
15
14
  test/tmp
16
15
  test/version_tmp
17
16
  tmp
17
+
18
+ # Ignore bundle configuration
19
+ /bin
20
+ .bundle
21
+ /vendor/bundle
@@ -0,0 +1,46 @@
1
+ AllCops:
2
+ Include:
3
+ - '**/Rakefile'
4
+ - '**/Gemfile'
5
+
6
+ AndOr:
7
+ Enabled: false
8
+
9
+ AsciiComments:
10
+ Enabled: false
11
+
12
+ Blocks:
13
+ Enabled: false
14
+
15
+ BracesAroundHashParameters:
16
+ Enabled: false
17
+
18
+ ClassAndModuleChildren:
19
+ Enabled: false
20
+
21
+ ClassLength:
22
+ Enabled: false
23
+
24
+ ConstantName:
25
+ Enabled: false
26
+
27
+ Documentation:
28
+ Enabled: false
29
+
30
+ Encoding:
31
+ Enabled: false
32
+
33
+ LineLength:
34
+ Max: 100
35
+
36
+ MethodLength:
37
+ Max: 12
38
+
39
+ RegexpLiteral:
40
+ Enabled: false
41
+
42
+ SignalException:
43
+ EnforcedStyle: only_raise
44
+
45
+ Validation:
46
+ Enabled: false
@@ -0,0 +1,17 @@
1
+ language: ruby
2
+ rvm:
3
+ - 2.0.0
4
+ - 2.1.8
5
+ - 2.2.4
6
+ - 2.3.0
7
+ - ruby-head
8
+ script:
9
+ - 'bundle exec rspec --color --format documentation'
10
+ notifications:
11
+ recipients:
12
+ - michele.bologna@gmail.com
13
+ - marius.colacioiu@gmail.com
14
+ sudo: false
15
+ addons:
16
+ code_climate:
17
+ repo_token: 31c466aad9acda9cfe23ab74ca7a9c50a2243f2dd473d972a70c795c2affdab0
data/Gemfile CHANGED
@@ -1,4 +1,13 @@
1
1
  source 'https://rubygems.org'
2
2
 
3
- # Specify your gem's dependencies in viaggiatreno.gemspec
4
3
  gemspec
4
+
5
+ group :test do
6
+ gem 'codeclimate-test-reporter', require: nil
7
+ gem 'guard-rspec', require: false
8
+ gem 'guard-rubocop', require: false
9
+ gem 'rspec'
10
+ gem 'simplecov', require: false
11
+ gem 'vcr'
12
+ gem 'webmock'
13
+ end
@@ -0,0 +1,12 @@
1
+ guard :rspec, cmd: 'rspec' do
2
+ watch(%r{^spec/.+_spec\.rb$})
3
+ watch(%r{^lib/(.+)\.rb$}) { |m| "spec/lib/#{m[1]}_spec.rb" }
4
+ watch('spec/spec_helper.rb') { "spec" }
5
+ end
6
+
7
+ guard :rubocop, all_on_start: false, cli: ['--format', 'clang'] do
8
+ watch(%r{^bin/flash$})
9
+ watch(%r{^lib/.+\.rb$})
10
+ watch(%r{^spec/.+\.rb$})
11
+ watch(%r{(?:.+/)?\.rubocop\.yml$}) { |m| File.dirname(m[0]) }
12
+ end
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2015 Michele Bologna
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
data/README.md CHANGED
@@ -1,6 +1,12 @@
1
1
  # Viaggiatreno
2
2
 
3
- TODO: Write a gem description
3
+ [![Build Status](https://travis-ci.org/mbologna/viaggiatreno.svg)](https://travis-ci.org/mbologna/viaggiatreno)
4
+ [![Code Climate](https://codeclimate.com/repos/569a5314b23bff7a6c011fb0/badges/7a1d250d86b806acee6c/gpa.svg)](https://codeclimate.com/repos/569a5314b23bff7a6c011fb0/feed)
5
+ [![Test Coverage](https://codeclimate.com/repos/569a5314b23bff7a6c011fb0/badges/7a1d250d86b806acee6c/coverage.svg)](https://codeclimate.com/repos/569a5314b23bff7a6c011fb0/coverage)
6
+ [![Issue Count](https://codeclimate.com/repos/569a5314b23bff7a6c011fb0/badges/7a1d250d86b806acee6c/issue_count.svg)](https://codeclimate.com/repos/569a5314b23bff7a6c011fb0/feed)
7
+
8
+ This gem parses Italian railway real-time status for trains (viaggiatreno.it).
9
+ Use this API to fetch online information about Italian trains.
4
10
 
5
11
  ## Installation
6
12
 
@@ -18,7 +24,7 @@ Or install it yourself as:
18
24
 
19
25
  ## Usage
20
26
 
21
- TODO: Write usage instructions here
27
+ See examples at http://michelebologna.net
22
28
 
23
29
  ## Contributing
24
30
 
data/Rakefile CHANGED
@@ -1 +1,2 @@
1
- require "bundler/gem_tasks"
1
+ require 'bundler/gem_tasks'
2
+ task default: 'build'
@@ -0,0 +1 @@
1
+ require 'viaggiatreno'
@@ -1,6 +1,5 @@
1
- require "viaggiatreno/version"
2
- require "viaggiatreno/Train"
3
-
1
+ require 'viaggiatreno/version'
2
+ require 'viaggiatreno/train'
4
3
 
5
4
  module Viaggiatreno
6
5
  end
@@ -0,0 +1,11 @@
1
+ class RegExpMatchInfo
2
+ # regex to match train status (string)
3
+ REGEXP_STATE_TRAVELING = /(Il treno viaggia.*)(Ultimo rilevamento a)(.*)/
4
+ REGEXP_STATE_NOT_DEPARTED = /Il treno non e' ancora partito/
5
+ REGEXP_STATE_ARRIVED = /Il treno e' arrivato.*/
6
+ REGEXP_DELAY_STR = /con (\d+) minuti di ([anticipo|ritardo]+)/
7
+ REGEXP_NODELAY_STR = /Il treno .* in orario.*/
8
+ REGEXP_STOP_ALREADY_DONE = /giaeffettuate/
9
+ STR_DELAY_STR = 'ritardo'.freeze
10
+ STR_TRAIN_NUMBER_URL_REPLACE = 'TRAINNUMBER'.freeze
11
+ end
@@ -0,0 +1,94 @@
1
+ require 'open-uri'
2
+ require 'nokogiri'
3
+ require_relative 'train_stop'
4
+ require_relative 'train_state'
5
+ require_relative 'string_utils'
6
+ require_relative 'regex_match_info'
7
+ require_relative 'train_stop_state'
8
+ require_relative 'xpath_match_info'
9
+ require_relative 'viaggiatreno_urls'
10
+
11
+ class Scraper
12
+ def initialize(train_number, train)
13
+ @site_info_main = ViaggiatrenoURLs::SITE_INFO_MAIN.gsub(
14
+ RegExpMatchInfo::STR_TRAIN_NUMBER_URL_REPLACE, train_number)
15
+ @site_info_details = ViaggiatrenoURLs::SITE_INFO_DETAILS.gsub(
16
+ RegExpMatchInfo::STR_TRAIN_NUMBER_URL_REPLACE, train_number)
17
+ @train = train
18
+ end
19
+
20
+ # fetch and parse basic train information (status, train)name, details)
21
+ def update_train
22
+ doc = Nokogiri::HTML(open(@site_info_main))
23
+ @train.status = StringUtils.remove_newlines_tabs_and_spaces(
24
+ doc.xpath(XPathMatchInfo::XPATH_STATUS).first)
25
+ @train.train_name = doc.xpath(XPathMatchInfo::XPATH_TRAIN_NAME).first.content
26
+ update_train_status(@train)
27
+ @train.delay = fetch_train_delay(@train.status)
28
+ end
29
+
30
+ def update_train_status(train)
31
+ case
32
+ when train.status =~ RegExpMatchInfo::REGEXP_STATE_NOT_DEPARTED
33
+ train.state = TrainState::NOT_DEPARTED
34
+ when train.status =~ RegExpMatchInfo::REGEXP_STATE_ARRIVED
35
+ train.state = TrainState::ARRIVED
36
+ when train.status =~ RegExpMatchInfo::REGEXP_STATE_TRAVELING
37
+ train.state = TrainState::TRAVELING
38
+ regex_match = train.status.match(
39
+ RegExpMatchInfo::REGEXP_STATE_TRAVELING)
40
+ train.last_update = regex_match[3].strip
41
+ train.status = regex_match[1].rstrip
42
+ end
43
+ end
44
+
45
+ def fetch_train_delay(status)
46
+ return nil if @train.state == TrainState::NOT_DEPARTED
47
+ if status =~ RegExpMatchInfo::REGEXP_NODELAY_STR
48
+ delay = 0
49
+ else
50
+ delay = status.match(RegExpMatchInfo::REGEXP_DELAY_STR)[1].to_i
51
+ if status.match(RegExpMatchInfo::REGEXP_DELAY_STR)[2] != RegExpMatchInfo::STR_DELAY_STR
52
+ delay *= -1 # train is ahead of schedule, delay is negative
53
+ end
54
+ end
55
+ delay
56
+ end
57
+
58
+ # fetch and parse train details (departing and arriving station,
59
+ # intermediate stops)
60
+ def update_train_details
61
+ doc = Nokogiri::HTML(open(@site_info_details))
62
+ doc.xpath(XPathMatchInfo::XPATH_DETAILS_GENERIC).each do |x|
63
+ @station_name = x.xpath(XPathMatchInfo::XPATH_DETAILS_STATION_NAME).first.to_s
64
+ arrival_time = fetch_trainstop_arrival_time(x)
65
+ @scheduled_arrival_time = arrival_time['scheduled_arrival_time']
66
+ @actual_arrival_time = arrival_time['actual_arrival_time']
67
+ @status = update_trainstop_status(x, @train, @status)
68
+ @train.add_stop(TrainStop.new(
69
+ @station_name, @scheduled_arrival_time,
70
+ @actual_arrival_time, @status))
71
+ end
72
+ end
73
+
74
+ def update_trainstop_status(x, train, status)
75
+ status = if x.attributes['class'].to_s =~ RegExpMatchInfo::REGEXP_STOP_ALREADY_DONE && \
76
+ train.state != TrainState::NOT_DEPARTED
77
+ TrainStopState::DONE
78
+ else
79
+ TrainStopState::TO_BE_DONE
80
+ end
81
+ status
82
+ end
83
+
84
+ def fetch_trainstop_arrival_time(xpath)
85
+ scheduled_arrival_time = StringUtils.remove_newlines_tabs_and_spaces(
86
+ xpath.xpath(XPathMatchInfo::XPATH_DETAILS_SCHEDULED_STOP_TIME).first).to_s
87
+ actual_arrival_time = StringUtils.remove_newlines_tabs_and_spaces(
88
+ xpath.xpath(XPathMatchInfo::XPATH_DETAILS_ACTUAL_STOP_TIME).first).to_s
89
+ {
90
+ 'scheduled_arrival_time' => scheduled_arrival_time,
91
+ 'actual_arrival_time' => actual_arrival_time
92
+ }
93
+ end
94
+ end
@@ -0,0 +1,6 @@
1
+ class StringUtils
2
+ # utility method
3
+ def self.remove_newlines_tabs_and_spaces(str)
4
+ str.content.delete("\r").delete("\n").tr("\t", ' ').gsub(/ +/, ' ').strip
5
+ end
6
+ end
@@ -0,0 +1,93 @@
1
+ require_relative 'scraper.rb'
2
+ require_relative 'train_state.rb'
3
+ require_relative 'train_stop.rb'
4
+
5
+ class Train
6
+ attr_accessor :train_number, :train_name, :delay, :status, :last_update, :state
7
+
8
+ def initialize(train_number)
9
+ @train_number = train_number
10
+ @scraper = Scraper.new(train_number.to_s, self)
11
+ update
12
+ end
13
+
14
+ def update
15
+ @scraper.update_train
16
+ end
17
+
18
+ def update_details
19
+ @train_stops = []
20
+ @scraper.update_train_details
21
+ end
22
+
23
+ def to_s
24
+ "#{@train_number} #{@train_name}: #{@status} state: #{@state}, \
25
+ delay: #{@delay}, last_update: #{@last_update}"
26
+ end
27
+
28
+ def add_stop(train_stop)
29
+ @train_stops << train_stop
30
+ end
31
+
32
+ def train_stops
33
+ update_details if @train_stops.nil?
34
+ @train_stops
35
+ end
36
+
37
+ def departing_station
38
+ train_stops.first.train_station.to_s
39
+ end
40
+
41
+ def arriving_station
42
+ train_stops.last.train_station.to_s
43
+ end
44
+
45
+ def scheduled_departing_time
46
+ train_stops.first.scheduled_stop_time.to_s
47
+ end
48
+
49
+ def scheduled_arriving_time
50
+ train_stops.last.scheduled_stop_time.to_s
51
+ end
52
+
53
+ def actual_departing_time
54
+ train_stops.first.actual_stop_time.to_s
55
+ end
56
+
57
+ def actual_arriving_time
58
+ train_stops.last.actual_stop_time.to_s
59
+ end
60
+
61
+ def scheduled_stop_time(station_name)
62
+ find_stop_time(station_name)['scheduled']
63
+ end
64
+
65
+ def actual_stop_time(station_name)
66
+ find_stop_time(station_name)['actual']
67
+ end
68
+
69
+ def last_stop
70
+ return train_stops.last.to_s if @state == TrainState::ARRIVED
71
+ train_stops.each_with_index do |train_stop, index|
72
+ if train_stop.status == TrainStopState::DONE && \
73
+ train_stops[index + 1].status == TrainStopState::TO_BE_DONE
74
+ return train_stop.to_s
75
+ end
76
+ end
77
+ nil
78
+ end
79
+
80
+ private
81
+
82
+ def find_stop_time(station_name)
83
+ train_stops.each do |train_stop|
84
+ if train_stop.train_station.to_s == station_name
85
+ return \
86
+ {
87
+ 'actual' => "#{train_stop.actual_stop_time} [#{train_stop.status}]",
88
+ 'scheduled' => "#{train_stop.scheduled_stop_time} [#{train_stop.status}]"
89
+ }
90
+ end
91
+ end
92
+ end
93
+ end
@@ -0,0 +1,6 @@
1
+ class TrainState
2
+ # Train status
3
+ TRAVELING = 'TRAVELING'.freeze
4
+ ARRIVED = 'ARRIVED'.freeze
5
+ NOT_DEPARTED = 'NOT DEPARTED'.freeze
6
+ end
@@ -0,0 +1,26 @@
1
+ require_relative 'train_stop_state'
2
+
3
+ class TrainStop
4
+ attr_accessor :train_station, :scheduled_stop_time, :actual_stop_time, :status
5
+
6
+ def initialize(train_station, scheduled_stop_time, actual_stop_time, status)
7
+ @train_station = train_station
8
+ @scheduled_stop_time = scheduled_stop_time
9
+ @actual_stop_time = actual_stop_time
10
+ @status = status
11
+ end
12
+
13
+ def to_s
14
+ retstr = ''
15
+ if @status == TrainStopState::DONE
16
+ done = 'X'
17
+ actual_or_expected = 'ACTUAL'
18
+ elsif @status == TrainStopState::TO_BE_DONE
19
+ done = ' '
20
+ actual_or_expected = 'EXPECTED'
21
+ end
22
+ retstr += "[#{done}] #{train_station} = SCHEDULED: #{scheduled_stop_time}"\
23
+ " #{actual_or_expected}: #{actual_stop_time}"
24
+ retstr
25
+ end
26
+ end