ninoxe 1.0.3 → 1.1.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.
Files changed (29) hide show
  1. checksums.yaml +15 -0
  2. data/app/models/chouette/access_point.rb +33 -12
  3. data/app/models/chouette/active_record.rb +1 -1
  4. data/app/models/chouette/line.rb +2 -0
  5. data/app/models/chouette/stop_area.rb +41 -3
  6. data/app/models/chouette/time_table.rb +351 -45
  7. data/app/models/chouette/time_table_date.rb +5 -5
  8. data/app/models/chouette/time_table_period.rb +16 -0
  9. data/app/models/chouette/trident_active_record.rb +27 -3
  10. data/app/models/chouette/vehicle_journey.rb +1 -1
  11. data/config/locales/en.yml +1 -0
  12. data/config/locales/fr.yml +1 -0
  13. data/db/migrate/20140617131630_add_on_demand_transportation_to_line.rb +5 -0
  14. data/db/migrate/20140617132236_add_details_to_vehicle_journey.rb +6 -0
  15. data/db/migrate/20140618071147_fix_column_name.rb +11 -0
  16. data/db/migrate/20140625143030_add_in_out_to_timetable_date.rb +5 -0
  17. data/db/migrate/20140626054725_set_in_out_to_timetable_date.rb +8 -0
  18. data/db/migrate/20140718141703_acts_as_taggable_on_migration.acts_as_taggable_on_engine.rb +31 -0
  19. data/db/migrate/20140718141704_add_missing_unique_indices.acts_as_taggable_on_engine.rb +20 -0
  20. data/db/migrate/20140718141705_add_taggings_counter_cache_to_tags.acts_as_taggable_on_engine.rb +15 -0
  21. data/db/migrate/20140718141706_add_missing_taggable_index.acts_as_taggable_on_engine.rb +10 -0
  22. data/db/migrate/20140820060801_add_zip_code_and_city_name_to_stop_area.rb +8 -0
  23. data/db/migrate/20140820060814_add_zip_code_and_city_name_to_access_point.rb +8 -0
  24. data/lib/factories/chouette_routes.rb +3 -1
  25. data/lib/factories/chouette_time_table.rb +2 -2
  26. data/lib/factories/chouette_vehicle_journey.rb +1 -0
  27. data/lib/ninoxe/version.rb +1 -1
  28. data/lib/ninoxe.rb +1 -0
  29. metadata +117 -118
checksums.yaml ADDED
@@ -0,0 +1,15 @@
1
+ ---
2
+ !binary "U0hBMQ==":
3
+ metadata.gz: !binary |-
4
+ Njg5Yzg4YjEzOWIyZGUxNjc2MWU5YTEzMWYyOThiZTVjZmFlZjI1Yg==
5
+ data.tar.gz: !binary |-
6
+ N2YzZmUzM2Y1MzYyNWYwMDI0MWViOGQzODQzNGEyYjY5MzY2NDY0Yg==
7
+ SHA512:
8
+ metadata.gz: !binary |-
9
+ YzUzNWM2ZWQwMmE4ODkzODYxZGZjNmE2MzViOGUzY2U4NDFiNTE0ZmQ5MDg5
10
+ ODkzZTJmNjY0YjU2MDdhNThiZWUzYzAxNTQ0N2VlN2U2Y2Q4NTI2NmRhZDJj
11
+ YjY3MTk5Y2Y4MmQzM2Q0MTJkODUyOTJlMDQzNDVlOGU3ZmJkZTA=
12
+ data.tar.gz: !binary |-
13
+ MDYwZWI4OTZmMDUzNzE0NjRhZjI5OGQ0NzVmZDg4ZjlmMTc2M2Q3ODY5Njk1
14
+ MTU3YzQ2OWIwYzZiODczMTUyNzQ2ZTBjNWE3OTIwYjlmYjcwZWE1NTA1ZDk3
15
+ ZGMwOTQzNmEwNzY2NTY4MTYxZDFiOGZlZTFlZjVjMjNlMGY2ZDg=
@@ -9,19 +9,16 @@ class Chouette::AccessPoint < Chouette::TridentActiveRecord
9
9
  belongs_to :stop_area
10
10
 
11
11
  attr_accessor :access_point_type
12
+ attr_writer :coordinates
13
+
12
14
  attr_accessible :objectid, :object_version, :creation_time, :creator_id, :name, :comment
13
15
  attr_accessible :longitude, :latitude, :long_lat_type
14
- #attr_accessible :x, :y, :projection_type
15
- attr_accessible :country_code, :street_name
16
+ attr_accessible :country_code, :street_name, :zip_code, :city_name
16
17
  attr_accessible :openning_time, :closing_time, :access_type, :access_point_type
17
18
  attr_accessible :mobility_restricted_suitability, :stairs_availability, :lift_availability
18
19
  attr_accessible :stop_area_id
20
+ attr_accessible :coordinates
19
21
 
20
- # workaround of ruby 1.8 private method y block attribute y reading access
21
- #def y
22
- # read_attribute :y
23
- #end
24
-
25
22
  validates_presence_of :name
26
23
  validates_presence_of :access_type
27
24
 
@@ -30,14 +27,38 @@ class Chouette::AccessPoint < Chouette::TridentActiveRecord
30
27
  validates_numericality_of :latitude, :less_than_or_equal_to => 90, :greater_than_or_equal_to => -90, :allow_nil => true
31
28
  validates_numericality_of :longitude, :less_than_or_equal_to => 180, :greater_than_or_equal_to => -180, :allow_nil => true
32
29
 
33
- #validates_presence_of :x, :if => :y
34
- #validates_presence_of :y, :if => :x
35
- #validates_numericality_of :x, :allow_nil => true
36
- #validates_numericality_of :y, :allow_nil => true
30
+ validates_format_of :coordinates, :with => %r{\A *-?(0?[0-9](\.[0-9]*)?|[0-8][0-9](\.[0-9]*)?|90(\.[0]*)?) *\, *-?(0?[0-9]?[0-9](\.[0-9]*)?|1[0-7][0-9](\.[0-9]*)?|180(\.[0]*)?) *\Z}, :allow_nil => true, :allow_blank => true
37
31
 
38
32
  def self.nullable_attributes
39
- [:street_name, :country_code, :comment, :long_lat_type]
33
+ [:street_name, :country_code, :comment, :long_lat_type, :zip_code, :city_name]
34
+ end
35
+
36
+ before_save :coordinates_to_lat_lng
37
+
38
+ def combine_lat_lng
39
+ if self.latitude.nil? || self.longitude.nil?
40
+ ""
41
+ else
42
+ self.latitude.to_s+","+self.longitude.to_s
43
+ end
44
+ end
45
+
46
+ def coordinates
47
+ @coordinates || combine_lat_lng
40
48
  end
49
+
50
+ def coordinates_to_lat_lng
51
+ if ! @coordinates.nil?
52
+ if @coordinates.empty?
53
+ self.latitude = nil
54
+ self.longitude = nil
55
+ else
56
+ self.latitude = BigDecimal.new(@coordinates.split(",").first)
57
+ self.longitude = BigDecimal.new(@coordinates.split(",").last)
58
+ end
59
+ @coordinates = nil
60
+ end
61
+ end
41
62
 
42
63
  def to_lat_lng
43
64
  Geokit::LatLng.new(latitude, longitude) if latitude and longitude
@@ -1,5 +1,5 @@
1
1
  #require "active_record"
2
-
2
+ require 'deep_cloneable'
3
3
  module Chouette
4
4
  class ActiveRecord < ::ActiveRecord::Base
5
5
 
@@ -6,11 +6,13 @@ class Chouette::Line < Chouette::TridentActiveRecord
6
6
  attr_accessible :transport_mode, :network_id, :company_id, :objectid, :object_version
7
7
  attr_accessible :creation_time, :creator_id, :name, :number, :published_name, :transport_mode_name
8
8
  attr_accessible :registration_number, :comment, :mobility_restricted_suitability, :int_user_needs
9
+ attr_accessible :flexible_service
9
10
 
10
11
  belongs_to :company
11
12
  belongs_to :network
12
13
  has_many :routes, :dependent => :destroy
13
14
  has_many :journey_patterns, :through => :routes
15
+ has_many :vehicle_journeys, :through => :journey_patterns
14
16
 
15
17
  has_and_belongs_to_many :group_of_lines, :class_name => 'Chouette::GroupOfLine', :order => 'group_of_lines.name'
16
18
  attr_accessible :group_of_lines, :group_of_line_ids, :group_of_line_tokens
@@ -15,13 +15,14 @@ class Chouette::StopArea < Chouette::TridentActiveRecord
15
15
 
16
16
  attr_accessor :stop_area_type
17
17
  attr_accessor :children_ids
18
+ attr_writer :coordinates
18
19
 
19
20
  attr_accessible :routing_stop_ids, :routing_line_ids, :children_ids, :stop_area_type, :parent_id, :objectid
20
21
  attr_accessible :object_version, :creation_time, :creator_id, :name, :comment, :area_type, :registration_number
21
22
  attr_accessible :nearest_topic_name, :fare_code, :longitude, :latitude, :long_lat_type
22
- attr_accessible :country_code, :street_name
23
+ attr_accessible :country_code, :street_name, :zip_code, :city_name
23
24
  attr_accessible :mobility_restricted_suitability, :stairs_availability, :lift_availability, :int_user_needs
24
-
25
+ attr_accessible :coordinates
25
26
 
26
27
  validates_uniqueness_of :registration_number, :allow_nil => true, :allow_blank => true
27
28
  validates_format_of :registration_number, :with => %r{\A[0-9A-Za-z_-]+\Z}, :allow_blank => true
@@ -33,11 +34,40 @@ class Chouette::StopArea < Chouette::TridentActiveRecord
33
34
  validates_numericality_of :latitude, :less_than_or_equal_to => 90, :greater_than_or_equal_to => -90, :allow_nil => true
34
35
  validates_numericality_of :longitude, :less_than_or_equal_to => 180, :greater_than_or_equal_to => -180, :allow_nil => true
35
36
 
37
+ validates_format_of :coordinates, :with => %r{\A *-?(0?[0-9](\.[0-9]*)?|[0-8][0-9](\.[0-9]*)?|90(\.[0]*)?) *\, *-?(0?[0-9]?[0-9](\.[0-9]*)?|1[0-7][0-9](\.[0-9]*)?|180(\.[0]*)?) *\Z}, :allow_nil => true, :allow_blank => true
38
+
36
39
  def self.nullable_attributes
37
- [:registration_number, :street_name, :country_code, :fare_code, :nearest_topic_name, :comment, :long_lat_type]
40
+ [:registration_number, :street_name, :country_code, :fare_code, :nearest_topic_name, :comment, :long_lat_type, :zip_code, :city_name]
38
41
  end
39
42
 
40
43
  after_update :clean_invalid_access_links
44
+
45
+ before_save :coordinates_to_lat_lng
46
+
47
+ def combine_lat_lng
48
+ if self.latitude.nil? || self.longitude.nil?
49
+ ""
50
+ else
51
+ self.latitude.to_s+","+self.longitude.to_s
52
+ end
53
+ end
54
+
55
+ def coordinates
56
+ @coordinates || combine_lat_lng
57
+ end
58
+
59
+ def coordinates_to_lat_lng
60
+ if ! @coordinates.nil?
61
+ if @coordinates.empty?
62
+ self.latitude = nil
63
+ self.longitude = nil
64
+ else
65
+ self.latitude = BigDecimal.new(@coordinates.split(",").first)
66
+ self.longitude = BigDecimal.new(@coordinates.split(",").last)
67
+ end
68
+ @coordinates = nil
69
+ end
70
+ end
41
71
 
42
72
  def children_in_depth
43
73
  return [] if self.children.empty?
@@ -278,4 +308,12 @@ class Chouette::StopArea < Chouette::TridentActiveRecord
278
308
  end
279
309
  end
280
310
 
311
+ def duplicate
312
+ sa = self.deep_clone :except => [:object_version, :parent_id, :registration_number]
313
+ sa.uniq_objectid
314
+ sa.name = I18n.t("activerecord.copy", :name => self.name)
315
+ sa
316
+ end
317
+
318
+
281
319
  end
@@ -1,13 +1,21 @@
1
1
  class Chouette::TimeTable < Chouette::TridentActiveRecord
2
2
  # FIXME http://jira.codehaus.org/browse/JRUBY-6358
3
3
  self.primary_key = "id"
4
-
4
+
5
5
  attr_accessible :objectid, :object_version, :creation_time, :creator_id, :version, :comment
6
6
  attr_accessible :int_day_types,:monday,:tuesday,:wednesday,:thursday,:friday,:saturday,:sunday
7
7
  attr_accessible :start_date, :end_date
8
- attr_accessible :school_holliday,:public_holliday,:market_day
9
8
  attr_accessor :monday,:tuesday,:wednesday,:thursday,:friday,:saturday,:sunday
10
- attr_accessor :school_holliday,:public_holliday,:market_day
9
+
10
+ acts_as_taggable
11
+ attr_accessible :tag_list, :tag_search
12
+ attr_accessor :tag_search
13
+
14
+ def self.ransackable_attributes auth_object = nil
15
+ (column_names + ['tag_search']) + _ransackers.keys
16
+ end
17
+
18
+ has_and_belongs_to_many :vehicle_journeys, :class_name => 'Chouette::VehicleJourney'
11
19
 
12
20
  has_many :dates, inverse_of: :time_table, :validate => :true, :class_name => "Chouette::TimeTableDate", :order => :date, :dependent => :destroy, :after_add => :shortcuts_update, :after_remove => :shortcuts_update
13
21
  has_many :periods, inverse_of: :time_table, :validate => :true, :class_name => "Chouette::TimeTablePeriod", :order => :period_start, :dependent => :destroy, :after_add => :shortcuts_update, :after_remove => :shortcuts_update
@@ -25,10 +33,10 @@ class Chouette::TimeTable < Chouette::TridentActiveRecord
25
33
  validates_associated :periods
26
34
 
27
35
  def self.start_validity_period
28
- [Chouette::TimeTable.minimum(:start_date)].compact.min
36
+ [Chouette::TimeTable.minimum(:start_date)].compact.min
29
37
  end
30
38
  def self.end_validity_period
31
- [Chouette::TimeTable.maximum(:end_date)].compact.max
39
+ [Chouette::TimeTable.maximum(:end_date)].compact.max
32
40
  end
33
41
 
34
42
  def shortcuts_update(date=nil)
@@ -40,7 +48,7 @@ class Chouette::TimeTable < Chouette::TridentActiveRecord
40
48
  else
41
49
  self.start_date=dates_array.min
42
50
  self.end_date=dates_array.max
43
- end
51
+ end
44
52
  #else
45
53
  # if dates_array.empty?
46
54
  # update_attributes :start_date => nil, :end_date => nil
@@ -88,17 +96,26 @@ class Chouette::TimeTable < Chouette::TridentActiveRecord
88
96
  end
89
97
 
90
98
  def include_in_dates?(day)
91
- self.dates.any?{ |d| d.date === day }
99
+ self.dates.any?{ |d| d.date === day && d.in_out == true }
100
+ end
101
+
102
+ def excluded_date?(day)
103
+ self.dates.any?{ |d| d.date === day && d.in_out == false }
92
104
  end
93
105
 
94
106
  def include_in_periods?(day)
95
- self.periods.any?{ |period| period.period_start <= day && day <= period.period_end && valid_days.include?(day.cwday) }
107
+ self.periods.any?{ |period| period.period_start <= day &&
108
+ day <= period.period_end &&
109
+ valid_days.include?(day.cwday) &&
110
+ ! excluded_date?(day) }
96
111
  end
97
112
 
98
113
  def include_in_overlap_dates?(day)
114
+ return false if self.excluded_date?(day)
115
+
99
116
  counter = self.dates.select{ |d| d.date === day}.size + self.periods.select{ |period| period.period_start <= day && day <= period.period_end && valid_days.include?(day.cwday) }.size
100
- counter <= 1 ? false : true
101
- end
117
+ counter <= 1 ? false : true
118
+ end
102
119
 
103
120
  def periods_max_date
104
121
  return nil if self.periods.empty?
@@ -107,11 +124,12 @@ class Chouette::TimeTable < Chouette::TridentActiveRecord
107
124
  max_end = self.periods.map(&:period_end).compact.max
108
125
  result = nil
109
126
 
110
- if max_end
111
- max_end.downto(max_end-7) do |date|
112
- result = date if result.nil? &&
113
- self.valid_days.include?(date.cwday) &&
114
- ( date >= min_start)
127
+ if max_end && min_start
128
+ max_end.downto( min_start) do |date|
129
+ if self.valid_days.include?(date.cwday) && !self.excluded_date?(date)
130
+ result = date
131
+ break
132
+ end
115
133
  end
116
134
  end
117
135
  result
@@ -123,24 +141,25 @@ class Chouette::TimeTable < Chouette::TridentActiveRecord
123
141
  max_end = self.periods.map(&:period_end).compact.max
124
142
  result = nil
125
143
 
126
- if min_start
127
- min_start.upto(min_start+7) do |date|
128
- result = date if result.nil? &&
129
- self.valid_days.include?(date.cwday) &&
130
- ( date <= max_end)
144
+ if max_end && min_start
145
+ min_start.upto(max_end) do |date|
146
+ if self.valid_days.include?(date.cwday) && !self.excluded_date?(date)
147
+ result = date
148
+ break
149
+ end
131
150
  end
132
151
  end
133
152
  result
134
153
  end
135
154
  def bounding_dates
136
- bounding_min = self.dates.map(&:date).compact.min
137
- bounding_max = self.dates.map(&:date).compact.max
155
+ bounding_min = self.dates.select{|d| d.in_out}.map(&:date).compact.min
156
+ bounding_max = self.dates.select{|d| d.in_out}.map(&:date).compact.max
138
157
 
139
158
  unless self.periods.empty?
140
159
  bounding_min = periods_min_date if periods_min_date &&
141
160
  (bounding_min.nil? || (periods_min_date < bounding_min))
142
-
143
- bounding_max = periods_max_date if periods_max_date &&
161
+
162
+ bounding_max = periods_max_date if periods_max_date &&
144
163
  (bounding_max.nil? || (bounding_max < periods_max_date))
145
164
  end
146
165
 
@@ -151,6 +170,11 @@ class Chouette::TimeTable < Chouette::TridentActiveRecord
151
170
  int_day_types & flag == flag
152
171
  end
153
172
 
173
+ def self.day_by_mask(int_day_types,flag)
174
+ int_day_types & flag == flag
175
+ end
176
+
177
+
154
178
  def valid_days
155
179
  # Build an array with day of calendar week (1-7, Monday is 1).
156
180
  [].tap do |valid_days|
@@ -164,6 +188,19 @@ class Chouette::TimeTable < Chouette::TridentActiveRecord
164
188
  end
165
189
  end
166
190
 
191
+ def self.valid_days(int_day_types)
192
+ # Build an array with day of calendar week (1-7, Monday is 1).
193
+ [].tap do |valid_days|
194
+ valid_days << 1 if day_by_mask(int_day_types,4)
195
+ valid_days << 2 if day_by_mask(int_day_types,8)
196
+ valid_days << 3 if day_by_mask(int_day_types,16)
197
+ valid_days << 4 if day_by_mask(int_day_types,32)
198
+ valid_days << 5 if day_by_mask(int_day_types,64)
199
+ valid_days << 6 if day_by_mask(int_day_types,128)
200
+ valid_days << 7 if day_by_mask(int_day_types,256)
201
+ end
202
+ end
203
+
167
204
  def monday
168
205
  day_by_mask(4)
169
206
  end
@@ -185,25 +222,16 @@ class Chouette::TimeTable < Chouette::TridentActiveRecord
185
222
  def sunday
186
223
  day_by_mask(256)
187
224
  end
188
- def school_holliday
189
- day_by_mask(512)
190
- end
191
- def public_holliday
192
- day_by_mask(1024)
193
- end
194
- def market_day
195
- day_by_mask(2048)
196
- end
197
-
225
+
198
226
  def set_day(day,flag)
199
- if (day == '1')
227
+ if day == '1' || day == true
200
228
  self.int_day_types |= flag
201
229
  else
202
230
  self.int_day_types &= ~flag
203
231
  end
204
232
  shortcuts_update
205
233
  end
206
-
234
+
207
235
  def monday=(day)
208
236
  set_day(day,4)
209
237
  end
@@ -225,15 +253,293 @@ class Chouette::TimeTable < Chouette::TridentActiveRecord
225
253
  def sunday=(day)
226
254
  set_day(day,256)
227
255
  end
228
- def school_holliday=(day)
229
- set_day(day,512)
230
- end
231
- def public_holliday=(day)
232
- set_day(day,1024)
233
- end
234
- def market_day=(day)
235
- set_day(day,2048)
236
- end
256
+
257
+ def effective_days_of_period(period,valid_days=self.valid_days)
258
+ days = []
259
+ period.period_start.upto(period.period_end) do |date|
260
+ if valid_days.include?(date.cwday) && !self.excluded_date?(date)
261
+ days << date
262
+ end
263
+ end
264
+ days
265
+ end
266
+
267
+ def effective_days(valid_days=self.valid_days)
268
+ days=self.effective_days_of_periods(valid_days)
269
+ self.dates.each do |d|
270
+ days |= [d.date] if d.in_out
271
+ end
272
+ days.sort
273
+ end
274
+
275
+ def effective_days_of_periods(valid_days=self.valid_days)
276
+ days = []
277
+ self.periods.each { |p| days |= self.effective_days_of_period(p,valid_days)}
278
+ days.sort
279
+ end
280
+
281
+ def clone_periods
282
+ periods = []
283
+ self.periods.each { |p| periods << p.copy}
284
+ periods
285
+ end
286
+
287
+ def included_days
288
+ days = []
289
+ self.dates.each do |d|
290
+ days |= [d.date] if d.in_out
291
+ end
292
+ days.sort
293
+ end
294
+
295
+ def excluded_days
296
+ days = []
297
+ self.dates.each do |d|
298
+ days |= [d.date] unless d.in_out
299
+ end
300
+ days.sort
301
+ end
302
+
303
+
304
+ # produce a copy of periods without anyone overlapping or including another
305
+ def optimize_periods
306
+ periods = self.clone_periods
307
+ optimized = []
308
+ i=0
309
+ while i < periods.length
310
+ p1 = periods[i]
311
+ optimized << p1
312
+ j= i+1
313
+ while j < periods.length
314
+ p2 = periods[j]
315
+ if p1.contains? p2
316
+ periods.delete p2
317
+ elsif p1.overlap? p2
318
+ p1.period_start = [p1.period_start,p2.period_start].min
319
+ p1.period_end = [p1.period_end,p2.period_end].max
320
+ periods.delete p2
321
+ else
322
+ j += 1
323
+ end
324
+ end
325
+ i+= 1
326
+ end
327
+ optimized.sort { |a,b| a.period_start <=> b.period_start}
328
+ end
329
+
330
+ # add a peculiar day or switch it from excluded to included
331
+ def add_included_day(d)
332
+ if self.excluded_date?(d)
333
+ self.dates.each do |date|
334
+ if date.date === d
335
+ date.in_out = true
336
+ end
337
+ end
338
+ elsif !self.include_in_dates?(d)
339
+ self.dates << Chouette::TimeTableDate.new(:date => d, :in_out => true)
340
+ end
341
+ end
342
+
343
+ # merge effective days from another timetable
344
+ def merge!(another_tt)
345
+ transaction do
346
+ # if one tt has no period, just merge lists
347
+ if self.periods.empty? || another_tt.periods.empty?
348
+ if !another_tt.periods.empty?
349
+ # copy periods
350
+ self.periods = another_tt.clone_periods
351
+ # set valid_days
352
+ self.int_day_types = another_tt.int_day_types
353
+ end
354
+ # merge dates
355
+ self.dates ||= []
356
+ another_tt.included_days.each do |d|
357
+ add_included_day d
358
+ end
359
+ else
360
+ # check if periods can be kept
361
+ common_day_types = self.int_day_types & another_tt.int_day_types & 508
362
+ # if common day types : merge periods
363
+ if common_day_types != 0
364
+ periods = self.optimize_periods
365
+ another_periods = another_tt.optimize_periods
366
+ # add not common days of both periods as peculiar days
367
+ self.effective_days_of_periods(self.class.valid_days(self.int_day_types ^ common_day_types)).each do |d|
368
+ self.dates |= [Chouette::TimeTableDate.new(:date => d, :in_out => true)]
369
+ end
370
+ another_tt.effective_days_of_periods(self.class.valid_days(another_tt.int_day_types ^ common_day_types)).each do |d|
371
+ add_included_day d
372
+ end
373
+ # merge periods
374
+ self.periods = periods | another_periods
375
+ self.int_day_types = common_day_types
376
+ self.periods = self.optimize_periods
377
+ else
378
+ # convert all period in days
379
+ self.effective_days_of_periods.each do |d|
380
+ self.dates << Chouette::TimeTableDate.new(:date => d, :in_out => true) unless self.include_in_dates?(d)
381
+ end
382
+ another_tt.effective_days_of_periods.each do |d|
383
+ add_included_day d
384
+ end
385
+ end
386
+ end
387
+ # if remained excluded dates are valid in other tt , remove it from result
388
+ self.dates.each do |date|
389
+ date.in_out = true if date.in_out == false && another_tt.include_day?(date.date)
390
+ end
391
+
392
+ # if peculiar dates are valid in new periods, remove them
393
+ if !self.periods.empty?
394
+ days_in_period = self.effective_days_of_periods
395
+ dates = []
396
+ self.dates.each do |date|
397
+ dates << date unless date.in_out && days_in_period.include?(date.date)
398
+ end
399
+ self.dates = dates
400
+ end
401
+ self.dates.sort! { |a,b| a.date <=> b.date}
402
+ self.save!
403
+ end
404
+ end
405
+
406
+ # remove dates form tt which aren't in another_tt
407
+ def intersect!(another_tt)
408
+ transaction do
409
+ common_day_types = self.int_day_types & another_tt.int_day_types & 508
410
+ # if both tt have periods with common day_types, intersect them
411
+ if !self.periods.empty? && !another_tt.periods.empty?
412
+ if common_day_types != 0
413
+ periods = []
414
+ days = []
415
+ valid_days = self.class.valid_days(common_day_types)
416
+ self.optimize_periods.each do |p1|
417
+ another_tt.optimize_periods.each do |p2|
418
+ if p1.overlap? p2
419
+ # create period for intersection
420
+ pi = Chouette::TimeTablePeriod.new(:period_start => [p1.period_start,p2.period_start].max,
421
+ :period_end => [p1.period_end,p2.period_end].min)
422
+ if !self.effective_days_of_period(pi,valid_days).empty? && !another_tt.effective_days_of_period(pi,valid_days).empty?
423
+ if pi.period_start == pi.period_end
424
+ days << pi.period_start
425
+ else
426
+ periods << pi
427
+ end
428
+ end
429
+ end
430
+ end
431
+ end
432
+ # intersect dates
433
+ days |= another_tt.intersects(self.included_days) & self.intersects(another_tt.included_days)
434
+ excluded_days = self.excluded_days | another_tt.excluded_days
435
+ self.dates.clear
436
+ days.each {|day| self.dates << Chouette::TimeTableDate.new( :date =>day, :in_out => true)}
437
+ self.periods = periods
438
+ common_day_types = 0 if periods.empty?
439
+ self.int_day_types = common_day_types
440
+ days_of_periods = self.effective_days_of_periods
441
+ excluded_days.each do |day|
442
+ self.dates << Chouette::TimeTableDate.new( :date =>day, :in_out => false) if days_of_periods.contains?(day)
443
+ end
444
+ end
445
+ else
446
+ # transform tt as effective dates and get common ones
447
+ days = another_tt.intersects(self.effective_days) & self.intersects(another_tt.effective_days)
448
+ self.dates.clear
449
+ days.each {|d| self.dates << Chouette::TimeTableDate.new( :date =>d, :in_out => true)}
450
+ self.periods.clear
451
+ self.int_day_types = 0
452
+ end
453
+ self.dates.sort! { |a,b| a.date <=> b.date}
454
+ self.save!
455
+ end
456
+ end
457
+
458
+
459
+ def disjoin!(another_tt)
460
+ transaction do
461
+ common_day_types = self.int_day_types & another_tt.int_day_types & 508
462
+ # if both tt have periods with common day_types, reduce first ones to exclude second
463
+ if !self.periods.empty? && !another_tt.periods.empty?
464
+ if common_day_types != 0
465
+ periods = []
466
+ days = []
467
+ valid_days = self.class.valid_days(self.int_day_types)
468
+ remain_days = self.class.valid_days(self.int_day_types & ~another_tt.int_day_types)
469
+ self.optimize_periods.each do |p1|
470
+ deleted = false
471
+ another_tt.optimize_periods.each do |p2|
472
+ if p2.contains? p1
473
+ # p1 is removed, keep remaining dates as included
474
+ days |= self.effective_days_of_period(p1,remain_days)
475
+ deleted = true
476
+ break
477
+ elsif p1.contains? p2
478
+ # p1 is broken in 2; keep remaining dates covered by p2 as included days
479
+ days |= self.effective_days_of_period(p2,remain_days)
480
+ if p1.period_start != p2.period_start
481
+ pi = Chouette::TimeTablePeriod.new(:period_start => p1.period_start,
482
+ :period_end => p2.period_start - 1)
483
+ if !self.effective_days_of_period(pi,valid_days).empty?
484
+ if pi.period_start == pi.period_end
485
+ days << pi.period_start
486
+ else
487
+ periods << pi
488
+ end
489
+ end
490
+ end
491
+ if p1.period_end != p2.period_end
492
+ p1.period_start = p2.period_end + 1
493
+ else
494
+ deleted = true
495
+ break
496
+ end
497
+ elsif p1.overlap? p2
498
+ if p2.period_start <= p1.period_start
499
+ p2.period_start = p1.period_start
500
+ p1.period_start = p2.period_end + 1
501
+ else
502
+ p2.period_end = p1.period_end
503
+ p1.period_end = p2.period_start - 1
504
+ end
505
+ days |= self.effective_days_of_period(p2,remain_days)
506
+ end
507
+ end
508
+ unless deleted || self.effective_days_of_period(p1,valid_days).empty?
509
+ if p1.period_start != p1.period_end
510
+ periods << p1
511
+ else
512
+ days << p1.period_start
513
+ end
514
+ end
515
+ end
516
+ # rebuild periods and dates
517
+ self.periods = periods
518
+ self.int_day_types = 0 if periods.empty?
519
+ days.each { |d| self.dates |= [Chouette::TimeTableDate.new( :date =>d, :in_out => true)] }
520
+ end
521
+ else
522
+ # otherwise remove or exclude dates and delete empty periods
523
+ # first remove peculiar dates
524
+ days = self.intersects(another_tt.effective_days)
525
+ self.dates -= self.dates.select{|date| days.include?(date.date)}
526
+ # then add excluded dates
527
+ self.intersects(another_tt.effective_days).each do |d|
528
+ self.dates |= [Chouette::TimeTableDate.new( :date =>d, :in_out => false)]
529
+ end
530
+ end
531
+ self.dates.sort! { |a,b| a.date <=> b.date}
532
+ self.periods.sort! { |a,b| a.period_start <=> b.period_start}
533
+ self.save!
534
+ end
535
+ end
536
+
537
+ def duplicate
538
+ tt = self.deep_clone :include => [:periods, :dates], :except => :object_version
539
+ tt.uniq_objectid
540
+ tt.comment = I18n.t("activerecord.copy", :name => self.comment)
541
+ tt
542
+ end
237
543
 
238
544
  end
239
545
 
@@ -2,18 +2,18 @@ class Chouette::TimeTableDate < Chouette::ActiveRecord
2
2
  self.primary_key = "id"
3
3
  belongs_to :time_table, inverse_of: :dates
4
4
  acts_as_list :scope => 'time_table_id = #{time_table_id}',:top_of_list => 0
5
-
5
+
6
6
  validates_presence_of :date
7
7
  validates_uniqueness_of :date, :scope => :time_table_id
8
8
 
9
- attr_accessible :date, :position, :time_table_id, :time_table
10
-
9
+ attr_accessible :date, :position, :time_table_id, :time_table, :in_out
10
+
11
11
  after_update :update_parent
12
-
12
+
13
13
  def self.model_name
14
14
  ActiveModel::Name.new Chouette::TimeTableDate, Chouette, "TimeTableDate"
15
15
  end
16
-
16
+
17
17
  def update_parent
18
18
  time_table.shortcuts_update
19
19
  end