1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
|
class VehicleJourneyImport
include ActiveModel::Model
extend EnhancedModelI18n
attr_accessor :file, :route
attr_accessor :created_vehicle_journey_count,:updated_vehicle_journey_count,:deleted_vehicle_journey_count
attr_accessor :created_journey_pattern_count,:error_count
validates_presence_of :file
validates_presence_of :route
def initialize(attributes = {})
attributes.each { |name, value| send("#{name}=", value) } if attributes
self.created_vehicle_journey_count = 0
self.updated_vehicle_journey_count = 0
self.created_journey_pattern_count = 0
self.deleted_vehicle_journey_count = 0
self.error_count = 0
end
def persisted?
false
end
def save
begin
Chouette::VehicleJourney.transaction do
if imported_vehicle_journeys.map(&:valid?).all?
imported_vehicle_journeys.each(&:save!)
true
else
imported_vehicle_journeys.each_with_index do |imported_vehicle_journey, index|
imported_vehicle_journey.errors.full_messages.each do |message|
errors.add :base, I18n.t("vehicle_journey_imports.errors.invalid_vehicle_journey", :column => index+1, :message => message)
end
end
raise
end
end
rescue Exception => exception
Rails.logger.error(exception.message)
Rails.logger.error(exception.backtrace)
errors.add :base, I18n.t("vehicle_journey_imports.errors.exception")
false
end
end
def imported_vehicle_journeys
@imported_vehicle_journeys ||= load_imported_vehicle_journeys
end
# Find journey pattern on stop points used in vehicle journey at stops
# if no stop_point used found, return nil to delete vehicle_journey if exists
# if only 1 stop_point used found, raise exception to stop import
def find_journey_pattern_schedule(column,hours_by_stop_point_ids)
stop_points_used = hours_by_stop_point_ids.reject{ |key,value| value == nil }.keys
return nil if stop_points_used.empty?
if stop_points_used.length == 1
errors.add :base, I18n.t("vehicle_journey_imports.errors.one_stop_point_used", :column => column)
raise
end
journey_pattern_founded = route.journey_patterns.select{ |jp| jp.stop_points.collect(&:id) == stop_points_used }.first
# If no journey pattern founded, create a new one
self.created_journey_pattern_count += 1 if journey_pattern_founded.nil?
journey_pattern_founded ? journey_pattern_founded : route.journey_patterns.create(:stop_points => Chouette::StopPoint.find(stop_points_used) )
end
def as_integer(v)
v.blank? ? nil : v.to_i
end
def as_boolean(v)
v.blank? ? nil : (v[1..1].downcase != "n")
end
def update_time_tables(vj,tm_ids)
vj.time_tables.clear
return unless tm_ids.present?
ids = tm_ids.split(",").map(&:to_i)
vj.time_tables << Chouette::TimeTable.where(:id => ids)
end
def update_footnotes(vj,ftn_ids)
vj.footnotes.clear
return unless ftn_ids.present?
ids = ftn_ids.split(",").map(&:to_i)
vj.footnotes << Chouette::Footnote.where(:id => ids, :line_id => vj.route.line.id)
end
def load_imported_vehicle_journeys
spreadsheet = open_spreadsheet(file)
vehicle_journeys = []
first_column = spreadsheet.column(1)
# fixed rows (first = 1)
number_row = 2
published_journey_name_row = 3
mobility_row = 4
flexible_service_row = 5
time_tables_row = 6
footnotes_row = 7
# rows in column (first = 0)
first_stop_row_index = 8
stop_point_ids = first_column[first_stop_row_index..spreadsheet.last_row].map(&:to_i)
# blank lines at end of file will produce id = 0 ; ignore them
last_stop_row_index = stop_point_ids.length + 7
while stop_point_ids.last == 0
stop_point_ids = stop_point_ids[0..-2]
last_stop_row_index -= 1
end
unless route.stop_points.collect(&:id) == stop_point_ids
errors.add :base, I18n.t("vehicle_journey_imports.errors.not_same_stop_points", :route => route.id)
raise
end
(3..spreadsheet.last_column).each do |i|
vehicle_journey_id = spreadsheet.column(i)[0]
hours_by_stop_point_ids = Hash[[stop_point_ids, spreadsheet.column(i)[first_stop_row_index..last_stop_row_index]].transpose]
journey_pattern = find_journey_pattern_schedule(i,hours_by_stop_point_ids)
vehicle_journey = route.vehicle_journeys.where(:id => vehicle_journey_id, :route_id => route.id).first_or_initialize
if journey_pattern.nil?
if vehicle_journey.id.present?
self.deleted_vehicle_journey_count += 1
vehicle_journey.delete
end
next
end
if vehicle_journey.id.present?
self.updated_vehicle_journey_count += 1
else
self.created_vehicle_journey_count += 1
end
# number
vehicle_journey.number = as_integer(spreadsheet.row(number_row)[i-1])
# published_name
vehicle_journey.published_journey_name = spreadsheet.row(published_journey_name_row)[i-1]
# flexible_service
vehicle_journey.flexible_service = as_boolean(spreadsheet.row(flexible_service_row)[i-1])
# mobility
vehicle_journey.mobility_restricted_suitability = as_boolean(spreadsheet.row(mobility_row)[i-1])
# time_tables
update_time_tables(vehicle_journey,spreadsheet.row(time_tables_row)[i-1])
update_footnotes(vehicle_journey,spreadsheet.row(footnotes_row)[i-1])
# journey_pattern
vehicle_journey.journey_pattern = journey_pattern
vehicle_journey.vehicle_journey_at_stops.clear
line = 0
hours_by_stop_point_ids.each_pair do |key, value|
line += 1
if value.present? # Create a vehicle journey at stop when time is present
begin
# force UTC to ignore timezone effects
main_time = Time.parse(value+" UTC")
if main_time.present?
vjas = Chouette::VehicleJourneyAtStop.new(:stop_point_id => key, :vehicle_journey_id => vehicle_journey.id, :departure_time => main_time, :arrival_time => main_time )
vehicle_journey.vehicle_journey_at_stops << vjas
end
rescue Exception => exception
errors.add :base, I18n.t("vehicle_journey_imports.errors.invalid_vehicle_journey_at_stop", :column => i, :line => line, :time => value)
raise exception
end
end
end
vehicle_journeys << vehicle_journey
end
vehicle_journeys
end
def open_spreadsheet(file)
case File.extname(file.original_filename)
when '.csv' then Roo::CSV.new(file.path, csv_options: {col_sep: ";"})
when '.xls' then Roo::Excel.new(file.path)
when '.xlsx' then Roo::Excelx.new(file.path)
else
raise "Unknown file type: #{file.original_filename}"
end
end
end
|