aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--app/controllers/concerns/ransack_date_filter.rb28
-rw-r--r--app/controllers/referential_vehicle_journeys_controller.rb7
-rw-r--r--app/models/chouette/purchase_window.rb1
-rw-r--r--app/models/chouette/time_table.rb1
-rw-r--r--app/models/chouette/vehicle_journey.rb21
-rw-r--r--app/views/referential_vehicle_journeys/_filters.html.slim14
-rw-r--r--config/locales/vehicle_journeys.en.yml4
-rw-r--r--config/locales/vehicle_journeys.fr.yml4
-rw-r--r--spec/factories/chouette_time_table.rb17
-rw-r--r--spec/models/chouette/vehicle_journey_spec.rb122
10 files changed, 201 insertions, 18 deletions
diff --git a/app/controllers/concerns/ransack_date_filter.rb b/app/controllers/concerns/ransack_date_filter.rb
index 0fbde91d3..055c01130 100644
--- a/app/controllers/concerns/ransack_date_filter.rb
+++ b/app/controllers/concerns/ransack_date_filter.rb
@@ -3,7 +3,15 @@ module RansackDateFilter
included do
- def set_date_time_params(param_name, klass)
+ def begin_range_var prefix
+ "@#{[prefix, "begin_range"].compact.join('_')}"
+ end
+
+ def end_range_var prefix
+ "@#{[prefix, "end_range"].compact.join('_')}"
+ end
+
+ def set_date_time_params(param_name, klass, prefix: nil)
start_date = []
end_date = []
@@ -16,26 +24,28 @@ module RansackDateFilter
params[:q].delete([param_name])
if klass == DateTime
- @begin_range = klass.new(*start_date,0,0,0) rescue nil
- @end_range = klass.new(*end_date,23,59,59) rescue nil
+ instance_variable_set begin_range_var(prefix), klass.new(*start_date,0,0,0) rescue nil
+ instance_variable_set end_range_var(prefix), klass.new(*end_date,23,59,59) rescue nil
else
- @begin_range = klass.new(*start_date) rescue nil
- @end_range = klass.new(*end_date) rescue nil
+ instance_variable_set begin_range_var(prefix), klass.new(*start_date) rescue nil
+ instance_variable_set end_range_var(prefix), klass.new(*end_date) rescue nil
end
end
end
# Fake ransack filter
def ransack_period_range **options
- return options[:scope] unless !!@begin_range && !!@end_range
+ prefix = options[:prefix]
+ return options[:scope] unless !!instance_variable_get(begin_range_var(prefix)) && !!instance_variable_get(end_range_var(prefix))
- if @begin_range > @end_range
+ scope = options[:scope]
+ if instance_variable_get(begin_range_var(prefix)) > instance_variable_get(end_range_var(prefix))
flash.now[:error] = options[:error_message]
else
- scope = options[:scope].send options[:query], @begin_range..@end_range
+ scope = scope.send options[:query], instance_variable_get(begin_range_var(prefix))..instance_variable_get(end_range_var(prefix))
end
scope
end
end
-end \ No newline at end of file
+end
diff --git a/app/controllers/referential_vehicle_journeys_controller.rb b/app/controllers/referential_vehicle_journeys_controller.rb
index 89b3703a0..b133602bc 100644
--- a/app/controllers/referential_vehicle_journeys_controller.rb
+++ b/app/controllers/referential_vehicle_journeys_controller.rb
@@ -3,6 +3,11 @@
#
class ReferentialVehicleJourneysController < ChouetteController
include ReferentialSupport
+ include RansackDateFilter
+
+ before_action only: [:index] { set_date_time_params("purchase_window", Date, prefix: :purchase_window) }
+ before_action only: [:index] { set_date_time_params("time_table", Date, prefix: :time_table) }
+
defaults :resource_class => Chouette::VehicleJourney, collection_name: :vehicle_journeys
requires_feature :referential_vehicle_journeys
@@ -12,6 +17,8 @@ class ReferentialVehicleJourneysController < ChouetteController
def collection
@q ||= end_of_association_chain
@q = @q.with_stop_area_ids(params[:q][:stop_area_ids]) if params[:q] && params[:q][:stop_area_ids]
+ @q = ransack_period_range(scope: @q, error_message: t('vehicle_journeys.errors.purchase_window'), query: :in_purchase_window, prefix: :purchase_window)
+ @q = ransack_period_range(scope: @q, error_message: t('vehicle_journeys.errors.time_table'), query: :with_matching_timetable, prefix: :time_table)
@q = @q.ransack(params[:q])
@vehicle_journeys ||= @q.result.order(:published_journey_name).includes(:vehicle_journey_at_stops).paginate page: params[:page], per_page: params[:per_page] || 10
@all_companies = Chouette::Company.where("id IN (#{@referential.vehicle_journeys.select(:company_id).to_sql})").distinct
diff --git a/app/models/chouette/purchase_window.rb b/app/models/chouette/purchase_window.rb
index 22bcc1de1..334493015 100644
--- a/app/models/chouette/purchase_window.rb
+++ b/app/models/chouette/purchase_window.rb
@@ -18,6 +18,7 @@ module Chouette
validates_presence_of :name, :referential
scope :contains_date, ->(date) { where('date ? <@ any (date_ranges)', date) }
+ scope :overlap_dates, ->(date_range) { where('daterange(?, ?) && any (date_ranges)', date_range.first, date_range.last + 1.day) }
def self.ransackable_scopes(auth_object = nil)
[:contains_date]
diff --git a/app/models/chouette/time_table.rb b/app/models/chouette/time_table.rb
index db97dd2fa..07bf35444 100644
--- a/app/models/chouette/time_table.rb
+++ b/app/models/chouette/time_table.rb
@@ -311,7 +311,6 @@ module Chouette
bounding_max = periods_max_date if periods_max_date &&
(bounding_max.nil? || (bounding_max < periods_max_date))
end
-
[bounding_min, bounding_max].compact
end
diff --git a/app/models/chouette/vehicle_journey.rb b/app/models/chouette/vehicle_journey.rb
index d94b69271..1756a7098 100644
--- a/app/models/chouette/vehicle_journey.rb
+++ b/app/models/chouette/vehicle_journey.rb
@@ -52,8 +52,29 @@ module Chouette
end
}
+ scope :in_purchase_window, ->(range){
+ purchase_windows = Chouette::PurchaseWindow.overlap_dates(range)
+ sql = purchase_windows.joins(:vehicle_journeys).select('vehicle_journeys.id').uniq.to_sql
+ where("id IN (#{sql})")
+ }
+
# We need this for the ransack object in the filters
ransacker :stop_area_ids
+ ransacker :purchase_window_date_gt
+
+ # returns VehicleJourneys with at least 1 day in their time_tables
+ # included in the given range
+ def self.with_matching_timetable date_range
+ out = []
+ time_tables = Chouette::TimeTable.where(id: self.joins("INNER JOIN time_tables_vehicle_journeys ON vehicle_journeys.id = time_tables_vehicle_journeys.vehicle_journey_id").pluck('time_tables_vehicle_journeys.time_table_id')).overlapping(date_range)
+ time_tables = time_tables.select do |time_table|
+ range = date_range
+ range = date_range & (time_table.start_date-1.day..time_table.end_date+1.day) || [] if time_table.start_date.present? && time_table.end_date.present?
+ range.any?{|d| time_table.include_day?(d) }
+ end
+ out += time_tables.map{|t| t.vehicle_journey_ids}.flatten
+ where(id: out)
+ end
# TODO: Remove this validator
# We've eliminated this validation because it prevented vehicle journeys
diff --git a/app/views/referential_vehicle_journeys/_filters.html.slim b/app/views/referential_vehicle_journeys/_filters.html.slim
index 1301d3dab..bfb5b77dd 100644
--- a/app/views/referential_vehicle_journeys/_filters.html.slim
+++ b/app/views/referential_vehicle_journeys/_filters.html.slim
@@ -21,10 +21,22 @@
= f.input :published_journey_name_gteq, label: false, wrapper_html: { class: 'w45'}
.form-group.w10.to= I18n.t('vehicle_journeys.form.to')
= f.input :published_journey_name_lteq, label: false, wrapper_html: { class: 'w45'}
-
.form-group.togglable
= f.label Chouette::StopArea.model_name.human.pluralize, required: false, class: 'control-label'
= f.input :stop_area_ids, collection: @all_stop_areas.select(:id, :name).order(name: :asc), checked: params[:q] && params[:q][:stop_area_ids], as: :check_boxes, label: false, label_method: lambda{|l| ("<span>" + l.name + "</span>").html_safe}, required: false, wrapper_html: { class: 'checkbox_list'}, multiple: true
+ .form-group.togglable
+ = f.label Chouette::VehicleJourney.human_attribute_name(:purchase_window), class: 'control-label'
+ .filter_menu
+ = f.simple_fields_for :purchase_window do |p|
+ = p.input :start_date, as: :date, label: t('simple_form.from'), wrapper_html: { class: 'date smart_date filter_menu-item' }, default: @purchase_window_begin_range, include_blank: @purchase_window_begin_range ? false : true
+ = p.input :end_date, as: :date, label: t('simple_form.to'), wrapper_html: { class: 'date smart_date filter_menu-item' }, default: @purchase_window_end_range, include_blank: @purchase_window_end_range ? false : true
+ .form-group.togglable
+ = f.label Chouette::TimeTable.model_name.human, class: 'control-label'
+ .filter_menu
+ = f.simple_fields_for :time_table do |p|
+ = p.input :start_date, as: :date, label: t('simple_form.from'), wrapper_html: { class: 'date smart_date filter_menu-item' }, default: @time_table_begin_range, include_blank: @time_table_begin_range ? false : true
+ = p.input :end_date, as: :date, label: t('simple_form.to'), wrapper_html: { class: 'date smart_date filter_menu-item' }, default: @time_table_end_range, include_blank: @time_table_end_range ? false : true
+
.actions
= link_to 'Effacer', referential_vehicle_journeys_path(@referential), class: 'btn btn-link'
diff --git a/config/locales/vehicle_journeys.en.yml b/config/locales/vehicle_journeys.en.yml
index 1c1f6c6bd..abb1da530 100644
--- a/config/locales/vehicle_journeys.en.yml
+++ b/config/locales/vehicle_journeys.en.yml
@@ -67,6 +67,9 @@ en:
time_range_filter: "Filter"
sidebar:
timeless: "Timeless vehicle journeys"
+ errors:
+ purchase_window: Invalid purchase window
+ time_table: Invalid dates
activerecord:
models:
vehicle_journey:
@@ -108,6 +111,7 @@ en:
footnote_ids: "Footnotes"
departure_time: "Departure"
arrival_time: "Arrival"
+ purchase_window: "Purchase availability"
errors:
models:
vehicle_journey:
diff --git a/config/locales/vehicle_journeys.fr.yml b/config/locales/vehicle_journeys.fr.yml
index 5749f8f10..ca8475812 100644
--- a/config/locales/vehicle_journeys.fr.yml
+++ b/config/locales/vehicle_journeys.fr.yml
@@ -67,6 +67,9 @@ fr:
time_range_filter: "Filtrer"
sidebar:
timeless: "Courses sans horaire"
+ errors:
+ purchase_window: Calendrier commercial invalide
+ time_table: Dates d'application invalides
activerecord:
models:
vehicle_journey:
@@ -108,6 +111,7 @@ fr:
footnote_ids: "Notes de bas de page"
departure_time: "Départ"
arrival_time: "Arrivée"
+ purchase_window: "Disponibilité commerciale"
errors:
models:
vehicle_journey:
diff --git a/spec/factories/chouette_time_table.rb b/spec/factories/chouette_time_table.rb
index 81a08ca2a..af48e1b42 100644
--- a/spec/factories/chouette_time_table.rb
+++ b/spec/factories/chouette_time_table.rb
@@ -11,18 +11,21 @@ FactoryGirl.define do
end
after(:create) do |time_table, evaluator|
-
- 0.upto(4) do |i|
- time_table.dates << create(:time_table_date, :time_table => time_table, :date => i.days.since.to_date, :in_out => true)
+ unless time_table.dates.any?
+ evaluator.dates_count.times do |i|
+ time_table.dates << create(:time_table_date, :time_table => time_table, :date => i.days.since.to_date, :in_out => true)
+ end
end
start_date = Date.today
end_date = start_date + 10
- 0.upto(4) do |i|
- time_table.periods << create(:time_table_period, :time_table => time_table, :period_start => start_date, :period_end => end_date)
- start_date = start_date + 20
- end_date = start_date + 10
+ unless time_table.periods.any?
+ evaluator.periods_count.times do |i|
+ time_table.periods << create(:time_table_period, :time_table => time_table, :period_start => start_date, :period_end => end_date)
+ start_date = start_date + 20
+ end_date = start_date + 10
+ end
end
time_table.save_shortcuts
time_table.update_checksum!
diff --git a/spec/models/chouette/vehicle_journey_spec.rb b/spec/models/chouette/vehicle_journey_spec.rb
index 21bbc1ba8..70661bcc5 100644
--- a/spec/models/chouette/vehicle_journey_spec.rb
+++ b/spec/models/chouette/vehicle_journey_spec.rb
@@ -24,6 +24,128 @@ describe Chouette::VehicleJourney, :type => :model do
it_behaves_like 'checksum support', :vehicle_journey
end
+ describe '#in_purchase_window' do
+ let(:start_date){2.month.ago.to_date}
+ let(:end_date){1.month.ago.to_date}
+
+ subject{Chouette::VehicleJourney.in_purchase_window start_date..end_date}
+
+ let!(:without_purchase_window){ create :vehicle_journey }
+ let!(:without_matching_purchase_window){
+ pw = create :purchase_window, referential: Referential.first, date_ranges: [(end_date+1.day..end_date+2.days)]
+ pw2 = create :purchase_window, referential: Referential.first, date_ranges: [(end_date+10.day..end_date+20.days)]
+ create :vehicle_journey, purchase_windows: [pw, pw2]
+ }
+ let!(:included_purchase_window){
+ pw = create :purchase_window, referential: Referential.first, date_ranges: [(start_date..end_date)]
+ pw2 = create :purchase_window, referential: Referential.first
+ create :vehicle_journey, purchase_windows: [pw, pw2]
+ }
+ let!(:overlapping_purchase_window){
+ pw = create :purchase_window, referential: Referential.first, date_ranges: [(end_date..end_date+1.day)]
+ pw2 = create :purchase_window, referential: Referential.first
+ create :vehicle_journey, purchase_windows: [pw, pw2]
+ }
+
+
+ it "should not include VJ with no purchase window" do
+ expect(subject).to_not include without_purchase_window
+ end
+
+ it "should not include VJ with no matching purchase window" do
+ expect(subject).to_not include without_matching_purchase_window
+ end
+
+ it "should include VJ with included purchase window" do
+ expect(subject).to include included_purchase_window
+ end
+
+ it "should include VJ with overlapping purchase_window purchase window" do
+ expect(subject).to include overlapping_purchase_window
+ end
+ end
+
+ describe '#in_time_table' do
+ let(:start_date){2.month.ago.to_date}
+ let(:end_date){1.month.ago.to_date}
+
+ subject{Chouette::VehicleJourney.with_matching_timetable start_date..end_date}
+
+ context "without time table" do
+ let!(:vehicle_journey){ create :vehicle_journey }
+ it "should not include VJ " do
+ expect(subject).to_not include vehicle_journey
+ end
+ end
+
+ context "without a time table matching on a regular day" do
+ let(:timetable){
+ period = create :time_table_period, period_start: start_date-2.day, period_end: start_date
+ create :time_table, periods: [period], dates_count: 0
+ }
+ let!(:vehicle_journey){ create :vehicle_journey, time_tables: [timetable] }
+ it "should include VJ " do
+ expect(subject).to include vehicle_journey
+ end
+ end
+
+ context "without a time table matching on a regular day" do
+ let(:timetable){
+ period = create :time_table_period, period_start: end_date, period_end: end_date+1.day
+ create :time_table, periods: [period], dates_count: 0
+ }
+ let!(:vehicle_journey){ create :vehicle_journey, time_tables: [timetable] }
+ it "should include VJ " do
+ expect(subject).to include vehicle_journey
+ end
+ end
+
+ context "with a time table with a matching period but not the right day" do
+ let(:start_date){end_date - 1.day}
+ let(:end_date){Time.now.end_of_week.to_date}
+
+ let(:timetable){
+ period = create :time_table_period, period_start: start_date-1.month, period_end: end_date+1.month
+ create :time_table, periods: [period], int_day_types: 4 + 8, dates_count: 0
+ }
+ let!(:vehicle_journey){ create :vehicle_journey, time_tables: [timetable] }
+ it "should not include VJ " do
+ expect(subject).to_not include vehicle_journey
+ end
+ end
+
+ context "with a time table with a matching period but day opted-out" do
+ let(:start_date){end_date - 1.day}
+ let(:end_date){Time.now.end_of_week.to_date}
+
+ let(:timetable){
+ period = create :time_table_period, period_start: start_date-1.month, period_end: start_date-1.day
+ date = create(:time_table_date, :date => start_date, in_out: false)
+ create :time_table, periods: [period], dates: [date]
+ }
+ let!(:vehicle_journey){ create :vehicle_journey, time_tables: [timetable] }
+ it "should not include VJ " do
+ expect(subject).to_not include vehicle_journey
+ end
+ end
+
+ context "with a time table with no matching period but not the right extra day" do
+ let(:start_date){end_date - 1.day}
+ let(:end_date){Time.now.end_of_week.to_date}
+
+ let(:timetable){
+ period = create :time_table_period, period_start: start_date-1.month, period_end: start_date-1.day
+ date = create(:time_table_date, :date => start_date, in_out: true)
+ create :time_table, periods: [period], dates: [date]
+ }
+ let!(:vehicle_journey){ create :vehicle_journey, time_tables: [timetable] }
+ it "should include VJ " do
+ expect(subject).to include vehicle_journey
+ end
+ end
+
+ end
+
describe "vjas_departure_time_must_be_before_next_stop_arrival_time",
skip: "Validation currently commented out because it interferes with day offsets" do