gtfs_engine 1.5.3 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (72) hide show
  1. checksums.yaml +5 -5
  2. data/Rakefile +6 -5
  3. data/app/controllers/gtfs_engine/agencies_controller.rb +2 -14
  4. data/app/controllers/gtfs_engine/application_controller.rb +2 -14
  5. data/app/controllers/gtfs_engine/calendar_dates_controller.rb +2 -14
  6. data/app/controllers/gtfs_engine/calendars_controller.rb +7 -19
  7. data/app/controllers/gtfs_engine/data_sets_controller.rb +10 -24
  8. data/app/controllers/gtfs_engine/fare_attributes_controller.rb +2 -14
  9. data/app/controllers/gtfs_engine/fare_rules_controller.rb +2 -14
  10. data/app/controllers/gtfs_engine/feed_infos_controller.rb +2 -14
  11. data/app/controllers/gtfs_engine/frequencies_controller.rb +2 -14
  12. data/app/controllers/gtfs_engine/routes_controller.rb +2 -14
  13. data/app/controllers/gtfs_engine/shapes_controller.rb +2 -14
  14. data/app/controllers/gtfs_engine/stop_times_controller.rb +2 -14
  15. data/app/controllers/gtfs_engine/stops_controller.rb +2 -14
  16. data/app/controllers/gtfs_engine/transfers_controller.rb +9 -21
  17. data/app/controllers/gtfs_engine/trips_controller.rb +4 -16
  18. data/app/helpers/gtfs_engine/default_views_helper.rb +8 -21
  19. data/app/helpers/gtfs_engine/fields_helper.rb +3 -15
  20. data/app/models/gtfs_engine/agency.rb +3 -15
  21. data/app/models/gtfs_engine/calendar.rb +21 -38
  22. data/app/models/gtfs_engine/calendar_date.rb +5 -17
  23. data/app/models/gtfs_engine/data_set.rb +20 -32
  24. data/app/models/gtfs_engine/fare_attribute.rb +3 -15
  25. data/app/models/gtfs_engine/fare_rule.rb +9 -23
  26. data/app/models/gtfs_engine/feed_info.rb +3 -15
  27. data/app/models/gtfs_engine/frequency.rb +5 -17
  28. data/app/models/gtfs_engine/route.rb +4 -17
  29. data/app/models/gtfs_engine/shape.rb +5 -17
  30. data/app/models/gtfs_engine/stop.rb +5 -18
  31. data/app/models/gtfs_engine/stop_time.rb +5 -19
  32. data/app/models/gtfs_engine/transfer.rb +5 -17
  33. data/app/models/gtfs_engine/trip.rb +8 -25
  34. data/app/views/gtfs_engine/calendars/dates.json.jsend +3 -17
  35. data/app/views/gtfs_engine/data_sets/index.json.jsend +4 -18
  36. data/app/views/gtfs_engine/data_sets/show.json.jsend +2 -16
  37. data/app/views/gtfs_engine/gtfs/index.json.jsend +1 -15
  38. data/app/views/gtfs_engine/gtfs/show.json.jsend +1 -15
  39. data/app/views/gtfs_engine/transfers/from.json.jsend +1 -15
  40. data/app/views/gtfs_engine/transfers/from_to.json.jsend +1 -15
  41. data/app/views/gtfs_engine/transfers/to.json.jsend +1 -15
  42. data/app/views/gtfs_engine/trips/block.json.jsend +1 -15
  43. data/bin/rails +5 -3
  44. data/config/routes.rb +3 -16
  45. data/db/migrate/20140320045108_create_gtfs_engine_data_sets.rb +2 -0
  46. data/db/migrate/20140320045232_create_gtfs_engine_calendars.rb +2 -0
  47. data/db/migrate/20140320045751_create_gtfs_engine_calendar_dates.rb +2 -0
  48. data/db/migrate/20140320050100_create_gtfs_engine_shapes.rb +2 -0
  49. data/db/migrate/20140320051140_create_gtfs_engine_routes.rb +4 -2
  50. data/db/migrate/20140320052005_create_gtfs_engine_stops.rb +4 -2
  51. data/db/migrate/20140320052508_create_gtfs_engine_trips.rb +3 -1
  52. data/db/migrate/20140320052907_create_gtfs_engine_stop_times.rb +2 -0
  53. data/db/migrate/20140401032609_create_gtfs_engine_agencies.rb +2 -0
  54. data/db/migrate/20140405235947_create_gtfs_engine_fare_attributes.rb +2 -0
  55. data/db/migrate/20140406063500_create_gtfs_engine_fare_rules.rb +3 -1
  56. data/db/migrate/20140406071922_create_gtfs_engine_frequencies.rb +2 -0
  57. data/db/migrate/20140406072309_create_gtfs_engine_transfers.rb +2 -0
  58. data/db/migrate/20140406073548_create_gtfs_engine_feed_infos.rb +2 -0
  59. data/lib/gtfs_engine.rb +2 -14
  60. data/lib/gtfs_engine/concerns.rb +2 -14
  61. data/lib/gtfs_engine/concerns/controllers.rb +10 -18
  62. data/lib/gtfs_engine/concerns/controllers/data_access.rb +28 -40
  63. data/lib/gtfs_engine/concerns/controllers/gtfs.rb +17 -50
  64. data/lib/gtfs_engine/engine.rb +2 -15
  65. data/lib/gtfs_engine/exceptions.rb +4 -19
  66. data/lib/gtfs_engine/json_responder.rb +9 -23
  67. data/lib/gtfs_engine/middleware/base.rb +6 -19
  68. data/lib/gtfs_engine/middleware/json_parse_errors.rb +6 -20
  69. data/lib/gtfs_engine/sources.rb +33 -44
  70. data/lib/gtfs_engine/version.rb +30 -37
  71. data/lib/tasks/gtfs_engine_tasks.rake +15 -25
  72. metadata +36 -43
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  class CreateGtfsEngineFareAttributes < ActiveRecord::Migration[4.2]
2
4
  TABLE = :gtfs_engine_fare_attributes
3
5
 
@@ -1,9 +1,11 @@
1
+ # frozen_string_literal: true
2
+
1
3
  class CreateGtfsEngineFareRules < ActiveRecord::Migration[4.2]
2
4
  TABLE = :gtfs_engine_fare_rules
3
5
 
4
6
  def change
5
7
  create_table TABLE do |t|
6
- t.string :fare_id, null: false
8
+ t.string :fare_id, null: false
7
9
  t.string :route_id
8
10
  t.string :origin_id
9
11
  t.string :destination_id
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  class CreateGtfsEngineFrequencies < ActiveRecord::Migration[4.2]
2
4
  TABLE = :gtfs_engine_frequencies
3
5
 
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  class CreateGtfsEngineTransfers < ActiveRecord::Migration[4.2]
2
4
  TABLE = :gtfs_engine_transfers
3
5
 
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  class CreateGtfsEngineFeedInfos < ActiveRecord::Migration[4.2]
2
4
  TABLE = :gtfs_engine_feed_infos
3
5
 
data/lib/gtfs_engine.rb CHANGED
@@ -1,17 +1,5 @@
1
- # This file is part of the KNOWtime server.
2
- #
3
- # The KNOWtime server is free software: you can redistribute it and/or modify it
4
- # under the terms of the GNU General Public License as published by the Free
5
- # Software Foundation, either version 3 of the License, or (at your option) any
6
- # later version.
7
- #
8
- # The KNOWtime server is distributed in the hope that it will be useful, but
9
- # WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
10
- # FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
11
- # details.
12
- #
13
- # You should have received a copy of the GNU General Public License
14
- # along with the KNOWtime server. If not, see <http://www.gnu.org/licenses/>.
1
+ # frozen_string_literal: true
2
+
15
3
  require 'gtfs_reader'
16
4
 
17
5
  require 'gtfs_engine/engine'
@@ -1,17 +1,5 @@
1
- # This file is part of the KNOWtime server.
2
- #
3
- # The KNOWtime server is free software: you can redistribute it and/or modify it
4
- # under the terms of the GNU General Public License as published by the Free
5
- # Software Foundation, either version 3 of the License, or (at your option) any
6
- # later version.
7
- #
8
- # The KNOWtime server is distributed in the hope that it will be useful, but
9
- # WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
10
- # FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
11
- # details.
12
- #
13
- # You should have received a copy of the GNU General Public License
14
- # along with the KNOWtime server. If not, see <http://www.gnu.org/licenses/>.
1
+ # frozen_string_literal: true
2
+
15
3
  module GtfsEngine::Concerns
16
4
  extend ActiveSupport::Autoload
17
5
 
@@ -1,20 +1,12 @@
1
- # This file is part of the KNOWtime server.
2
- #
3
- # The KNOWtime server is free software: you can redistribute it and/or modify it
4
- # under the terms of the GNU General Public License as published by the Free
5
- # Software Foundation, either version 3 of the License, or (at your option) any
6
- # later version.
7
- #
8
- # The KNOWtime server is distributed in the hope that it will be useful, but
9
- # WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
10
- # FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
11
- # details.
12
- #
13
- # You should have received a copy of the GNU General Public License
14
- # along with the KNOWtime server. If not, see <http://www.gnu.org/licenses/>.
15
- module GtfsEngine::Concerns::Controllers
16
- extend ActiveSupport::Autoload
1
+ # frozen_string_literal: true
17
2
 
18
- autoload :DataAccess
19
- autoload :Gtfs
3
+ module GtfsEngine
4
+ module Concerns
5
+ module Controllers
6
+ extend ActiveSupport::Autoload
7
+
8
+ autoload :DataAccess
9
+ autoload :Gtfs
10
+ end
11
+ end
20
12
  end
@@ -1,47 +1,35 @@
1
- # This file is part of the KNOWtime server.
2
- #
3
- # The KNOWtime server is free software: you can redistribute it and/or modify it
4
- # under the terms of the GNU General Public License as published by the Free
5
- # Software Foundation, either version 3 of the License, or (at your option) any
6
- # later version.
7
- #
8
- # The KNOWtime server is distributed in the hope that it will be useful, but
9
- # WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
10
- # FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
11
- # details.
12
- #
13
- # You should have received a copy of the GNU General Public License
14
- # along with the KNOWtime server. If not, see <http://www.gnu.org/licenses/>.
1
+ # frozen_string_literal: true
15
2
 
16
- # A Controller +Concern+ for convenient access to the +DataSet+ specified in
17
- # the URL.
18
- module GtfsEngine::Concerns::Controllers::DataAccess
19
- extend ActiveSupport::Concern
3
+ module GtfsEngine
4
+ module Concerns
5
+ module Controllers
6
+ module DataAccess
7
+ extend ActiveSupport::Concern
20
8
 
21
-
22
- #@param param_key <Symbol> The key of the URL param to use as the feed's ID
23
- #@return <DataSet>
24
- def data(param_key=:data_set_id)
25
- key = params[param_key]
26
- (@data_sets ||= {})[key] =
27
- Rails.cache.fetch "data_set_#{key}" do
28
- if param_is_data_set_name? param_key
29
- GtfsEngine::DataSet.where(name: key).newest
30
- else
31
- GtfsEngine::DataSet.find params[param_key]
32
- end
9
+ # @param param_key <Symbol> The key of the URL param to use as the feed's ID
10
+ # @return <DataSet>
11
+ def data(param_key = :data_set_id)
12
+ key = params[param_key]
13
+ (@data_sets ||= {})[key] =
14
+ Rails.cache.fetch("data_set_#{key}") do
15
+ if param_is_data_set_name?(param_key)
16
+ GtfsEngine::DataSet.where(name: key).newest
17
+ else
18
+ GtfsEngine::DataSet.find(params[param_key])
19
+ end
20
+ end
33
21
  end
34
- end
35
-
36
-
37
- def data_cache(key)
38
- Rails.cache.fetch("#{data.id}::#{key}") { yield }
39
- end
40
22
 
23
+ def data_cache(key, &blk)
24
+ Rails.cache.fetch("#{data.id}::#{key}", &blk)
25
+ end
41
26
 
42
- #@return <Bool> +true+ if the key is the name of the GTFS feed,
43
- # instead of its ID
44
- def param_is_data_set_name?(param_key)
45
- not /[[:digit:]]/ === params[param_key].try(:first)
27
+ # @return <Bool> +true+ if the key is the name of the GTFS feed,
28
+ # instead of its ID
29
+ def param_is_data_set_name?(param_key)
30
+ !(/[[:digit:]]/ === params[param_key].try(:first))
31
+ end
32
+ end
33
+ end
46
34
  end
47
35
  end
@@ -1,26 +1,10 @@
1
- # This file is part of the KNOWtime server.
2
- #
3
- # The KNOWtime server is free software: you can redistribute it and/or modify it
4
- # under the terms of the GNU General Public License as published by the Free
5
- # Software Foundation, either version 3 of the License, or (at your option) any
6
- # later version.
7
- #
8
- # The KNOWtime server is distributed in the hope that it will be useful, but
9
- # WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
10
- # FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
11
- # details.
12
- #
13
- # You should have received a copy of the GNU General Public License
14
- # along with the KNOWtime server. If not, see <http://www.gnu.org/licenses/>.
15
- #
16
- # This controller +Concern+ provides the functionality common among most of
17
- # the GTFS controllers.
1
+ # frozen_string_literal: true
2
+
18
3
  module GtfsEngine::Concerns::Controllers::Gtfs
19
4
  extend ActiveSupport::Concern
20
5
 
21
-
22
6
  included do
23
- around_filter :gtfs_cache, only: [:index, :show]
7
+ around_action :gtfs_cache, only: %i[index show]
24
8
 
25
9
  rescue_from GtfsEngine::UnknownFilters, with: :unknown_filter
26
10
  rescue_from ActiveRecord::StatementInvalid, with: :statement_invalid
@@ -28,23 +12,20 @@ module GtfsEngine::Concerns::Controllers::Gtfs
28
12
  helper_method :filter
29
13
  end
30
14
 
31
-
32
15
  module ClassMethods
33
- #@return [Symbol] the unique key for this GTFS association
34
- def gtfs_id(id=nil)
16
+ # @return [Symbol] the unique key for this GTFS association
17
+ def gtfs_id(id = nil)
35
18
  @gtfs_id = id unless id.nil?
36
- @gtfs_id or controller_name.singularize.foreign_key
19
+ @gtfs_id || controller_name.singularize.foreign_key
37
20
  end
38
21
 
39
-
40
22
  def filters(*attrs)
41
23
  attrs.flatten!
42
24
  @filters = attrs unless attrs.empty?
43
25
  @filters ||= []
44
26
  end
45
27
 
46
-
47
- #@return [Class] the +ActiveRecord::Base+ class associated with this
28
+ # @return [Class] the +ActiveRecord::Base+ class associated with this
48
29
  # controller
49
30
  def record_class
50
31
  @record_class ||= begin
@@ -53,7 +34,6 @@ module GtfsEngine::Concerns::Controllers::Gtfs
53
34
  end
54
35
  end
55
36
 
56
-
57
37
  # GET / collection of elements for the given GTFS type
58
38
  # The returned collection may be filtered with query parameters
59
39
  def index
@@ -61,15 +41,13 @@ module GtfsEngine::Concerns::Controllers::Gtfs
61
41
  respond_with @records, template: 'gtfs_engine/gtfs/index'
62
42
  end
63
43
 
64
-
65
44
  # GET /:id for a specific element of the given GTFS type
66
45
  def show
67
46
  @record = record
68
47
  respond_with @record, template: 'gtfs_engine/gtfs/show'
69
48
  end
70
49
 
71
-
72
- #@return <ActionController::Parameters> the map of fields to filter;
50
+ # @return <ActionController::Parameters> the map of fields to filter;
73
51
  # derived from the query string
74
52
  def filter
75
53
  @filter ||= begin
@@ -82,53 +60,45 @@ module GtfsEngine::Concerns::Controllers::Gtfs
82
60
  unless unknown.empty?
83
61
  raise GtfsEngine::UnknownFilters.new(unknown), 'unknown filter'
84
62
  end
63
+
85
64
  query_params = ActionController::Parameters.new query
86
65
  query_params.permit filters
87
66
  end
88
67
  end
89
68
 
90
-
91
69
  protected
92
70
 
93
-
94
- #@return [ActiveRecord::Relation] all the records in this GTFS association
71
+ # @return [ActiveRecord::Relation] all the records in this GTFS association
95
72
  def collection
96
73
  data.send controller_name
97
74
  end
98
75
 
99
-
100
- #@return [ActiveRecord::Relation] all the records in this GTFS
76
+ # @return [ActiveRecord::Relation] all the records in this GTFS
101
77
  # association that match the filter specified in the query string
102
78
  def filtered_collection
103
79
  collection.where filter
104
80
  end
105
81
 
106
-
107
- #@return [ActiveRecord::Base] the record identified by +params[:id]+ in this
82
+ # @return [ActiveRecord::Base] the record identified by +params[:id]+ in this
108
83
  # GTFS association
109
84
  def record
110
85
  collection.find_by! gtfs_id => params[:id]
111
86
  end
112
87
 
113
-
114
88
  def gtfs_id
115
89
  self.class.gtfs_id
116
90
  end
117
91
 
118
-
119
92
  def filters
120
93
  self.class.filters
121
94
  end
122
95
 
123
-
124
96
  def query
125
97
  request.query_parameters
126
98
  end
127
99
 
128
-
129
100
  private
130
101
 
131
-
132
102
  def format
133
103
  request.format.to_sym
134
104
  end
@@ -144,23 +114,21 @@ module GtfsEngine::Concerns::Controllers::Gtfs
144
114
  yield if stale? options
145
115
  end
146
116
 
147
-
148
- #@param ex [GtfsEngine::UnknownFilter]
117
+ # @param ex [GtfsEngine::UnknownFilter]
149
118
  def unknown_filter(ex)
150
119
  render status: :bad_request, jsend: {
151
120
  error: 'unknown filter', data: ex.to_h
152
121
  }
153
122
  end
154
123
 
155
-
156
124
  def statement_invalid(ex)
157
125
  inner = ex.original_exception
158
126
  case inner
159
127
  when PG::InvalidDatetimeFormat, PG::DatetimeFieldOverflow
160
128
  lines = inner.message.split "\n"
161
- /.*"([^"]+)"[^"]+/ === lines[1][0..lines[2].size] and begin
129
+ (/.*"([^"]+)"[^"]+/ === lines[1][0..lines[2].size]) && begin
162
130
  render status: :bad_request, jsend: {
163
- message: 'invalid date', data: {$1 => 'invalid date'}
131
+ message: 'invalid date', data: { Regexp.last_match(1) => 'invalid date' }
164
132
  }
165
133
  end
166
134
  else
@@ -168,10 +136,9 @@ module GtfsEngine::Concerns::Controllers::Gtfs
168
136
  end
169
137
  end
170
138
 
171
-
172
- def record_not_found(ex)
139
+ def record_not_found(_ex)
173
140
  render status: :not_found, jsend: {
174
- error: 'record not found', data: {gtfs_id => params[:id]}
141
+ error: 'record not found', data: { gtfs_id => params[:id] }
175
142
  }
176
143
  end
177
144
  end
@@ -1,17 +1,4 @@
1
- # This file is part of the KNOWtime server.
2
- #
3
- # The KNOWtime server is free software: you can redistribute it and/or modify it
4
- # under the terms of the GNU General Public License as published by the Free
5
- # Software Foundation, either version 3 of the License, or (at your option) any
6
- # later version.
7
- #
8
- # The KNOWtime server is distributed in the hope that it will be useful, but
9
- # WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
10
- # FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
11
- # details.
12
- #
13
- # You should have received a copy of the GNU General Public License
14
- # along with the KNOWtime server. If not, see <http://www.gnu.org/licenses/>.
1
+ # frozen_string_literal: true
15
2
 
16
3
  # The following line is required for jsend_wrapper/rails to be available when
17
4
  # mounted in another rails application.
@@ -33,7 +20,7 @@ module GtfsEngine
33
20
 
34
21
  initializer 'gtfs_engine.middleware' do |app|
35
22
  app.config.middleware.insert_before ActionDispatch::Cookies,
36
- GtfsEngine::Middleware::JsonParseErrors
23
+ GtfsEngine::Middleware::JsonParseErrors
37
24
  end
38
25
  end
39
26
  end
@@ -1,34 +1,19 @@
1
- # This file is part of the KNOWtime server.
2
- #
3
- # The KNOWtime server is free software: you can redistribute it and/or modify it
4
- # under the terms of the GNU General Public License as published by the Free
5
- # Software Foundation, either version 3 of the License, or (at your option) any
6
- # later version.
7
- #
8
- # The KNOWtime server is distributed in the hope that it will be useful, but
9
- # WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
10
- # FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
11
- # details.
12
- #
13
- # You should have received a copy of the GNU General Public License
14
- # along with the KNOWtime server. If not, see <http://www.gnu.org/licenses/>.
1
+ # frozen_string_literal: true
2
+
15
3
  module GtfsEngine
16
4
  Error = Class.new StandardError
17
5
 
18
-
19
6
  class UnknownFilters < Error
20
7
  attr_reader :fields
21
8
 
22
-
23
- #@param fields [Array<String>] the names of the unknown fields which the
9
+ # @param fields [Array<String>] the names of the unknown fields which the
24
10
  # caller tried to filter with
25
11
  def initialize(fields)
26
12
  @fields = fields
27
13
  end
28
14
 
29
-
30
15
  def to_hash
31
- fields.each_with_object( {} ) do |field, hash|
16
+ fields.each_with_object({}) do |field, hash|
32
17
  hash[field] = message
33
18
  end
34
19
  end
@@ -1,26 +1,10 @@
1
- # This file is part of the KNOWtime server.
2
- #
3
- # The KNOWtime server is free software: you can redistribute it and/or modify it
4
- # under the terms of the GNU General Public License as published by the Free
5
- # Software Foundation, either version 3 of the License, or (at your option) any
6
- # later version.
7
- #
8
- # The KNOWtime server is distributed in the hope that it will be useful, but
9
- # WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
10
- # FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
11
- # details.
12
- #
13
- # You should have received a copy of the GNU General Public License
14
- # along with the KNOWtime server. If not, see <http://www.gnu.org/licenses/>.
1
+ # frozen_string_literal: true
2
+
15
3
  require 'action_controller/responder'
16
4
 
17
5
  class GtfsEngine::JsonResponder < ActionController::Responder
18
- def to_json
19
- if has_errors?
20
- display_errors
21
- else
22
- default_render
23
- end
6
+ def to_json(*_args)
7
+ has_errors? ? display_errors : default_render
24
8
  end
25
9
 
26
10
  protected
@@ -28,8 +12,10 @@ class GtfsEngine::JsonResponder < ActionController::Responder
28
12
  def display_errors
29
13
  options[:status] ||= :unprocessable_entity
30
14
 
31
- controller.render status: options[:status], jsend: {
32
- error: 'could not process request', data: resource_errors[:errors]
33
- }
15
+ controller.render(
16
+ status: options[:status],
17
+ jsend: { error: 'could not process request',
18
+ data: resource_errors[:errors] }
19
+ )
34
20
  end
35
21
  end