ninoxe 1.0.3 → 1.1.0

Sign up to get free protection for your applications and to get access to all the features.
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