aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorXinhui2017-06-16 17:58:08 +0200
committerXinhui2017-06-16 17:58:08 +0200
commit641b1458236d2718a76ffaf0c04a5998623276bf (patch)
treedb738af25bcdd7b3b400a0f8db5e6350ef373c51
parent6886441ce86bcd720b27cdd089567def5b9d771a (diff)
parent9ef3d205aa091d509455b3607d5ecc74431c6196 (diff)
downloadchouette-core-641b1458236d2718a76ffaf0c04a5998623276bf.tar.bz2
Merge branch 'master' into staging
-rw-r--r--Gemfile6
-rw-r--r--Gemfile.lock21
-rw-r--r--INSTALL.md14
-rw-r--r--app/assets/javascripts/application.js1
-rw-r--r--app/assets/javascripts/cleanup.coffee9
-rw-r--r--app/assets/javascripts/es6_browserified/itineraries/index.js36
-rw-r--r--app/assets/javascripts/es6_browserified/itineraries/reducers/stopPoints.js8
-rw-r--r--app/assets/javascripts/es6_browserified/journey_patterns/components/CreateModal.js150
-rw-r--r--app/assets/javascripts/es6_browserified/journey_patterns/components/EditModal.js3
-rw-r--r--app/assets/javascripts/es6_browserified/journey_patterns/components/JourneyPattern.js2
-rw-r--r--app/assets/javascripts/es6_browserified/time_tables/actions/index.js15
-rw-r--r--app/assets/javascripts/es6_browserified/time_tables/components/ConfirmModal.js4
-rw-r--r--app/assets/javascripts/es6_browserified/time_tables/components/ErrorModal.js39
-rw-r--r--app/assets/javascripts/es6_browserified/time_tables/components/Navigate.js6
-rw-r--r--app/assets/javascripts/es6_browserified/time_tables/components/SaveTimetable.js7
-rw-r--r--app/assets/javascripts/es6_browserified/time_tables/containers/App.js2
-rw-r--r--app/assets/javascripts/es6_browserified/time_tables/containers/ConfirmModal.js5
-rw-r--r--app/assets/javascripts/es6_browserified/time_tables/containers/ErrorModal.js21
-rw-r--r--app/assets/javascripts/es6_browserified/time_tables/containers/SaveTimetable.js12
-rw-r--r--app/assets/javascripts/es6_browserified/time_tables/reducers/metas.js3
-rw-r--r--app/assets/javascripts/es6_browserified/time_tables/reducers/modal.js3
-rw-r--r--app/assets/javascripts/es6_browserified/time_tables/reducers/pagination.js7
-rw-r--r--app/assets/javascripts/es6_browserified/vehicle_journeys/components/Filters.js10
-rw-r--r--app/assets/javascripts/es6_browserified/vehicle_journeys/components/tools/DuplicateVehicleJourney.js101
-rw-r--r--app/assets/javascripts/es6_browserified/vehicle_journeys/components/tools/NotesEditVehicleJourney.js36
-rw-r--r--app/assets/javascripts/es6_browserified/vehicle_journeys/components/tools/select2s/MissionSelect2.js2
-rw-r--r--app/assets/javascripts/es6_browserified/vehicle_journeys/index.js2
-rw-r--r--app/assets/javascripts/es6_browserified/vehicle_journeys/reducers/modal.js6
-rw-r--r--app/assets/javascripts/es6_browserified/vehicle_journeys/reducers/pagination.js1
-rw-r--r--app/assets/javascripts/es6_browserified/vehicle_journeys/reducers/vehicleJourneys.js4
-rw-r--r--app/assets/javascripts/forms.coffee9
-rw-r--r--app/assets/javascripts/main_menu.coffee2
-rw-r--r--app/assets/javascripts/nav_panels.coffee2
-rw-r--r--app/assets/javascripts/routing_constraint_zones.coffee90
-rw-r--r--app/assets/javascripts/select2.coffee4
-rw-r--r--app/assets/javascripts/selectable_table.coffee3
-rw-r--r--app/assets/javascripts/time_table_combinations.coffee3
-rw-r--r--app/assets/stylesheets/components/_tables.sass60
-rw-r--r--app/controllers/autocomplete_calendars_controller.rb2
-rw-r--r--app/controllers/calendars_controller.rb4
-rw-r--r--app/controllers/clean_ups_controller.rb2
-rw-r--r--app/controllers/devise/cas_sessions_controller.rb7
-rw-r--r--app/controllers/referentials_controller.rb7
-rw-r--r--app/controllers/route_stop_points_controller.rb3
-rw-r--r--app/controllers/routing_constraint_zones_controller.rb61
-rw-r--r--app/controllers/users/login_controller.rb0
-rw-r--r--app/controllers/vehicle_journeys_controller.rb24
-rw-r--r--app/helpers/newapplication_helper.rb4
-rw-r--r--app/models/chouette/route.rb4
-rw-r--r--app/models/chouette/time_table.rb25
-rw-r--r--app/models/chouette/time_table_date.rb2
-rw-r--r--app/models/chouette/vehicle_journey.rb31
-rw-r--r--app/models/chouette/vehicle_journey_at_stop.rb3
-rw-r--r--app/models/chouette/vehicle_journey_at_stops_day_offset.rb42
-rw-r--r--app/models/clean_up.rb101
-rw-r--r--app/models/referential.rb26
-rw-r--r--app/models/referential_cloning.rb3
-rw-r--r--app/models/user.rb6
-rw-r--r--app/policies/boiv_policy.rb2
-rw-r--r--app/policies/chain.rb57
-rw-r--r--app/policies/journey_pattern_policy.rb1
-rw-r--r--app/policies/line_policy.rb10
-rw-r--r--app/policies/login_policy.rb13
-rw-r--r--app/policies/referential_policy.rb5
-rw-r--r--app/policies/route_policy.rb9
-rw-r--r--app/policies/routing_constraint_zone_policy.rb9
-rw-r--r--app/policies/time_table_policy.rb11
-rw-r--r--app/views/calendars/_filters.html.slim2
-rw-r--r--app/views/calendars/_form.html.slim4
-rw-r--r--app/views/layouts/application.html.slim6
-rw-r--r--app/views/referential_lines/index.js.slim1
-rw-r--r--app/views/referential_lines/show.html.slim13
-rw-r--r--app/views/referentials/_form.html.slim26
-rw-r--r--app/views/referentials/show.html.slim7
-rw-r--r--app/views/routes/_form.html.slim4
-rw-r--r--app/views/routes/new.html.slim2
-rw-r--r--app/views/routes/show.html.slim6
-rw-r--r--app/views/routing_constraint_zones/_filters.html.slim16
-rw-r--r--app/views/routing_constraint_zones/_form.html.slim36
-rw-r--r--app/views/routing_constraint_zones/edit.html.slim15
-rw-r--r--app/views/routing_constraint_zones/index.html.slim37
-rw-r--r--app/views/routing_constraint_zones/new.html.slim13
-rw-r--r--app/views/routing_constraint_zones/show.html.slim44
-rw-r--r--app/views/time_table_combinations/_form.html.slim12
-rw-r--r--app/views/time_tables/_form.html.slim2
-rw-r--r--app/views/time_tables/index.html.slim2
-rw-r--r--app/views/time_tables/show.html.slim1
-rw-r--r--app/views/vehicle_journeys/index.html.slim3
-rw-r--r--app/views/vehicle_journeys/show.rabl11
-rw-r--r--app/workers/referential_cloning_worker.rb9
-rwxr-xr-xbin/spring14
-rw-r--r--config/initializers/active_record.rb4
-rw-r--r--config/initializers/simple_form_bootstrap.rb4
-rw-r--r--config/locales/actions.en.yml1
-rw-r--r--config/locales/actions.fr.yml1
-rw-r--r--config/locales/devise.en.yml1
-rw-r--r--config/locales/devise.fr.yml1
-rw-r--r--config/locales/enumerize.en.yml7
-rw-r--r--config/locales/enumerize.fr.yml7
-rw-r--r--config/locales/routing_constraint_zones.en.yml7
-rw-r--r--config/locales/routing_constraint_zones.fr.yml13
-rw-r--r--config/locales/time_tables.en.yml1
-rw-r--r--config/locales/time_tables.fr.yml3
-rw-r--r--db/migrate/20170531154114_add_default_to_vehicle_journey_at_stops_arrival_and_departure_day_offset.rb6
-rw-r--r--db/migrate/20170605135126_add_date_type_to_clean_ups.rb5
-rw-r--r--db/migrate/20170607141031_change_begin_date_from_clean_ups.rb9
-rw-r--r--db/migrate/20170607141317_change_end_date_from_clean_ups.rb9
-rw-r--r--db/schema.rb29
-rw-r--r--install/README9
-rwxr-xr-xinstall/deploy-helper.sh102
-rw-r--r--install/sidekiq-stif-boiv.service52
-rwxr-xr-xinstall/stif-boiv-setup.sh84
-rw-r--r--install/stif-boiv.conf38
-rw-r--r--install/template-stif-boiv.sql11
-rw-r--r--lib/af83/schema_cloner.rb151
-rw-r--r--lib/af83/stored_procedures.rb44
-rw-r--r--lib/sql/clone_schema.sql114
-rw-r--r--lib/tasks/install.rake18
-rw-r--r--lib/tasks/referential.rake4
-rw-r--r--spec/controllers/devise/cas_sessions_controller_spec.rb27
-rw-r--r--spec/controllers/imports_controller_spec.rb2
-rw-r--r--spec/controllers/route_stop_points_controller_spec.rb4
-rw-r--r--spec/factories/chouette_time_table.rb1
-rw-r--r--spec/factories/chouette_vehicle_journey.rb9
-rw-r--r--spec/factories/clean_ups.rb13
-rw-r--r--spec/features/calendars_spec.rb10
-rw-r--r--spec/features/lines_spec.rb5
-rw-r--r--spec/features/referentials_permissions_spec.rb54
-rw-r--r--spec/features/referentials_spec.rb6
-rw-r--r--spec/features/routes_spec.rb3
-rw-r--r--spec/features/routing_constraint_zones_spec.rb4
-rw-r--r--spec/features/users/connection_spec.rb0
-rw-r--r--spec/features/workbenches_spec.rb2
-rw-r--r--spec/javascripts/time_table/reducers/pagination_spec.js7
-rw-r--r--spec/javascripts/vehicle_journeys/reducers/vehicle_journeys_spec.js4
-rw-r--r--spec/lib/af83/cloning/clone_schema_spec.rb113
-rw-r--r--spec/lib/af83/stored_procedure_spec.rb20
-rw-r--r--spec/lib/af83/stored_procedures/clone_schema_spec.rb167
-rw-r--r--spec/lib/time_duration_spec.rb14
-rw-r--r--spec/models/chouette/time_table_spec.rb82
-rw-r--r--spec/models/chouette/vehicle_journey_at_stops_day_offset_spec.rb90
-rw-r--r--spec/models/chouette/vehicle_journey_spec.rb63
-rw-r--r--spec/models/clean_up_spec.rb225
-rw-r--r--spec/models/referential_cloning_spec.rb10
-rw-r--r--spec/models/time_table_combination_spec.rb4
-rw-r--r--spec/models/user_spec.rb2
-rw-r--r--spec/policies/boiv_policy_spec.rb1
-rw-r--r--spec/policies/login_policy_spec.rb15
-rw-r--r--spec/support/bare_sql.rb58
-rw-r--r--spec/support/custom_matchers.rb7
-rw-r--r--spec/support/devise.rb9
-rw-r--r--spec/support/hash.rb6
-rw-r--r--spec/support/pg_catalog.rb57
-rw-r--r--spec/support/pundit/policies.rb7
-rw-r--r--spec/views/referentials/new.html.erb_spec.rb8
-rw-r--r--spec/workers/referential_cloning_worker_spec.rb30
156 files changed, 2308 insertions, 1035 deletions
diff --git a/Gemfile b/Gemfile
index b7844cf28..814a5ef14 100644
--- a/Gemfile
+++ b/Gemfile
@@ -20,8 +20,6 @@ gem 'browserify-rails'
# Use jquery as the JavaScript library
gem 'jquery-rails', '~> 3.1.4' # Update to v4 for Rails 4.2
-# Turbolinks makes following links in your web application faster. Read more: https://github.com/rails/turbolinks
-gem 'turbolinks'
# Build JSON APIs with ease. Read more: https://github.com/rails/jbuilder
gem 'jbuilder', '~> 2.0'
# bundle exec rake doc:rails generates the API under doc/api.
@@ -112,6 +110,8 @@ gem 'ransack'
gem "squeel", github: 'activerecord-hackery/squeel'
gem 'active_attr'
+gem 'sequel'
+
gem 'draper'
gem 'enumerize', '~> 0.10.0'
@@ -148,6 +148,8 @@ group :development do
gem 'quiet_assets'
gem 'license_finder'
gem 'bundler-audit'
+ gem 'spring-commands-rspec'
+ gem 'dbshell-rails'
platforms :ruby_20, :ruby_21, :ruby_22 do
gem 'better_errors'
diff --git a/Gemfile.lock b/Gemfile.lock
index 4e1337ec5..e87e6bb3b 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -9,10 +9,10 @@ GIT
GIT
remote: git@github.com:af83/stif-codifline-api.git
- revision: 02108a647514ca36e4377deecf3ffcce99359139
+ revision: c0efb26bf202e0770348bdac060b14c28e575ac2
specs:
codifligne (0.0.2)
- nokogiri (~> 1.6)
+ nokogiri (>= 1.7.2)
GIT
remote: git@github.com:af83/stif-reflex-api.git
@@ -180,6 +180,7 @@ GEM
daemons (1.2.4)
database_cleaner (1.5.3)
dbf (3.1.0)
+ dbshell-rails (0.0.2)
debug_inspector (0.0.2)
deep_cloneable (2.0.2)
activerecord (>= 3.1.0, < 5.0.0)
@@ -307,7 +308,7 @@ GEM
mime-types-data (~> 3.2015)
mime-types-data (3.2016.0521)
mimemagic (0.3.2)
- mini_portile2 (2.1.0)
+ mini_portile2 (2.2.0)
minitest (5.10.1)
multi_json (1.12.1)
multi_test (0.1.2)
@@ -321,8 +322,8 @@ GEM
net-ssh-gateway (2.0.0)
net-ssh (>= 4.0.0)
newrelic_rpm (4.0.0.332)
- nokogiri (1.7.1)
- mini_portile2 (~> 2.1.0)
+ nokogiri (1.8.0)
+ mini_portile2 (~> 2.2.0)
open4 (1.3.4)
orm_adapter (0.5.0)
parser (2.4.0.0)
@@ -461,6 +462,7 @@ GEM
rdoc (~> 4.0)
select2-rails (4.0.3)
thor (~> 0.14)
+ sequel (4.47.0)
shoulda-matchers (3.1.1)
activesupport (>= 4.0.0)
sidekiq (4.2.10)
@@ -492,6 +494,8 @@ GEM
slop (3.6.0)
spring (2.0.1)
activesupport (>= 4.2)
+ spring-commands-rspec (1.0.4)
+ spring (>= 0.9.1)
sprockets (2.12.4)
hike (~> 1.2)
multi_json (~> 1.0)
@@ -523,9 +527,6 @@ GEM
json (>= 1.8, < 3.0)
parser (>= 2.3.0.7)
rainbow (>= 1.99.1, < 3.0)
- turbolinks (5.0.1)
- turbolinks-source (~> 5)
- turbolinks-source (5.0.0)
tzinfo (1.2.3)
thread_safe (~> 0.1)
uglifier (2.7.2)
@@ -580,6 +581,7 @@ DEPENDENCIES
cucumber-rails
daemons
database_cleaner
+ dbshell-rails
deep_cloneable (~> 2.0.0)
devise (~> 3.5.4)
devise-async
@@ -646,6 +648,7 @@ DEPENDENCIES
sawyer (~> 0.6.0)
sdoc (~> 0.4.0)
select2-rails (~> 4.0, >= 4.0.3)
+ sequel
shoulda-matchers (~> 3.1)
sidekiq
simple_form (~> 3.1.0)
@@ -654,12 +657,12 @@ DEPENDENCIES
sinatra
slim-rails (~> 3.1)
spring
+ spring-commands-rspec
sqlite3
squeel!
teaspoon-jasmine
therubyracer (~> 0.12)
transpec
- turbolinks
uglifier (~> 2.7.2)
webmock
whenever!
diff --git a/INSTALL.md b/INSTALL.md
index 1d5badb79..330230a40 100644
--- a/INSTALL.md
+++ b/INSTALL.md
@@ -19,7 +19,7 @@ Go into your local repro and install the gems
bundle
-### Caveats
+### Installation Caveats
`libv8` might cause you troubles, depending on your local configuration. If you have `libv8` installed (probably because of `node.js`) you might need to tell bundler/Rubygems to use the system version.
@@ -40,6 +40,18 @@ You will get the correct value of `<version>` from bundler's error message.
As documented [here](https://github.com/dryade/georuby-ext/issues/2) we need some more libs before we can start the `rake` setup tasks. On mac/OS the easiest way is just to install `postgis` now with `homebrew` as this will
install all needed libraries.
+Also if on Linux you might discover a problem as late as when launching `rake`.
+In case of a stacktrace similar to this one
+
+```
+$ bundle exec rake --trace -T
+rake aborted!
+LoadError: library names list must not be empty
+```
+
+you need to install `libproj4-dev` on your system.
+
+
### Postgres
#### Create user
diff --git a/app/assets/javascripts/application.js b/app/assets/javascripts/application.js
index b90f7539d..0024b62b5 100644
--- a/app/assets/javascripts/application.js
+++ b/app/assets/javascripts/application.js
@@ -4,7 +4,6 @@
// It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the
// the compiled file.
//
-//= require turbolinks
//= require jquery
//= require jquery_ujs
//= require modernizr
diff --git a/app/assets/javascripts/cleanup.coffee b/app/assets/javascripts/cleanup.coffee
new file mode 100644
index 000000000..169a006a9
--- /dev/null
+++ b/app/assets/javascripts/cleanup.coffee
@@ -0,0 +1,9 @@
+$(document).on("change", 'input[name="clean_up[date_type]"]', (e) ->
+ type = $(this).val()
+ end_date = $('.cleanup_end_date_wrapper')
+
+ if type == 'between'
+ end_date.removeClass('hidden').show()
+ else
+ end_date.hide()
+)
diff --git a/app/assets/javascripts/es6_browserified/itineraries/index.js b/app/assets/javascripts/es6_browserified/itineraries/index.js
index 53f7bc7f9..2f1e9d180 100644
--- a/app/assets/javascripts/es6_browserified/itineraries/index.js
+++ b/app/assets/javascripts/es6_browserified/itineraries/index.js
@@ -38,7 +38,7 @@ const getInitialState = () => {
for_alighting: v.for_alighting || "normal",
longitude: v.longitude || 0,
latitude: v.latitude || 0,
- comment: v.comment,
+ comment: v.comment ? v.comment.replace("&#39;", "\'") : '',
olMap: {
isOpened: false,
json: {}
@@ -66,11 +66,31 @@ render(
document.querySelector('input[name=commit]').addEventListener('click', (event)=>{
let state = store.getState()
- state.stopPoints.map((stopPoint, i) => {
- addInput('id', (datas[i]) ? datas[i].stoppoint_id : '', i)
- addInput('stop_area_id',stopPoint.stoparea_id, i)
- addInput('position',i, i)
- addInput('for_boarding',stopPoint.for_boarding, i)
- addInput('for_alighting',stopPoint.for_alighting, i)
- })
+
+ if(state.stopPoints.length >= 2) {
+ state.stopPoints.map((stopPoint, i) => {
+ addInput('id', (datas[i]) ? datas[i].stoppoint_id : '', i)
+ addInput('stop_area_id',stopPoint.stoparea_id, i)
+ addInput('position',i, i)
+ addInput('for_boarding',stopPoint.for_boarding, i)
+ addInput('for_alighting',stopPoint.for_alighting, i)
+ })
+ if(state.stopPoints.length < datas.length){
+ for(var j= state.stopPoints.length; j < datas.length; j++){
+ updateFormForDeletion(datas[j])
+ }
+ }
+ } else {
+ event.preventDefault()
+ let msg = "L'itinéraire doit comporter au moins deux arrêts"
+ $('#stop_points').find('.subform').after("<div class='alert alert-danger'><span class='fa fa-lg fa-exclamation-circle'></span><span>" + msg + "</span></div>")
+ }
})
+
+const updateFormForDeletion = (stop) =>{
+ if (stop.stoppoint_id !== undefined){
+ let now = Date.now()
+ addInput('id', stop.stoppoint_id, now)
+ addInput('_destroy', 'true', now)
+ }
+}
diff --git a/app/assets/javascripts/es6_browserified/itineraries/reducers/stopPoints.js b/app/assets/javascripts/es6_browserified/itineraries/reducers/stopPoints.js
index 18fee8bd1..24c3e5d87 100644
--- a/app/assets/javascripts/es6_browserified/itineraries/reducers/stopPoints.js
+++ b/app/assets/javascripts/es6_browserified/itineraries/reducers/stopPoints.js
@@ -19,13 +19,6 @@ const stopPoint = (state = {}, action, length) => {
return state
}
}
-const updateFormForDeletion = (stop) =>{
- if (stop.stoppoint_id !== undefined){
- let now = Date.now()
- addInput('id', stop.stoppoint_id, now)
- addInput('_destroy', 'true', now)
- }
-}
const stopPoints = (state = [], action) => {
switch (action.type) {
@@ -49,7 +42,6 @@ const stopPoints = (state = [], action) => {
...state.slice(action.index + 2)
]
case 'DELETE_STOP':
- updateFormForDeletion(state[action.index])
return [
...state.slice(0, action.index),
...state.slice(action.index + 1).map((stopPoint)=>{
diff --git a/app/assets/javascripts/es6_browserified/journey_patterns/components/CreateModal.js b/app/assets/javascripts/es6_browserified/journey_patterns/components/CreateModal.js
index aa2d208df..5aade3348 100644
--- a/app/assets/javascripts/es6_browserified/journey_patterns/components/CreateModal.js
+++ b/app/assets/javascripts/es6_browserified/journey_patterns/components/CreateModal.js
@@ -22,90 +22,90 @@ class CreateModal extends Component {
}
if(this.props.status.fetchSuccess == true) {
return (
- <div className='row mt-md'>
- <div className='col-lg-12 text-right'>
- <button
- type='button'
- className='btn btn-outline-primary'
- data-toggle='modal'
- data-target='#NewJourneyPatternModal'
- onClick={this.props.onOpenCreateModal}
- >
- Ajouter une mission
- </button>
+ <div className="select_toolbox">
+ <ul>
+ <li className='st_action'>
+ <button
+ type='button'
+ data-toggle='modal'
+ data-target='#NewJourneyPatternModal'
+ onClick={this.props.onOpenCreateModal}
+ >
+ <span className="fa fa-plus"></span>
+ </button>
- <div className={ 'modal fade ' + ((this.props.modal.type == 'create') ? 'in' : '') } id='NewJourneyPatternModal'>
- <div className='modal-container'>
- <div className='modal-dialog'>
- <div className='modal-content'>
- <div className='modal-header'>
- <h4 className='modal-title'>Ajouter une mission</h4>
- </div>
+ <div className={ 'modal fade ' + ((this.props.modal.type == 'create') ? 'in' : '') } id='NewJourneyPatternModal'>
+ <div className='modal-container'>
+ <div className='modal-dialog'>
+ <div className='modal-content'>
+ <div className='modal-header'>
+ <h4 className='modal-title'>Ajouter une mission</h4>
+ </div>
- {(this.props.modal.type == 'create') && (
- <form>
- <div className='modal-body'>
- <div className='form-group'>
- <label className='control-label is-required'>Nom</label>
- <input
- type='text'
- ref='name'
- className='form-control'
- onKeyDown={(e) => actions.resetValidation(e.currentTarget)}
- required
- />
- </div>
- <div className='row'>
- <div className='col-lg-6 col-md-6 col-sm-6 col-xs-6'>
- <div className='form-group'>
- <label className='control-label is-required'>Nom public</label>
- <input
- type='text'
- ref='published_name'
- className='form-control'
- onKeyDown={(e) => actions.resetValidation(e.currentTarget)}
- required
- />
- </div>
+ {(this.props.modal.type == 'create') && (
+ <form>
+ <div className='modal-body'>
+ <div className='form-group'>
+ <label className='control-label is-required'>Nom</label>
+ <input
+ type='text'
+ ref='name'
+ className='form-control'
+ onKeyDown={(e) => actions.resetValidation(e.currentTarget)}
+ required
+ />
</div>
- <div className='col-lg-6 col-md-6 col-sm-6 col-xs-6'>
- <div className='form-group'>
- <label className='control-label is-required'>Code mission</label>
- <input
- type='text'
- ref='registration_number'
- className='form-control'
- onKeyDown={(e) => actions.resetValidation(e.currentTarget)}
- required
- />
+ <div className='row'>
+ <div className='col-lg-6 col-md-6 col-sm-6 col-xs-6'>
+ <div className='form-group'>
+ <label className='control-label is-required'>Nom public</label>
+ <input
+ type='text'
+ ref='published_name'
+ className='form-control'
+ onKeyDown={(e) => actions.resetValidation(e.currentTarget)}
+ required
+ />
+ </div>
+ </div>
+ <div className='col-lg-6 col-md-6 col-sm-6 col-xs-6'>
+ <div className='form-group'>
+ <label className='control-label'>Code mission</label>
+ <input
+ type='text'
+ ref='registration_number'
+ className='form-control'
+ onKeyDown={(e) => actions.resetValidation(e.currentTarget)}
+ />
+ </div>
</div>
</div>
</div>
- </div>
- <div className='modal-footer'>
- <button
- className='btn btn-link'
- data-dismiss='modal'
- type='button'
- onClick={this.props.onModalClose}
- >
- Annuler
- </button>
- <button
- className='btn btn-primary'
- type='button'
- onClick={this.handleSubmit.bind(this)}
- >
- Valider
- </button>
- </div>
- </form>
- )}
+ <div className='modal-footer'>
+ <button
+ className='btn btn-link'
+ data-dismiss='modal'
+ type='button'
+ onClick={this.props.onModalClose}
+ >
+ Annuler
+ </button>
+ <button
+ className='btn btn-primary'
+ type='button'
+ onClick={this.handleSubmit.bind(this)}
+ >
+ Valider
+ </button>
+ </div>
+ </form>
+ )}
+ </div>
</div>
</div>
</div>
- </div>
- </div>
+ </li>
+ </ul>
</div>
)
} else {
diff --git a/app/assets/javascripts/es6_browserified/journey_patterns/components/EditModal.js b/app/assets/javascripts/es6_browserified/journey_patterns/components/EditModal.js
index 81c59b2ce..1fc935932 100644
--- a/app/assets/javascripts/es6_browserified/journey_patterns/components/EditModal.js
+++ b/app/assets/javascripts/es6_browserified/journey_patterns/components/EditModal.js
@@ -63,7 +63,7 @@ class EditModal extends Component {
</div>
<div className='col-lg-6 col-md-6 col-sm-6 col-xs-6'>
<div className='form-group'>
- <label className='control-label is-required'>Code mission</label>
+ <label className='control-label'>Code mission</label>
<input
type='text'
ref='registration_number'
@@ -71,7 +71,6 @@ class EditModal extends Component {
id={this.props.modal.modalProps.index}
defaultValue={this.props.modal.modalProps.journeyPattern.registration_number}
onKeyDown={(e) => actions.resetValidation(e.currentTarget)}
- required
/>
</div>
</div>
diff --git a/app/assets/javascripts/es6_browserified/journey_patterns/components/JourneyPattern.js b/app/assets/javascripts/es6_browserified/journey_patterns/components/JourneyPattern.js
index 14ddf2b99..43c40a4d5 100644
--- a/app/assets/javascripts/es6_browserified/journey_patterns/components/JourneyPattern.js
+++ b/app/assets/javascripts/es6_browserified/journey_patterns/components/JourneyPattern.js
@@ -14,7 +14,7 @@ class JourneyPattern extends Component{
let vjURL = routeURL + '/vehicle_journeys?jp=' + jpOid
return (
- <a data-turbolinks="false" href={vjURL}>Horaires des courses</a>
+ <a href={vjURL}>Horaires des courses</a>
)
}
diff --git a/app/assets/javascripts/es6_browserified/time_tables/actions/index.js b/app/assets/javascripts/es6_browserified/time_tables/actions/index.js
index 41d247b21..951664129 100644
--- a/app/assets/javascripts/es6_browserified/time_tables/actions/index.js
+++ b/app/assets/javascripts/es6_browserified/time_tables/actions/index.js
@@ -122,6 +122,9 @@ const actions = {
type : 'OPEN_CONFIRM_MODAL',
callback
}),
+ showErrorModal: () => ({
+ type: 'OPEN_ERROR_MODAL'
+ }),
closeModal : () => ({
type : 'CLOSE_MODAL'
}),
@@ -185,9 +188,13 @@ const actions = {
return improvedCM
},
- checkConfirmModal: (event, callback, stateChanged,dispatch) => {
+ checkConfirmModal: (event, callback, stateChanged, dispatch, metas, timetable) => {
if(stateChanged === true){
- return actions.openConfirmModal(callback)
+ if(timetable.time_table_periods.length == 0 && _.some(metas.day_types)){
+ return actions.showErrorModal()
+ }else{
+ return actions.openConfirmModal(callback)
+ }
}else{
dispatch(actions.fetchingApi())
return callback
@@ -201,8 +208,8 @@ const actions = {
start = new Date(start)
end = new Date(end)
_.each(periods, (period, i) => {
- if(index != i && !period.deleted){
- if((new Date(period.period_start) <= start && new Date(period.period_end) >= start) || (new Date(period.period_start) <= end && new Date(period.period_end) >= end))
+ if(index !== i && !period.deleted){
+ if((new Date(period.period_start) <= start && new Date(period.period_end) >= start) || (new Date(period.period_start) <= end && new Date(period.period_end) >= end) || (start >= new Date(period.period_start) && end <= new Date(period.period_end)) || (start <= new Date(period.period_start) && end >= new Date(period.period_end)))
error = 'Les périodes ne peuvent pas se chevaucher'
}
})
diff --git a/app/assets/javascripts/es6_browserified/time_tables/components/ConfirmModal.js b/app/assets/javascripts/es6_browserified/time_tables/components/ConfirmModal.js
index c2229d991..40ae0eccf 100644
--- a/app/assets/javascripts/es6_browserified/time_tables/components/ConfirmModal.js
+++ b/app/assets/javascripts/es6_browserified/time_tables/components/ConfirmModal.js
@@ -2,7 +2,7 @@ var React = require('react')
var Component = require('react').Component
var PropTypes = require('react').PropTypes
-const ConfirmModal = ({dispatch, modal, onModalAccept, onModalCancel, journeyPatterns}) => (
+const ConfirmModal = ({dispatch, modal, onModalAccept, onModalCancel, timetable, metas}) => (
<div className={ 'modal fade ' + ((modal.type == 'confirm') ? 'in' : '') } id='ConfirmModal'>
<div className='modal-container'>
<div className='modal-dialog'>
@@ -28,7 +28,7 @@ const ConfirmModal = ({dispatch, modal, onModalAccept, onModalCancel, journeyPat
className='btn btn-primary'
data-dismiss='modal'
type='button'
- onClick = {() => {onModalAccept(modal.confirmModal.callback, journeyPatterns)}}
+ onClick = {() => {onModalAccept(modal.confirmModal.callback, timetable, metas)}}
>
Valider
</button>
diff --git a/app/assets/javascripts/es6_browserified/time_tables/components/ErrorModal.js b/app/assets/javascripts/es6_browserified/time_tables/components/ErrorModal.js
new file mode 100644
index 000000000..31ed256ea
--- /dev/null
+++ b/app/assets/javascripts/es6_browserified/time_tables/components/ErrorModal.js
@@ -0,0 +1,39 @@
+var React = require('react')
+var Component = require('react').Component
+var PropTypes = require('react').PropTypes
+
+const ErrorModal = ({dispatch, modal, onModalClose}) => (
+ <div className={ 'modal fade ' + ((modal.type == 'error') ? 'in' : '') } id='ErrorModal'>
+ <div className='modal-container'>
+ <div className='modal-dialog'>
+ <div className='modal-content'>
+ <div className='modal-header'>
+ <h4 className='modal-title'>Erreur</h4>
+ </div>
+ <div className='modal-body'>
+ <div className='mt-md mb-md'>
+ <p>Un calendrier d'application ne peut pas avoir de journée(s) d'application sans période(s).</p>
+ </div>
+ </div>
+ <div className='modal-footer'>
+ <button
+ className='btn btn-link'
+ data-dismiss='modal'
+ type='button'
+ onClick= {() => {onModalClose()}}
+ >
+ Retour
+ </button>
+ </div>
+ </div>
+ </div>
+ </div>
+ </div>
+)
+
+ErrorModal.propTypes = {
+ modal: PropTypes.object.isRequired,
+ onModalClose: PropTypes.func.isRequired
+}
+
+module.exports = ErrorModal
diff --git a/app/assets/javascripts/es6_browserified/time_tables/components/Navigate.js b/app/assets/javascripts/es6_browserified/time_tables/components/Navigate.js
index 74ca36ea6..6a3690cb0 100644
--- a/app/assets/javascripts/es6_browserified/time_tables/components/Navigate.js
+++ b/app/assets/javascripts/es6_browserified/time_tables/components/Navigate.js
@@ -39,7 +39,7 @@ let Navigate = ({ dispatch, metas, timetable, pagination, status, filters}) => {
value={month}
onClick={e => {
e.preventDefault()
- dispatch(actions.checkConfirmModal(e, actions.changePage(dispatch, e.currentTarget.value), pagination.stateChanged, dispatch))
+ dispatch(actions.checkConfirmModal(e, actions.changePage(dispatch, e.currentTarget.value), pagination.stateChanged, dispatch, metas, timetable))
}}
>
{actions.monthName(month) + ' ' + new Date(month).getFullYear()}
@@ -56,7 +56,7 @@ let Navigate = ({ dispatch, metas, timetable, pagination, status, filters}) => {
<button
onClick={e => {
e.preventDefault()
- dispatch(actions.checkConfirmModal(e, actions.goToPreviousPage(dispatch, pagination), pagination.stateChanged, dispatch))
+ dispatch(actions.checkConfirmModal(e, actions.goToPreviousPage(dispatch, pagination), pagination.stateChanged, dispatch, metas, timetable))
}}
type='button'
data-target='#ConfirmModal'
@@ -66,7 +66,7 @@ let Navigate = ({ dispatch, metas, timetable, pagination, status, filters}) => {
<button
onClick={e => {
e.preventDefault()
- dispatch(actions.checkConfirmModal(e, actions.goToNextPage(dispatch, pagination), pagination.stateChanged, dispatch))
+ dispatch(actions.checkConfirmModal(e, actions.goToNextPage(dispatch, pagination), pagination.stateChanged, dispatch, metas, timetable))
}}
type='button'
data-target='#ConfirmModal'
diff --git a/app/assets/javascripts/es6_browserified/time_tables/components/SaveTimetable.js b/app/assets/javascripts/es6_browserified/time_tables/components/SaveTimetable.js
index 24c91f282..e8c0aa3ba 100644
--- a/app/assets/javascripts/es6_browserified/time_tables/components/SaveTimetable.js
+++ b/app/assets/javascripts/es6_browserified/time_tables/components/SaveTimetable.js
@@ -2,6 +2,7 @@ var React = require('react')
var Component = require('react').Component
var PropTypes = require('react').PropTypes
var actions = require('../actions')
+var _ = require('lodash')
class SaveTimetable extends Component{
constructor(props){
@@ -18,7 +19,11 @@ class SaveTimetable extends Component{
type='button'
onClick={e => {
e.preventDefault()
- actions.submitTimetable(this.props.dispatch, this.props.timetable, this.props.metas)
+ if(this.props.timetable.time_table_periods.length == 0 && _.some(this.props.metas.day_types)){
+ this.props.onShowErrorModal()
+ }else{
+ actions.submitTimetable(this.props.getDispatch(), this.props.timetable, this.props.metas)
+ }
}}
>
Valider
diff --git a/app/assets/javascripts/es6_browserified/time_tables/containers/App.js b/app/assets/javascripts/es6_browserified/time_tables/containers/App.js
index 7c75377ea..02f0ddbd8 100644
--- a/app/assets/javascripts/es6_browserified/time_tables/containers/App.js
+++ b/app/assets/javascripts/es6_browserified/time_tables/containers/App.js
@@ -8,6 +8,7 @@ var Navigate = require('./Navigate')
var PeriodForm = require('./PeriodForm')
var SaveTimetable = require('./SaveTimetable')
var ConfirmModal = require('./ConfirmModal')
+var ErrorModal = require('./ErrorModal')
class App extends Component {
componentDidMount(){
@@ -24,6 +25,7 @@ class App extends Component {
<PeriodForm />
<SaveTimetable />
<ConfirmModal />
+ <ErrorModal />
</div>
</div>
)
diff --git a/app/assets/javascripts/es6_browserified/time_tables/containers/ConfirmModal.js b/app/assets/javascripts/es6_browserified/time_tables/containers/ConfirmModal.js
index 8095e1e7c..6282c1d1d 100644
--- a/app/assets/javascripts/es6_browserified/time_tables/containers/ConfirmModal.js
+++ b/app/assets/javascripts/es6_browserified/time_tables/containers/ConfirmModal.js
@@ -5,13 +5,14 @@ var ConfirmModal = require('../components/ConfirmModal')
const mapStateToProps = (state) => {
return {
modal: state.modal,
- journeyPatterns: state.journeyPatterns
+ timetable: state.timetable,
+ metas: state.metas
}
}
const mapDispatchToProps = (dispatch) => {
return {
- onModalAccept: (next, timetable, metas, state) =>{
+ onModalAccept: (next, timetable, metas) =>{
dispatch(actions.fetchingApi())
actions.submitTimetable(dispatch, timetable, metas, next)
},
diff --git a/app/assets/javascripts/es6_browserified/time_tables/containers/ErrorModal.js b/app/assets/javascripts/es6_browserified/time_tables/containers/ErrorModal.js
new file mode 100644
index 000000000..16a7d45dd
--- /dev/null
+++ b/app/assets/javascripts/es6_browserified/time_tables/containers/ErrorModal.js
@@ -0,0 +1,21 @@
+var actions = require('../actions')
+var connect = require('react-redux').connect
+var ErrorModal = require('../components/ErrorModal')
+
+const mapStateToProps = (state) => {
+ return {
+ modal: state.modal
+ }
+}
+
+const mapDispatchToProps = (dispatch) => {
+ return {
+ onModalClose: () =>{
+ dispatch(actions.closeModal())
+ }
+ }
+}
+
+const ErrorModalContainer = connect(mapStateToProps, mapDispatchToProps)(ErrorModal)
+
+module.exports = ErrorModalContainer
diff --git a/app/assets/javascripts/es6_browserified/time_tables/containers/SaveTimetable.js b/app/assets/javascripts/es6_browserified/time_tables/containers/SaveTimetable.js
index 7d2684dde..b5539e7d8 100644
--- a/app/assets/javascripts/es6_browserified/time_tables/containers/SaveTimetable.js
+++ b/app/assets/javascripts/es6_browserified/time_tables/containers/SaveTimetable.js
@@ -11,6 +11,16 @@ const mapStateToProps = (state) => {
}
}
-const SaveTimetable = connect(mapStateToProps)(SaveTimetableComponent)
+const mapDispatchToProps = (dispatch) => {
+ return {
+ onShowErrorModal: () => {
+ dispatch(actions.showErrorModal())
+ },
+ getDispatch: () => {
+ return dispatch
+ }
+ }
+}
+const SaveTimetable = connect(mapStateToProps, mapDispatchToProps)(SaveTimetableComponent)
module.exports = SaveTimetable
diff --git a/app/assets/javascripts/es6_browserified/time_tables/reducers/metas.js b/app/assets/javascripts/es6_browserified/time_tables/reducers/metas.js
index 4f1e7a528..2ce084efd 100644
--- a/app/assets/javascripts/es6_browserified/time_tables/reducers/metas.js
+++ b/app/assets/javascripts/es6_browserified/time_tables/reducers/metas.js
@@ -12,6 +12,9 @@ const metas = (state = {}, action) => {
color: action.json.color,
calendar: action.json.calendar ? action.json.calendar : null
})
+ case 'RECEIVE_MONTH':
+ let dt = (typeof state.day_types === 'string') ? actions.strToArrayDayTypes(state.day_types) : state.day_types
+ return _.assign({}, state, {day_types: dt})
case 'INCLUDE_DATE_IN_PERIOD':
case 'EXCLUDE_DATE_FROM_PERIOD':
case 'DELETE_PERIOD':
diff --git a/app/assets/javascripts/es6_browserified/time_tables/reducers/modal.js b/app/assets/javascripts/es6_browserified/time_tables/reducers/modal.js
index 56486a105..69f7b206e 100644
--- a/app/assets/javascripts/es6_browserified/time_tables/reducers/modal.js
+++ b/app/assets/javascripts/es6_browserified/time_tables/reducers/modal.js
@@ -19,6 +19,9 @@ const modal = (state = {}, action) => {
callback: action.callback,
}
})
+ case 'OPEN_ERROR_MODAL':
+ $('#ErrorModal').modal('show')
+ return _.assign({}, state, {type: 'error'})
case 'CLOSE_PERIOD_FORM':
newModalProps = _.assign({}, state.modalProps, {active: false})
return _.assign({}, state, {modalProps: newModalProps})
diff --git a/app/assets/javascripts/es6_browserified/time_tables/reducers/pagination.js b/app/assets/javascripts/es6_browserified/time_tables/reducers/pagination.js
index 660484c58..3d96fb7b7 100644
--- a/app/assets/javascripts/es6_browserified/time_tables/reducers/pagination.js
+++ b/app/assets/javascripts/es6_browserified/time_tables/reducers/pagination.js
@@ -5,8 +5,12 @@ const pagination = (state = {}, action) => {
case 'RECEIVE_TIME_TABLES':
return _.assign({}, state, {
currentPage: action.json.current_periode_range,
- periode_range: action.json.periode_range
+ periode_range: action.json.periode_range,
+ stateChanged: false
})
+ case 'RECEIVE_MONTH':
+ case 'RECEIVE_ERRORS':
+ return _.assign({}, state, {stateChanged: false})
case 'GO_TO_PREVIOUS_PAGE':
case 'GO_TO_NEXT_PAGE':
let nextPage = action.nextPage ? 1 : -1
@@ -22,7 +26,6 @@ const pagination = (state = {}, action) => {
case 'VALIDATE_PERIOD_FORM':
case 'UPDATE_COMMENT':
case 'UPDATE_COLOR':
- case 'UPDATE_DAY_TYPES':
toggleOnConfirmModal('modal')
return _.assign({}, state, {stateChanged: true})
default:
diff --git a/app/assets/javascripts/es6_browserified/vehicle_journeys/components/Filters.js b/app/assets/javascripts/es6_browserified/vehicle_journeys/components/Filters.js
index e2d03e195..f74bfa71d 100644
--- a/app/assets/javascripts/es6_browserified/vehicle_journeys/components/Filters.js
+++ b/app/assets/javascripts/es6_browserified/vehicle_journeys/components/Filters.js
@@ -107,7 +107,9 @@ const Filters = ({filters, pagination, onFilter, onResetFilters, onUpdateStartTi
onChange={onToggleWithoutSchedule}
checked={filters.query.withoutSchedule}
></input>
- <span className='switch-label' data-checkedvalue='Non' data-uncheckedvalue='Oui'></span>
+ <span className='switch-label' data-checkedvalue='Non' data-uncheckedvalue='Oui'>
+ {filters.query.withoutSchedule ? 'Oui' : 'Non'}
+ </span>
</label>
</div>
</div>
@@ -117,7 +119,7 @@ const Filters = ({filters, pagination, onFilter, onResetFilters, onUpdateStartTi
<div className="ffg-row">
{/* Switch avec/sans calendrier */}
<div className='form-group has_switch'>
- <label className='control-label pull-left'>Afficher les courses sans calendrier</label>
+ <label className='control-label pull-left'>Afficher les courses avec calendrier</label>
<div className='form-group pull-left' style={{padding: 0}}>
<div className='checkbox'>
<label>
@@ -126,7 +128,9 @@ const Filters = ({filters, pagination, onFilter, onResetFilters, onUpdateStartTi
onChange={onToggleWithoutTimeTable}
checked={filters.query.withoutTimeTable}
></input>
- <span className='switch-label' data-checkedvalue='Oui' data-uncheckedvalue='Non'></span>
+ <span className='switch-label' data-checkedvalue='Non' data-uncheckedvalue='Oui'>
+ {filters.query.withoutTimeTable ? 'Oui' : 'Non'}
+ </span>
</label>
</div>
</div>
diff --git a/app/assets/javascripts/es6_browserified/vehicle_journeys/components/tools/DuplicateVehicleJourney.js b/app/assets/javascripts/es6_browserified/vehicle_journeys/components/tools/DuplicateVehicleJourney.js
index f5e01de7c..0cf102693 100644
--- a/app/assets/javascripts/es6_browserified/vehicle_journeys/components/tools/DuplicateVehicleJourney.js
+++ b/app/assets/javascripts/es6_browserified/vehicle_journeys/components/tools/DuplicateVehicleJourney.js
@@ -53,72 +53,75 @@ class DuplicateVehicleJourney extends Component {
<div className='modal-header'>
<h4 className='modal-title'>Dupliquer une course</h4>
{(this.props.modal.type == 'duplicate') && (
- <em>Dupliquer les horaires de la course {actions.getSelected(this.props.vehicleJourneys)[0].objectid}</em>
+ <em>Dupliquer les horaires de la course {actions.humanOID(actions.getSelected(this.props.vehicleJourneys)[0].objectid)}</em>
)}
</div>
{(this.props.modal.type == 'duplicate') && (
- <form>
+ <form className='form-horizontal'>
<div className='modal-body'>
- <div className='row'>
- <div className='col-lg-3 col-md-3 col-sm-3 col-xs-3'>
- <div className='form-group'>
- <label className='control-label is-required'>Horaire de départ</label>
- <span className={'input-group time' + (actions.getSelected(this.props.vehicleJourneys).length > 1 ? ' disabled' : '')}>
- <input
- type='number'
- ref='duplicate_time_hh'
- min='00'
- max='23'
- className='form-control'
- defaultValue={this.getDefaultValue('hour')}
- disabled={(actions.getSelected(this.props.vehicleJourneys).length > 1 ? 'disabled' : '')}
- />
- <span>:</span>
- <input
- type='number'
- ref='duplicate_time_mm'
- min='00'
- max='59'
- className='form-control'
- defaultValue={this.getDefaultValue('minute')}
- disabled={(actions.getSelected(this.props.vehicleJourneys).length > 1 ? 'disabled' : '')}
- />
- </span>
- </div>
- </div>
-
- <div className='col-lg-6 col-md-6 col-sm-6 col-xs-6'>
- <div className='form-group'>
- <label className='control-label is-required'>Nombre de courses à créer et dupliquer</label>
+ <div className={'form-group ' + (actions.getSelected(this.props.vehicleJourneys).length > 1 ? 'hidden' : '' )}>
+ <label className='control-label is-required col-sm-8'>Horaire de départ indicatif</label>
+ <span className="col-sm-4">
+ <span className={'input-group time' + (actions.getSelected(this.props.vehicleJourneys).length > 1 ? ' disabled' : '')}>
<input
type='number'
- ref='duplicate_number'
- min='1'
- max='20'
+ ref='duplicate_time_hh'
+ min='00'
+ max='23'
className='form-control'
- onKeyDown={(e) => actions.resetValidation(e.currentTarget)}
- required
+ defaultValue={this.getDefaultValue('hour')}
+ disabled={(actions.getSelected(this.props.vehicleJourneys).length > 1 ? 'disabled' : '')}
/>
- </div>
- </div>
-
- <div className='col-lg-3 col-md-3 col-sm-3 col-xs-3'>
- <div className='form-group'>
- <label className='control-label is-required'>Avec un décalage de</label>
+ <span>:</span>
<input
type='number'
- ref='additional_time'
- min='-59'
+ ref='duplicate_time_mm'
+ min='00'
max='59'
className='form-control'
- onKeyDown={(e) => actions.resetValidation(e.currentTarget)}
- required
+ defaultValue={this.getDefaultValue('minute')}
+ disabled={(actions.getSelected(this.props.vehicleJourneys).length > 1 ? 'disabled' : '')}
/>
- </div>
+ </span>
+ </span>
+ </div>
+
+ <div className='form-group'>
+ <label className='control-label is-required col-sm-8'>Nombre de courses à créer et dupliquer</label>
+ <div className="col-sm-4">
+ <input
+ type='number'
+ style={{'width': 104}}
+ ref='duplicate_number'
+ min='1'
+ max='20'
+ defaultValue='1'
+ className='form-control'
+ onKeyDown={(e) => actions.resetValidation(e.currentTarget)}
+ required
+ />
+ </div>
+ </div>
+
+ <div className='form-group'>
+ <label className='control-label is-required col-sm-8'>Décalage à partir duquel on créé les courses</label>
+ <div className="col-sm-4">
+ <input
+ type='number'
+ style={{'width': 104}}
+ ref='additional_time'
+ min='-59'
+ max='59'
+ defaultValue='0'
+ className='form-control'
+ onKeyDown={(e) => actions.resetValidation(e.currentTarget)}
+ required
+ />
</div>
</div>
</div>
+
<div className='modal-footer'>
<button
className='btn btn-link'
diff --git a/app/assets/javascripts/es6_browserified/vehicle_journeys/components/tools/NotesEditVehicleJourney.js b/app/assets/javascripts/es6_browserified/vehicle_journeys/components/tools/NotesEditVehicleJourney.js
index d4c3f4231..4c18ef96f 100644
--- a/app/assets/javascripts/es6_browserified/vehicle_journeys/components/tools/NotesEditVehicleJourney.js
+++ b/app/assets/javascripts/es6_browserified/vehicle_journeys/components/tools/NotesEditVehicleJourney.js
@@ -2,6 +2,7 @@ var React = require('react')
var Component = require('react').Component
var PropTypes = require('react').PropTypes
var actions = require('../../actions')
+var _ = require('lodash')
class NotesEditVehicleJourney extends Component {
constructor(props) {
@@ -27,16 +28,28 @@ class NotesEditVehicleJourney extends Component {
type='button'
className='btn btn-outline-danger btn-xs'
onClick={() => this.props.onToggleFootnoteModal(lf, false)}
- ><span className="fa fa-trash"></span></button>
+ ><span className="fa fa-trash"></span> Retirer</button>
}else{
return <button
type='button'
className='btn btn-outline-primary btn-xs'
onClick={() => this.props.onToggleFootnoteModal(lf, true)}
- ><span className="fa fa-plus"></span></button>
+ ><span className="fa fa-plus"></span> Ajouter</button>
}
}
+ filterFN() {
+ return _.filter(window.line_footnotes, (lf, i) => {
+ let bool = true
+ _.map(this.props.modal.modalProps.vehicleJourney.footnotes, (f, j) => {
+ if(lf.id === f.id) {
+ bool = false
+ }
+ })
+ return bool
+ })
+ }
+
render() {
if(this.props.status.isFetching == true) {
return false
@@ -65,7 +78,24 @@ class NotesEditVehicleJourney extends Component {
{(this.props.modal.type == 'notes_edit') && (
<form>
<div className='modal-body'>
- {window.line_footnotes.map((lf, i) =>
+ <h3>Notes associées</h3>
+ {(this.props.modal.modalProps.vehicleJourney.footnotes).map((lf, i) =>
+ <div
+ key={i}
+ className='panel panel-default'
+ >
+ <div className='panel-heading'>
+ <h4 className='panel-title clearfix'>
+ <div className='pull-left' style={{paddingTop: '3px'}}>{lf.code}</div>
+ <div className='pull-right'>{this.renderFootnoteButton(lf, this.props.modal.modalProps.vehicleJourney.footnotes)}</div>
+ </h4>
+ </div>
+ <div className='panel-body'><p>{lf.label}</p></div>
+ </div>
+ )}
+
+ <h3 className='mt-lg'>Sélectionnez les notes à associer à cette course :</h3>
+ {this.filterFN().map((lf, i) =>
<div
key={i}
className='panel panel-default'
diff --git a/app/assets/javascripts/es6_browserified/vehicle_journeys/components/tools/select2s/MissionSelect2.js b/app/assets/javascripts/es6_browserified/vehicle_journeys/components/tools/select2s/MissionSelect2.js
index 36aaa5cfd..b3df767ab 100644
--- a/app/assets/javascripts/es6_browserified/vehicle_journeys/components/tools/select2s/MissionSelect2.js
+++ b/app/assets/javascripts/es6_browserified/vehicle_journeys/components/tools/select2s/MissionSelect2.js
@@ -50,7 +50,7 @@ class BSelect4 extends React.Component{
},
cache: true
},
- minimumInputLength: 2,
+ minimumInputLength: 0,
escapeMarkup: function (markup) { return markup; },
templateResult: formatRepo
}}
diff --git a/app/assets/javascripts/es6_browserified/vehicle_journeys/index.js b/app/assets/javascripts/es6_browserified/vehicle_journeys/index.js
index 2a76ae43a..97aa60526 100644
--- a/app/assets/javascripts/es6_browserified/vehicle_journeys/index.js
+++ b/app/assets/javascripts/es6_browserified/vehicle_journeys/index.js
@@ -48,7 +48,7 @@ var initialState = {
comment: ''
},
withoutSchedule: true,
- withoutTimeTable: false
+ withoutTimeTable: true
}
},
diff --git a/app/assets/javascripts/es6_browserified/vehicle_journeys/reducers/modal.js b/app/assets/javascripts/es6_browserified/vehicle_journeys/reducers/modal.js
index 9f071069d..229fd2058 100644
--- a/app/assets/javascripts/es6_browserified/vehicle_journeys/reducers/modal.js
+++ b/app/assets/javascripts/es6_browserified/vehicle_journeys/reducers/modal.js
@@ -90,11 +90,15 @@ const modal = (state = {}, action) => {
case 'CREATE_VEHICLEJOURNEY_MODAL':
let selectedJP = {}
if (window.jpOrigin){
+ let stopAreas = _.map(window.jpOriginStopPoints, (sa, i) =>{
+ return _.assign({}, {stop_area_short_description : {id : sa.stop_area_id}})
+ })
selectedJP = {
id: window.jpOrigin.id,
name: window.jpOrigin.name,
published_name: window.jpOrigin.published_name,
- objectid: window.jpOrigin.objectid
+ objectid: window.jpOrigin.objectid,
+ stop_areas: stopAreas
}
}
return {
diff --git a/app/assets/javascripts/es6_browserified/vehicle_journeys/reducers/pagination.js b/app/assets/javascripts/es6_browserified/vehicle_journeys/reducers/pagination.js
index ee59fca6d..a8189fc97 100644
--- a/app/assets/javascripts/es6_browserified/vehicle_journeys/reducers/pagination.js
+++ b/app/assets/javascripts/es6_browserified/vehicle_journeys/reducers/pagination.js
@@ -2,6 +2,7 @@ var _ = require('lodash')
const pagination = (state = {}, action) => {
switch (action.type) {
case 'RECEIVE_JOURNEY_PATTERNS':
+ case 'RECEIVE_VEHICLE_JOURNEYS':
return _.assign({}, state, {stateChanged: false})
case 'GO_TO_PREVIOUS_PAGE':
if (action.pagination.page > 1){
diff --git a/app/assets/javascripts/es6_browserified/vehicle_journeys/reducers/vehicleJourneys.js b/app/assets/javascripts/es6_browserified/vehicle_journeys/reducers/vehicleJourneys.js
index 9dea63e07..c7e8d58e7 100644
--- a/app/assets/javascripts/es6_browserified/vehicle_journeys/reducers/vehicleJourneys.js
+++ b/app/assets/javascripts/es6_browserified/vehicle_journeys/reducers/vehicleJourneys.js
@@ -42,7 +42,9 @@ const vehicleJourney= (state = {}, action, keep) => {
time_tables: [],
vehicle_journey_at_stops: pristineVjasList,
selected: false,
- deletable: false
+ deletable: false,
+ transport_mode: window.transportMode ? window.transportMode : 'undefined',
+ transport_submode: window.transportSubmode ? window.transportSubmode : 'undefined'
}
case 'DUPLICATE_VEHICLEJOURNEY':
case 'SHIFT_VEHICLEJOURNEY':
diff --git a/app/assets/javascripts/forms.coffee b/app/assets/javascripts/forms.coffee
index 6b00e9c26..12d82fef1 100644
--- a/app/assets/javascripts/forms.coffee
+++ b/app/assets/javascripts/forms.coffee
@@ -41,10 +41,11 @@ isEdge = !isIE && !!window.StyleMedia
else
$(selectedStatus).css('color', selectedValue)
-$(document).on 'turbolinks:load', togglableFilter
-$(document).on 'turbolinks:load', submitMover
-$(document).on 'turbolinks:load', switchInput
-$(document).on 'turbolinks:load', colorSelector
+$ ->
+ togglableFilter()
+ submitMover()
+ switchInput()
+ colorSelector()
if isIE || isEdge
$(document).on 'click', '.formSubmitr', (e)->
diff --git a/app/assets/javascripts/main_menu.coffee b/app/assets/javascripts/main_menu.coffee
index f6266f06b..a12c47576 100644
--- a/app/assets/javascripts/main_menu.coffee
+++ b/app/assets/javascripts/main_menu.coffee
@@ -1,4 +1,4 @@
-$(document).on 'turbolinks:load', ->
+$ ->
link = []
ptitleCont = ""
diff --git a/app/assets/javascripts/nav_panels.coffee b/app/assets/javascripts/nav_panels.coffee
index 829db5ad0..25f15f063 100644
--- a/app/assets/javascripts/nav_panels.coffee
+++ b/app/assets/javascripts/nav_panels.coffee
@@ -1,4 +1,4 @@
-$(document).on 'turbolinks:load', ->
+$ ->
$('#menu_top [data-panel="toggle"]').on 'click', (e) ->
e.preventDefault()
$(this).siblings().removeClass 'active'
diff --git a/app/assets/javascripts/routing_constraint_zones.coffee b/app/assets/javascripts/routing_constraint_zones.coffee
index fc032f074..ceb9fb218 100644
--- a/app/assets/javascripts/routing_constraint_zones.coffee
+++ b/app/assets/javascripts/routing_constraint_zones.coffee
@@ -1,28 +1,62 @@
-fill_stop_points_options = ->
- stop_point_select = $('#routing_constraint_zone_stop_point_ids')
- stop_point_select.empty()
- referential_id = document.location.pathname.match(/\d+/g)[0]
- line_id = document.location.pathname.match(/\d+/g)[1]
- route_id = $('#routing_constraint_zone_route_id').val()
-
- if errors_on_form()
- stop_point_ids = eval($('#stop_point_ids').val())
-
- $.ajax
- url: "/referentials/#{referential_id}/lines/#{line_id}/routes/#{route_id}/stop_points"
- dataType: 'json'
- success: (data, textStatus, jqXHR) ->
- for stop_point in data
- selected = $.inArray(stop_point.id, stop_point_ids) != -1
- stop_point_select.append "<option value='#{stop_point.id}'" + "#{if selected then ' selected' else ''}" + ">#{stop_point.name}</option>"
- error: (jqXHR, textStatus, errorThrown) ->
- console.log textStatus
- console.log errorThrown
-
-errors_on_form = ->
- document.location.pathname.endsWith('routing_constraint_zones') && $('#new_routing_constraint_zone').length
-
-$(document).on 'turbolinks:load', ->
- if document.location.pathname.endsWith('routing_constraint_zones/new') || errors_on_form()
- fill_stop_points_options()
- $('#routing_constraint_zone_route_id').change(fill_stop_points_options)
+@ITL_stoppoints = ->
+ routeID = $('#routing_constraint_zone_route_id').val()
+
+ if (routeID)
+ origin = window.location.origin
+ path = window.location.pathname.split('/', 5).join('/')
+ reqURL = origin + path + '/routes/' + routeID + '/stop_points.json'
+
+ $.ajax
+ url: reqURL
+ dataType: 'json'
+ error: (jqXHR, textStatus, errorThrown) ->
+ console.log(errorThrown)
+ success: (collection, textStatus, jqXHR) ->
+ html = ''
+ stopAreaBaseURL = origin + window.location.pathname.split('/', 3).join('/') + '/stop_areas/'
+
+ collection.forEach (item, index) ->
+ html += "<div class='nested-fields'><div class='wrapper'>
+ <div><a href='" + stopAreaBaseURL + item.stop_area_id + "' class='navlink' title='Voir l&#39;arrêt'><span>" + item.name + "</span></a></div>
+ <div><span>" + item.city_name + " (" + item.zip_code + ")</span></div>
+ <div>
+ <span class='has_radio'>
+ <input type='checkbox' name='routing_constraint_zone[stop_point_ids][" + index + "]' value='" + item.id + "'>
+ <span class='radio-label'></span>
+ </span>
+ </div>
+ </div></div>"
+
+ $('#ITL_stoppoints').find('.nested-fields').remove()
+ $('#ITL_stoppoints').find('.nested-head').after(html)
+
+ # VALIDATION
+ selection = []
+ $('#ITL_stoppoints').on 'click', "input[type='checkbox']", (e) ->
+ v = $(e.target).val()
+
+ if ( $.inArray(v, selection) != -1 )
+ selection.splice(selection.indexOf(v), 1)
+ else
+ selection.push(v)
+
+ alertMsg = "<div class='alert alert-danger' style='margin-bottom:15px;'>
+ <p class='small'>Un ITL doit comporter au moins deux arrêts</p>
+ </div>"
+
+ $(document).on 'click', "input[type='submit']", (e)->
+ inputName = $('#routing_constraint_zone_name').val()
+
+ $('.alert-danger').remove()
+
+ if ( selection.length < 2 && inputName != "")
+ e.preventDefault()
+ $('#routing_constraint_zone_name').closest('.form-group').removeClass('has-error').find('.help-block').remove()
+ $('#ITL_stoppoints').prepend(alertMsg)
+
+$ ->
+ ITL_stoppoints()
+
+ $('#routing_constraint_zone_route_id').on 'change', ->
+ $('.alert-danger').remove()
+ ITL_stoppoints()
diff --git a/app/assets/javascripts/select2.coffee b/app/assets/javascripts/select2.coffee
index af3dc6d75..d54ddb811 100644
--- a/app/assets/javascripts/select2.coffee
+++ b/app/assets/javascripts/select2.coffee
@@ -20,6 +20,7 @@ bind_select2_ajax = (el, cfg = {}) ->
delay: 125,
processResults: (data, params) -> results: data
minimumInputLength: 3
+ placeholder: target.data('select2ed-placeholder')
templateResult: (item) ->
item.text
templateSelection: (item) ->
@@ -39,4 +40,5 @@ bind_select2_ajax = (el, cfg = {}) ->
-$(document).on 'turbolinks:load', select_2
+$ ->
+ select_2()
diff --git a/app/assets/javascripts/selectable_table.coffee b/app/assets/javascripts/selectable_table.coffee
index 4086bf6c2..681c8f9e6 100644
--- a/app/assets/javascripts/selectable_table.coffee
+++ b/app/assets/javascripts/selectable_table.coffee
@@ -53,4 +53,5 @@
.addClass 'noselect'
.children('.info-msg').children('span').text(selection.length)
-$(document).on 'turbolinks:load', selectTable
+$ ->
+ selectTable()
diff --git a/app/assets/javascripts/time_table_combinations.coffee b/app/assets/javascripts/time_table_combinations.coffee
index 8923af958..6104da7ff 100644
--- a/app/assets/javascripts/time_table_combinations.coffee
+++ b/app/assets/javascripts/time_table_combinations.coffee
@@ -3,4 +3,5 @@
$(this).closest('.has_switch').siblings('.form-group').each ->
$(this).toggleClass('hidden')
-$(document).on 'turbolinks:load', combinedTypeToggle
+$ ->
+ combinedTypeToggle()
diff --git a/app/assets/stylesheets/components/_tables.sass b/app/assets/stylesheets/components/_tables.sass
index 0cd91a1a8..e3a33e131 100644
--- a/app/assets/stylesheets/components/_tables.sass
+++ b/app/assets/stylesheets/components/_tables.sass
@@ -82,6 +82,66 @@
border-top: 2px solid $darkgrey
margin-top: 15px
+ // Specific for tables displaying stop points
+ &.has-stoppoints
+ tbody
+ > tr > td:first-child
+ position: relative
+ padding-left: 25px
+
+ &:before
+ content: ''
+ display: block
+ width: 10px
+ height: 10px
+ background-color: #fff
+ border: 2px solid $blue
+ border-radius: 50%
+ position: absolute
+ z-index: 5
+ left: 5px
+ top: 50%
+ margin-top: -5px
+
+ &:after
+ content: ''
+ display: block
+ width: 4px
+ margin: 0 3px
+ background-color: rgba($grey, 0.5)
+ position: absolute
+ z-index: 3
+ top: 0
+ left: 5px
+ bottom: 0
+
+ > tr:first-child > td:first-child
+ &:after
+ content: ''
+ top: 50%
+
+ > tr:last-child > td:first-child
+ &:after
+ content: ''
+ bottom: 50%
+
+ > tr:first-child > td:first-child, > tr:last-child > td:first-child
+ &:before
+ content: '•'
+ color: $blue
+ text-align: center
+ font-size: 28px
+ letter-spacing: 0
+ text-indent: -0.01em
+ line-height: 12px
+ width: 15px
+ height: 15px
+ left: 2px
+ top: 50%
+ margin-top: -8px
+
+
+
// select_toolbox
.select_toolbox
padding: 10px
diff --git a/app/controllers/autocomplete_calendars_controller.rb b/app/controllers/autocomplete_calendars_controller.rb
index f85f529fc..2b85fcff3 100644
--- a/app/controllers/autocomplete_calendars_controller.rb
+++ b/app/controllers/autocomplete_calendars_controller.rb
@@ -2,6 +2,6 @@ class AutocompleteCalendarsController < ApplicationController
respond_to :json, :only => [:autocomplete]
def autocomplete
- @calendars = current_organisation.referentials.search(params[:q]).result.paginate(page: params[:page])
+ @calendars = current_organisation.calendars.search(params[:q]).result.paginate(page: params[:page])
end
end
diff --git a/app/controllers/calendars_controller.rb b/app/controllers/calendars_controller.rb
index 5370d9cbb..86d567882 100644
--- a/app/controllers/calendars_controller.rb
+++ b/app/controllers/calendars_controller.rb
@@ -33,10 +33,8 @@ class CalendarsController < BreadcrumbController
def collection
return @calendars if @calendars
- scope = Calendar.where('organisation_id = ?', current_organisation.id)
-
+ scope = Calendar.where('organisation_id = ? OR shared = ?', current_organisation.id, true)
scope = shared_scope(scope)
-
@q = scope.ransack(params[:q])
calendars = @q.result
diff --git a/app/controllers/clean_ups_controller.rb b/app/controllers/clean_ups_controller.rb
index 1e4835775..b9ff225b3 100644
--- a/app/controllers/clean_ups_controller.rb
+++ b/app/controllers/clean_ups_controller.rb
@@ -14,6 +14,6 @@ class CleanUpsController < ChouetteController
end
def clean_up_params
- params.require(:clean_up).permit(:keep_lines, :keep_stops, :keep_companies, :keep_networks, :keep_group_of_lines, :begin_date, :end_date)
+ params.require(:clean_up).permit(:date_type, :begin_date, :end_date)
end
end
diff --git a/app/controllers/devise/cas_sessions_controller.rb b/app/controllers/devise/cas_sessions_controller.rb
index ecc7e9f7e..0a9d9ecb2 100644
--- a/app/controllers/devise/cas_sessions_controller.rb
+++ b/app/controllers/devise/cas_sessions_controller.rb
@@ -16,7 +16,12 @@ class Devise::CasSessionsController < Devise::SessionsController
end
def service
- redirect_to after_sign_in_path_for(warden.authenticate!(:scope => resource_name))
+ warden.authenticate!(:scope => resource_name)
+ if LoginPolicy.new(current_user).boiv?
+ redirect_to after_sign_in_path_for(current_user)
+ else
+ redirect_to root_path, flash: {alert: t('devise.sessions.new.unauthorized')}
+ end
end
def unregistered
diff --git a/app/controllers/referentials_controller.rb b/app/controllers/referentials_controller.rb
index 437444f29..aa5b359da 100644
--- a/app/controllers/referentials_controller.rb
+++ b/app/controllers/referentials_controller.rb
@@ -74,7 +74,6 @@ class ReferentialsController < BreadcrumbController
alias_method :current_referential, :referential
helper_method :current_referential
-
def resource
@referential ||= current_organisation.find_referential(params[:id])
end
@@ -130,13 +129,7 @@ class ReferentialsController < BreadcrumbController
params.require(:referential).permit(
:id,
:name,
- :slug,
- :prefix,
- :time_zone,
- :upper_corner,
- :lower_corner,
:organisation_id,
- :projection_type,
:data_format,
:archived_at,
:created_from_id,
diff --git a/app/controllers/route_stop_points_controller.rb b/app/controllers/route_stop_points_controller.rb
index e12acb33b..730bd08a9 100644
--- a/app/controllers/route_stop_points_controller.rb
+++ b/app/controllers/route_stop_points_controller.rb
@@ -11,8 +11,7 @@ class RouteStopPointsController < ChouetteController
def index
respond_to do |format|
- format.json { render json: referential.lines.find(params[:line_id]).routes.find(params[:route_id]).stop_points.map { |sp| { id: sp.id, name: sp.name } } }
+ format.json { render json: referential.lines.find(params[:line_id]).routes.find(params[:route_id]).stop_points.map { |sp| { id: sp.id, stop_area_id: sp.stop_area.id, name: sp.name, zip_code: sp.stop_area.zip_code, city_name: sp.stop_area.city_name } } }
end
end
end
-
diff --git a/app/controllers/routing_constraint_zones_controller.rb b/app/controllers/routing_constraint_zones_controller.rb
index f2f74e801..7707427b0 100644
--- a/app/controllers/routing_constraint_zones_controller.rb
+++ b/app/controllers/routing_constraint_zones_controller.rb
@@ -1,22 +1,71 @@
class RoutingConstraintZonesController < ChouetteController
- defaults resource_class: Chouette::RoutingConstraintZone
+ include PolicyChecker
+ defaults resource_class: Chouette::RoutingConstraintZone
respond_to :html, :xml, :json
- before_action :remove_empty_stop_point, only: [:create, :update]
+ before_action :check_stoppoint_param, only: [:create, :update]
belongs_to :referential do
belongs_to :line, parent_class: Chouette::Line
end
- include PolicyChecker
+ def index
+ @routing_constraint_zones = collection
+ end
+
+ def show
+ @routing_constraint_zone = collection.find(params[:id])
+ end
+
+ protected
+
+ def collection
+ @q = resource.routing_constraint_zones.search(params[:q])
+
+ if sort_column && sort_direction
+ @routing_constraint_zones ||= @q.result(distinct: true).order(sort_column + ' ' + sort_direction)
+ else
+ @routing_constraint_zones ||= @q.result(distinct: true).order(:name)
+ end
+ @routing_constraint_zones = @routing_constraint_zones.paginate(page: params[:page], per_page: 10)
+ end
private
+ def sort_column
+ (Chouette::RoutingConstraintZone.column_names).include?(params[:sort]) ? params[:sort] : 'name'
+ end
+ def sort_direction
+ %w[asc desc].include?(params[:direction]) ? params[:direction] : 'asc'
+ end
+
+ def resource
+ @referential = Referential.find params[:referential_id]
+ @line = @referential.lines.find params[:line_id]
+ end
+
def routing_constraint_zone_params
- params.require(:routing_constraint_zone).permit(:name, { stop_point_ids: [] }, :line_id, :route_id, :objectid, :object_version, :creator_id)
+ params.require(:routing_constraint_zone).permit(
+ :name,
+ { stop_point_ids: [] },
+ :line_id,
+ :route_id,
+ :objectid,
+ :object_version,
+ :creator_id
+ )
end
- def remove_empty_stop_point
- params.require(:routing_constraint_zone)[:stop_point_ids].delete('')
+ def check_stoppoint_param
+ spArr = []
+ if params.require(:routing_constraint_zone)[:stop_point_ids] and params.require(:routing_constraint_zone)[:stop_point_ids].length >= 2
+ params.require(:routing_constraint_zone)[:stop_point_ids].each do |k,v|
+ spArr << v
+ end
+ params.require(:routing_constraint_zone)[:stop_point_ids] = spArr
+ else
+ Rails.logger.error("Error: An ITL must have at least two stop points")
+ end
end
+
end
diff --git a/app/controllers/users/login_controller.rb b/app/controllers/users/login_controller.rb
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/app/controllers/users/login_controller.rb
diff --git a/app/controllers/vehicle_journeys_controller.rb b/app/controllers/vehicle_journeys_controller.rb
index 71787ba78..fe2e2137f 100644
--- a/app/controllers/vehicle_journeys_controller.rb
+++ b/app/controllers/vehicle_journeys_controller.rb
@@ -64,7 +64,13 @@ class VehicleJourneysController < ChouetteController
}
end
- @jp_origin = Chouette::JourneyPattern.find_by(objectid: params[:jp])
+ @transport_mode = route.line['transport_mode']
+ @transport_submode = route.line['transport_submode']
+
+ if params[:jp]
+ @jp_origin = Chouette::JourneyPattern.find_by(objectid: params[:jp])
+ @jp_origin_stop_points = @jp_origin.stop_points
+ end
index! do
if collection.out_of_bounds?
@@ -111,12 +117,18 @@ class VehicleJourneysController < ChouetteController
end
def maybe_filter_out_journeys_with_time_tables(scope)
- if params[:q] &&
- params[:q][:vehicle_journey_without_time_table] == 'true'
- return scope
- .without_time_tables
+ if params[:q] && params[:q][:vehicle_journey_without_time_table] == 'false'
+ return scope.without_time_tables
end
+ # if params[:q]
+ # if params[:q][:vehicle_journey_without_time_table] == 'true'
+ # return scope.without_time_tables
+ # end
+ # else
+ # return scope.without_time_tables
+ # end
+
scope
end
@@ -129,7 +141,7 @@ class VehicleJourneysController < ChouetteController
def adapted_params
params.tap do |adapted_params|
- adapted_params.merge!( :route => parent)
+ adapted_params.merge!(:route => parent)
hour_entry = "vehicle_journey_at_stops_departure_time_gt(4i)".to_sym
if params[:q] && params[:q][ hour_entry]
adapted_params[:q].merge! hour_entry => (params[:q][ hour_entry].to_i - utc_offset)
diff --git a/app/helpers/newapplication_helper.rb b/app/helpers/newapplication_helper.rb
index e189199a2..3d43e9fc7 100644
--- a/app/helpers/newapplication_helper.rb
+++ b/app/helpers/newapplication_helper.rb
@@ -16,7 +16,7 @@ module NewapplicationHelper
end
columns.map do |k, v|
- if ["ID Codif", "Oid", "OiD", "ID Reflex", "Arrêt de départ", "Arrêt d'arrivée", "Période de validité englobante", "Période englobante", "Nombre de courses associées", "Journées d'application"].include? k
+ if ["ID Codif", "Oid", "OiD", "ID Reflex", "Arrêt de départ", "Arrêt d'arrivée", "Période de validité englobante", "Période englobante", "Nombre de courses associées", "Journées d'application", "Arrêts de l'itinéraire", "Arrêts inclus dans l'ITL"].include? k
hcont << content_tag(:th, k)
else
hcont << content_tag(:th, sortable_columns(collection, k))
@@ -160,7 +160,7 @@ module NewapplicationHelper
end.join.html_safe
end
- # content_tag :div, trigger + menu, class: 'btn-group'
+ content_tag :div, trigger + menu, class: 'btn-group'
end
diff --git a/app/models/chouette/route.rb b/app/models/chouette/route.rb
index 9f6344055..76905bf2b 100644
--- a/app/models/chouette/route.rb
+++ b/app/models/chouette/route.rb
@@ -63,8 +63,10 @@ class Chouette::Route < Chouette::TridentActiveRecord
end
accepts_nested_attributes_for :stop_points, :allow_destroy => :true
- # validates_presence_of :name
+ validates_presence_of :name
+ validates_presence_of :published_name
validates_presence_of :line
+
# validates_presence_of :direction
# validates_presence_of :wayback
diff --git a/app/models/chouette/time_table.rb b/app/models/chouette/time_table.rb
index 42879c6d5..4186af6d2 100644
--- a/app/models/chouette/time_table.rb
+++ b/app/models/chouette/time_table.rb
@@ -135,10 +135,17 @@ class Chouette::TimeTable < Chouette::TridentActiveRecord
[Chouette::TimeTable.maximum(:end_date)].compact.max
end
+ def add_exclude_date(in_out, date)
+ self.dates.create!({in_out: in_out, date: date})
+ end
+
def actualize
self.dates.clear
self.periods.clear
- self.merge! self.calendar.convert_to_time_table
+ from = self.calendar.convert_to_time_table
+ self.dates = from.dates
+ self.periods = from.periods
+ self.save
end
def month_inspect(date)
@@ -470,18 +477,17 @@ class Chouette::TimeTable < Chouette::TridentActiveRecord
# merge effective days from another timetable
def merge!(another_tt)
transaction do
+ # merge dates
+ self.dates ||= []
+ another_tt.included_days.each do |d|
+ add_included_day d
+ end
+
# if one tt has no period, just merge lists
if self.periods.empty? || another_tt.periods.empty?
if !another_tt.periods.empty?
# copy periods
self.periods = another_tt.clone_periods
- # set valid_days
- self.int_day_types = another_tt.int_day_types
- end
- # merge dates
- self.dates ||= []
- another_tt.included_days.each do |d|
- add_included_day d
end
else
# check if periods can be kept
@@ -540,7 +546,6 @@ class Chouette::TimeTable < Chouette::TridentActiveRecord
self.dates.clear
days.each {|d| self.dates << Chouette::TimeTableDate.new( :date =>d, :in_out => true)}
self.periods.clear
- self.int_day_types = 0
self.dates.to_a.sort! { |a,b| a.date <=> b.date}
self.save!
end
@@ -555,8 +560,6 @@ class Chouette::TimeTable < Chouette::TridentActiveRecord
days = self.effective_days - days_to_exclude
self.dates.clear
self.periods.clear
- self.int_day_types = 0
-
days.each {|d| self.dates << Chouette::TimeTableDate.new( :date =>d, :in_out => true)}
self.dates.to_a.sort! { |a,b| a.date <=> b.date}
diff --git a/app/models/chouette/time_table_date.rb b/app/models/chouette/time_table_date.rb
index 4624ae88e..b881c9a5d 100644
--- a/app/models/chouette/time_table_date.rb
+++ b/app/models/chouette/time_table_date.rb
@@ -6,6 +6,8 @@ class Chouette::TimeTableDate < Chouette::ActiveRecord
validates_presence_of :date
validates_uniqueness_of :date, :scope => :time_table_id
+ scope :in_dates, -> { where(in_out: true) }
+
def self.model_name
ActiveModel::Name.new Chouette::TimeTableDate, Chouette, "TimeTableDate"
end
diff --git a/app/models/chouette/vehicle_journey.rb b/app/models/chouette/vehicle_journey.rb
index 72d554e96..44dd85864 100644
--- a/app/models/chouette/vehicle_journey.rb
+++ b/app/models/chouette/vehicle_journey.rb
@@ -28,17 +28,37 @@ module Chouette
has_and_belongs_to_many :time_tables, :class_name => 'Chouette::TimeTable', :foreign_key => "vehicle_journey_id", :association_foreign_key => "time_table_id"
has_many :stop_points, -> { order("stop_points.position") }, :through => :vehicle_journey_at_stops
- validates :vehicle_journey_at_stops,
+ validates :vehicle_journey_at_stops, :vjas_departure_time_must_be_before_next_stop_arrival_time,
vehicle_journey_at_stops_are_in_increasing_time_order: true
validates_presence_of :number
- before_validation :set_default_values
+ before_validation :set_default_values,
+ :calculate_vehicle_journey_at_stop_day_offset
+
+ def vjas_departure_time_must_be_before_next_stop_arrival_time
+ notice = 'departure time must be before next stop arrival time'
+ vehicle_journey_at_stops.each_with_index do |current_stop, index|
+ next_stop = vehicle_journey_at_stops[index + 1]
+
+ next unless next_stop && (next_stop[:arrival_time] < current_stop[:departure_time])
+
+ current_stop.errors.add(:departure_time, notice)
+ self.errors.add(:vehicle_journey_at_stops, notice)
+ end
+ end
+
def set_default_values
if number.nil?
self.number = 0
end
end
+ def calculate_vehicle_journey_at_stop_day_offset
+ Chouette::VehicleJourneyAtStopsDayOffset.new(
+ vehicle_journey_at_stops
+ ).update
+ end
+
scope :without_any_time_table, -> { joins('LEFT JOIN "time_tables_vehicle_journeys" ON "time_tables_vehicle_journeys"."vehicle_journey_id" = "vehicle_journeys"."id" LEFT JOIN "time_tables" ON "time_tables"."id" = "time_tables_vehicle_journeys"."time_table_id"').where(:time_tables => { :id => nil}) }
scope :without_any_passing_time, -> { joins('LEFT JOIN "vehicle_journey_at_stops" ON "vehicle_journey_at_stops"."vehicle_journey_id" = "vehicle_journeys"."id"').where(vehicle_journey_at_stops: { id: nil }) }
@@ -50,9 +70,12 @@ module Chouette
def vehicle_journey_at_stops_matrix
at_stops = self.vehicle_journey_at_stops.to_a.dup
+ active_stop_point_ids = journey_pattern.stop_points.map(&:id)
+
(route.stop_points.map(&:id) - at_stops.map(&:stop_point_id)).each do |id|
- # Set stop_point id for fake vjas with no departure time yep.
- at_stops.insert(route.stop_points.map(&:id).index(id), Chouette::VehicleJourneyAtStop.new(stop_point_id: id))
+ vjas = Chouette::VehicleJourneyAtStop.new(stop_point_id: id)
+ vjas.dummy = !active_stop_point_ids.include?(id)
+ at_stops.insert(route.stop_points.map(&:id).index(id), vjas)
end
at_stops
end
diff --git a/app/models/chouette/vehicle_journey_at_stop.rb b/app/models/chouette/vehicle_journey_at_stop.rb
index 553531422..5dfec8352 100644
--- a/app/models/chouette/vehicle_journey_at_stop.rb
+++ b/app/models/chouette/vehicle_journey_at_stop.rb
@@ -9,7 +9,7 @@ module Chouette
belongs_to :stop_point
belongs_to :vehicle_journey
- attr_accessor :_destroy
+ attr_accessor :_destroy, :dummy
validate :arrival_must_be_before_departure
def arrival_must_be_before_departure
@@ -27,6 +27,7 @@ module Chouette
after_initialize :set_virtual_attributes
def set_virtual_attributes
@_destroy = false
+ @dummy = false
end
end
diff --git a/app/models/chouette/vehicle_journey_at_stops_day_offset.rb b/app/models/chouette/vehicle_journey_at_stops_day_offset.rb
new file mode 100644
index 000000000..9f577dded
--- /dev/null
+++ b/app/models/chouette/vehicle_journey_at_stops_day_offset.rb
@@ -0,0 +1,42 @@
+module Chouette
+ class VehicleJourneyAtStopsDayOffset
+ def initialize(at_stops)
+ @at_stops = at_stops
+ end
+
+ def calculate!
+ arrival_offset = 0
+ departure_offset = 0
+
+ @at_stops.inject(nil) do |prior_stop, stop|
+ next stop if prior_stop.nil?
+
+ if stop.arrival_time < prior_stop.departure_time ||
+ stop.arrival_time < prior_stop.arrival_time
+ arrival_offset += 1
+ end
+
+ if stop.departure_time < stop.arrival_time ||
+ stop.departure_time < prior_stop.departure_time
+ departure_offset += 1
+ end
+
+ stop.arrival_day_offset = arrival_offset
+ stop.departure_day_offset = departure_offset
+
+ stop
+ end
+ end
+
+ def save
+ @at_stops.each do |at_stop|
+ at_stop.save
+ end
+ end
+
+ def update
+ calculate!
+ save
+ end
+ end
+end
diff --git a/app/models/clean_up.rb b/app/models/clean_up.rb
index a44bb46a4..a791065aa 100644
--- a/app/models/clean_up.rb
+++ b/app/models/clean_up.rb
@@ -1,9 +1,13 @@
class CleanUp < ActiveRecord::Base
+ extend Enumerize
include AASM
belongs_to :referential
has_one :clean_up_result
+ enumerize :date_type, in: %i(between before after)
+
validates :begin_date, presence: true
+ validates :date_type, presence: true
after_commit :perform_cleanup, :on => :create
def perform_cleanup
@@ -11,25 +15,96 @@ class CleanUp < ActiveRecord::Base
end
def clean
- result = {}
- result['time_table_count'] = self.clean_time_tables
- result['vehicle_journey_count'] = self.clean_vehicle_journeys
- result['journey_pattern_count'] = self.clean_journey_patterns
- result
+ {}.tap do |result|
+ result['time_table'] = send("destroy_time_tables_#{self.date_type}").try(:count)
+ result['time_table_date'] = send("destroy_time_tables_dates_#{self.date_type}").try(:count)
+ result['time_table_period'] = send("destroy_time_tables_periods_#{self.date_type}").try(:count)
+ self.overlapping_periods.each do |period|
+ exclude_dates_in_overlapping_period(period)
+ end
+ end
+ end
+
+ def destroy_time_tables_between
+ time_tables = Chouette::TimeTable.where('end_date <= ? AND start_date >= ?', self.end_date, self.begin_date)
+ self.destroy_time_tables(time_tables)
+ end
+
+ def destroy_time_tables_before
+ time_tables = Chouette::TimeTable.where('end_date < ?', self.begin_date)
+ self.destroy_time_tables(time_tables)
+ end
+
+ def destroy_time_tables_after
+ time_tables = Chouette::TimeTable.where('start_date > ?', self.begin_date)
+ self.destroy_time_tables(time_tables)
+ end
+
+ def destroy_time_tables_dates_before
+ Chouette::TimeTableDate.in_dates.where('date < ?', self.begin_date).destroy_all
+ end
+
+ def destroy_time_tables_dates_after
+ Chouette::TimeTableDate.in_dates.where('date > ?', self.begin_date).destroy_all
+ end
+
+ def destroy_time_tables_dates_between
+ Chouette::TimeTableDate.in_dates.where('date >= ? AND date <= ?', self.begin_date, self.end_date).destroy_all
end
- def clean_time_tables
- Chouette::TimeTable.validity_out_between?(begin_date, end_date).delete_all
+ def destroy_time_tables_periods_before
+ Chouette::TimeTablePeriod.where('period_end < ?', self.begin_date).destroy_all
end
- def clean_vehicle_journeys
- ids = Chouette::VehicleJourney.includes(:time_tables).where(:time_tables => {id: nil}).pluck(:id)
- Chouette::VehicleJourney.where(id: ids).delete_all
+ def destroy_time_tables_periods_after
+ Chouette::TimeTablePeriod.where('period_start > ?', self.begin_date).destroy_all
end
- def clean_journey_patterns
- ids = Chouette::JourneyPattern.includes(:vehicle_journeys).where(:vehicle_journeys => {id: nil}).pluck(:id)
- Chouette::JourneyPattern.where(id: ids).delete_all
+ def destroy_time_tables_periods_between
+ Chouette::TimeTablePeriod.where('period_start >= ? AND period_end <= ?', self.begin_date, self.end_date).destroy_all
+ end
+
+ def overlapping_periods
+ self.end_date = self.begin_date if self.date_type != 'between'
+ Chouette::TimeTablePeriod.where('(period_start, period_end) OVERLAPS (?, ?)', self.begin_date, self.end_date)
+ end
+
+ def exclude_dates_in_overlapping_period(period)
+ days_in_period = period.period_start..period.period_end
+ day_out = period.time_table.dates.where(in_out: false).map(&:date)
+ # check if day is greater or less then cleanup date
+ if date_type != 'between'
+ operator = date_type == 'after' ? '>' : '<'
+ to_exclude_days = days_in_period.map do |day|
+ day if day.public_send(operator, self.begin_date)
+ end
+ else
+ days_in_cleanup_periode = (self.begin_date..self.end_date)
+ to_exclude_days = days_in_period & days_in_cleanup_periode
+ end
+
+ to_exclude_days.to_a.compact.each do |day|
+ # we ensure day is not already an exclude date
+ if !day_out.include?(day)
+ self.add_exclude_date(period.time_table, day)
+ end
+ end
+ end
+
+ def add_exclude_date(time_table, day)
+ day_in = time_table.dates.where(in_out: true).map(&:date)
+ unless day_in.include?(day)
+ time_table.add_exclude_date(false, day)
+ else
+ time_table.dates.where(date: day).take.update_attribute(:in_out, false)
+ end
+ end
+
+ def destroy_time_tables(time_tables)
+ time_tables.each do |time_table|
+ time_table.vehicle_journeys.map(&:destroy)
+ end
+ time_tables.destroy_all
end
aasm column: :status do
diff --git a/app/models/referential.rb b/app/models/referential.rb
index 83d507320..0ce325bd6 100644
--- a/app/models/referential.rb
+++ b/app/models/referential.rb
@@ -1,13 +1,13 @@
-# -*- coding: utf-8 -*-
class Referential < ActiveRecord::Base
include DataFormatEnumerations
validates_presence_of :name
validates_presence_of :slug
validates_presence_of :prefix
- validates_presence_of :time_zone
- validates_presence_of :upper_corner
- validates_presence_of :lower_corner
+ # Fixme #3657
+ # validates_presence_of :time_zone
+ # validates_presence_of :upper_corner
+ # validates_presence_of :lower_corner
validates_uniqueness_of :slug
validates_uniqueness_of :name
@@ -127,7 +127,7 @@ class Referential < ActiveRecord::Base
end
def self.new_from(from, organisation:)
- Referential.new({
+ Referential.new(
name: I18n.t("activerecord.copy", :name => from.name),
slug: "#{from.slug}_clone",
prefix: from.prefix,
@@ -139,7 +139,7 @@ class Referential < ActiveRecord::Base
workbench: from.workbench,
created_from: from,
metadatas: from.metadatas.map { |m| ReferentialMetadata.new_from(m) }
- })
+ )
end
def self.available_srids
@@ -180,7 +180,9 @@ class Referential < ActiveRecord::Base
before_validation :assign_line_and_stop_area_referential, :on => :create, if: :workbench, unless: :created_from
before_validation :clone_associations, :on => :create, if: :created_from
- before_create :create_schema
+ before_validation :assign_slug, :on => :create
+ before_validation :assign_prefix, :on => :create
+ before_create :create_schema, unless: :created_from
after_create :clone_schema, if: :created_from
@@ -282,13 +284,21 @@ class Referential < ActiveRecord::Base
end
def clone_schema
- ReferentialCloning.create(source_referential: self.created_from, target_referential: self)
+ ReferentialCloning.create(source_referential: created_from, target_referential: self)
end
def create_schema
Apartment::Tenant.create slug
end
+ def assign_slug
+ self.slug ||= "#{self.name.parameterize.gsub('-', '_')}_#{Time.now.to_i}"
+ end
+
+ def assign_prefix
+ self.prefix = self.organisation.name.parameterize.gsub('-', '_')
+ end
+
def assign_line_and_stop_area_referential
self.line_referential = workbench.line_referential
self.stop_area_referential = workbench.stop_area_referential
diff --git a/app/models/referential_cloning.rb b/app/models/referential_cloning.rb
index 2f34093e2..5bf283814 100644
--- a/app/models/referential_cloning.rb
+++ b/app/models/referential_cloning.rb
@@ -6,7 +6,8 @@ class ReferentialCloning < ActiveRecord::Base
private
def perform_clone
- ReferentialCloningWorker.perform_async(self.id)
+ ReferentialCloningWorker.perform_async(id)
+ # ReferentialCloningWorker.new.perform(id)
end
aasm column: :status do
diff --git a/app/models/user.rb b/app/models/user.rb
index 14dbeb4d7..1a06746da 100644
--- a/app/models/user.rb
+++ b/app/models/user.rb
@@ -43,6 +43,8 @@ class User < ActiveRecord::Base
self.name = extra[:full_name]
self.email = extra[:email]
self.organisation = Organisation.sync_update extra[:organisation_code], extra[:organisation_name], extra[:functional_scope]
+ # TODO: Discuss the following behavior in the light of how the portal's permissions will evolve
+ # boiv:edit-offer does not imply boiv:read-offer, which needs to be provided specifically for any connection rights
self.permissions = extra[:permissions].include?('boiv:edit-offer') ? @@edit_offer_permissions : []
end
@@ -71,10 +73,10 @@ class User < ActiveRecord::Base
user.locked_at = el['locked_at']
user.organisation = Organisation.sync_update el['organization_code'], el['organization_name'], el['functional_scope']
user.synced_at = Time.now
+ # TODO: Discuss the following behavior in the light of how the portal's permissions will evolve
+ # boiv:edit-offer does not imply boiv:read-offer, which needs to be provided specifically for any connection rights
user.permissions = el['permissions'].include?('boiv:edit-offer') ? @@edit_offer_permissions : []
- user.permissions += el['permissions'].grep( %r{^\Aboiv:read-offer\z} )
user.save
- puts "✓ user #{user.username} has been updated" unless Rails.env.test?
end
end
diff --git a/app/policies/boiv_policy.rb b/app/policies/boiv_policy.rb
index 4270dc686..444006aa4 100644
--- a/app/policies/boiv_policy.rb
+++ b/app/policies/boiv_policy.rb
@@ -1,6 +1,6 @@
-require_relative 'chain'
class BoivPolicy < ApplicationPolicy
+
def boiv_read_offer?
organisation_match? && user.has_permission?('boiv:read-offer')
end
diff --git a/app/policies/chain.rb b/app/policies/chain.rb
deleted file mode 100644
index 4bf96bd84..000000000
--- a/app/policies/chain.rb
+++ /dev/null
@@ -1,57 +0,0 @@
-module Policies
- # Implements the `chain_policies` macro as follows
- #
- # chain_policies <method_chain>, policies:
- #
- # e.g.
- #
- # chain_policies [:archived?, :!], policies: %i{create? edit?}
- #
- # which would establish a precondition `not archived` for the `create?` and `edit?`
- # method, it is semantically identical to instrumenting both methods
- # as follows:
- #
- # def create? # or edit?
- # archived?.! && <original code of method>
- # end
- module Chain
-
- # A local chain store implemented to avoid any possible side effect on client policies.
- defined_chains = {}
-
- # Using `define_method` in order to close over `defined_chains`
- # We need to store the chains because the methods they will apply to
- # are not defined yet.
- define_method :chain_policies do |*conditions, policies:|
- policies.each do | meth_name |
- # self represents the client Policy
- defined_chains[[self, meth_name]] = conditions
- end
- end
- # Intercept method definition and check if a policy_chain has been registered for it‥.
- define_method :method_added do |meth_name, *args, &blk|
- # Delete potentially registered criteria conditions to‥.
- # (i) protect against endless recursion via (:merthod_added → :define_method → :method_added → ‥.
- # (ii) get the condition
- conditions = defined_chains.delete([self, meth_name])
- return unless conditions
-
- instrument_method(meth_name, conditions)
- end
-
- private
-
- # Access to the closure is not necessary anymore, normal metaprogramming can take place :)
- def instrument_method(meth_name, conditions)
- orig_method = instance_method(meth_name)
- # In case of warnings remove original method here, depends on Ruby Version, ok in 2.3.1
- define_method meth_name do |*a, &b|
- # Method chain describing the chained policy precondition.
- conditions.inject(self) do | result, msg |
- result.send msg
- end &&
- orig_method.bind(self).(*a, &b)
- end
- end
- end
-end
diff --git a/app/policies/journey_pattern_policy.rb b/app/policies/journey_pattern_policy.rb
index 9d13624c5..01ce2cbbb 100644
--- a/app/policies/journey_pattern_policy.rb
+++ b/app/policies/journey_pattern_policy.rb
@@ -1,4 +1,5 @@
class JourneyPatternPolicy < BoivPolicy
+
class Scope < Scope
def resolve
scope
diff --git a/app/policies/line_policy.rb b/app/policies/line_policy.rb
index c3e0051c8..b829040af 100644
--- a/app/policies/line_policy.rb
+++ b/app/policies/line_policy.rb
@@ -1,8 +1,4 @@
-require_relative 'chain'
class LinePolicy < BoivPolicy
- extend Policies::Chain
-
- chain_policies :archived?, :!, policies: %i{create_footnote? destroy_footnote? edit_footnote?}
class Scope < Scope
def resolve
@@ -19,15 +15,15 @@ class LinePolicy < BoivPolicy
def destroy? ; create? end
def create_footnote?
- user.has_permission?('footnotes.create')
+ !archived? && user.has_permission?('footnotes.create')
end
def edit_footnote?
- user.has_permission?('footnotes.edit')
+ !archived? && user.has_permission?('footnotes.edit')
end
def destroy_footnote?
- user.has_permission?('footnotes.destroy')
+ !archived? && user.has_permission?('footnotes.destroy')
end
def update_footnote? ; edit_footnote? end
diff --git a/app/policies/login_policy.rb b/app/policies/login_policy.rb
new file mode 100644
index 000000000..3364c37ac
--- /dev/null
+++ b/app/policies/login_policy.rb
@@ -0,0 +1,13 @@
+# Headless as described here https://github.com/elabs/pundit#headless-policies
+class LoginPolicy
+
+ attr_reader :user
+ def initialize user
+ @user = user
+ end
+
+ def boiv?
+ !(user.permissions || []).grep(%r{\Aboiv:.}).empty?
+ end
+
+end
diff --git a/app/policies/referential_policy.rb b/app/policies/referential_policy.rb
index 4a5e85ead..e531c6c19 100644
--- a/app/policies/referential_policy.rb
+++ b/app/policies/referential_policy.rb
@@ -25,6 +25,11 @@ class ReferentialPolicy < BoivPolicy
organisation_match? && create?
end
+ def common_lines?
+ # TODO: Replace with correct BL ASA available, c.f. https://projects.af83.io/issues/2692
+ true
+ end
+
def unarchive? ; archive? end
def update? ; edit? end
def new? ; create? end
diff --git a/app/policies/route_policy.rb b/app/policies/route_policy.rb
index dba3a27da..ca9b02164 100644
--- a/app/policies/route_policy.rb
+++ b/app/policies/route_policy.rb
@@ -1,23 +1,20 @@
class RoutePolicy < BoivPolicy
- extend Policies::Chain
class Scope < Scope
def resolve
scope
end
end
- chain_policies :archived?, :!, policies: %i{create? destroy? edit?}
-
def create?
- user.has_permission?('routes.create') # organisation match via referential is checked in the view
+ !archived? && user.has_permission?('routes.create') # organisation match via referential is checked in the view
end
def edit?
- organisation_match? && user.has_permission?('routes.edit')
+ !archived? && organisation_match? && user.has_permission?('routes.edit')
end
def destroy?
- organisation_match? && user.has_permission?('routes.destroy')
+ !archived? && organisation_match? && user.has_permission?('routes.destroy')
end
def update? ; edit? end
diff --git a/app/policies/routing_constraint_zone_policy.rb b/app/policies/routing_constraint_zone_policy.rb
index abba5639c..da311bc03 100644
--- a/app/policies/routing_constraint_zone_policy.rb
+++ b/app/policies/routing_constraint_zone_policy.rb
@@ -1,23 +1,20 @@
class RoutingConstraintZonePolicy < BoivPolicy
- extend Policies::Chain
class Scope < Scope
def resolve
scope
end
end
- chain_policies :archived?, :!, policies: %i{create? destroy? edit?}
-
def create?
- user.has_permission?('routing_constraint_zones.create') # organisation match via referential is checked in the view
+ !archived? && user.has_permission?('routing_constraint_zones.create') # organisation match via referential is checked in the view
end
def edit?
- organisation_match? && user.has_permission?('routing_constraint_zones.edit')
+ !archived? && organisation_match? && user.has_permission?('routing_constraint_zones.edit')
end
def destroy?
- organisation_match? && user.has_permission?('routing_constraint_zones.destroy')
+ !archived? && organisation_match? && user.has_permission?('routing_constraint_zones.destroy')
end
def update? ; edit? end
diff --git a/app/policies/time_table_policy.rb b/app/policies/time_table_policy.rb
index efab6ac00..e915ede6a 100644
--- a/app/policies/time_table_policy.rb
+++ b/app/policies/time_table_policy.rb
@@ -1,5 +1,4 @@
class TimeTablePolicy < BoivPolicy
- extend Policies::Chain
class Scope < Scope
def resolve
@@ -7,22 +6,20 @@ class TimeTablePolicy < BoivPolicy
end
end
- chain_policies :archived?, :!, policies: %i{create? destroy? duplicate? edit?}
-
def create?
- user.has_permission?('time_tables.create') # organisation match via referential is checked in the view
+ !archived? && user.has_permission?('time_tables.create') # organisation match via referential is checked in the view
end
def edit?
- organisation_match? && user.has_permission?('time_tables.edit')
+ !archived? && organisation_match? && user.has_permission?('time_tables.edit')
end
def destroy?
- organisation_match? && user.has_permission?('time_tables.destroy')
+ !archived? && organisation_match? && user.has_permission?('time_tables.destroy')
end
def duplicate?
- organisation_match? && create?
+ !archived? && organisation_match? && create?
end
def update? ; edit? end
diff --git a/app/views/calendars/_filters.html.slim b/app/views/calendars/_filters.html.slim
index 4f625e4f0..6bcf25f99 100644
--- a/app/views/calendars/_filters.html.slim
+++ b/app/views/calendars/_filters.html.slim
@@ -1,7 +1,7 @@
= search_form_for @q, url: calendars_path, builder: SimpleForm::FormBuilder, html: { method: :get, class: 'form form-filter' } do |f|
.ffg-row
.input-group.search_bar
- = f.search_field :short_name_cont, class: 'form-control', placeholder: 'Indiquez un nom de calendrier...'
+ = f.search_field :name_or_short_name_cont, class: 'form-control', placeholder: 'Indiquez un nom/nom court de calendrier...'
span.input-group-btn
button.btn.btn-default#search_btn type='submit'
span.fa.fa-search
diff --git a/app/views/calendars/_form.html.slim b/app/views/calendars/_form.html.slim
index 9aed9f7d8..3c152c61d 100644
--- a/app/views/calendars/_form.html.slim
+++ b/app/views/calendars/_form.html.slim
@@ -6,8 +6,8 @@
- if policy(@calendar).share?
.form-group.has_switch
- = f.label :shared, class: 'col-sm-4 control-label'
- = f.input :shared, as: :boolean, checked_value: true, unchecked_value: false, label: content_tag(:span, t("#{@calendar.shared}"), class: 'switch-label', data: {checkedValue: t('true'), uncheckedValue: t('false')}), wrapper_html: { class: 'col-sm-8'}
+ = f.label :shared, class: 'col-sm-4 col-xs-5 control-label'
+ = f.input :shared, as: :boolean, checked_value: true, unchecked_value: false, label: content_tag(:span, t("#{@calendar.shared}"), class: 'switch-label', data: {checkedValue: t('true'), uncheckedValue: t('false')}), wrapper_html: { class: 'col-sm-8 col-xs-7'}
.separator
diff --git a/app/views/layouts/application.html.slim b/app/views/layouts/application.html.slim
index dc1ec9766..6eab2a761 100644
--- a/app/views/layouts/application.html.slim
+++ b/app/views/layouts/application.html.slim
@@ -8,10 +8,10 @@ html lang=I18n.locale
title STIF BOIV
- = stylesheet_link_tag 'base', media: 'all', 'data-turbolinks-track': true
- = stylesheet_link_tag 'application', media: 'all', 'data-turbolinks-track': true
+ = stylesheet_link_tag 'base'
+ = stylesheet_link_tag 'application'
- = javascript_include_tag 'application', 'data-turbolinks-track': true
+ = javascript_include_tag 'application'
body
diff --git a/app/views/referential_lines/index.js.slim b/app/views/referential_lines/index.js.slim
deleted file mode 100644
index 0165fb077..000000000
--- a/app/views/referential_lines/index.js.slim
+++ /dev/null
@@ -1 +0,0 @@
-| $('#lines').html("#{escape_javascript(render('lines'))}"); \ No newline at end of file
diff --git a/app/views/referential_lines/show.html.slim b/app/views/referential_lines/show.html.slim
index 1e24f0f15..db99381d3 100644
--- a/app/views/referential_lines/show.html.slim
+++ b/app/views/referential_lines/show.html.slim
@@ -1,26 +1,23 @@
/ PageHeader
= pageheader 'map-marker',
@line.name,
- 'Lorem ipsum dolor sit amet',
+ '',
t('last_update', time: l(@line.updated_at, format: :short)) do
/ Below is secundary actions & optional contents
.row
.col-lg-12.text-right.mb-sm
- / = link_to t('lines.actions.show_network'), [@referential, @line.network], class: 'btn btn-primary'
- / = link_to t('lines.actions.show_company'), [@referential, @line.company], class: 'btn btn-primary'
-
= link_to @line.human_attribute_name(:footnotes), referential_line_footnotes_path(@referential, @line), class: 'btn btn-primary'
- = link_to Chouette::RoutingConstraintZone.model_name.human.pluralize(:fr), referential_line_routing_constraint_zones_path(@referential, @line), class: 'btn btn-primary disabled'
+ = link_to t('routing_constraint_zones.index.title'), referential_line_routing_constraint_zones_path(@referential, @line), class: 'btn btn-primary'
- if policy(Chouette::Line).create? && @referential.organisation == current_organisation
- = link_to t('lines.actions.new'), new_referential_line_path(@referential), class: 'btn btn-primary'
+ = link_to t('actions.new'), new_referential_line_path(@referential), class: 'btn btn-primary'
- if policy(@line).update?
- = link_to t('lines.actions.edit'), edit_referential_line_path(@referential, @line), class: 'btn btn-primary'
+ = link_to t('actions.edit'), edit_referential_line_path(@referential, @line), class: 'btn btn-primary'
- if policy(@line).destroy?
= link_to referential_line_path(@referential, @line), method: :delete, data: {confirm: t('lines.actions.destroy_confirm')}, class: 'btn btn-primary' do
span.fa.fa-trash
- span = t('lines.actions.destroy')
+ span = t('actions.destroy')
- if !@line.hub_restricted? || (@line.hub_restricted? && @line.routes.size < 2)
- if policy(Chouette::Route).create? && @referential.organisation == current_organisation
diff --git a/app/views/referentials/_form.html.slim b/app/views/referentials/_form.html.slim
index f209b278e..31d71bcdc 100644
--- a/app/views/referentials/_form.html.slim
+++ b/app/views/referentials/_form.html.slim
@@ -3,34 +3,16 @@
.row
.col-lg-12
= form.input :name
-
- - if @referential.new_record?
- = form.input :slug, input_html: { title: t('formtastic.titles.referential.slug') }
- - else
- = form.input :slug, disabled: true, input_html: { title: t('formtastic.titles.referential.slug') }
-
- if @referential.new_record?
- if @referential.created_from
= form.input :created_from, disabled: true, input_html: { value: Referential.find(@referential.created_from_id).name }
.hidden = form.input :created_from_id, as: :hidden
-
- = form.input :prefix, input_html: { title: t("formtastic.titles.referential.prefix") }
-
- else
- if @referential.created_from
= form.input :created_from, disabled: true, input_html: { value: Referential.find(@referential.created_from_id).name }
- = form.input :prefix, input_html: { title: t("formtastic.titles.referential.prefix") }
-
- = form.input :projection_type, as: :select, collection: Referential.available_srids
- = form.input :time_zone
-
-
- = form.input :upper_corner, input_html: {title: t("formtastic.titles.referential.upper_corner")}
- = form.input :lower_corner, input_html: {title: t("formtastic.titles.referential.lower_corner")}
-
.separator
-
+
= form.simple_fields_for :metadatas do |subform|
.row
.col-lg-12
@@ -40,7 +22,7 @@
.alert.alert-danger
- @referential.errors[:metadatas].each do |msg|
p.small = "- #{msg}"
-
+
.subform
.nested-head
.wrapper
@@ -55,10 +37,10 @@
= t('simple_form.labels.referential.metadatas.periods.end')
abbr title='requis' *
div
-
+
= subform.simple_fields_for :periods do |period_form|
= render 'period_fields', f: period_form
-
+
.links.nested-linker
= link_to_add_association 'Ajouter une période', subform, :periods, class: 'btn btn-outline-primary'
diff --git a/app/views/referentials/show.html.slim b/app/views/referentials/show.html.slim
index befa851ab..3c1e36302 100644
--- a/app/views/referentials/show.html.slim
+++ b/app/views/referentials/show.html.slim
@@ -75,9 +75,12 @@
.container-fluid
.row
.col-lg-8.col-ld-offset-2.col-md-8.col-md-offset-2.col-sm-8.col-sm-offset-2.col-xs-12
+ = f.input :date_type, as: :radio_buttons, label: false
= f.input :begin_date, as: :date, label: t('titles.clean_up.begin_date'),:wrapper_html => { class: 'date', title: t('titles.clean_up.begin_date') }
- = f.input :end_date, as: :date, label: t('titles.clean_up.end_date'), :wrapper_html => { class: 'date', title: t('titles.clean_up.end_date') }
+
+ = f.input :end_date, as: :date, label: t('titles.clean_up.end_date'), :wrapper_html => { class: 'date cleanup_end_date_wrapper hidden', title: t('titles.clean_up.end_date') }
.modal-footer
button.btn.btn-link type='button' data-dismiss='modal' Annuler
- = f.button :submit, t('actions.clean_up') , class: 'btn btn-primary'
+ - unless policy(@referential).archived?
+ = f.button :submit, t('actions.clean_up') , class: 'btn btn-primary'
diff --git a/app/views/routes/_form.html.slim b/app/views/routes/_form.html.slim
index ad389c482..bee86f91e 100644
--- a/app/views/routes/_form.html.slim
+++ b/app/views/routes/_form.html.slim
@@ -6,8 +6,8 @@
= f.input :published_name
.form-group.has_switch
- = f.label :wayback, class: 'col-sm-4 control-label'
- = f.input :wayback, as: :boolean, checked_value: :straight_forward, unchecked_value: :backward, label: content_tag(:span, @route.wayback_text, class: 'switch-label', data: {checkedValue: t('enumerize.route.direction.straight_forward'), uncheckedValue: t('enumerize.route.direction.backward')}), wrapper_html: { class: 'col-sm-8'}
+ = f.label :wayback, class: 'col-sm-4 col-xs-5 control-label'
+ = f.input :wayback, as: :boolean, checked_value: :straight_forward, unchecked_value: :backward, label: content_tag(:span, @route.wayback_text, class: 'switch-label', data: {checkedValue: t('enumerize.route.direction.straight_forward'), uncheckedValue: t('enumerize.route.direction.backward')}), wrapper_html: { class: 'col-sm-8 col-xs-7'}
= f.input :opposite_route_id, collection: @forward, disabled: @route.wayback.straight_forward?, wrapper_html: {class: input_opposite_route_id_css(@route, 'straight_forward')}
diff --git a/app/views/routes/new.html.slim b/app/views/routes/new.html.slim
index 6ac95f023..102467677 100644
--- a/app/views/routes/new.html.slim
+++ b/app/views/routes/new.html.slim
@@ -1,7 +1,7 @@
/ PageHeader
= pageheader 'map-marker',
t('routes.new.title'),
- 'Lorem ipsum dolor sit amet'
+ ''
/ PageContent
.page_content
diff --git a/app/views/routes/show.html.slim b/app/views/routes/show.html.slim
index d994e3861..6d2d4e90d 100644
--- a/app/views/routes/show.html.slim
+++ b/app/views/routes/show.html.slim
@@ -3,15 +3,15 @@
@route.name,
'Lorem ipsum dolor sit amet',
t('last_update', time: l(@route.updated_at, format: :short)),
- (policy(@route).edit? ? link_to(t('actions.edit'), edit_referential_line_route_path(@referential, @line, @route), data: {turbolinks: false}, class: 'btn btn-default') : '') do
+ (policy(@route).edit? ? link_to(t('actions.edit'), edit_referential_line_route_path(@referential, @line, @route), class: 'btn btn-default') : '') do
/ Below is secundary actions & optional contents (filters, ...)
.row.mb-sm
.col-lg-12.text-right
- if @route_sp.any?
- = link_to t('journey_patterns.index.title'), [@referential, @line, @route, :journey_patterns_collection], data: {turbolinks: false}, class: 'btn btn-primary'
+ = link_to t('journey_patterns.index.title'), [@referential, @line, @route, :journey_patterns_collection], class: 'btn btn-primary'
- if @route.journey_patterns.present?
- = link_to t('vehicle_journeys.actions.index'), [@referential, @line, @route, :vehicle_journeys], data: {turbolinks: false}, class: 'btn btn-primary'
+ = link_to t('vehicle_journeys.actions.index'), [@referential, @line, @route, :vehicle_journeys], class: 'btn btn-primary'
= link_to t('vehicle_journey_exports.new.title'), referential_line_route_vehicle_journey_exports_path(@referential, @line, @route, format: :zip), class: 'btn btn-primary'
diff --git a/app/views/routing_constraint_zones/_filters.html.slim b/app/views/routing_constraint_zones/_filters.html.slim
new file mode 100644
index 000000000..18ef40d61
--- /dev/null
+++ b/app/views/routing_constraint_zones/_filters.html.slim
@@ -0,0 +1,16 @@
+= search_form_for @q, url: referential_line_routing_constraint_zones_path(@referential, @line), class: 'form form-filter' do |f|
+ .ffg-row
+ .input-group.search_bar
+ = f.search_field :name_or_objectid_cont, class: 'form-control', placeholder: "Indiquez un nom d'ITL ou un OiD..."
+ span.input-group-btn
+ button.btn.btn-default#search-btn type='submit'
+ span.fa.fa-search
+
+ .ffg-row
+ .form-group
+ = f.label 'Itinéraire associé', required: false, class: 'control-label'
+ = f.input :route_id, as: :select, collection: @line.routing_constraint_zones.pluck(:route_id), label: false, label_method: lambda {|r| @line.routing_constraint_zones.find_by(route_id: r).route_name}, input_html: { 'data-select2ed': 'true', 'data-select2ed-placeholder': 'Indiquez un itinéraire...' }, wrapper_html: { class: 'select2ed'}
+
+ .actions
+ = link_to 'Effacer', referential_line_routing_constraint_zones_path(@referential, @line), class: 'btn btn-link'
+ = f.submit 'Filtrer', class: 'btn btn-default'
diff --git a/app/views/routing_constraint_zones/_form.html.slim b/app/views/routing_constraint_zones/_form.html.slim
index b1c77a44b..3d4764ef7 100644
--- a/app/views/routing_constraint_zones/_form.html.slim
+++ b/app/views/routing_constraint_zones/_form.html.slim
@@ -1,19 +1,25 @@
-= simple_form_for [@referential, @line, @routing_constraint_zone] do |f|
- .row
- .col-lg-6.col-sm-12
- = f.input :name
- .row
- .col-lg-6.col-sm-12
- = f.input :route_id, collection: @line.routes.select { |route| route.stop_points.count > 2 }, include_blank: false
- .row
- .col-lg-6.col-sm-12
- - stop_points_collection = @routing_constraint_zone.persisted? ? @routing_constraint_zone.route.stop_points : []
- = f.input :stop_point_ids, as: :select, collection: stop_points_collection, selected: @routing_constraint_zone.stop_point_ids, label: Chouette::StopPoint.model_name.human.pluralize.capitalize, label_method: :name, input_html: { 'data-select2ed': 'true', 'data-select2ed-placeholder': 'Sélection des arrêts sur séquence d\'arrêts', 'multiple': 'multiple', style: 'width: 100%' }
+= simple_form_for [@referential, @line, @routing_constraint_zone], html: {class: 'form-horizontal', id: 'itl_form'}, wrapper: :horizontal_form do |form|
.row
- .col-lg-12.text-right
- = link_to 'Annuler', :back, class: 'btn btn-link'
- = f.button :submit, class: 'btn btn-danger'
+ .col-lg-12
+ = form.input :name
+ = form.input :route_id, collection: @line.routes.select { |route| route.stop_points.count > 2 }, include_blank: false
+
+ .separator
+
+ #ITL_stoppoints
+ .subform
+ .nested-head
+ .wrapper
+ div
+ .form-group
+ label.control-label Arrêt
+ div
+ .form-group
+ label.control-label Commune
+ div
+
+ = hidden_field_tag 'stop_point_ids', @routing_constraint_zone.stop_point_ids.to_s, id: 'stop_point_ids'
-= hidden_field_tag 'stop_point_ids', @routing_constraint_zone.stop_point_ids.to_s, id: 'stop_point_ids'
+ = form.button :submit, t('actions.submit'), class: 'btn btn-default formSubmitr', form: 'itl_form'
diff --git a/app/views/routing_constraint_zones/edit.html.slim b/app/views/routing_constraint_zones/edit.html.slim
index fcb0d08a8..d81a347e0 100644
--- a/app/views/routing_constraint_zones/edit.html.slim
+++ b/app/views/routing_constraint_zones/edit.html.slim
@@ -1,5 +1,12 @@
-= title_tag t('.title', routing_constraint_zone: @routing_constraint_zone.name)
+/ PageHeader
+= pageheader 'map-marker',
+ t('.title'),
+ '',
+ t('last_update', time: l(@routing_constraint_zone.updated_at, format: :short))
-.row
- .col-lg-8.col-lg-offset-2.col-md-8.col-md-offset-2.col-sm-8.col-sm-offset-2
- == render 'form'
+/ PageContent
+.page_content
+ .container-fluid
+ .row
+ .col-lg-8.col-lg-offset-2.col-md-8.col-md-offset-2.col-sm-10.col-sm-offset-1
+ == render 'form'
diff --git a/app/views/routing_constraint_zones/index.html.slim b/app/views/routing_constraint_zones/index.html.slim
index 620b0f7db..248efedb4 100644
--- a/app/views/routing_constraint_zones/index.html.slim
+++ b/app/views/routing_constraint_zones/index.html.slim
@@ -1,11 +1,30 @@
-= title_tag Chouette::RoutingConstraintZone.model_name.human.pluralize(:fr)
+/ PageHeader
+= pageheader 'map-marker',
+ t('routing_constraint_zones.index.title'),
+ '',
+ '',
+ ((policy(Chouette::RoutingConstraintZone).create? && @referential.organisation == current_organisation) ? link_to(t('actions.new'), new_referential_line_routing_constraint_zone_path(@referential, @line), class: 'btn btn-primary') : '')
-- if policy(Chouette::RoutingConstraintZone).create? && @referential.organisation == current_organisation
- = link_to t('routing_constraint_zones.actions.new'), new_referential_line_routing_constraint_zone_path(@referential, @line)
+/ PageContent
+.page_content
+ .container-fluid
+ - if params[:q].present? or @routing_constraint_zones.any?
+ .row
+ .col-lg-12
+ = render 'filters'
-- if @routing_constraint_zones.any?
- = table_builder @routing_constraint_zones,
- { objectid: 'objectid', name: 'name', route: 'route_name', stop_points_count: 'stop_points_count' },
- [:show, :edit, :delete],
- [],
- 'table table-bordered'
+ - if @routing_constraint_zones.any?
+ .row
+ .col-lg-12
+ = table_builder @routing_constraint_zones,
+ { 'Oid' => Proc.new { |n| n.try(:objectid).try(:local_id) },
+ :name => 'name', :stop_points_count => 'stop_points_count',
+ :route => 'route_name' },
+ [:show, :edit, :delete],
+ [],
+ 'table has-filter has-search'
+
+ - unless @routing_constraint_zones.any?
+ .row.mt-xs
+ .col-lg-12
+ = replacement_msg t('routing_constraint_zones.search_no_results')
diff --git a/app/views/routing_constraint_zones/new.html.slim b/app/views/routing_constraint_zones/new.html.slim
index fcb0d08a8..f6ae62825 100644
--- a/app/views/routing_constraint_zones/new.html.slim
+++ b/app/views/routing_constraint_zones/new.html.slim
@@ -1,5 +1,10 @@
-= title_tag t('.title', routing_constraint_zone: @routing_constraint_zone.name)
+/ PageHeader
+= pageheader 'map-marker',
+ t('.title')
-.row
- .col-lg-8.col-lg-offset-2.col-md-8.col-md-offset-2.col-sm-8.col-sm-offset-2
- == render 'form'
+/ PageContent
+.page_content
+ .container-fluid
+ .row
+ .col-lg-8.col-lg-offset-2.col-md-8.col-md-offset-2.col-sm-10.col-sm-offset-1
+ == render 'form'
diff --git a/app/views/routing_constraint_zones/show.html.slim b/app/views/routing_constraint_zones/show.html.slim
index 351784ecc..f0c244387 100644
--- a/app/views/routing_constraint_zones/show.html.slim
+++ b/app/views/routing_constraint_zones/show.html.slim
@@ -1,17 +1,35 @@
-= title_tag @routing_constraint_zone.name
+/ PageHeader
+= pageheader 'map-marker',
+ @routing_constraint_zone.name,
+ '',
+ t('last_update', time: l(@routing_constraint_zone.updated_at, format: :short)) do
-p
- label => "#{@routing_constraint_zone.human_attribute_name(:name)} : "
- = @routing_constraint_zone.name
+ / Below is secundary actions & optional contents
+ .row
+ .col-lg-12.text-right.mb-sm
+ - if policy(@routing_constraint_zone).update?
+ = link_to t('actions.edit'), edit_referential_line_routing_constraint_zone_path(@referential, @line, @routing_constraint_zone), class: 'btn btn-primary'
-p
- label => "#{Chouette::Route.model_name.human.capitalize} : "
- = link_to @routing_constraint_zone.route.name, referential_line_route_path(@referential, @line, @routing_constraint_zone.route)
+ - if policy(@routing_constraint_zone).destroy?
+ = link_to referential_line_routing_constraint_zone_path(@referential, @line, @routing_constraint_zone), method: :delete, data: {confirm: t('routing_constraint_zones.actions.destroy_confirm')}, class: 'btn btn-primary' do
+ span.fa.fa-trash
+ span = t('actions.destroy')
-p
- label => "#{Chouette::StopPoint.model_name.human.pluralize.capitalize} : "
- br
- - @routing_constraint_zone.stop_points.each do |stop_point|
- = link_to stop_point.name, referential_stop_area_path(@referential, stop_point.stop_area)
- br
+/ PageContent
+.page_content
+ .container-fluid
+ .row
+ .col-lg-6.col-md-6.col-sm-12.col-xs-12
+ = definition_list t('metadatas'),
+ { @routing_constraint_zone.human_attribute_name(:name) => @routing_constraint_zone.try(:name),
+ @routing_constraint_zone.human_attribute_name(:route) => link_to(@routing_constraint_zone.try(:route_name), [@referential, @line, @routing_constraint_zone.route]),
+ @routing_constraint_zone.human_attribute_name(:line) => link_to(@line.name, [@referential, @line])}
+ .row
+ .col-lg-12
+ = table_builder @routing_constraint_zone.route.stop_points,
+ { "Arrêts de l'itinéraire" => 'name',
+ "Arrêts inclus dans l'ITL" => Proc.new{ |rsp| (@routing_constraint_zone.stop_points.collect{|c| c.name}.include? rsp.name) ? 'Oui' : 'Non' } },
+ [],
+ [],
+ 'table has-stoppoints'
diff --git a/app/views/time_table_combinations/_form.html.slim b/app/views/time_table_combinations/_form.html.slim
index d8bebf0c4..b4f818828 100644
--- a/app/views/time_table_combinations/_form.html.slim
+++ b/app/views/time_table_combinations/_form.html.slim
@@ -2,21 +2,21 @@
.row
.col-lg-12
.form-group.has_switch
- = f.label :combined_type, class: 'col-sm-4 control-label required' do
+ = f.label :combined_type, class: 'col-sm-4 col-xs-5 control-label required' do
= 'Type de calendriers '
abbr title='Champ requis' *
- = f.input :combined_type, as: :boolean, checked_value: 'time_table', unchecked_value: 'calendar', required: false, label: content_tag(:span, t("time_table_combinations.combined_type.#{@combination.combined_type}"), class: 'switch-label', data: { checkedValue: 'Calendriers', uncheckedValue: 'Modèles de calendriers' }), wrapper_html: { class: 'col-sm-8' }
+ = f.input :combined_type, as: :boolean, checked_value: 'time_table', unchecked_value: 'calendar', required: false, label: content_tag(:span, t("time_table_combinations.combined_type.#{@combination.combined_type}"), class: 'switch-label', data: { checkedValue: 'Calendriers', uncheckedValue: 'Modèles de calendriers' }), wrapper_html: { class: 'col-sm-8 col-xs-7' }
- = f.input :time_table_id, as: :select, input_html: {class: 'tt_combination_target', style: "width: 100%", data: { 'select2-ajax': 'true', term: 'comment_or_objectid_cont', url: referential_autocomplete_time_tables_path(@referential, format: :json)}}, wrapper_html: {class: @combination.combined_type != 'time_table' ? 'hidden' : ''}
+ = f.input :time_table_id, as: :select, input_html: {class: 'tt_combination_target', style: "width: 100%", data: { 'select2-ajax': 'true', 'select2ed-placeholder': 'Indiquez un calendrier...', term: 'comment_or_objectid_cont', url: referential_autocomplete_time_tables_path(@referential, format: :json)}}, wrapper_html: {class: @combination.combined_type != 'time_table' ? 'hidden' : ''}
- = f.input :calendar_id, as: :select, input_html: { class: 'tt_combination_target', style: "width: 100%", data: { 'select2-ajax': 'true', term: 'name_cont', url: autocomplete_calendars_path}}, wrapper_html: {class: @combination.combined_type != 'calendar' ? 'hidden' : ''}
+ = f.input :calendar_id, as: :select, input_html: { class: 'tt_combination_target', style: "width: 100%", data: { 'select2-ajax': 'true', 'select2ed-placeholder': 'Indiquez un modèle de calendrier...', term: 'name_cont', url: autocomplete_calendars_path}}, wrapper_html: {class: @combination.combined_type != 'calendar' ? 'hidden' : ''}
.separator
.row
.col-lg-12
- = f.label :operation, class: 'col-sm-4 control-label'
- = f.input :operation, as: :radio_buttons, label: false, collection: TimeTableCombination.operations, label_method: lambda{|o| t("time_table_combinations.operations.#{o}")}, wrapper_html: { class: 'col-sm-8' }
+ = f.label :operation, class: 'col-sm-4 col-xs-5 control-label'
+ = f.input :operation, as: :radio_buttons, label: false, collection: TimeTableCombination.operations, label_method: lambda{|o| t("time_table_combinations.operations.#{o}")}, wrapper_html: { class: 'col-sm-8 col-xs-7' }
= f.button :submit, t('actions.submit'), class: 'btn btn-default formSubmitr', form: 'tt_combination_form'
diff --git a/app/views/time_tables/_form.html.slim b/app/views/time_tables/_form.html.slim
index d4e1d838e..d06fdf444 100644
--- a/app/views/time_tables/_form.html.slim
+++ b/app/views/time_tables/_form.html.slim
@@ -5,7 +5,7 @@
= form.input :comment, :input_html => { :title => t("formtastic.titles#{format_restriction_for_locales(@referential)}.time_table.comment")}
- if @time_table.new_record? && !@time_table.created_from
- = form.input :calendar_id, as: :select, collection: current_organisation.calendars
+ = form.input :calendar_id, as: :select, input_html: { class: 'tt_target', style: "width: 100%", data: { 'select2-ajax': 'true', 'select2ed-placeholder': 'Indiquez un modèle de calendrier...', term: 'name_cont', url: autocomplete_calendars_path}}
- if @time_table.created_from
= form.input :created_from, disabled: true, input_html: { value: @time_table.created_from.comment }
diff --git a/app/views/time_tables/index.html.slim b/app/views/time_tables/index.html.slim
index c17f96c85..402b09b98 100644
--- a/app/views/time_tables/index.html.slim
+++ b/app/views/time_tables/index.html.slim
@@ -20,7 +20,7 @@
"Nombre de courses associées" => Proc.new{ |tt| tt.vehicle_journeys.count },
"Journées d'application" => Proc.new{ |tt| (%w(monday tuesday wednesday thursday friday saturday sunday).collect{|d| tt.send(d) ? t("calendars.days.#{d}") : '' }).reject{|a| a.empty?}.join(', ').html_safe },
:calendar => Proc.new{ |tt| tt.calendar ? tt.calendar.try(:name) : '-' }, :updated_at => Proc.new {|tt| l(tt.updated_at, format: :short)} },
- [:show, :edit, :duplicate, :delete],
+ [:show, :edit, :duplicate, :actualize, :delete],
[],
'table has-search'
diff --git a/app/views/time_tables/show.html.slim b/app/views/time_tables/show.html.slim
index 1c5984a7d..2e71ebb9e 100644
--- a/app/views/time_tables/show.html.slim
+++ b/app/views/time_tables/show.html.slim
@@ -4,6 +4,7 @@
= pageheader 'map-marker',
@time_table.comment,
'',
+ t('last_update', time: l(@time_table.updated_at, format: :short)),
(policy(@time_table).edit? ? link_to(t('actions.edit'), edit_referential_time_table_path(@referential, @time_table), class: 'btn btn-default') : '')
/ Below is secundary actions & optional contents (filters, ...)
diff --git a/app/views/vehicle_journeys/index.html.slim b/app/views/vehicle_journeys/index.html.slim
index 38e07ad82..93f4e3221 100644
--- a/app/views/vehicle_journeys/index.html.slim
+++ b/app/views/vehicle_journeys/index.html.slim
@@ -16,6 +16,9 @@
| window.route_id = #{params[:route_id]};
| window.stopPoints = #{(@stop_points_list.to_json).html_safe};
| window.jpOrigin = #{(@jp_origin.to_json).html_safe};
+ | window.jpOriginStopPoints = #{(@jp_origin_stop_points.to_json).html_safe};
+ | window.transportMode = #{(@transport_mode.to_json).html_safe};
+ | window.transportSubmode = #{(@transport_submode.to_json).html_safe};
| window.vehicleJourneysLength = #{@vehicle_journeys.total_entries()};
| window.vehicleJourneysPerPage = #{@ppage};
| window.line_footnotes = #{raw @footnotes};
diff --git a/app/views/vehicle_journeys/show.rabl b/app/views/vehicle_journeys/show.rabl
index 1ef9bc6b5..fce16dfb3 100644
--- a/app/views/vehicle_journeys/show.rabl
+++ b/app/views/vehicle_journeys/show.rabl
@@ -31,18 +31,19 @@ end
child(:vehicle_journey_at_stops_matrix, :object_root => false) do |vehicle_stops|
node do |vehicle_stop|
- node(:dummy) { !vehicle_stop.id? }
+ node(:dummy) { vehicle_stop.dummy }
+
node(:stop_area_object_id) do
- vehicle_stop.stop_point ? vehicle_stop.stop_point.stop_area.objectid : nil
+ vehicle_stop.stop_point.stop_area.objectid
end
node(:stop_point_objectid) do
- vehicle_stop.stop_point ? vehicle_stop.stop_point.objectid : nil
+ vehicle_stop.stop_point.objectid
end
node(:stop_area_name) do
- vehicle_stop.stop_point ? vehicle_stop.stop_point.stop_area.name : nil
+ vehicle_stop.stop_point.stop_area.name
end
node(:stop_area_cityname) do
- vehicle_stop.stop_point ? vehicle_stop.stop_point.stop_area.city_name : nil
+ vehicle_stop.stop_point.stop_area.city_name
end
[:id, :connecting_service_id, :boarding_alighting_possibility].map do |att|
diff --git a/app/workers/referential_cloning_worker.rb b/app/workers/referential_cloning_worker.rb
index ef3acd529..6592160ec 100644
--- a/app/workers/referential_cloning_worker.rb
+++ b/app/workers/referential_cloning_worker.rb
@@ -1,13 +1,12 @@
class ReferentialCloningWorker
include Sidekiq::Worker
- sidekiq_options queue: 'wip'
# Replace default apartment created schema with clone schema from source referential
def perform(id)
ref_cloning = ReferentialCloning.find id
source_schema = ref_cloning.source_referential.slug
- target_schema = "#{source_schema}_tmp"
+ target_schema = ref_cloning.target_referential.slug
clone_schema ref_cloning, source_schema, target_schema
end
@@ -17,9 +16,9 @@ class ReferentialCloningWorker
def clone_schema ref_cloning, source_schema, target_schema
ref_cloning.run!
- StoredProcedures.invoke_stored_procedure(:clone_schema, source_schema, target_schema, true)
- execute_sql "DROP SCHEMA #{source_schema} CASCADE;"
- execute_sql "ALTER SCHEMA #{target_schema} RENAME TO #{source_schema};"
+ AF83::SchemaCloner
+ .new(source_schema, target_schema)
+ .clone_schema
ref_cloning.successful!
rescue Exception => e
diff --git a/bin/spring b/bin/spring
index 7b45d374f..fb2ec2ebb 100755
--- a/bin/spring
+++ b/bin/spring
@@ -4,12 +4,14 @@
# It gets overwritten when you run the `spring binstub` command.
unless defined?(Spring)
- require "rubygems"
- require "bundler"
+ require 'rubygems'
+ require 'bundler'
- if match = Bundler.default_lockfile.read.match(/^GEM$.*?^ (?: )*spring \((.*?)\)$.*?^$/m)
- Gem.paths = { "GEM_PATH" => [Bundler.bundle_path.to_s, *Gem.path].uniq }
- gem "spring", match[1]
- require "spring/binstub"
+ lockfile = Bundler::LockfileParser.new(Bundler.default_lockfile.read)
+ spring = lockfile.specs.detect { |spec| spec.name == "spring" }
+ if spring
+ Gem.use_paths Gem.dir, Bundler.bundle_path.to_s, *Gem.path
+ gem 'spring', spring.version
+ require 'spring/binstub'
end
end
diff --git a/config/initializers/active_record.rb b/config/initializers/active_record.rb
index bdf9e0b4b..0a7378532 100644
--- a/config/initializers/active_record.rb
+++ b/config/initializers/active_record.rb
@@ -1,5 +1 @@
-require_relative '../../lib/af83/stored_procedures'
-
ActiveRecord::ConnectionAdapters::PostgreSQLAdapter::NATIVE_DATABASE_TYPES[:primary_key] = "bigserial primary key"
-
-StoredProcedures.create_stored_procedure(:clone_schema)
diff --git a/config/initializers/simple_form_bootstrap.rb b/config/initializers/simple_form_bootstrap.rb
index 67dfe1ceb..d90ea6398 100644
--- a/config/initializers/simple_form_bootstrap.rb
+++ b/config/initializers/simple_form_bootstrap.rb
@@ -58,9 +58,9 @@ SimpleForm.setup do |config|
b.optional :pattern
b.optional :min_max
b.optional :readonly
- b.use :label, class: 'col-sm-4 control-label'
+ b.use :label, class: 'col-sm-4 col-xs-5 control-label'
- b.wrapper tag: 'div', class: 'col-sm-8' do |ba|
+ b.wrapper tag: 'div', class: 'col-sm-8 col-xs-7' do |ba|
ba.use :input, class: 'form-control'
ba.use :error, wrap_with: { tag: 'span', class: 'help-block small' }
ba.use :hint, wrap_with: { tag: 'p', class: 'help-block small' }
diff --git a/config/locales/actions.en.yml b/config/locales/actions.en.yml
index 6e2dd3aa2..fe4d3d4e5 100644
--- a/config/locales/actions.en.yml
+++ b/config/locales/actions.en.yml
@@ -5,6 +5,7 @@ en:
delete: "Delete"
search: "Search"
add: "Add new"
+ new: "Add new"
show: "See"
archive: "Archive"
unarchive: "Unarchive"
diff --git a/config/locales/actions.fr.yml b/config/locales/actions.fr.yml
index 8f2fc90f8..ae0537ebb 100644
--- a/config/locales/actions.fr.yml
+++ b/config/locales/actions.fr.yml
@@ -6,6 +6,7 @@ fr:
search: "Chercher"
submit: "Valider"
add: 'Ajouter'
+ new: 'Ajouter'
show: 'Consulter'
archive: 'Conserver'
unarchive: 'Déconserver'
diff --git a/config/locales/devise.en.yml b/config/locales/devise.en.yml
index af6feed2c..453b094fc 100644
--- a/config/locales/devise.en.yml
+++ b/config/locales/devise.en.yml
@@ -13,6 +13,7 @@ validating conformance of data wrt Neptune (French standard NFP 99 506)."
introduction2: "The application is deployed in Saas mode and supports :"
introduction_item1: "several data exchange formats (Neptune, GTFS, CSV ... Netex coming soon)"
introduction_item2: "and also several base map backgrounds (Google, OSM, IGN)."
+ unauthorized: "Failed to connect due to missing IBOO connection rights"
confirmations:
new:
title: Resend confirmation instructions
diff --git a/config/locales/devise.fr.yml b/config/locales/devise.fr.yml
index 54b2ff680..d3fd6d03e 100644
--- a/config/locales/devise.fr.yml
+++ b/config/locales/devise.fr.yml
@@ -14,6 +14,7 @@ norme Neptune (NFP 99 506)"
introduction2: "Déployé en mode Saas, ce logiciel est ouvert et peut gérer"
introduction_item1: "plusieurs formats (Neptune, GTFS, CSV, prochainement Netex)"
introduction_item2: "plusieurs fonds cartographiques, notamment IGN, OSM et Google."
+ unauthorized: "Vous ne pouvez pas vous connecter car vous n'avez pas les permissions pour accéder à IBOO"
confirmations:
new:
title: Renvoyer le mail de confirmation
diff --git a/config/locales/enumerize.en.yml b/config/locales/enumerize.en.yml
index 8857e593c..f76237288 100644
--- a/config/locales/enumerize.en.yml
+++ b/config/locales/enumerize.en.yml
@@ -105,6 +105,11 @@ en:
zdlp: ZDLp
zdlr: ZDLr
lda: LDA
+ clean_up:
+ date_type:
+ between: Between two dates
+ before: Before date
+ after: After date
line:
transport_mode:
interchange: Interchange
@@ -193,7 +198,7 @@ en:
regionalRail: 'Regional rail'
interregionalRail: 'Interregional rail'
longDistance: 'Long distance'
- international: 'International'
+ intermational: 'International'
sleeperRailService: 'Sleeper rail service'
nightRail: 'Night rail'
carTransportRailService: 'Car transport rail service'
diff --git a/config/locales/enumerize.fr.yml b/config/locales/enumerize.fr.yml
index 8b6c8092d..14aedd155 100644
--- a/config/locales/enumerize.fr.yml
+++ b/config/locales/enumerize.fr.yml
@@ -104,6 +104,11 @@ fr:
zdlp: ZDLp
zdlr: ZDLr
lda: LDA
+ clean_up:
+ date_type:
+ between: Entre deux dates
+ before: Avant une date
+ after: Après une date
line:
transport_mode:
interchange: Interconnection
@@ -193,7 +198,7 @@ fr:
regionalRail: 'Train régional'
interregionalRail: 'Train interrégional'
longDistance: 'Longue distance'
- international: 'International'
+ intermational: Internationale
sleeperRailService: 'Train à couchettes'
nightRail: 'Train de nuit'
carTransportRailService: 'Service ferroviaire de transport de voitures'
diff --git a/config/locales/routing_constraint_zones.en.yml b/config/locales/routing_constraint_zones.en.yml
index 2d4412dba..59d4484a8 100644
--- a/config/locales/routing_constraint_zones.en.yml
+++ b/config/locales/routing_constraint_zones.en.yml
@@ -11,7 +11,8 @@ en:
updated_at: Updated at
objectid: Object ID
stop_points_count: Number of stop points
- route: Route
+ route: Associated route
+ route_id: Associated route
errors:
models:
routing_constraint_zone:
@@ -21,6 +22,7 @@ en:
stop_points_not_from_route: 'Stop point does not belong to the Route of this Routing constraint zone.'
all_stop_points_selected: 'All stop points from route cannot be selected.'
routing_constraint_zones:
+ search_no_results: "No ITL matches your query"
actions:
new: New routing constraint zone
edit: Edit this routing constraint zone
@@ -32,4 +34,5 @@ en:
title: "Update routing constraint zone %{routing_constraint_zone}"
show:
title: "Routing constraint zone %{routing_constraint_zone}"
-
+ index:
+ title: "Interdictions of local trafficous"
diff --git a/config/locales/routing_constraint_zones.fr.yml b/config/locales/routing_constraint_zones.fr.yml
index 36dcf9301..4faff6606 100644
--- a/config/locales/routing_constraint_zones.fr.yml
+++ b/config/locales/routing_constraint_zones.fr.yml
@@ -6,12 +6,13 @@ fr:
routing_constraint_zone:
name: Nom
stop_areas: Arrêts
- line: Ligne
+ line: Ligne associée
created_at: "Créé le"
updated_at: "Edité le"
objectid: Object ID
stop_points_count: Nombre d'arrêts
- route: Itinéraire
+ route: Itinéraire associé
+ route_id: Itinéraire associé
errors:
models:
routing_constraint_zone:
@@ -21,14 +22,14 @@ fr:
stop_points_not_from_route: "Arrêt sur séquence d'arrêts n'appartient pas à la Route de cette Zone de contrainte."
all_stop_points_selected: "Une zone de contrainte ne peut pas couvrir tous les arrêts d'une ligne."
routing_constraint_zones:
+ search_no_results: "Aucune ITL ne correspond à votre recherche"
actions:
- new: Ajouter une zone de contrainte
- edit: Editer cette zone de contrainte
- destroy: Supprimer cette zone de contrainte
destroy_confirm: Etes vous sûr de supprimer cette zone de contrainte ?
new:
- title: Ajouter une zone de contrainte
+ title: Ajouter un ITL
edit:
title: "Editer la zone de contrainte %{routing_constraint_zone}"
show:
title: "Zone de contrainte %{routing_constraint_zone}"
+ index:
+ title: "Interdictions de traffic local"
diff --git a/config/locales/time_tables.en.yml b/config/locales/time_tables.en.yml
index 6c5aac012..dc6c6d9f4 100644
--- a/config/locales/time_tables.en.yml
+++ b/config/locales/time_tables.en.yml
@@ -87,6 +87,7 @@ en:
calendars: "Calendar view"
calendar_details: "Calendar details"
calendar: "Calendar"
+ calendar_id: "Calendar"
dates: "Peculiar dates"
date: "On"
excluded_dates: "Excluded dates"
diff --git a/config/locales/time_tables.fr.yml b/config/locales/time_tables.fr.yml
index 36a588eb4..886aaa263 100644
--- a/config/locales/time_tables.fr.yml
+++ b/config/locales/time_tables.fr.yml
@@ -68,7 +68,7 @@ fr:
other: "calendriers"
attributes:
time_table:
- comment: "Nom"
+ comment: "Nom du calendrier"
color: "Couleur associée"
bounding_dates: 'Période contenue dans le calendrier'
version: "Abréviation"
@@ -89,6 +89,7 @@ fr:
calendars: "Calendrier"
calendar_details: "Données du calendrier"
calendar: Modèle de calendrier
+ calendar_id: Modèle de calendrier
dates: "Dates particulières"
date: "Le"
excluded_dates: "Dates exclues"
diff --git a/db/migrate/20170531154114_add_default_to_vehicle_journey_at_stops_arrival_and_departure_day_offset.rb b/db/migrate/20170531154114_add_default_to_vehicle_journey_at_stops_arrival_and_departure_day_offset.rb
new file mode 100644
index 000000000..593d5822d
--- /dev/null
+++ b/db/migrate/20170531154114_add_default_to_vehicle_journey_at_stops_arrival_and_departure_day_offset.rb
@@ -0,0 +1,6 @@
+class AddDefaultToVehicleJourneyAtStopsArrivalAndDepartureDayOffset < ActiveRecord::Migration
+ def change
+ change_column_default :vehicle_journey_at_stops, :arrival_day_offset, 0
+ change_column_default :vehicle_journey_at_stops, :departure_day_offset, 0
+ end
+end
diff --git a/db/migrate/20170605135126_add_date_type_to_clean_ups.rb b/db/migrate/20170605135126_add_date_type_to_clean_ups.rb
new file mode 100644
index 000000000..693e1ccce
--- /dev/null
+++ b/db/migrate/20170605135126_add_date_type_to_clean_ups.rb
@@ -0,0 +1,5 @@
+class AddDateTypeToCleanUps < ActiveRecord::Migration
+ def change
+ add_column :clean_ups, :date_type, :string
+ end
+end
diff --git a/db/migrate/20170607141031_change_begin_date_from_clean_ups.rb b/db/migrate/20170607141031_change_begin_date_from_clean_ups.rb
new file mode 100644
index 000000000..850616b18
--- /dev/null
+++ b/db/migrate/20170607141031_change_begin_date_from_clean_ups.rb
@@ -0,0 +1,9 @@
+class ChangeBeginDateFromCleanUps < ActiveRecord::Migration
+ def up
+ change_column :clean_ups, :begin_date, :date
+ end
+
+ def down
+ change_column :clean_ups, :begin_date, :datetime
+ end
+end
diff --git a/db/migrate/20170607141317_change_end_date_from_clean_ups.rb b/db/migrate/20170607141317_change_end_date_from_clean_ups.rb
new file mode 100644
index 000000000..e47197ff1
--- /dev/null
+++ b/db/migrate/20170607141317_change_end_date_from_clean_ups.rb
@@ -0,0 +1,9 @@
+class ChangeEndDateFromCleanUps < ActiveRecord::Migration
+ def up
+ change_column :clean_ups, :end_date, :date
+ end
+
+ def down
+ change_column :clean_ups, :end_date, :datetime
+ end
+end
diff --git a/db/schema.rb b/db/schema.rb
index a45181687..e64e5c04a 100644
--- a/db/schema.rb
+++ b/db/schema.rb
@@ -11,7 +11,7 @@
#
# It's strongly recommended that you check this file into your version control system.
-ActiveRecord::Schema.define(version: 20170502130327) do
+ActiveRecord::Schema.define(version: 20170607141317) do
# These are extensions that must be enabled in order to support this database
enable_extension "plpgsql"
@@ -108,10 +108,11 @@ ActiveRecord::Schema.define(version: 20170502130327) do
t.datetime "started_at"
t.datetime "ended_at"
t.integer "referential_id", limit: 8
- t.datetime "begin_date"
+ t.date "begin_date"
t.datetime "created_at"
t.datetime "updated_at"
- t.datetime "end_date"
+ t.date "end_date"
+ t.string "date_type"
end
add_index "clean_ups", ["referential_id"], name: "index_clean_ups_on_referential_id", using: :btree
@@ -283,7 +284,7 @@ ActiveRecord::Schema.define(version: 20170502130327) do
t.datetime "started_at"
t.datetime "ended_at"
t.string "token_download"
- t.string "type"
+ t.string "type", limit: 255
end
add_index "imports", ["referential_id"], name: "index_imports_on_referential_id", using: :btree
@@ -601,7 +602,7 @@ ActiveRecord::Schema.define(version: 20170502130327) do
create_table "stop_areas", id: :bigserial, force: :cascade do |t|
t.integer "parent_id", limit: 8
- t.string "objectid", null: false
+ t.string "objectid", null: false
t.integer "object_version", limit: 8
t.string "creator_id"
t.string "name"
@@ -610,8 +611,8 @@ ActiveRecord::Schema.define(version: 20170502130327) do
t.string "registration_number"
t.string "nearest_topic_name"
t.integer "fare_code"
- t.decimal "longitude", precision: 19, scale: 16
- t.decimal "latitude", precision: 19, scale: 16
+ t.decimal "longitude", precision: 19, scale: 16
+ t.decimal "latitude", precision: 19, scale: 16
t.string "long_lat_type"
t.string "country_code"
t.string "street_name"
@@ -629,7 +630,7 @@ ActiveRecord::Schema.define(version: 20170502130327) do
t.datetime "deleted_at"
t.datetime "created_at"
t.datetime "updated_at"
- t.string "stif_type"
+ t.string "stif_type", limit: 255
end
add_index "stop_areas", ["name"], name: "index_stop_areas_on_name", using: :btree
@@ -696,18 +697,18 @@ ActiveRecord::Schema.define(version: 20170502130327) do
add_index "time_table_periods", ["time_table_id"], name: "index_time_table_periods_on_time_table_id", using: :btree
create_table "time_tables", id: :bigserial, force: :cascade do |t|
- t.string "objectid", null: false
- t.integer "object_version", limit: 8, default: 1
+ t.string "objectid", null: false
+ t.integer "object_version", limit: 8, default: 1
t.string "creator_id"
t.string "version"
t.string "comment"
- t.integer "int_day_types", default: 0
+ t.integer "int_day_types", default: 0
t.date "start_date"
t.date "end_date"
t.integer "calendar_id", limit: 8
t.datetime "created_at"
t.datetime "updated_at"
- t.string "color"
+ t.string "color", limit: 255
t.integer "created_from_id"
end
@@ -783,8 +784,8 @@ ActiveRecord::Schema.define(version: 20170502130327) do
t.time "departure_time"
t.string "for_boarding"
t.string "for_alighting"
- t.integer "departure_day_offset"
- t.integer "arrival_day_offset"
+ t.integer "departure_day_offset", default: 0
+ t.integer "arrival_day_offset", default: 0
end
add_index "vehicle_journey_at_stops", ["stop_point_id"], name: "index_vehicle_journey_at_stops_on_stop_pointid", using: :btree
diff --git a/install/README b/install/README
new file mode 100644
index 000000000..9825e6575
--- /dev/null
+++ b/install/README
@@ -0,0 +1,9 @@
+Script d'initialisation de l'environnement logiciel pour accueillir le BOIV
+Prérequis : debian 8 (Jessie) netinst
+
+Paramètres : exporter les variables d'environnements
+DATABASE_HOST permet de régler l'hôte qui héberge la base de données
+valeur par défaut : localhost
+
+Procédure : en tant qu'utilisateur root, exécuter la commande depuis le répertoire courrant
+./stif-boiv-setup.sh
diff --git a/install/deploy-helper.sh b/install/deploy-helper.sh
new file mode 100755
index 000000000..487c4539c
--- /dev/null
+++ b/install/deploy-helper.sh
@@ -0,0 +1,102 @@
+#!/bin/bash -e
+
+export BASEDIR=$PREFIX/var/www/stif-boiv
+
+export RUN_USER=www-data
+export RUN_GROUP=src
+
+export SUDO=""
+
+function setup() {
+ mkdir -p $BASEDIR
+ mkdir -p $BASEDIR/releases $BASEDIR/shared
+
+ $SUDO mkdir -p $PREFIX/etc/stif-boiv
+ ln -fs $PREFIX/etc/stif-boiv $BASEDIR/shared/config
+
+ mkdir -p $BASEDIR/shared/config/environments
+
+ mkdir -p $BASEDIR/shared/public
+ mkdir -p $BASEDIR/shared/public/uploads
+ mkdir -p $BASEDIR/shared/public/assets
+
+ mkdir -p $BASEDIR/shared/tmp/uploads
+
+ $SUDO chown $RUN_USER:$RUN_GROUP $BASEDIR/shared/public/uploads $BASEDIR/shared/tmp/uploads
+
+ default_config
+}
+
+function default_config() {
+ DATABASE_PASSWORD=${DATABASE_PASSWORD:-FIXME}
+ DATABASE_HOST=${DATABASE_HOST:-"localhost"}
+
+ cd $BASEDIR/shared/config
+
+ if [ ! -f secrets.yml ]; then
+ cat > secrets.yml <<EOF
+production:
+ secret_key_base: `echo $RANDOM | sha512sum | cut -f1 -d' '`
+EOF
+ fi
+
+ if [ ! -f database.yml ]; then
+ cat > database.yml <<EOF
+production:
+ adapter: postgresql
+ encoding: unicode
+ pool: 5
+
+ host: $DATABASE_HOST
+ database: stif-boiv
+
+ username: stif-boiv
+ password: $DATABASE_PASSWORD
+EOF
+ fi
+}
+
+function install() {
+ tar_file=$1
+
+ # stif-boiv-20160617154541.tar
+ release_name=`echo $tar_file | sed 's/.*-\([0-9]*\)\.tar/\1/g'`
+
+ RELEASE_PATH=$BASEDIR/releases/$release_name
+
+ if [ -d $RELEASE_PATH ]; then
+ echo "Release directory $RELEASE_PATH already exists"
+ return
+ fi
+
+ mkdir -p $RELEASE_PATH
+
+ tar -xf $tar_file -C $RELEASE_PATH
+
+ cd $RELEASE_PATH
+
+ mkdir -p tmp
+
+ for directory in public/uploads tmp/uploads public/assets; do
+ local_directory=$BASEDIR/shared/$directory
+ release_directory=$directory
+
+ rm -rf $release_directory
+ ln -s $local_directory $release_directory
+ done
+
+ for file in secrets.yml database.yml environments/production.rb; do
+ local_file=$BASEDIR/shared/config/$file
+ release_file=config/$file
+
+ rm $release_file && ln -fs $local_file $release_file
+ done
+
+ echo "Release installed into $RELEASE_PATH"
+}
+
+command=$1
+shift
+
+set -x
+$command $@
diff --git a/install/sidekiq-stif-boiv.service b/install/sidekiq-stif-boiv.service
new file mode 100644
index 000000000..90c6ed391
--- /dev/null
+++ b/install/sidekiq-stif-boiv.service
@@ -0,0 +1,52 @@
+#
+# systemd unit file for CentOS 7, Ubuntu 15.04
+#
+# Customize this file based on your bundler location, app directory, etc.
+# Put this in /usr/lib/systemd/system (CentOS) or /lib/systemd/system (Ubuntu).
+# Run:
+# - systemctl enable sidekiq
+# - systemctl {start,stop,restart} sidekiq
+#
+# This file corresponds to a single Sidekiq process. Add multiple copies
+# to run multiple processes (sidekiq-1, sidekiq-2, etc).
+#
+# See Inspeqtor's Systemd wiki page for more detail about Systemd:
+# https://github.com/mperham/inspeqtor/wiki/Systemd
+#
+[Unit]
+Description=sidekiq stif-boiv
+# start us only once the network and logging subsystems are available,
+# consider adding redis-server.service if Redis is local and systemd-managed.
+After=syslog.target network.target
+
+# See these pages for lots of options:
+# http://0pointer.de/public/systemd-man/systemd.service.html
+# http://0pointer.de/public/systemd-man/systemd.exec.html
+[Service]
+Type=simple
+WorkingDirectory=/var/www/stif-boiv/current
+PIDFile=/var/run/sidekiq-stif-boiv.pid
+
+PermissionsStartOnly=true
+ExecStartPre=-/bin/touch /var/run/sidekiq-stif-boiv.pid
+ExecStartPre=/bin/chown www-data:www-data /var/run/sidekiq-stif-boiv.pid
+
+ExecStart= /usr/local/bin/bundle exec sidekiq -e production -P /var/run/sidekiq-stif-boiv.pid
+
+User=www-data
+Group=www-data
+UMask=0002
+
+# if we crash, restart
+RestartSec=10
+Restart=on-failure
+
+# output goes to /var/log/syslog
+StandardOutput=syslog
+StandardError=syslog
+
+# This will default to "bundler" if we don't specify it
+SyslogIdentifier=stif-boiv/sidekiq
+
+[Install]
+WantedBy=multi-user.target
diff --git a/install/stif-boiv-setup.sh b/install/stif-boiv-setup.sh
new file mode 100755
index 000000000..a2b8bd4a6
--- /dev/null
+++ b/install/stif-boiv-setup.sh
@@ -0,0 +1,84 @@
+#!/bin/bash -e
+
+DATABASE_HOST=${DATABASE_HOST:-"localhost"}
+
+# mandatory packages and distribution upgrade
+apt-get update && apt-get dist-upgrade
+apt-get install -y wget sudo
+
+# ruby
+echo "==== Installation de Ruby 2.3"
+cat > /etc/apt/sources.list.d/bearstech.list <<EOF
+deb http://deb.bearstech.com/debian jessie-bearstech main
+EOF
+
+wget -q -O - http://deb.bearstech.com/bearstech-archive.gpg | apt-key add -
+apt-get update
+apt-get install -y ruby2.3 ruby2.3-dev
+apt-get install -y libsqlite3-dev libproj-dev libpq-dev
+gem2.3 install bundler
+
+# Apache / Passenger
+echo "==== Installation de Apache 2.4 et Passenger"
+apt-get install -y apache2 libapache2-mod-passenger
+
+cp stif-boiv.conf /etc/apache2/sites-available/
+
+a2enmod expires
+a2ensite stif-boiv
+
+# Redis
+
+echo "==== Installation de Redis"
+
+apt-get install -y redis-server
+
+# Sidekiq
+
+echo "==== Installation de Sidekiq comme service"
+cp sidekiq-stif-boiv.service /etc/systemd/system/
+systemctl enable sidekiq-stif-boiv
+
+
+echo "==== Installation de PostgreSQL"
+if [ "x$DATABASE_HOST" = "xlocalhost" ]; then
+apt-get install -y postgresql-9.4 postgresql-9.4-postgis-2.1 postgresql-contrib-9.4
+[ -d /usr/local/share/postgresql ] || mkdir -p /usr/local/share/postgresql/
+cp template-stif-boiv.sql /usr/local/share/postgresql/
+pushd .
+cd /usr/local/share/postgresql
+sudo -u postgres createdb --encoding UTF-8 template_stif_boiv < template-stif-boiv.sql
+popd
+echo "Saisissez le mot de passe de la base de données. Il vous sera redemandé ultérieurement"
+sudo -u postgres createuser --pwprompt stif_boiv
+sudo -u postgres createdb --owner stif_boiv --template template_stif_boiv stif_boiv
+else
+echo "W! Base de donnée externe : Pas d'installation"
+fi
+
+# NodeJS
+
+echo "==== Installation de NodeJS 5.x"
+apt-get install -y apt-transport-https
+
+cat > /etc/apt/sources.list.d/nodesource.list <<EOF
+deb https://deb.nodesource.com/node_5.x jessie main
+EOF
+
+wget -q -O - https://deb.nodesource.com/gpgkey/nodesource.gpg.key | apt-key add -
+apt-get update
+
+apt-get install -y nodejs
+
+# Configuration de l'applicatif
+
+echo "==== Paramétrage de l'applicatif"
+echo -n "Veuillez saisir à nouveau le mot de passe d'accès à la base de données :"
+read -s DATABASE_PASSWORD
+
+export DATABASE_PASSWORD
+
+PGPASSWORD=$DATABASE_PASSWORD PGHOST=$DATABASE_HOST PGUSER=stif_boiv psql -q -c 'select 1' stif_boiv >/dev/null 2>&1 && echo "Mot de passe correct"
+./deploy-helper.sh setup
+
+echo "!!! Configuration intiale terminée. Vous pouvez maintenant déployer l'applicatif"
diff --git a/install/stif-boiv.conf b/install/stif-boiv.conf
new file mode 100644
index 000000000..6b2abe4eb
--- /dev/null
+++ b/install/stif-boiv.conf
@@ -0,0 +1,38 @@
+<VirtualHost *:80>
+ ServerName boiv.stif.info
+
+ DocumentRoot /var/www/stif-boiv/current/public
+
+ PassengerDefaultUser www-data
+ PassengerUserSwitching off
+
+ PassengerRuby /usr/bin/ruby2.3
+ RackEnv production
+
+ ExpiresActive On
+
+ ExpiresByType application/javascript "access plus 1 year"
+ ExpiresByType application/x-javascript "access plus 1 year"
+ ExpiresByType application/x-shockwave-flash "access plus 1 year"
+ ExpiresByType image/gif "access plus 1 year"
+ ExpiresByType image/ico "access plus 1 year"
+ ExpiresByType image/jpeg "access plus 1 year"
+ ExpiresByType image/jpg "access plus 1 year"
+ ExpiresByType image/png "access plus 1 year"
+ ExpiresByType image/vnd.microsoft.icon "access plus 1 year"
+ ExpiresByType image/x-icon "access plus 1 year"
+ ExpiresByType text/css "access plus 1 year"
+ ExpiresByType text/javascript "access plus 1 year"
+ ExpiresByType font/truetype "access plus 1 year"
+ ExpiresByType application/x-font-ttf "access plus 1 year"
+
+ <Directory /var/www/stif-boiv/current/public>
+ AllowOverride None
+ Require all granted
+ </Directory>
+
+ <Location /sidekiq>
+ # Replace with correct policy
+ Require all denied
+ </Location>
+</virtualHost>
diff --git a/install/template-stif-boiv.sql b/install/template-stif-boiv.sql
new file mode 100644
index 000000000..4d4a19afe
--- /dev/null
+++ b/install/template-stif-boiv.sql
@@ -0,0 +1,11 @@
+set ON_ERROR_STOP=on;
+
+CREATE SCHEMA shared_extensions;
+GRANT ALL ON SCHEMA shared_extensions TO PUBLIC;
+CREATE EXTENSION IF NOT EXISTS postgis WITH SCHEMA shared_extensions;
+CREATE EXTENSION IF NOT EXISTS hstore WITH SCHEMA shared_extensions;
+
+UPDATE pg_database SET datistemplate = TRUE WHERE datname = 'template_stif_boiv';
+
+VACUUM FULL FREEZE;
+
diff --git a/lib/af83/schema_cloner.rb b/lib/af83/schema_cloner.rb
new file mode 100644
index 000000000..6ffb646f3
--- /dev/null
+++ b/lib/af83/schema_cloner.rb
@@ -0,0 +1,151 @@
+module AF83
+ class SchemaCloner
+
+ attr_reader :source_schema, :target_schema
+
+ def clone_schema
+ assure_schema_preconditons
+ create_target_schema
+ end
+
+ private
+
+ def adjust_default table_name, column_name, default_val
+ changed_default = default_val.sub(%r{\Anextval\('#{source_schema}}, "nextval('#{target_schema}")
+ execute "ALTER TABLE #{target_schema}.#{table_name} ALTER COLUMN #{column_name} SET DEFAULT #{changed_default}"
+ end
+
+ def adjust_defaults table_name
+ pairs = execute <<-EOSQL
+ SELECT column_name, column_default
+ FROM information_schema.columns
+ WHERE table_schema = '#{target_schema}' AND table_name = '#{table_name}' AND
+ column_default LIKE 'nextval(''#{source_schema}%::regclass)'
+ EOSQL
+ pairs.each do | pair |
+ adjust_default table_name, pair['column_name'], pair['column_default']
+ end
+ end
+
+ def alter_sequence sequence_name
+ seq_props = execute_get_ostruct( "SELECT * FROM #{source_schema}.#{sequence_name}" )
+ cycle_on_off = seq_props.is_cycled == 't' ? '' : 'NO'
+ execute <<-EOSQL
+ ALTER SEQUENCE #{target_schema}.#{sequence_name}
+ INCREMENT BY #{seq_props.increment_by}
+ MINVALUE #{seq_props.min_value}
+ MAXVALUE #{seq_props.max_value}
+ START WITH #{seq_props.start_value}
+ RESTART WITH #{seq_props.last_value}
+ CACHE #{seq_props.cache_value}
+ #{cycle_on_off} CYCLE;
+
+ -- TODO: What is this good for?
+ SELECT setval('#{target_schema}.#{sequence_name}', #{seq_props.last_value}, '#{seq_props.is_called}');
+ EOSQL
+ end
+
+ def assure_schema_preconditons
+ raise RuntimeError, "Source Schema #{source_schema} does not exist" unless source
+ log 'found', source
+ end
+
+ def clone_foreign_key fk_desc
+ relname, conname, constraint_def = fk_desc.values_at(*%w[relname conname constraint_def])
+ constraint_def = constraint_def.sub(" REFERENCES #{source_schema}.", " REFERENCES #{target_schema}.")
+ execute <<-EOSQL
+ ALTER TABLE #{target_schema}.#{relname} ADD CONSTRAINT #{conname} #{constraint_def}
+ EOSQL
+ end
+ def clone_foreign_keys
+ get_foreign_keys.each(&method(:clone_foreign_key))
+ end
+
+ def clone_sequence sequence_name
+ create_sequence sequence_name
+ alter_sequence sequence_name
+ end
+ def clone_sequences
+ source_sequence_names.each(&method(:clone_sequence))
+ end
+
+ def clone_table table_name
+ create_table table_name
+ end
+
+ def clone_tables
+ table_names.each(&method(:clone_table))
+ end
+
+ def create_sequence sequence_name
+ execute "CREATE SEQUENCE #{target_schema}.#{sequence_name}"
+ end
+ def create_table table_name
+ execute "CREATE TABLE #{target_schema}.#{table_name} (LIKE #{source_schema}.#{table_name} INCLUDING ALL)"
+ execute "INSERT INTO #{target_schema}.#{table_name} SELECT * FROM #{source_schema}.#{table_name}"
+ adjust_defaults table_name
+ end
+ def create_target_schema
+ execute("CREATE SCHEMA IF NOT EXISTS #{target_schema}")
+ clone_sequences
+ clone_tables
+ clone_foreign_keys
+ end
+ def execute(str)
+ connection.execute(str).to_a
+ end
+ def execute_get_first(str)
+ execute(str).first
+ end
+ def execute_get_ostruct(str)
+ OpenStruct.new(execute_get_first(str))
+ end
+ def execute_get_values(str)
+ execute(str).flat_map(&:values)
+ end
+
+ def get_foreign_keys
+ execute <<-EOS
+ SELECT rn.relname, ct.conname, pg_get_constraintdef(ct.oid) AS constraint_def
+ FROM pg_constraint ct JOIN pg_class rn ON rn.oid = ct.conrelid
+ WHERE connamespace = #{source['oid']} AND rn.relkind = 'r' AND ct.contype = 'f'
+ EOS
+ end
+ def get_columns(table_name)
+ end
+
+ def initialize(source_schema, target_schema)
+ @source_schema = source_schema
+ @target_schema = target_schema
+ end
+
+ def log(*messages)
+ messages.each do | message |
+ Rails.logger.info "SchemaCloner: #{message.inspect}"
+ end
+ end
+
+ #
+ # Memvars
+ # -------
+ def connection
+ @__connection__ ||= ActiveRecord::Base.connection
+ end
+
+ def source
+ @__source__ ||= execute("SELECT oid FROM pg_namespace WHERE nspname = '#{source_schema}' LIMIT 1").first;
+ end
+ def source_sequence_names
+ @__source_sequence_names__ ||=
+ execute_get_values \
+ "SELECT sequence_name::text FROM information_schema.sequences WHERE sequence_schema = '#{source_schema}'"
+ end
+ def source_oid
+ @__source_oid__ ||= source["oid"].to_i;
+ end
+ def table_names
+ @__table_names__ ||= execute_get_values \
+ "SELECT TABLE_NAME::text FROM information_schema.tables WHERE table_schema = '#{ source_schema }' AND table_type = 'BASE TABLE'"
+ end
+ end
+end
diff --git a/lib/af83/stored_procedures.rb b/lib/af83/stored_procedures.rb
deleted file mode 100644
index 698f3861d..000000000
--- a/lib/af83/stored_procedures.rb
+++ /dev/null
@@ -1,44 +0,0 @@
-module StoredProcedures extend self
-
- def invoke_stored_procedure(name, *params)
- name = name.to_s
- raise ArgumentError, "no such stored procedure #{name.inspect}" unless stored_procedures[name]
- invocation = "#{name}(#{quote_params(params)})"
- ActiveRecord::Base.connection.execute "SELECT #{invocation}"
- end
-
- def create_stored_procedure(name)
- name = name.to_s
- sql_file = File.expand_path("../../sql/#{name}.sql", __FILE__)
- raise ArgumentError, "missing sql file #{sql_file.inspect}" unless File.readable? sql_file
-
- # We could store the file's content for reload without application restart if desired.
- stored_procedures[name] = true
-
- ActiveRecord::Base.connection.execute File.read(sql_file)
- end
-
- private
- def quote_params(params)
- params
- .map(&method(:quote_param))
- .join(", ")
- end
-
- def quote_param(param)
- case param
- when String
- "'#{param}'"
- when TrueClass
- "TRUE"
- when FalseClass
- "FALSE"
- else
- param
- end
- end
-
- def stored_procedures
- @__stored_procedures__ ||= {}
- end
-end
diff --git a/lib/sql/clone_schema.sql b/lib/sql/clone_schema.sql
deleted file mode 100644
index 9fb88466b..000000000
--- a/lib/sql/clone_schema.sql
+++ /dev/null
@@ -1,114 +0,0 @@
-CREATE OR REPLACE FUNCTION clone_schema( source_schema text, dest_schema text, include_recs boolean) RETURNS void AS
-$BODY$
- DECLARE
- src_oid oid;
- tbl_oid oid;
- func_oid oid;
- object text;
- buffer text;
- srctbl text;
- default_ text;
- column_ text;
- qry text;
- dest_qry text;
- v_def text;
- seqval bigint;
- sq_last_value bigint;
- sq_max_value bigint;
- sq_start_value bigint;
- sq_increment_by bigint;
- sq_min_value bigint;
- sq_cache_value bigint;
- sq_log_cnt bigint;
- sq_is_called boolean;
- sq_is_cycled boolean;
- sq_cycled char(10);
- BEGIN
- -- Assure that source_schema exists
- SELECT oid INTO src_oid FROM pg_namespace WHERE nspname = quote_ident(source_schema);
- IF NOT FOUND THEN
- RAISE NOTICE 'source schema % does not exist!', source_schema;
- RETURN;
- END IF;
-
- -- Refute that dest_schema exists and then create it
- PERFORM nspname FROM pg_namespace WHERE nspname = quote_ident(dest_schema);
- IF FOUND THEN
- RAISE NOTICE 'dest schema % already exists!', dest_schema;
- RETURN;
- END IF;
- EXECUTE 'CREATE SCHEMA ' || quote_ident(dest_schema) ;
-
- -- loop over sequences, creating the same one in the destination namespace, ...
- FOR object IN
- SELECT sequence_name::text FROM information_schema.sequences WHERE sequence_schema = quote_ident(source_schema)
- LOOP
- EXECUTE 'CREATE SEQUENCE ' || quote_ident(dest_schema) || '.' || quote_ident(object);
-
- -- ...storing the attributes of the sequence from the source namespace in local variables, ...
- srctbl := quote_ident(source_schema) || '.' || quote_ident(object);
- EXECUTE 'SELECT last_value, max_value, start_value, increment_by, min_value, cache_value, log_cnt, is_cycled, is_called FROM ' || srctbl || ';' INTO sq_last_value, sq_max_value, sq_start_value, sq_increment_by, sq_min_value, sq_cache_value, sq_log_cnt, sq_is_cycled, sq_is_called;
- IF sq_is_cycled THEN
- sq_cycled := 'CYCLE';
- ELSE
- sq_cycled := 'NO CYCLE';
- END IF;
- EXECUTE 'ALTER SEQUENCE ' || (dest_schema) || '.' || quote_ident(object) || ' INCREMENT BY ' || sq_increment_by || ' MINVALUE ' || sq_min_value || ' MAXVALUE ' || sq_max_value || ' START WITH ' || sq_start_value || ' RESTART ' || sq_min_value || ' CACHE ' || sq_cache_value || sq_cycled || ' ;' ;
-
- buffer := quote_ident(dest_schema) || '.' || quote_ident(object);
- IF include_recs THEN
- EXECUTE 'SELECT setval( ''' || buffer || ''', ' || sq_last_value || ', ' || sq_is_called || ');' ;
- ELSE
- EXECUTE 'SELECT setval( ''' || buffer || ''', ' || sq_start_value || ', ' || sq_is_called || ');' ;
- END IF;
- END LOOP;
-
- -- loop over tables in source schema,...
- FOR object IN
- SELECT TABLE_NAME::text FROM information_schema.tables WHERE table_schema = quote_ident(source_schema) AND table_type = 'BASE TABLE'
- LOOP
- -- ...creating the table in the destination schema, potentially including the records
- buffer := dest_schema || '.' || quote_ident(object);
- EXECUTE 'CREATE TABLE ' || buffer || '(LIKE ' || quote_ident(source_schema) || '.' || quote_ident(object) || ' INCLUDING ALL)';
- IF include_recs THEN
- EXECUTE 'INSERT INTO ' || buffer || ' SELECT * FROM ' || quote_ident(source_schema) || '.' || quote_ident(object) || ';';
- END IF;
-
- -- alter table, assuring the destination schema's table has:
- -- * the same defaults
- FOR column_, default_
- IN SELECT column_name::text, REPLACE(column_default::text, source_schema, dest_schema)
- FROM information_schema.COLUMNS
- WHERE table_schema = dest_schema AND TABLE_NAME = object AND column_default LIKE 'nextval(%' || quote_ident(source_schema) || '%::regclass)'
- LOOP
- EXECUTE 'ALTER TABLE ' || buffer || ' ALTER COLUMN ' || column_ || ' SET DEFAULT ' || default_;
- END LOOP;
- END LOOP;
-
- /* SELECT 'ALTER TABLE ' || quote_ident(dest_schema) || '.' || quote_ident(rn.relname) || ' ADD CONSTRAINT ' || quote_ident(ct.conname) || ' ' || pg_get_constraintdef(ct.oid) || ';' FROM pg_constraint ct JOIN pg_class rn ON rn.oid = ct.conrelid WHERE connamespace = src_oid AND rn.relkind = 'r' AND ct.contype = 'f' LIMIT 2; */
- -- apply all constraints on tables in destination schema
- FOR qry IN
- SELECT 'ALTER TABLE ' || quote_ident(dest_schema) || '.' || quote_ident(rn.relname) || ' ADD CONSTRAINT ' || quote_ident(ct.conname) || ' ' || REPLACE(pg_get_constraintdef(ct.oid), source_schema || '.', dest_schema || '.') || ';'
- FROM pg_constraint ct JOIN pg_class rn ON rn.oid = ct.conrelid WHERE connamespace = src_oid AND rn.relkind = 'r' AND ct.contype = 'f'
- LOOP
- EXECUTE qry;
- END LOOP;
-
- -- create views from source schema in destination schema
- FOR object IN
- SELECT table_name::text, view_definition FROM information_schema.views WHERE table_schema = quote_ident(source_schema)
- LOOP
- buffer := dest_schema || '.' || quote_ident(object);
- SELECT view_definition INTO v_def FROM information_schema.views WHERE table_schema = quote_ident(source_schema) AND table_name = quote_ident(object);
- EXECUTE 'CREATE OR REPLACE VIEW ' || buffer || ' AS ' || v_def || ';' ;
- END LOOP;
-
- FOR func_oid IN SELECT oid FROM pg_proc WHERE pronamespace = src_oid
- LOOP
- SELECT pg_get_functiondef(func_oid) INTO qry;
- SELECT replace(qry, source_schema, dest_schema) INTO dest_qry;
- EXECUTE dest_qry;
- END LOOP;
- RETURN;
- END;
-$BODY$ LANGUAGE plpgsql VOLATILE COST 100;
diff --git a/lib/tasks/install.rake b/lib/tasks/install.rake
new file mode 100644
index 000000000..f32f3f240
--- /dev/null
+++ b/lib/tasks/install.rake
@@ -0,0 +1,18 @@
+task :package do
+ release_name = Time.now.strftime('%Y%m%d%H%M%S')
+
+ rm_rf "tmp/package"
+ mkdir_p "tmp/package"
+
+ sh "git archive --format=tar --output=tmp/package/stif-boiv-release-#{release_name}.tar HEAD"
+
+ sh "bundle package --all"
+ sh "tar -rf tmp/package/stif-boiv-release-#{release_name}.tar vendor/cache"
+
+ %w{deploy-helper.sh README sidekiq-stif-boiv.service stif-boiv.conf stif-boiv-setup.sh template-stif-boiv.sql}.each do |f|
+ cp "install/#{f}", "tmp/package/#{f}"
+ end
+
+ sh "tar -czf stif-boiv-#{release_name}.tar.gz -C tmp/package ."
+ sh "rm -rf tmp/package vendor/cache"
+end
diff --git a/lib/tasks/referential.rake b/lib/tasks/referential.rake
index 76f1b4c00..ce1ded4fc 100644
--- a/lib/tasks/referential.rake
+++ b/lib/tasks/referential.rake
@@ -22,7 +22,9 @@ namespace :referential do
stop_areas = workbench.stop_area_referential.stop_areas.last(10)
4.times do |i|
- route_attrs = { line: line, name: "Route #{Faker::Name.unique.name}" }
+ name = Faker::Name.unique.name
+ route_attrs = { line: line, name: "Route #{name}", published_name: "Published #{name}" }
+
if i.even?
route_attrs[:wayback] = :straight_forward
route = Chouette::Route.create!(route_attrs)
diff --git a/spec/controllers/devise/cas_sessions_controller_spec.rb b/spec/controllers/devise/cas_sessions_controller_spec.rb
new file mode 100644
index 000000000..950d141fd
--- /dev/null
+++ b/spec/controllers/devise/cas_sessions_controller_spec.rb
@@ -0,0 +1,27 @@
+RSpec.describe Devise::CasSessionsController, type: :controller do
+
+ login_user
+
+ context 'login is correctly redirected' do
+ it 'to #service' do
+ get :new
+ expect(response).to redirect_to(unauthenticated_root_path)
+ end
+ end
+
+ context 'user does not have any boiv:.+ permission' do
+ xit 'cannot login and will be redirected to the login page, with a corresponding message' do
+ get :service
+ expect(controller).to set_flash[:alert].to(%r{IBOO})
+ expect(response).to redirect_to("http://stif-portail-dev.af83.priv/sessions/login?service=http%3A%2F%2Ftest.host%2Fusers%2Fservice")
+ end
+ end
+
+ context 'user does have a boiv:.+ permission' do
+ it 'can login and will be redirected to the referentials page' do
+ @user.update_attribute :permissions, (@user.permissions << 'boiv:UnameIt')
+ get :service
+ expect(response).to redirect_to(authenticated_root_path)
+ end
+ end
+end
diff --git a/spec/controllers/imports_controller_spec.rb b/spec/controllers/imports_controller_spec.rb
index bffb89338..7b575ab61 100644
--- a/spec/controllers/imports_controller_spec.rb
+++ b/spec/controllers/imports_controller_spec.rb
@@ -1,5 +1,3 @@
-require 'rails_helper'
-
RSpec.describe ImportsController, :type => :controller do
login_user
diff --git a/spec/controllers/route_stop_points_controller_spec.rb b/spec/controllers/route_stop_points_controller_spec.rb
index 2f5fa41c7..ac9e2f11b 100644
--- a/spec/controllers/route_stop_points_controller_spec.rb
+++ b/spec/controllers/route_stop_points_controller_spec.rb
@@ -15,9 +15,7 @@ RSpec.describe RouteStopPointsController, type: :controller do
end
it 'returns a JSON of stop areas' do
- expect(response.body).to eq(route.stop_points.map { |sp| { id: sp.id, name: sp.name } }.to_json)
+ expect(response.body).to eq(route.stop_points.map { |sp| { id: sp.id, stop_area_id: sp.stop_area.id, name: sp.name, zip_code: sp.stop_area.zip_code, city_name: sp.stop_area.city_name } }.to_json)
end
end
end
-
-
diff --git a/spec/factories/chouette_time_table.rb b/spec/factories/chouette_time_table.rb
index f462349cf..6480df79d 100644
--- a/spec/factories/chouette_time_table.rb
+++ b/spec/factories/chouette_time_table.rb
@@ -1,6 +1,7 @@
FactoryGirl.define do
factory :time_table_date, :class => Chouette::TimeTableDate do
+ association :time_table, :factory => :time_table
end
factory :time_table_period, :class => Chouette::TimeTablePeriod do
diff --git a/spec/factories/chouette_vehicle_journey.rb b/spec/factories/chouette_vehicle_journey.rb
index 452909f23..e7ecb79ac 100644
--- a/spec/factories/chouette_vehicle_journey.rb
+++ b/spec/factories/chouette_vehicle_journey.rb
@@ -18,11 +18,16 @@ FactoryGirl.define do
after(:create) do |vehicle_journey, evaluator|
vehicle_journey.journey_pattern.stop_points.each_with_index do |stop_point, index|
+ prev_stop = vehicle_journey.vehicle_journey_at_stops[index - 1]
+
+ arrival_time = prev_stop ? prev_stop[:departure_time] + 1.minute : evaluator.stop_arrival_time
+ departure_time = prev_stop ? arrival_time + 1.minute : evaluator.stop_departure_time
+
vehicle_journey.vehicle_journey_at_stops << create(:vehicle_journey_at_stop,
:vehicle_journey => vehicle_journey,
:stop_point => stop_point,
- :arrival_time => "2000-01-01 #{evaluator.stop_arrival_time} UTC",
- :departure_time => "2000-01-01 #{evaluator.stop_departure_time} UTC")
+ :arrival_time => "2000-01-01 #{arrival_time} UTC",
+ :departure_time => "2000-01-01 #{departure_time} UTC")
end
end
diff --git a/spec/factories/clean_ups.rb b/spec/factories/clean_ups.rb
index 41165ac16..d3746c3b2 100644
--- a/spec/factories/clean_ups.rb
+++ b/spec/factories/clean_ups.rb
@@ -1,15 +1,6 @@
FactoryGirl.define do
factory :clean_up do
- status "MyString"
-started_at "2016-11-14 14:45:18"
-ended_at "2016-11-14 14:45:18"
-referential nil
-keep_lines false
-keep_stops false
-keep_companies false
-keep_networks false
-keep_group_of_lines false
-expected_date "2016-11-14 14:45:18"
+ begin_date { Date.today}
+ end_date { Date.today + 1.month }
end
-
end
diff --git a/spec/features/calendars_spec.rb b/spec/features/calendars_spec.rb
index 2089939bb..e15624295 100644
--- a/spec/features/calendars_spec.rb
+++ b/spec/features/calendars_spec.rb
@@ -19,12 +19,20 @@ describe 'Calendars', type: :feature do
context 'filtering' do
it 'supports filtering by short name' do
- fill_in 'q[short_name_cont]', with: calendars.first.short_name
+ fill_in 'q[name_or_short_name_cont]', with: calendars.first.short_name
click_button 'search_btn'
expect(page).to have_content(calendars.first.short_name)
expect(page).not_to have_content(calendars.last.short_name)
end
+ it 'supports filtering by name' do
+ fill_in 'q[name_or_short_name_cont]', with: calendars.first.name
+ click_button 'search_btn'
+ expect(page).to have_content(calendars.first.name)
+ expect(page).not_to have_content(calendars.last.name)
+ end
+
+
it 'supports filtering by shared' do
shared_calendar = create :calendar, organisation_id: 1, shared: true
visit calendars_path
diff --git a/spec/features/lines_spec.rb b/spec/features/lines_spec.rb
index e7e1e601c..a55f30ebc 100644
--- a/spec/features/lines_spec.rb
+++ b/spec/features/lines_spec.rb
@@ -1,7 +1,4 @@
-# -*- coding: utf-8 -*-
-require 'spec_helper'
-
-describe "Lines", :type => :feature do
+describe "Lines", type: :feature do
login_user
let(:line_referential) { create :line_referential }
diff --git a/spec/features/referentials_permissions_spec.rb b/spec/features/referentials_permissions_spec.rb
new file mode 100644
index 000000000..0216eeeb0
--- /dev/null
+++ b/spec/features/referentials_permissions_spec.rb
@@ -0,0 +1,54 @@
+# -*- coding: utf-8 -*-
+
+describe "Referentials", :type => :feature do
+
+ login_user
+ let(:referential) { Referential.first }
+
+ let( :edit_link_text ){ I18n.t('actions.edit') }
+ let( :destroy_link_text ){ I18n.t('actions.destroy') }
+
+
+ context 'permissions' do
+ before do
+ allow_any_instance_of(ReferentialPolicy).to receive(:organisation_match?).and_return organisation_match
+ visit path
+ end
+
+ context 'on show view with common lines' do
+ let( :path ){ referential_path(referential) }
+ before do
+ allow_any_instance_of(ReferentialPolicy).to receive(:common_lines?).and_return common_lines
+ end
+
+ context 'if organisations match →' do
+ let( :organisation_match ){ true }
+ let( :common_lines ){ false }
+
+ it 'shows the edit button' do
+ expected_href = edit_referential_path(referential)
+ expect( page ).to have_link(edit_link_text, href: expected_href)
+ end
+ it 'shows the delete button' do
+ expected_href = referential_path(referential)
+ expect( page ).to have_css(%{a[href=#{expected_href.inspect}] span}, text: destroy_link_text)
+ end
+ end
+
+ context 'if organisations do not match →' do
+ let( :organisation_match ){ false }
+ let( :common_lines ){ true }
+
+ it 'does not show the delete button' do
+ expected_href = edit_referential_path(referential)
+ expect( page ).not_to have_link(edit_link_text, href: expected_href)
+ end
+ it 'does not show the delete button' do
+ expected_href = referential_path(referential)
+ expect( page ).not_to have_css(%{a[href=#{expected_href.inspect}] span}, text: destroy_link_text)
+ end
+ end
+ end
+
+ end
+end
diff --git a/spec/features/referentials_spec.rb b/spec/features/referentials_spec.rb
index 3c2258a3a..337271fea 100644
--- a/spec/features/referentials_spec.rb
+++ b/spec/features/referentials_spec.rb
@@ -1,6 +1,4 @@
# -*- coding: utf-8 -*-
-require 'spec_helper'
-
describe "Referentials", :type => :feature do
login_user
@@ -111,13 +109,9 @@ describe "Referentials", :type => :feature do
end
describe "create" do
-
it "should" do
visit new_referential_path
fill_in "Nom", :with => "Test"
- fill_in "Code", :with => "test"
- fill_in "Point haut/droite de l'emprise par défaut", :with => "0.0, 0.0"
- fill_in "Point bas/gauche de l'emprise par défaut", :with => "1.0, 1.0"
click_button "Valider"
expect(Referential.where(:name => "Test")).not_to be_nil
diff --git a/spec/features/routes_spec.rb b/spec/features/routes_spec.rb
index 6d9ba990d..28015f011 100644
--- a/spec/features/routes_spec.rb
+++ b/spec/features/routes_spec.rb
@@ -36,10 +36,13 @@ describe "Routes", :type => :feature do
visit referential_line_path(referential, line)
click_link "Ajouter un itinéraire"
fill_in "route_name", :with => "A to B"
+ fill_in "route_published_name", :with => "Published A to B"
# select 'Aller', :from => "route_direction"
check('route[wayback]')
click_button("Valider")
expect(page).to have_content("A to B")
+ expect(page).to have_content("Published A to B")
+
end
end
diff --git a/spec/features/routing_constraint_zones_spec.rb b/spec/features/routing_constraint_zones_spec.rb
index 9e8c7dad4..b116b38bd 100644
--- a/spec/features/routing_constraint_zones_spec.rb
+++ b/spec/features/routing_constraint_zones_spec.rb
@@ -20,7 +20,7 @@ describe 'RoutingConstraintZones', type: :feature do
context 'user has permission to create routing_constraint_zones' do
it 'shows a create link for routing_constraint_zones' do
- expect(page).to have_content(I18n.t('routing_constraint_zones.actions.new'))
+ expect(page).to have_content(I18n.t('actions.new'))
end
end
@@ -28,7 +28,7 @@ describe 'RoutingConstraintZones', type: :feature do
it 'does not show a create link for routing_constraint_zones' do
@user.update_attribute(:permissions, [])
visit referential_line_routing_constraint_zones_path(referential, line)
- expect(page).not_to have_content(I18n.t('routing_constraint_zones.actions.new'))
+ expect(page).not_to have_content(I18n.t('actions.new'))
end
end
diff --git a/spec/features/users/connection_spec.rb b/spec/features/users/connection_spec.rb
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/spec/features/users/connection_spec.rb
diff --git a/spec/features/workbenches_spec.rb b/spec/features/workbenches_spec.rb
index c11fbd03d..953eb2bf5 100644
--- a/spec/features/workbenches_spec.rb
+++ b/spec/features/workbenches_spec.rb
@@ -46,8 +46,6 @@ describe 'Workbenches', type: :feature do
click_link I18n.t('actions.add')
fill_in "referential[name]", with: "Referential to test creation" # Nom du JDD
- fill_in "referential[slug]", with: "test" # Code
- fill_in "referential[prefix]", with: "test" # Prefix Neptune
select workbench.lines.first.id, from: 'referential[metadatas_attributes][0][lines][]' # Lignes
click_button "Valider"
diff --git a/spec/javascripts/time_table/reducers/pagination_spec.js b/spec/javascripts/time_table/reducers/pagination_spec.js
index 740ded3ac..5da58427e 100644
--- a/spec/javascripts/time_table/reducers/pagination_spec.js
+++ b/spec/javascripts/time_table/reducers/pagination_spec.js
@@ -118,11 +118,4 @@ describe('pagination reducer', () => {
})
).toEqual(Object.assign({}, state, {stateChanged: true}))
})
- it('should handle UPDATE_DAY_TYPES', () => {
- expect(
- paginationReducer(state, {
- type: 'UPDATE_DAY_TYPES'
- })
- ).toEqual(Object.assign({}, state, {stateChanged: true}))
- })
})
diff --git a/spec/javascripts/vehicle_journeys/reducers/vehicle_journeys_spec.js b/spec/javascripts/vehicle_journeys/reducers/vehicle_journeys_spec.js
index 662c3d82f..23ebc3d9f 100644
--- a/spec/javascripts/vehicle_journeys/reducers/vehicle_journeys_spec.js
+++ b/spec/javascripts/vehicle_journeys/reducers/vehicle_journeys_spec.js
@@ -111,7 +111,9 @@ describe('vehicleJourneys reducer', () => {
time_tables: [],
vehicle_journey_at_stops: pristineVjasList,
selected: false,
- deletable: false
+ deletable: false,
+ transport_mode: 'undefined',
+ transport_submode: 'undefined'
}, ...state])
})
diff --git a/spec/lib/af83/cloning/clone_schema_spec.rb b/spec/lib/af83/cloning/clone_schema_spec.rb
new file mode 100644
index 000000000..3d541f3e9
--- /dev/null
+++ b/spec/lib/af83/cloning/clone_schema_spec.rb
@@ -0,0 +1,113 @@
+RSpec.describe AF83::SchemaCloner, type: :pg_catalog do
+ let( :source_schema ){ "source_schema" }
+ let( :target_schema ){ "target_schema" }
+ let( :child_table ){ "children" }
+ let( :parent_table ){ "parents" }
+
+ subject { described_class.new source_schema, target_schema }
+
+ context "after cloning" do
+ before do
+ create_schema_with_tables
+ subject.clone_schema
+ end
+
+ it "table information is correctly duplicated" do
+ expect(get_table_information(source_schema, child_table))
+ .to eq([{"table_schema"=>"source_schema",
+ "table_name"=>"children",
+ "table_type"=>"BASE TABLE",
+ "self_referencing_column_name"=>nil,
+ "reference_generation"=>nil,
+ "user_defined_type_catalog"=>nil,
+ "user_defined_type_schema"=>nil,
+ "user_defined_type_name"=>nil,
+ "is_insertable_into"=>"YES",
+ "is_typed"=>"NO",
+ "commit_action"=>nil}])
+
+ expect( get_table_information(target_schema, child_table))
+ .to eq([{"table_schema"=>"target_schema",
+ "table_name"=>"children",
+ "table_type"=>"BASE TABLE",
+ "self_referencing_column_name"=>nil,
+ "reference_generation"=>nil,
+ "user_defined_type_catalog"=>nil,
+ "user_defined_type_schema"=>nil,
+ "user_defined_type_name"=>nil,
+ "is_insertable_into"=>"YES",
+ "is_typed"=>"NO",
+ "commit_action"=>nil}])
+ end
+
+ it "table content is the same and sequences are synchronized" do
+ expect_same_content(parent_table)
+ expect_same_content(child_table)
+
+ expect_same_sequence_params("#{parent_table}_id_seq")
+ expect_same_sequence_params("#{child_table}_id_seq")
+ end
+
+ it "has correctly updated default values" do
+ child_table_pk_default = get_columns(target_schema, child_table)
+ .find{ |col| col["column_name"] == "id" }["column_default"]
+ expect( child_table_pk_default ).to eq("nextval('#{target_schema}.children_id_seq'::regclass)")
+ end
+
+ it "has the correct foreign keys" do
+ expect( get_foreign_keys(target_schema, child_table) )
+ .to eq([{
+ "constraint_name" => "children_parents",
+ "constraint_def" => "FOREIGN KEY (parents_id) REFERENCES target_schema.parents(id)"}])
+ end
+
+ xit "it has the correct unique keys UNTESTABLE SO FAR" do
+ insert source_schema, child_table, "#{parent_table}_id" => 1, some_key: 400
+ insert target_schema, child_table, "#{parent_table}_id" => 1, some_key: 400
+ reinsert_sql = "INSERT INTO #{source_schema}.#{child_table} (#{parent_table}_id, some_key) VALUES (1, 400)"
+ expect{ execute(reinsert_sql) rescue nil}.not_to change{ execute("SELECT COUNT(*) FROM #{source_schema}.#{child_table}") }
+
+ # expect{ insert(target_schema, child_table, "#{parent_table}_id" => 1, some_key: 400) }.to raise_error(ActiveRecord::RecordNotUnique)
+ end
+
+ it "inserts are independent" do
+ insert source_schema, child_table, "#{parent_table}_id" => 1, some_key: 400
+ insert target_schema, child_table, "#{parent_table}_id" => 1, some_key: 400
+ last_source = get_content(source_schema, child_table).last
+ last_target = get_content(target_schema, child_table).last
+
+ expect( last_source ).to eq("id"=>"3", "parents_id"=>"1", "some_key"=>"400", "is_orphan"=>"f")
+ expect( last_target ).to eq("id"=>"3", "parents_id"=>"1", "some_key"=>"400", "is_orphan"=>"f")
+ end
+
+ end
+
+ def create_schema_with_tables
+ execute <<-EOSQL
+ DROP SCHEMA IF EXISTS #{source_schema} CASCADE;
+ CREATE SCHEMA #{source_schema};
+
+ CREATE TABLE #{source_schema}.#{parent_table} (
+ id bigserial PRIMARY KEY
+ );
+ CREATE TABLE #{source_schema}.#{child_table} (
+ id bigserial PRIMARY KEY,
+ #{parent_table}_id bigint,
+ some_key bigint NOT NULL,
+ is_orphan boolean DEFAULT false
+ );
+
+ CREATE UNIQUE INDEX #{child_table}_some_key_idx ON #{source_schema}.#{child_table} (some_key);
+
+ ALTER TABLE #{source_schema}.#{child_table}
+ ADD CONSTRAINT #{child_table}_#{parent_table}
+ FOREIGN KEY( #{parent_table}_id ) REFERENCES #{source_schema}.#{parent_table}(id);
+
+ INSERT INTO #{source_schema}.#{parent_table} VALUES (DEFAULT);
+ INSERT INTO #{source_schema}.#{parent_table} VALUES (DEFAULT);
+ EOSQL
+ insert source_schema, child_table, "#{parent_table}_id" => 1, some_key: 200
+ insert source_schema, child_table, "#{parent_table}_id" => 2, some_key: 300, is_orphan: true
+ end
+
+end
diff --git a/spec/lib/af83/stored_procedure_spec.rb b/spec/lib/af83/stored_procedure_spec.rb
deleted file mode 100644
index 2530d7fc1..000000000
--- a/spec/lib/af83/stored_procedure_spec.rb
+++ /dev/null
@@ -1,20 +0,0 @@
-require 'rails_helper'
-
-RSpec.describe StoredProcedures do
-
-
- before do
- described_class.create_stored_procedure(:clone_schema)
- end
-
- let( :source_schema_name ){ "parissudest201604" }
- let( :dest_schema_name ){ "#{source_schema_name}_v1"}
-
- context "Error cases" do
- it "raises an error if stored procedure does not exist" do
- expect{ described_class.invoke_stored_procedure(:idonotexist) }
- .to raise_error(ArgumentError, %r{no such stored procedure "idonotexist"})
- end
- end
-
-end
diff --git a/spec/lib/af83/stored_procedures/clone_schema_spec.rb b/spec/lib/af83/stored_procedures/clone_schema_spec.rb
deleted file mode 100644
index c387ddc7d..000000000
--- a/spec/lib/af83/stored_procedures/clone_schema_spec.rb
+++ /dev/null
@@ -1,167 +0,0 @@
-require 'spec_helper'
-
-include Support::PGCatalog
-
-RSpec.describe StoredProcedures do
- let( :source_schema ){ "source_schema" }
- let( :target_schema ){ "target_schema" }
- let( :child_table ){ "children" }
- let( :parent_table ){ "parents" }
-
- before do
- create_schema_with_tables
- StoredProcedures.create_stored_procedure :clone_schema
- end
-
- # :meta specs are not run, as the describe the testing methd and not the application
- context "meta specs describe source schema's introspection", :meta do
- it "table information is correctly read" do
- expect(get_table_information(source_schema, child_table))
- .to eq([{"table_schema"=>"source_schema",
- "table_name"=>"children",
- "table_type"=>"BASE TABLE",
- "self_referencing_column_name"=>nil,
- "reference_generation"=>nil,
- "user_defined_type_catalog"=>nil,
- "user_defined_type_schema"=>nil,
- "user_defined_type_name"=>nil,
- "is_insertable_into"=>"YES",
- "is_typed"=>"NO",
- "commit_action"=>nil}])
-
- expect( get_table_information(target_schema, child_table) ).to be_empty
- end
-
- it "sequences are correctly read" do
- expect(get_sequences(source_schema, child_table))
- .to eq([{"sequence_name"=>"#{child_table}_id_seq",
- "last_value"=>"1",
- "start_value"=>"1",
- "increment_by"=>"1",
- "max_value"=>"9223372036854775807",
- "min_value"=>"1",
- "cache_value"=>"1",
- "log_cnt"=>"0",
- "is_cycled"=>"f",
- "is_called"=>"f"}])
-
- expect(get_sequences(source_schema, parent_table))
- .to eq([{"sequence_name"=>"#{parent_table}_id_seq",
- "last_value"=>"1",
- "start_value"=>"1",
- "increment_by"=>"1",
- "max_value"=>"9223372036854775807",
- "min_value"=>"1",
- "cache_value"=>"1",
- "log_cnt"=>"0",
- "is_cycled"=>"f",
- "is_called"=>"f"}])
- end
-
- it "shows foreign key constraints are correctly read" do
- expect( get_foreign_keys(source_schema, child_table) )
- .to eq([{
- "constraint_name" => "children_parents",
- "constraint_def" => "FOREIGN KEY (parents_id) REFERENCES source_schema.parents(id)"}])
- end
- end
-
- context "before cloning" do
- it "target schema does not exist" do
- expect( get_schema_oid(target_schema) ).to be_nil
- end
- end
-
- context "after cloning" do
- before do
- described_class.invoke_stored_procedure(:clone_schema, source_schema, target_schema, false)
- end
-
- it "target schema does exist" do
- expect( get_schema_oid(target_schema) ).not_to be_nil
- end
-
- it "table information is correctly read" do
- expect(get_table_information(source_schema, child_table))
- .to eq([{"table_schema"=>"source_schema",
- "table_name"=>"children",
- "table_type"=>"BASE TABLE",
- "self_referencing_column_name"=>nil,
- "reference_generation"=>nil,
- "user_defined_type_catalog"=>nil,
- "user_defined_type_schema"=>nil,
- "user_defined_type_name"=>nil,
- "is_insertable_into"=>"YES",
- "is_typed"=>"NO",
- "commit_action"=>nil}])
-
- expect( get_table_information(target_schema, child_table))
- .to eq([{"table_schema"=>"target_schema",
- "table_name"=>"children",
- "table_type"=>"BASE TABLE",
- "self_referencing_column_name"=>nil,
- "reference_generation"=>nil,
- "user_defined_type_catalog"=>nil,
- "user_defined_type_schema"=>nil,
- "user_defined_type_name"=>nil,
- "is_insertable_into"=>"YES",
- "is_typed"=>"NO",
- "commit_action"=>nil}])
- end
-
- it "has the correct sequences" do
- expect(get_sequences(target_schema, child_table))
- .to eq([{"sequence_name"=>"#{child_table}_id_seq",
- "last_value"=>"1",
- "start_value"=>"1",
- "increment_by"=>"1",
- "max_value"=>"9223372036854775807",
- "min_value"=>"1",
- "cache_value"=>"1",
- "log_cnt"=>"0",
- "is_cycled"=>"f",
- "is_called"=>"f"}])
-
- expect(get_sequences(target_schema, parent_table))
- .to eq([{"sequence_name"=>"#{parent_table}_id_seq",
- "last_value"=>"1",
- "start_value"=>"1",
- "increment_by"=>"1",
- "max_value"=>"9223372036854775807",
- "min_value"=>"1",
- "cache_value"=>"1",
- "log_cnt"=>"0",
- "is_cycled"=>"f",
- "is_called"=>"f"}])
- end
-
- it "has the correct foreign keys" do
- expect( get_foreign_keys(target_schema, child_table) )
- .to eq([{
- "constraint_name" => "children_parents",
- "constraint_def" => "FOREIGN KEY (parents_id) REFERENCES target_schema.parents(id)"}])
- end
-
- end
-
-end
-
-def create_schema_with_tables
- execute("CREATE SCHEMA IF NOT EXISTS #{source_schema}")
- execute <<-EOSQL
- DROP SCHEMA IF EXISTS #{source_schema} CASCADE;
- CREATE SCHEMA #{source_schema};
-
- CREATE TABLE #{source_schema}.#{parent_table} (
- id bigserial PRIMARY KEY
- );
- CREATE TABLE #{source_schema}.#{child_table} (
- id bigserial PRIMARY KEY,
- #{parent_table}_id bigint
- );
- ALTER TABLE #{source_schema}.#{child_table}
- ADD CONSTRAINT #{child_table}_#{parent_table}
- FOREIGN KEY( #{parent_table}_id ) REFERENCES #{source_schema}.#{parent_table}(id);
- EOSQL
-end
-
diff --git a/spec/lib/time_duration_spec.rb b/spec/lib/time_duration_spec.rb
index 1cba1f6d5..bf23f7ba6 100644
--- a/spec/lib/time_duration_spec.rb
+++ b/spec/lib/time_duration_spec.rb
@@ -4,15 +4,17 @@ describe TimeDuration do
describe ".exceeds_gap?" do
context "when duration is 4.hours" do
it "should return false if gap < 1.hour" do
- t1 = Time.now
- t2 = Time.now + 3.minutes
- expect(TimeDuration.exceeds_gap?(4.hours, t1, t2)).to be_falsey
+ earlier = Time.now
+ later = Time.now + 3.minutes
+
+ expect(TimeDuration.exceeds_gap?(4.hours, earlier, later)).to be false
end
it "should return true if gap > 4.hour" do
- t1 = Time.now
- t2 = Time.now + (4.hours + 1.minutes)
- expect(TimeDuration.exceeds_gap?(4.hours, t1, t2)).to be_truthy
+ earlier = Time.now
+ later = Time.now + (4.hours + 1.minutes)
+
+ expect(TimeDuration.exceeds_gap?(4.hours, earlier, later)).to be true
end
it "returns true when `earlier` is later than `later`" do
diff --git a/spec/models/chouette/time_table_spec.rb b/spec/models/chouette/time_table_spec.rb
index 3d45bd346..7a8863cb3 100644
--- a/spec/models/chouette/time_table_spec.rb
+++ b/spec/models/chouette/time_table_spec.rb
@@ -1,12 +1,47 @@
require 'spec_helper'
describe Chouette::TimeTable, :type => :model do
-
subject { create(:time_table) }
it { is_expected.to validate_presence_of :comment }
it { is_expected.to validate_uniqueness_of :objectid }
+ context "merge with calendar" do
+ let(:calendar) { create(:calendar) }
+
+ it 'should add calendar dates to time_table' do
+ subject.dates.clear
+ subject.merge!(calendar.convert_to_time_table)
+ expect(subject.dates.map(&:date)).to include(*calendar.dates)
+ end
+ end
+
+ describe "actualize" do
+ let(:calendar) { create(:calendar) }
+ let(:int_day_types) { 508 }
+
+ before do
+ subject.int_day_types = int_day_types
+ subject.calendar = calendar
+ subject.save
+ subject.actualize
+ end
+
+ it 'should override dates' do
+ expect(subject.dates.map(&:date)).to match_array calendar.dates
+ end
+
+ it 'should override periods' do
+ [:period_start, :period_end].each do |key|
+ expect(subject.periods.map(&key)).to match_array calendar.convert_to_time_table.periods.map(&key)
+ end
+ end
+
+ it 'should not change int_day_types' do
+ expect(subject.int_day_types).to eq(int_day_types)
+ end
+ end
+
describe "Update state" do
def time_table_to_state time_table
time_table.slice('id', 'comment').tap do |item|
@@ -119,7 +154,7 @@ describe Chouette::TimeTable, :type => :model do
end
it 'should create new include date' do
- day = state['current_month'].first
+ day = state['current_month'].find{|d| !d['excluded_date'] && !d['include_date'] }
date = Date.parse(day['date'])
day['include_date'] = true
expect(subject.included_days).not_to include(date)
@@ -131,7 +166,7 @@ describe Chouette::TimeTable, :type => :model do
end
it 'should create new exclude date' do
- day = state['current_month'].first
+ day = state['current_month'].find{|d| !d['excluded_date'] && !d['include_date']}
date = Date.parse(day['date'])
day['excluded_date'] = true
expect(subject.excluded_days).not_to include(date)
@@ -973,7 +1008,7 @@ end
expect(subject.periods[2].period_start).to eq(Date.new(2014, 8, 1))
expect(subject.periods[2].period_end).to eq(Date.new(2014, 8, 12))
end
- it "should have common day_types" do
+ it "should not modify day_types" do
expect(subject.int_day_types).to eq(4|16|128)
end
it "should have dates for thursdays and fridays" do
@@ -1007,9 +1042,6 @@ end
it "should have no period" do
expect(subject.periods.size).to eq(0)
end
- it "should have no day_types" do
- expect(subject.int_day_types).to eq(0)
- end
it "should have date all common days" do
expect(subject.dates.size).to eq(3)
expect(subject.dates[0].date).to eq(Date.new(2014,7,16))
@@ -1038,7 +1070,7 @@ end
it "should have 0 period" do
expect(subject.periods.size).to eq(0)
end
- it "should have no day_types" do
+ it "should not modify day_types" do
expect(subject.int_day_types).to eq(0)
end
it "should have date reduced for period" do
@@ -1063,8 +1095,8 @@ end
it "should have 0 result periods" do
expect(subject.periods.size).to eq(0)
end
- it "should have no day_types" do
- expect(subject.int_day_types).to eq(0)
+ it "should not modify day_types" do
+ expect(subject.int_day_types).to eq(4|8|16)
end
it "should have 1 date " do
expect(subject.dates.size).to eq(1)
@@ -1093,9 +1125,6 @@ end
it "should have 0 periods" do
expect(subject.periods.size).to eq(0)
end
- it "should have 0 day_types" do
- expect(subject.int_day_types).to eq(0)
- end
it "should have only dates " do
expect(subject.dates.size).to eq(11)
expect(subject.dates[0].date).to eq(Date.new(2014,6,30))
@@ -1134,7 +1163,7 @@ end
it "should have 0 period" do
expect(subject.periods.size).to eq(0)
end
- it "should have no remained day_types" do
+ it "should not modify day_types" do
expect(subject.int_day_types).to eq(0)
end
it "should have date reduced for period" do
@@ -1163,9 +1192,6 @@ end
it "should have 0 result periods" do
expect(subject.periods.size).to eq(0)
end
- it "should have no remained day_types" do
- expect(subject.int_day_types).to eq(0)
- end
it "should have dates for period reduced" do
expect(subject.dates.size).to eq(4)
expect(subject.dates[0].date).to eq(Date.new(2014,7,3))
@@ -1197,9 +1223,6 @@ end
it "should have 0 result periods" do
expect(subject.periods.size).to eq(0)
end
- it "should have no remained day_types" do
- subject.int_day_types == 0
- end
it "should have 3 dates left" do
expect(subject.dates.size).to eq(3)
expect(subject.dates[0].date).to eq(Date.new(2014,7,16))
@@ -1233,10 +1256,6 @@ end
expect(subject.periods.size).to eq(0)
end
- it "should have no remained day_types" do
- subject.int_day_types == 0
- end
-
it "should have 0 dates left" do
expect(subject.dates.size).to eq(0)
end
@@ -1261,10 +1280,6 @@ end
expect(subject.periods.size).to eq(0)
end
- it "should have 0 day_types" do
- expect(subject.int_day_types).to eq(0)
- end
-
it "should have 6 dates " do
expect(subject.dates.size).to eq(6)
expect(subject.dates[0].date).to eq(Date.new(2014,8,11))
@@ -1303,8 +1318,8 @@ end
end
end
- it "should have 0 day_types" do
- expect(subject.int_day_types).to eq(0)
+ it "should not modify day_types" do
+ expect(subject.int_day_types).to eq(4|8|16)
end
it "should have 1 dates " do
@@ -1342,8 +1357,8 @@ end
end
end
- it "should have 0 day_types" do
- expect(subject.int_day_types).to eq(0)
+ it "should not modify day_types" do
+ expect(subject.int_day_types).to eq(4|8|16)
end
it "should have only 1 dates " do
@@ -1373,9 +1388,6 @@ end
it "should have same 0 result periods" do
expect(subject.periods.size).to eq(0)
end
- it "should have 0 day_types" do
- expect(subject.int_day_types).to eq(0)
- end
it "should have 0 dates " do
expect(subject.dates.size).to eq(0)
end
diff --git a/spec/models/chouette/vehicle_journey_at_stops_day_offset_spec.rb b/spec/models/chouette/vehicle_journey_at_stops_day_offset_spec.rb
new file mode 100644
index 000000000..69a2d5cb9
--- /dev/null
+++ b/spec/models/chouette/vehicle_journey_at_stops_day_offset_spec.rb
@@ -0,0 +1,90 @@
+require 'spec_helper'
+
+describe Chouette::VehicleJourneyAtStop do
+ describe "#calculate" do
+ it "increments day offset when departure & arrival are on different sides
+ of midnight" do
+ at_stops = []
+ [
+ ['22:30', '22:35'],
+ ['23:50', '00:05'],
+ ['00:30', '00:35'],
+ ].each do |arrival_time, departure_time|
+ at_stops << build_stubbed(
+ :vehicle_journey_at_stop,
+ arrival_time: arrival_time,
+ departure_time: departure_time
+ )
+ end
+
+ offsetter = Chouette::VehicleJourneyAtStopsDayOffset.new(at_stops)
+
+ offsetter.calculate!
+
+ expect(at_stops[0].arrival_day_offset).to eq(0)
+ expect(at_stops[0].departure_day_offset).to eq(0)
+
+ expect(at_stops[1].arrival_day_offset).to eq(0)
+ expect(at_stops[1].departure_day_offset).to eq(1)
+
+ expect(at_stops[2].arrival_day_offset).to eq(1)
+ expect(at_stops[2].departure_day_offset).to eq(1)
+ end
+
+ it "increments day offset when an at_stop passes midnight the next day" do
+ at_stops = []
+ [
+ ['22:30', '22:35'],
+ ['01:02', '01:14'],
+ ].each do |arrival_time, departure_time|
+ at_stops << build_stubbed(
+ :vehicle_journey_at_stop,
+ arrival_time: arrival_time,
+ departure_time: departure_time
+ )
+ end
+
+ offsetter = Chouette::VehicleJourneyAtStopsDayOffset.new(at_stops)
+
+ offsetter.calculate!
+
+ expect(at_stops[0].arrival_day_offset).to eq(0)
+ expect(at_stops[0].departure_day_offset).to eq(0)
+
+ expect(at_stops[1].arrival_day_offset).to eq(1)
+ expect(at_stops[1].departure_day_offset).to eq(1)
+ end
+
+ it "increments day offset for multi-day offsets" do
+ at_stops = []
+ [
+ ['22:30', '22:35'],
+ ['01:02', '01:14'],
+ ['04:30', '04:35'],
+ ['00:00', '00:04'],
+ ].each do |arrival_time, departure_time|
+ at_stops << build_stubbed(
+ :vehicle_journey_at_stop,
+ arrival_time: arrival_time,
+ departure_time: departure_time
+ )
+ end
+
+ offsetter = Chouette::VehicleJourneyAtStopsDayOffset.new(at_stops)
+
+ offsetter.calculate!
+
+ expect(at_stops[0].arrival_day_offset).to eq(0)
+ expect(at_stops[0].departure_day_offset).to eq(0)
+
+ expect(at_stops[1].arrival_day_offset).to eq(1)
+ expect(at_stops[1].departure_day_offset).to eq(1)
+
+ expect(at_stops[2].arrival_day_offset).to eq(1)
+ expect(at_stops[2].departure_day_offset).to eq(1)
+
+ expect(at_stops[3].arrival_day_offset).to eq(2)
+ expect(at_stops[3].departure_day_offset).to eq(2)
+ end
+ end
+end
diff --git a/spec/models/chouette/vehicle_journey_spec.rb b/spec/models/chouette/vehicle_journey_spec.rb
index 4a108d7c0..8f9080b99 100644
--- a/spec/models/chouette/vehicle_journey_spec.rb
+++ b/spec/models/chouette/vehicle_journey_spec.rb
@@ -1,7 +1,38 @@
require 'spec_helper'
+
describe Chouette::VehicleJourney, :type => :model do
- describe "state_update" do
+ describe "vjas_departure_time_must_be_before_next_stop_arrival_time" do
+ let(:vehicle_journey) { create :vehicle_journey }
+ let(:vjas) { vehicle_journey.vehicle_journey_at_stops }
+
+ it 'should add errors a stop departure_time is greater then next stop arrival time' do
+ vjas[0][:departure_time] = vjas[1][:arrival_time] + 1.minute
+ vehicle_journey.validate
+
+ expect(vjas[0].errors[:departure_time]).not_to be_blank
+ expect(vehicle_journey.errors[:vehicle_journey_at_stops].count).to eq(1)
+ expect(vehicle_journey).not_to be_valid
+ end
+
+ it 'should consider valid to have departure_time equal to next stop arrival time' do
+ vjas[0][:departure_time] = vjas[1][:arrival_time]
+ vehicle_journey.validate
+
+ expect(vjas[0].errors[:departure_time]).to be_blank
+ expect(vehicle_journey.errors[:vehicle_journey_at_stops]).to be_empty
+ expect(vehicle_journey).to be_valid
+ end
+
+ it 'should not add errors when departure_time is less then next stop arrival time' do
+ vehicle_journey.validate
+ vjas.each do |stop|
+ expect(stop.errors).to be_empty
+ end
+ expect(vehicle_journey).to be_valid
+ end
+ end
+ describe "state_update" do
def vehicle_journey_at_stop_to_state vjas
at_stop = {'stop_area_object_id' => vjas.stop_point.stop_area.objectid }
[:id, :connecting_service_id, :boarding_alighting_possibility].map do |att|
@@ -136,16 +167,12 @@ describe Chouette::VehicleJourney, :type => :model do
it 'should return errors when validation failed' do
state['published_journey_name'] = 'edited_name'
- # Exceeds_gap departure time validation failed
- prev = state['vehicle_journey_at_stops'].last(2).first
- last = state['vehicle_journey_at_stops'].last
- prev['departure_time']['hour'] = '01'
- last['departure_time']['hour'] = '23'
+ state['vehicle_journey_at_stops'].last['departure_time']['hour'] = '23'
expect {
Chouette::VehicleJourney.state_update(route, collection)
}.not_to change(vehicle_journey, :published_journey_name)
- expect(state['errors'][:vehicle_journey_at_stops].size).to eq 1
+ expect(state['vehicle_journey_at_stops'].last['errors']).not_to be_empty
end
it 'should delete vj with deletable set to true from state' do
@@ -190,8 +217,8 @@ describe Chouette::VehicleJourney, :type => :model do
end
end
- describe '.vehicle_journey_at_stops_matrix' do
- it 'should fill missing VehicleJourneyAtStop with dummy' do
+ describe '#vehicle_journey_at_stops_matrix' do
+ it 'should fill missing vjas with dummy vjas' do
vehicle_journey.journey_pattern.stop_points.delete_all
vehicle_journey.vehicle_journey_at_stops.delete_all
@@ -201,14 +228,28 @@ describe Chouette::VehicleJourney, :type => :model do
expect(at_stops.count).to eq route.stop_points.count
end
- it 'should fill VehicleJourneyAtStop with new vjas when vj has been save without departure time' do
+ it 'should set dummy to false for active stop_points vjas' do
+ # Destroy vjas but stop_points is still active
+ # it should fill a new vjas without dummy flag
+ vehicle_journey.vehicle_journey_at_stops[3].destroy
+ at_stops = vehicle_journey.reload.vehicle_journey_at_stops_matrix
+ expect(at_stops[3].dummy).to be false
+ end
+
+ it 'should set dummy to true for deactivated stop_points vjas' do
+ vehicle_journey.journey_pattern.stop_points.delete(vehicle_journey.journey_pattern.stop_points.first)
+ at_stops = vehicle_journey.reload.vehicle_journey_at_stops_matrix
+ expect(at_stops.first.dummy).to be true
+ end
+
+ it 'should fill vjas for active stop_points without vjas yet' do
vehicle_journey.vehicle_journey_at_stops.destroy_all
at_stops = vehicle_journey.reload.vehicle_journey_at_stops_matrix
expect(at_stops.map(&:stop_point_id)).to eq vehicle_journey.journey_pattern.stop_points.map(&:id)
end
- it 'should keep index order of VehicleJourneyAtStop' do
+ it 'should keep index order of vjas' do
vehicle_journey.vehicle_journey_at_stops[3].destroy
at_stops = vehicle_journey.reload.vehicle_journey_at_stops_matrix
diff --git a/spec/models/clean_up_spec.rb b/spec/models/clean_up_spec.rb
index c495abdfe..4b1bf4da9 100644
--- a/spec/models/clean_up_spec.rb
+++ b/spec/models/clean_up_spec.rb
@@ -1,20 +1,227 @@
require 'rails_helper'
RSpec.describe CleanUp, :type => :model do
- let(:cleaner) { CleanUp.new }
it { should validate_presence_of(:begin_date) }
+ it { should validate_presence_of(:date_type) }
it { should belong_to(:referential) }
- it 'should delete vehiclejourneys without timetables' do
- create_list(:vehicle_journey, 2)
- create_list(:vehicle_journey, 2, time_tables:[create(:time_table)])
- expect(cleaner.clean_vehicle_journeys).to eq 2
+ context '#exclude_dates_in_overlapping_period with :before date_type' do
+ let(:time_table) { create(:time_table) }
+ let(:period) { time_table.periods[0] }
+ let(:cleaner) { create(:clean_up, date_type: :before) }
+
+ it 'should add exclude date into period for overlapping period' do
+ days_in_period = (period.period_start..period.period_end).count
+ cleaner.begin_date = period.period_end
+
+ expect { cleaner.exclude_dates_in_overlapping_period(period) }.to change {
+ time_table.dates.where(in_out: false).count
+ }.by(days_in_period - 1)
+ end
+
+ it 'should not add exclude date if no overlapping found' do
+ cleaner.begin_date = period.period_start
+ expect { cleaner.exclude_dates_in_overlapping_period(period) }.to_not change {
+ time_table.dates.where(in_out: false).count
+ }
+ end
+ end
+
+ context '#exclude_dates_in_overlapping_period with :after date_type' do
+ let(:time_table) { create(:time_table) }
+ let(:period) { time_table.periods[0] }
+ let(:cleaner) { create(:clean_up, date_type: :after) }
+
+ it 'should add exclude date into period for overlapping period' do
+ days_in_period = (period.period_start..period.period_end).count
+ cleaner.begin_date = period.period_start + 1.day
+ expect { cleaner.exclude_dates_in_overlapping_period(period) }.to change {
+ time_table.dates.where(in_out: false).count
+ }.by(days_in_period - 2)
+ end
+
+ it 'should not add exclude date if no overlapping found' do
+ cleaner.begin_date = period.period_end
+ expect { cleaner.exclude_dates_in_overlapping_period(period) }.to_not change {
+ time_table.dates.where(in_out: false).count
+ }
+ end
+ end
+
+ context '#exclude_dates_in_overlapping_period with :between date_type' do
+ let(:time_table) { create(:time_table) }
+ let(:period) { time_table.periods[0] }
+ let(:cleaner) { create(:clean_up, date_type: :between, begin_date: period.period_start + 3.day, end_date: period.period_end) }
+
+ it 'should add exclude date into period for overlapping period' do
+ expected_day_out = (cleaner.begin_date..cleaner.end_date).count
+ expect { cleaner.exclude_dates_in_overlapping_period(period) }.to change {
+ time_table.dates.where(in_out: false).count
+ }.by(expected_day_out)
+ end
+
+ it 'should not add exclude date if no overlapping found' do
+ cleaner.begin_date = period.period_end + 1.day
+ cleaner.end_date = cleaner.begin_date + 1.day
+
+ expect { cleaner.exclude_dates_in_overlapping_period(period) }.to_not change {
+ time_table.dates.where(in_out: false).count
+ }
+ end
+ end
+
+ context '#overlapping_periods' do
+ let(:cleaner) { create(:clean_up, date_type: :before, end_date: nil) }
+ let(:time_table) { create(:time_table) }
+
+ it 'should detect overlapping periods' do
+ cleaner.begin_date = time_table.periods[0].period_start
+ expect(cleaner.overlapping_periods).to include(time_table.periods[0])
+ end
+
+ it 'should not return none overlapping periods' do
+ cleaner.begin_date = time_table.periods[0].period_start - 1.day
+ expect(cleaner.overlapping_periods).to_not include(time_table.periods[0])
+ end
+ end
+
+ context '#clean' do
+ let(:cleaner) { create(:clean_up, date_type: :before) }
+
+ it 'should call destroy_time_tables_before' do
+ cleaner.date_type = :before
+ expect(cleaner).to receive(:destroy_time_tables_before)
+ expect(cleaner).to receive(:destroy_time_tables_dates_before)
+ expect(cleaner).to receive(:destroy_time_tables_periods_before)
+ cleaner.clean
+ end
+
+ it 'should call destroy_time_tables_after' do
+ cleaner.date_type = :after
+ expect(cleaner).to receive(:destroy_time_tables_after)
+ expect(cleaner).to receive(:destroy_time_tables_dates_after)
+ expect(cleaner).to receive(:destroy_time_tables_periods_after)
+ cleaner.clean
+ end
+
+ it 'should call destroy_time_tables_between' do
+ cleaner.date_type = :between
+ expect(cleaner).to receive(:destroy_time_tables_between)
+ expect(cleaner).to receive(:destroy_time_tables_dates_between)
+ expect(cleaner).to receive(:destroy_time_tables_periods_between)
+ cleaner.clean
+ end
+ end
+
+ context '#destroy_time_tables_dates_between' do
+ let!(:time_table) { create(:time_table) }
+ let(:cleaner) { create(:clean_up, date_type: :between) }
+
+ before do
+ time_table.periods.clear
+ time_table.save
+ cleaner.begin_date = time_table.start_date
+ cleaner.end_date = time_table.end_date
+ end
+
+ it 'should destroy record' do
+ expect{ cleaner.destroy_time_tables_dates_between }.to change {
+ Chouette::TimeTableDate.count
+ }.by(-time_table.dates.count)
+ end
+
+ it 'should not destroy record not in range' do
+ cleaner.begin_date = time_table.end_date + 1.day
+ cleaner.end_date = cleaner.begin_date + 1.day
+
+ expect{ cleaner.destroy_time_tables_dates_between }.to_not change {
+ Chouette::TimeTableDate.count
+ }
+ end
+ end
+
+ context '#destroy_time_tables_dates_after' do
+ let!(:time_table_date) { create(:time_table_date, date: Date.yesterday, in_out: true) }
+ let(:cleaner) { create(:clean_up, date_type: :after, begin_date: time_table_date.date) }
+
+ it 'should destroy record' do
+ count = Chouette::TimeTableDate.where('date > ?', cleaner.begin_date).count
+ expect{ cleaner.destroy_time_tables_dates_after }.to change {
+ Chouette::TimeTableDate.count
+ }.by(-count)
+ end
+ end
+
+ context '#destroy_time_tables_between' do
+ let!(:time_table) { create(:time_table ) }
+ let(:cleaner) { create(:clean_up, date_type: :after, begin_date: time_table.start_date, end_date: time_table.end_date) }
+
+ it 'should destroy time_tables with validity period in purge range' do
+ expect{ cleaner.destroy_time_tables_between }.to change {
+ Chouette::TimeTable.count
+ }.by(-1)
+ end
+
+ it 'should not destroy time_tables if not totaly inside purge range' do
+ cleaner.begin_date = time_table.start_date + 1.day
+ expect{ cleaner.destroy_time_tables_between }.to_not change {
+ Chouette::TimeTable.count
+ }
+ end
+ end
+
+ context '#destroy_time_tables_after' do
+ let!(:time_table) { create(:time_table ) }
+ let(:cleaner) { create(:clean_up, date_type: :after, begin_date: time_table.start_date - 1.day) }
+
+ it 'should destroy time_tables with start_date > purge begin_date' do
+ expect{ cleaner.destroy_time_tables_after }.to change {
+ Chouette::TimeTable.count
+ }.by(-1)
+ end
+
+ it 'should not destroy time_tables with start_date < purge begin date' do
+ cleaner.begin_date = time_table.end_date
+ expect{ cleaner.destroy_time_tables_after }.to_not change {
+ Chouette::TimeTable.count
+ }
+ end
end
- it 'should delete journeypatterns without vehicle journeys' do
- create_list(:journey_pattern, 2)
- create_list(:vehicle_journey, 2, journey_pattern: create(:journey_pattern))
- expect(cleaner.clean_journey_patterns).to eq 2
+ context '#destroy_time_tables' do
+ let!(:time_table) { create(:time_table) }
+ let(:cleaner) { create(:clean_up, date_type: :before) }
+
+ it 'should destroy all time_tables' do
+ expect{cleaner.destroy_time_tables(Chouette::TimeTable.all)}.to change {
+ Chouette::TimeTable.count
+ }.by(-1)
+ end
+
+ it 'should destroy associated vehicle_journeys' do
+ create(:vehicle_journey, time_tables: [time_table])
+ expect{cleaner.destroy_time_tables(Chouette::TimeTable.all)}.to change {
+ Chouette::VehicleJourney.count
+ }.by(-1)
+ end
+ end
+
+ context '#destroy_time_tables_before' do
+ let!(:time_table) { create(:time_table ) }
+ let(:cleaner) { create(:clean_up, date_type: :before, begin_date: time_table.end_date + 1.day) }
+
+ it 'should destroy time_tables with end_date < purge begin_date' do
+ expect{ cleaner.destroy_time_tables_before }.to change {
+ Chouette::TimeTable.count
+ }.by(-1)
+ end
+
+ it 'should not destroy time_tables with end_date > purge begin date' do
+ cleaner.begin_date = Date.today
+ expect{ cleaner.destroy_time_tables_before }.to_not change {
+ Chouette::TimeTable.count
+ }
+ end
end
end
diff --git a/spec/models/referential_cloning_spec.rb b/spec/models/referential_cloning_spec.rb
index 30391b53e..5acd433ec 100644
--- a/spec/models/referential_cloning_spec.rb
+++ b/spec/models/referential_cloning_spec.rb
@@ -1,4 +1,4 @@
-require 'rails_helper'
+require 'spec_helper'
RSpec.describe ReferentialCloning, :type => :model do
it 'should have a valid factory' do
@@ -7,4 +7,12 @@ RSpec.describe ReferentialCloning, :type => :model do
it { should belong_to :source_referential }
it { should belong_to :target_referential }
+
+ describe "ReferentialCloningWorker" do
+ let(:referential_cloning) { FactoryGirl.create(:referential_cloning) }
+
+ it "should schedule a job in worker" do
+ expect{referential_cloning.run_callbacks(:commit)}.to change {ReferentialCloningWorker.jobs.count}.by(1)
+ end
+ end
end
diff --git a/spec/models/time_table_combination_spec.rb b/spec/models/time_table_combination_spec.rb
index 0a8b3296a..3e60fa444 100644
--- a/spec/models/time_table_combination_spec.rb
+++ b/spec/models/time_table_combination_spec.rb
@@ -87,7 +87,7 @@ describe TimeTableCombination, :type => :model do
end
it "should intersect combined to source" do
- expect(source.int_day_types).to eq(0)
+ expect(source.int_day_types).to eq(508)
expect(source.periods.size).to eq(1)
expect(source.dates.size).to eq(0)
@@ -119,7 +119,7 @@ describe TimeTableCombination, :type => :model do
end
it "should disjoin combined to source" do
- expect(source.int_day_types).to eq(0)
+ expect(source.int_day_types).to eq(508)
expect(source.periods.size).to eq(1)
expect(source.dates.size).to eq(0)
diff --git a/spec/models/user_spec.rb b/spec/models/user_spec.rb
index 5c7aa0b98..6f98e5ce7 100644
--- a/spec/models/user_spec.rb
+++ b/spec/models/user_spec.rb
@@ -118,7 +118,7 @@ describe User, :type => :model do
context 'permissions' do
it 'should give edit permissions to user if user has "edit offer" permission in portail' do
User.portail_sync
- expect(User.find_by(username: 'vlatka.pavisic').permissions).not_to be_empty
+ expect(User.find_by(username: 'vlatka.pavisic').permissions).to include_all(User.edit_offer_permissions)
expect(User.find_by(username: 'pierre.vabre').permissions).to be_empty
end
end
diff --git a/spec/policies/boiv_policy_spec.rb b/spec/policies/boiv_policy_spec.rb
index bf09cdcd9..514534adc 100644
--- a/spec/policies/boiv_policy_spec.rb
+++ b/spec/policies/boiv_policy_spec.rb
@@ -11,5 +11,4 @@ RSpec.describe BoivPolicy, type: :policy do
permissions :show? do
it_behaves_like 'permitted policy and same organisation', 'boiv:read-offer'
end
-
end
diff --git a/spec/policies/login_policy_spec.rb b/spec/policies/login_policy_spec.rb
new file mode 100644
index 000000000..132e57433
--- /dev/null
+++ b/spec/policies/login_policy_spec.rb
@@ -0,0 +1,15 @@
+RSpec.describe LoginPolicy, type: :policy do
+ permissions :boiv? do
+ it 'no permission starting with boiv:. → denies' do
+ expect( LoginPolicy.new(user_context.user) ).not_to be_boiv
+ end
+
+ with_user_permission 'boiv:anything' do
+ it { expect( LoginPolicy.new(user_context.user) ).to be_boiv }
+ end
+ with_user_permission 'boiv:' do
+ it { expect( LoginPolicy.new(user_context.user) ).not_to be_boiv }
+ end
+ end
+
+end
diff --git a/spec/support/bare_sql.rb b/spec/support/bare_sql.rb
new file mode 100644
index 000000000..03a50ef77
--- /dev/null
+++ b/spec/support/bare_sql.rb
@@ -0,0 +1,58 @@
+module Support
+ module BareSQL
+
+ def insert(schema, table, values)
+ execute "INSERT INTO #{schema}.#{table} (#{_keys(values)}) VALUES (#{_values values})"
+ end
+
+ def execute(sql)
+ base_connection.execute(sql)
+ end
+
+ def expect_same_content(table_name)
+ expected_content = get_content(source_schema, table_name)
+ actual_content = get_content(target_schema, table_name)
+ expect( actual_content ).to eq(expected_content)
+ end
+
+ def expect_same_sequence_params(sequence_name)
+ expected_seq = Hash.without(get_sequences(source_schema, sequence_name).first, 'log_cnt')
+ actual_seq = Hash.without(get_sequences(target_schema, sequence_name).first, 'log_cnt')
+ expect( actual_seq ).to eq(expected_seq)
+ end
+
+ def get_content(schema_name, table_name)
+ execute("SELECT * FROM #{schema_name}.#{table_name}").to_a
+ end
+
+ private
+
+ def base_connection
+ ActiveRecord::Base.connection
+ end
+
+ def _keys(values)
+ values.keys.map(&:to_s).join(", ")
+ end
+
+ def _values(values)
+ values
+ .values
+ .map(&method(:_format))
+ .join(', ')
+ end
+
+ def _format(val)
+ case val
+ when String
+ "'#{val}'"
+ when TrueClass
+ "'t'"
+ when FalseClass
+ "'f'"
+ else
+ val.to_s
+ end
+ end
+ end
+end
diff --git a/spec/support/custom_matchers.rb b/spec/support/custom_matchers.rb
new file mode 100644
index 000000000..bdc3efaa0
--- /dev/null
+++ b/spec/support/custom_matchers.rb
@@ -0,0 +1,7 @@
+require 'rspec/expectations'
+
+RSpec::Matchers.define :include_all do |expected|
+ match do |actual|
+ ( expected - actual ).empty?
+ end
+end
diff --git a/spec/support/devise.rb b/spec/support/devise.rb
index 14e316bea..d4a279a41 100644
--- a/spec/support/devise.rb
+++ b/spec/support/devise.rb
@@ -36,8 +36,8 @@ module DeviseRequestHelper
end
module DeviseControllerHelper
- def login_user
- before(:each) do
+ def setup_user
+ before do
@request.env["devise.mapping"] = Devise.mappings[:user]
organisation = Organisation.where(:code => "first").first_or_create(attributes_for(:organisation))
@user = create(:user, :organisation => organisation,
@@ -47,6 +47,11 @@ module DeviseControllerHelper
'access_points.create', 'access_points.edit', 'access_points.destroy', 'access_links.create', 'access_links.edit', 'access_links.destroy',
'connection_links.create', 'connection_links.edit', 'connection_links.destroy', 'route_sections.create', 'route_sections.edit', 'route_sections.destroy',
'referentials.create', 'referentials.edit', 'referentials.destroy'])
+ end
+ end
+ def login_user()
+ setup_user
+ before do
sign_in @user
end
end
diff --git a/spec/support/hash.rb b/spec/support/hash.rb
new file mode 100644
index 000000000..ec9a2f895
--- /dev/null
+++ b/spec/support/hash.rb
@@ -0,0 +1,6 @@
+class << Hash
+ def without(hash, *keys)
+ nk = hash.keys - keys
+ Hash[*nk.zip(hash.values_at(*nk)).flatten]
+ end
+end
diff --git a/spec/support/pg_catalog.rb b/spec/support/pg_catalog.rb
index bb61adba5..ca02f2550 100644
--- a/spec/support/pg_catalog.rb
+++ b/spec/support/pg_catalog.rb
@@ -1,11 +1,12 @@
+require_relative 'bare_sql'
module Support
module PGCatalog
- # TODO: Check what of the follwowing can be done with ActiveRecord. E.g.
- # @connection.foreign_keys(table)...
+ include Support::BareSQL
def get_columns(schema_name, table_name)
- execute("SELECT * from information_schema.columns WHERE table_name = '#{table_name}' AND table_schema = '#{schema_name}'")
+ execute("SELECT column_name, column_default FROM information_schema.columns WHERE table_name = '#{table_name}' AND table_schema = '#{schema_name}'").to_a
end
+
def get_foreign_keys(schema_oid, table_name)
schema_oid = get_schema_oid(schema_oid) unless Integer === schema_oid
return [] unless schema_oid
@@ -20,11 +21,8 @@ module Support
.first
end
- def get_sequences(schema_name, table_name)
- sequences = execute <<-EOSQL
- SELECT sequence_name FROM information_schema.sequences
- WHERE sequence_schema = '#{schema_name}' AND sequence_name LIKE '#{table_name}%'
- EOSQL
+ def get_sequences(schema_name, sequence_name)
+ sequences = execute(sequence_query(schema_name, sequence_name))
sequences.values.flatten.map do | sequence |
execute "SELECT * from #{schema_name}.#{sequence}"
end.flat_map(&:to_a)
@@ -38,39 +36,20 @@ module Support
private
- def base_connection
- ActiveRecord::Base.connection
- end
-
- def execute(sql)
- base_connection.execute(sql)
- end
def foreign_key_query(schema_oid, table_name)
- key = [:foreign_key_query, schema_oid, table_name]
- get_or_create_query(key){ <<-EOQ
- SELECT ct.conname AS constraint_name, pg_get_constraintdef(ct.oid) AS constraint_def
- FROM pg_constraint ct JOIN pg_class rn ON rn.oid = ct.conrelid
- WHERE connamespace = #{schema_oid} AND rn.relname = '#{table_name}' AND rn.relkind = 'r' AND ct.contype = 'f'
- EOQ
- }
- end
-
- def sequence_properties_query(schema_name, sequence_name)
- key = [:sequence_properies_query, schema_name, sequence_name]
- get_or_create_query(key){ <<-EOQ
- Coming Soon
- EOQ
- }
-
- end
-
- def get_or_create_query(query_key, &query_value)
- queries.fetch(query_key){ queries[query_key] = query_value.() }
+ <<-EOQ
+ SELECT ct.conname AS constraint_name, pg_get_constraintdef(ct.oid) AS constraint_def
+ FROM pg_constraint ct JOIN pg_class rn ON rn.oid = ct.conrelid
+ WHERE connamespace = #{schema_oid} AND rn.relname = '#{table_name}' AND rn.relkind = 'r' AND ct.contype = 'f'
+ EOQ
end
- def queries
- @__queries__ ||= {}
+ def sequence_query(schema_name, sequence_name)
+ <<-EOQ
+ SELECT sequence_name FROM information_schema.sequences
+ WHERE sequence_schema = '#{schema_name}' AND sequence_name = '#{sequence_name}'
+ EOQ
end
def without_keys(*keys)
@@ -82,3 +61,7 @@ module Support
end
end
end
+
+RSpec.configure do | conf |
+ conf.include Support::PGCatalog, type: :pg_catalog
+end
diff --git a/spec/support/pundit/policies.rb b/spec/support/pundit/policies.rb
index 637a2a528..e18309226 100644
--- a/spec/support/pundit/policies.rb
+++ b/spec/support/pundit/policies.rb
@@ -16,6 +16,7 @@ module Support
for_user.permissions ||= []
for_user.permissions += permissions.flatten
end
+
end
module PoliciesMacros
@@ -27,6 +28,12 @@ module Support
let( :user ) { create :user }
end
end
+ def with_user_permission(permission, &blk)
+ it "with user permission #{permission.inspect}" do
+ add_permissions(permission, for_user: user)
+ blk.()
+ end
+ end
end
end
end
diff --git a/spec/views/referentials/new.html.erb_spec.rb b/spec/views/referentials/new.html.erb_spec.rb
index 0673b4578..554e71d29 100644
--- a/spec/views/referentials/new.html.erb_spec.rb
+++ b/spec/views/referentials/new.html.erb_spec.rb
@@ -5,15 +5,9 @@ describe "referentials/new", :type => :view do
before(:each) do
assign(:referential, Referential.new)
end
-
+
it "should have a textfield for name" do
render
expect(rendered).to have_field("referential[name]")
end
-
- it "should have a textfield for slug" do
- render
- expect(rendered).to have_field("referential[slug]")
- end
-
end
diff --git a/spec/workers/referential_cloning_worker_spec.rb b/spec/workers/referential_cloning_worker_spec.rb
index 85d771742..52ed8913b 100644
--- a/spec/workers/referential_cloning_worker_spec.rb
+++ b/spec/workers/referential_cloning_worker_spec.rb
@@ -9,37 +9,29 @@ RSpec.describe ReferentialCloningWorker do
let( :worker ){ described_class.new }
+ def make_referential(schema_name)
+ return OpenStruct.new( slug: schema_name )
+ end
let( :source_schema ){ "source_schema" }
- let( :target_schema ){ "#{source_schema}_tmp" }
- let( :referential_cloning ){ OpenStruct.new(source_referential: OpenStruct.new(slug: source_schema)) }
+ let( :target_schema ){ "target_schema" }
+ let( :referential_cloning ){ OpenStruct.new(source_referential: make_referential(source_schema),
+ target_referential: make_referential(target_schema)) }
+ let( :cloner ){ 'cloner' }
+
before do
expect( ReferentialCloning ).to receive(:find).with(id).and_return(referential_cloning)
- expect( StoredProcedures )
- .to receive(:invoke_stored_procedure)
- .with(:clone_schema, source_schema, target_schema, true)
-
- expect( worker ).to receive(:execute_sql).with( "DROP SCHEMA #{source_schema} CASCADE;" )
+ expect( AF83::SchemaCloner ).to receive(:new).with( source_schema, target_schema ).and_return(cloner)
+ expect( cloner ).to receive(:clone_schema)
expect( referential_cloning ).to receive(:run!)
end
it "invokes the correct stored procedure, updates the database and the AASM" do
- expect( worker ).to receive(:execute_sql).with( "ALTER SCHEMA #{target_schema} RENAME TO #{source_schema};" )
expect( referential_cloning ).to receive(:successful!)
worker.perform(id)
end
-
- it "handles failure correctly" do
- expect( worker )
- .to receive(:execute_sql)
- .with( "ALTER SCHEMA #{target_schema} RENAME TO #{source_schema};" )
- .and_raise(RuntimeError)
-
- expect( referential_cloning ).to receive(:failed!)
- worker.perform(id)
- end
end
-
+
end