calendar-assistant 0.9.0 → 0.10.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 (37) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +2 -1
  3. data/README.md +43 -22
  4. data/Rakefile +33 -6
  5. data/bin/calendar-assistant +2 -1
  6. data/lib/calendar_assistant.rb +14 -14
  7. data/lib/calendar_assistant/available_block.rb +1 -1
  8. data/lib/calendar_assistant/calendar_assistant.rb +21 -25
  9. data/lib/calendar_assistant/cli.rb +10 -10
  10. data/lib/calendar_assistant/cli/authorizer.rb +14 -14
  11. data/lib/calendar_assistant/cli/command_service.rb +2 -2
  12. data/lib/calendar_assistant/cli/commands.rb +47 -40
  13. data/lib/calendar_assistant/cli/config.rb +16 -17
  14. data/lib/calendar_assistant/cli/event_presenter.rb +2 -2
  15. data/lib/calendar_assistant/cli/event_set_presenter.rb +3 -3
  16. data/lib/calendar_assistant/cli/helpers.rb +7 -8
  17. data/lib/calendar_assistant/cli/linter_event_presenter.rb +3 -3
  18. data/lib/calendar_assistant/cli/linter_event_set_presenter.rb +4 -4
  19. data/lib/calendar_assistant/cli/printer.rb +15 -15
  20. data/lib/calendar_assistant/config.rb +11 -11
  21. data/lib/calendar_assistant/config/token_store.rb +4 -4
  22. data/lib/calendar_assistant/date_helpers.rb +1 -2
  23. data/lib/calendar_assistant/event.rb +50 -50
  24. data/lib/calendar_assistant/event_repository.rb +12 -13
  25. data/lib/calendar_assistant/event_repository_factory.rb +1 -1
  26. data/lib/calendar_assistant/event_set.rb +14 -14
  27. data/lib/calendar_assistant/extensions/google_apis_extensions.rb +1 -1
  28. data/lib/calendar_assistant/extensions/launchy_extensions.rb +2 -2
  29. data/lib/calendar_assistant/has_duration.rb +4 -5
  30. data/lib/calendar_assistant/lint_event_repository.rb +3 -3
  31. data/lib/calendar_assistant/location_config_validator.rb +3 -3
  32. data/lib/calendar_assistant/location_event_repository.rb +7 -8
  33. data/lib/calendar_assistant/predicate_collection.rb +1 -2
  34. data/lib/calendar_assistant/scheduler.rb +4 -5
  35. data/lib/calendar_assistant/string_helpers.rb +1 -1
  36. data/lib/calendar_assistant/version.rb +1 -1
  37. metadata +18 -4
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 71db4f83ef4cb314c49b308e9fbf752b958823bb320aa7db5d404665e23c3eb9
4
- data.tar.gz: 8bd30697df26b5a3975e9a06add5de2c49be64344a3787a09576d3b081bbe121
3
+ metadata.gz: 9acd200de6edb4b52d97c1994e25d657195e595c1042ef676c552ff6904d6c88
4
+ data.tar.gz: 59926908cc71682f6480d561323eebdc29aec95fdf351c8a73a6dddefed9f9fc
5
5
  SHA512:
6
- metadata.gz: b1701eb120de9b9ebdb5065695762eeca199b043e26554386feac9eae62b9ad9cd2b2aa6abe63e7de90716c5e55b74a7fa4e80bcc0056ab29f98c4d4ff840d56
7
- data.tar.gz: 0f5f584849f62313666931d943f0b070d0e34d3388f94df57d6ca2b7751db6c23f2ef9ed65b691980ddfda2f54a3ebbd255a356186aae25c0eab25c24abd57a8
6
+ metadata.gz: 9756ccc795a449ac0fde9707b5b87f6d03457b7ba22a7c802a9073e592c1c758d561223b873e7c2c6b71a336d80329998a0556545183a1ad536d3fab1f25b703
7
+ data.tar.gz: eb497928c8fd6ae16fad8ee0d2b093cd3417338457367c99a8c8efb94c3a8a48d467215e1f93eddfe0f0e4f147312defc7649d3431f8e0fe4f9e24f0e481d3d8
data/Gemfile CHANGED
@@ -6,7 +6,8 @@ group :optional do
6
6
  # These gems are not required for a functioning test suite, so not listing them in the gemspec.
7
7
  # I personally find them useful in the developer role, though.
8
8
  gem "autotest"
9
+ gem "pry"
9
10
  gem "rspec-autotest"
11
+ gem "rufo"
10
12
  gem "test_notifier"
11
- gem "pry"
12
13
  end
data/README.md CHANGED
@@ -36,6 +36,7 @@
36
36
  * [`show`: View your calendar events](#show-view-your-calendar-events)
37
37
  * [`lint`: Calendar events that require action](#lint-calendar-events-that-require-action)
38
38
  * [`config`: View your configuration parameters](#config-view-your-configuration-parameters)
39
+ * [`interactive`: Interactive Console](#interactive-interactive-console)
39
40
  - [Development](#development)
40
41
  * [Running Tests Locally](#running-tests-locally)
41
42
  * [Continuous Integration](#continuous-integration)
@@ -160,12 +161,11 @@ Options:
160
161
  # Default: true
161
162
 
162
163
  Description:
163
- This command will walk you through setting up a Google Cloud Project, enabling the Google Calendar
164
- API, and saving the credentials necessary to access the API on behalf of users.
164
+ This command will walk you through setting up a Google Cloud Project, enabling the Google Calendar API, and saving the
165
+ credentials necessary to access the API on behalf of users.
165
166
 
166
- If you already have downloaded client credentials, you don't need to run this command. Instead,
167
- rename the downloaded JSON file to
168
- `/home/user/.calendar-assistant.client`
167
+ If you already have downloaded client credentials, you don't need to run this command. Instead, rename the downloaded
168
+ JSON file to `/home/user/.calendar-assistant.client`
169
169
  </pre>
170
170
 
171
171
 
@@ -182,14 +182,13 @@ Options:
182
182
  # Default: true
183
183
 
184
184
  Description:
185
- Create and authorize a named profile (e.g., "work", "home", "flastname@company.tld") to access your
186
- calendar.
185
+ Create and authorize a named profile (e.g., "work", "home", "flastname@company.tld") to access your calendar.
187
186
 
188
- When setting up a profile, you'll be asked to visit a URL to authenticate, grant authorization, and
189
- generate and persist an access token.
187
+ When setting up a profile, you'll be asked to visit a URL to authenticate, grant authorization, and generate and persist
188
+ an access token.
190
189
 
191
- In order for this to work, you'll need to have set up your API client credentials. Run
192
- `calendar-assistant help setup` for instructions.
190
+ In order for this to work, you'll need to have set up your API client credentials. Run `calendar-assistant help setup`
191
+ for instructions.
193
192
  </pre>
194
193
 
195
194
 
@@ -197,17 +196,18 @@ Description:
197
196
 
198
197
  <pre>
199
198
  Commands:
200
- calendar-assistant authorize PROFILE_NAME # create (or validate) a profil...
201
- calendar-assistant availability [DATE | DATERANGE | TIMERANGE] # Show your availability for a ...
202
- calendar-assistant config # Dump your configuration param...
203
- calendar-assistant help [COMMAND] # Describe available commands o...
204
- calendar-assistant join [TIME] # Open the URL for a video call...
205
- calendar-assistant lint [DATE | DATERANGE | TIMERANGE] # Lint your events for a date o...
206
- calendar-assistant location [DATE | DATERANGE] # Show your location for a date...
207
- calendar-assistant location-set LOCATION [DATE | DATERANGE] # Set your location to LOCATION...
208
- calendar-assistant setup # Link your local calendar-assi...
209
- calendar-assistant show [DATE | DATERANGE | TIMERANGE] # Show your events for a date o...
210
- calendar-assistant version # Display the version of calend...
199
+ calendar-assistant authorize PROFILE_NAME # create (or validate) a profile named NAME with ca...
200
+ calendar-assistant availability [DATE | DATERANGE | TIMERANGE] # Show your availability for a date or range of dat...
201
+ calendar-assistant config # Dump your configuration parameters (merge of defa...
202
+ calendar-assistant help [COMMAND] # Describe available commands or one specific command
203
+ calendar-assistant interactive # interactive console for calendar assistant
204
+ calendar-assistant join [TIME] # Open the URL for a video call attached to your me...
205
+ calendar-assistant lint [DATE | DATERANGE | TIMERANGE] # Lint your events for a date or range of dates (de...
206
+ calendar-assistant location [DATE | DATERANGE] # Show your location for a date or range of dates (...
207
+ calendar-assistant location-set LOCATION [DATE | DATERANGE] # Set your location to LOCATION for a date or range...
208
+ calendar-assistant setup # Link your local calendar-assistant installation t...
209
+ calendar-assistant show [DATE | DATERANGE | TIMERANGE] # Show your events for a date or range of dates (de...
210
+ calendar-assistant version # Display the version of calendar-assistant
211
211
 
212
212
  Options:
213
213
  -h, -?, [--help], [--no-help]
@@ -579,6 +579,27 @@ meeting-length = "30m"
579
579
  start-of-day = "9am"
580
580
  </pre>
581
581
 
582
+ ### `interactive`: Interactive Console
583
+
584
+ <pre>
585
+ Usage:
586
+ calendar-assistant interactive
587
+
588
+ Options:
589
+ -h, -?, [--help], [--no-help]
590
+ [--debug], [--no-debug] # how dare you suggest there are bugs
591
+ -f, [--formatting], [--no-formatting] # Enable Text Formatting
592
+ # Default: true
593
+
594
+ interactive console for calendar assistant
595
+ </pre>
596
+
597
+ For example, check the version of calendar assistant
598
+
599
+ ```
600
+ calendar-assistant> version
601
+ 0.0.0
602
+ ```
582
603
 
583
604
  ## Development
584
605
 
data/Rakefile CHANGED
@@ -2,13 +2,14 @@
2
2
  require "rspec/core/rake_task"
3
3
  require "concourse"
4
4
  require "license_finder"
5
+ require "tempfile"
6
+ require "rainbow"
5
7
 
6
8
  #
7
9
  # concourse
8
10
  #
9
11
  Concourse.new("calendar-assistant", fly_target: "calendar-assistants").create_tasks!
10
12
 
11
-
12
13
  #
13
14
  # spec tasks
14
15
  #
@@ -17,26 +18,38 @@ RSpec::Core::RakeTask.new("spec")
17
18
  task "test" => "spec" # undocumented alias for 'spec'
18
19
 
19
20
  desc "Run unit specs"
20
- RSpec::Core::RakeTask.new("spec:unit") do |t|
21
+ RSpec::Core::RakeTask.new("spec:unit") do |t|
21
22
  t.rspec_opts ||= []
22
23
  t.rspec_opts << " --tag=~type:aruba"
23
24
  end
24
25
 
25
26
  desc "Run feature specs"
26
- RSpec::Core::RakeTask.new("spec:feature") do |t|
27
+ RSpec::Core::RakeTask.new("spec:feature") do |t|
27
28
  t.rspec_opts ||= []
28
29
  t.rspec_opts << " --tag=type:aruba"
29
30
  end
30
31
 
31
- desc "Ensure all dependencies meet license requirements."
32
+ desc "Ensure all dependencies meet license requirements"
32
33
  task "license_finder" do
33
34
  LicenseFinder::CLI::Main.start(["report"])
34
35
  LicenseFinder::CLI::Main.start([])
35
36
  end
36
37
 
37
- desc "Run unit specs, feature specs, and license finder"
38
- task "default" => ["spec", "license_finder"]
38
+ #
39
+ # readme tasks
40
+ #
41
+ desc "Generate the README.md from its erb template"
42
+ task "readme" do
43
+ sh "./generate-readme"
44
+ end
39
45
 
46
+ desc "Generate the README.md from its erb template"
47
+ task "readme:check" do
48
+ file = Tempfile.new("readme").path
49
+ sh "./generate-readme #{file}"
50
+ sh "diff -C3 #{file} README.md"
51
+ puts Rainbow("README.md looks good").green
52
+ end
40
53
 
41
54
  #
42
55
  # docker docker docker
@@ -53,3 +66,17 @@ end
53
66
 
54
67
  desc "Build and push a docker image for testing"
55
68
  task "docker" => ["docker:build", "docker:push"]
69
+
70
+ #
71
+ # formatting
72
+ #
73
+ desc "Format ruby code"
74
+ task "format" do
75
+ sh "rufo lib spec", verbose: true
76
+ end
77
+
78
+ #
79
+ # default
80
+ #
81
+ desc "Run unit specs, feature specs, license finder, and check the README"
82
+ task "default" => ["spec", "license_finder", "readme:check"]
@@ -1,9 +1,10 @@
1
1
  #! /usr/bin/env ruby
2
- libdir = File.join( File.dirname(__FILE__), "..", "lib")
2
+ libdir = File.join(File.dirname(__FILE__), "..", "lib")
3
3
  $LOAD_PATH.unshift(libdir) unless $LOAD_PATH.include?(libdir)
4
4
 
5
5
  require "calendar_assistant"
6
6
 
7
+ Encoding.default_internal = Encoding::UTF_8
7
8
  Rainbow.enabled = true
8
9
 
9
10
  begin
@@ -25,21 +25,21 @@ require "active_support/time" # Time doesn't trigger autoload
25
25
  require "calendar_assistant/calendar_assistant"
26
26
 
27
27
  class CalendarAssistant
28
- autoload :VERSION, "calendar_assistant/version"
29
- autoload :Config, "calendar_assistant/config"
30
- autoload :StringHelpers, "calendar_assistant/string_helpers"
31
- autoload :DateHelpers, "calendar_assistant/date_helpers"
32
- autoload :HasDuration, "calendar_assistant/has_duration"
33
- autoload :AvailableBlock, "calendar_assistant/available_block"
34
- autoload :Event, "calendar_assistant/event"
35
- autoload :EventRepository, "calendar_assistant/event_repository"
36
- autoload :EventRepositoryFactory, "calendar_assistant/event_repository_factory"
37
- autoload :EventSet, "calendar_assistant/event_set"
38
- autoload :Scheduler, "calendar_assistant/scheduler"
39
- autoload :LocalService, "calendar_assistant/local_service"
28
+ autoload :VERSION, "calendar_assistant/version"
29
+ autoload :Config, "calendar_assistant/config"
30
+ autoload :StringHelpers, "calendar_assistant/string_helpers"
31
+ autoload :DateHelpers, "calendar_assistant/date_helpers"
32
+ autoload :HasDuration, "calendar_assistant/has_duration"
33
+ autoload :AvailableBlock, "calendar_assistant/available_block"
34
+ autoload :Event, "calendar_assistant/event"
35
+ autoload :EventRepository, "calendar_assistant/event_repository"
36
+ autoload :EventRepositoryFactory, "calendar_assistant/event_repository_factory"
37
+ autoload :EventSet, "calendar_assistant/event_set"
38
+ autoload :Scheduler, "calendar_assistant/scheduler"
39
+ autoload :LocalService, "calendar_assistant/local_service"
40
40
  autoload :LocationEventRepository, "calendar_assistant/location_event_repository"
41
- autoload :LintEventRepository, "calendar_assistant/lint_event_repository"
42
- autoload :PredicateCollection, "calendar_assistant/predicate_collection"
41
+ autoload :LintEventRepository, "calendar_assistant/lint_event_repository"
42
+ autoload :PredicateCollection, "calendar_assistant/predicate_collection"
43
43
  autoload :LocationConfigValidator, "calendar_assistant/location_config_validator"
44
44
  end
45
45
 
@@ -9,4 +9,4 @@ class CalendarAssistant
9
9
  @end = HasDuration.cast_datetime(params[:end]) if params[:end]
10
10
  end
11
11
  end
12
- end
12
+ end
@@ -1,46 +1,42 @@
1
1
  # coding: utf-8
2
2
  class CalendarAssistant
3
- class BaseException < RuntimeError ; end
3
+ class BaseException < RuntimeError; end
4
4
 
5
- EMOJI_PLANE = "🛪" # U+1F6EA NORTHEAST-POINTING AIRPLANE
6
- EMOJI_1_1 = "👫" # MAN AND WOMAN HOLDING HANDS
5
+ EMOJI_PLANE = "🛪" # U+1F6EA NORTHEAST-POINTING AIRPLANE
6
+ EMOJI_1_1 = "👫" # MAN AND WOMAN HOLDING HANDS
7
7
 
8
8
  attr_reader :service, :calendar, :config
9
9
 
10
- def self.date_range_cast time_range
10
+ def self.date_range_cast(time_range)
11
11
  time_range.first.to_date..(time_range.last + 1.day).to_date
12
12
  end
13
13
 
14
- def self.in_tz time_zone, &block
14
+ def self.in_tz(time_zone, &block)
15
15
  # this is totally not thread-safe
16
16
  orig_time_tz = Time.zone
17
- orig_env_tz = ENV['TZ']
17
+ orig_env_tz = ENV["TZ"]
18
18
  begin
19
19
  unless time_zone.nil?
20
20
  Time.zone = time_zone
21
- ENV['TZ'] = time_zone
21
+ ENV["TZ"] = time_zone
22
22
  end
23
23
  yield
24
24
  ensure
25
25
  Time.zone = orig_time_tz
26
- ENV['TZ'] = orig_env_tz
26
+ ENV["TZ"] = orig_env_tz
27
27
  end
28
28
  end
29
29
 
30
-
31
- def initialize config=Config.new,
30
+ def initialize(config = Config.new,
32
31
  event_repository_factory: EventRepositoryFactory,
33
- service:
34
- @config = config
32
+ service:) @config = config
35
33
  @service = service
36
-
37
34
  @calendar = service.get_calendar Config::DEFAULT_CALENDAR_ID
38
35
  @event_repository_factory = event_repository_factory
39
36
  @event_repositories = {} # type, calendar_id → event_repository
40
- @event_predicates = PredicateCollection.build(config.must_be, config.must_not_be)
41
- end
37
+ @event_predicates = PredicateCollection.build(config.must_be, config.must_not_be) end
42
38
 
43
- def in_env &block
39
+ def in_env(&block)
44
40
  # this is totally not thread-safe
45
41
  config.in_env do
46
42
  in_tz do
@@ -49,21 +45,21 @@ class CalendarAssistant
49
45
  end
50
46
  end
51
47
 
52
- def in_tz &block
48
+ def in_tz(&block)
53
49
  CalendarAssistant.in_tz calendar.time_zone do
54
50
  yield
55
51
  end
56
52
  end
57
53
 
58
- def lint_events time_range
54
+ def lint_events(time_range)
59
55
  calendar_ids = config.calendar_ids
60
56
  if calendar_ids.length > 1
61
57
  raise BaseException, "CalendarAssistant#lint_events only supports one person (for now)"
62
58
  end
63
- event_repository(calendar_ids.first, type: :lint).find(time_range, predicates: @event_predicates)
59
+ event_repository(calendar_ids.first, type: :lint).find(time_range, predicates: @event_predicates)
64
60
  end
65
61
 
66
- def find_events time_range
62
+ def find_events(time_range)
67
63
  calendar_ids = config.calendar_ids
68
64
  if calendar_ids.length > 1
69
65
  raise BaseException, "CalendarAssistant#find_events only supports one person (for now)"
@@ -71,7 +67,7 @@ class CalendarAssistant
71
67
  event_repository(calendar_ids.first).find(time_range, predicates: @event_predicates)
72
68
  end
73
69
 
74
- def availability time_range
70
+ def availability(time_range)
75
71
  calendar_ids = config.calendar_ids
76
72
  ers = calendar_ids.map do |calendar_id|
77
73
  event_repository calendar_id
@@ -79,14 +75,14 @@ class CalendarAssistant
79
75
  Scheduler.new(self, ers).available_blocks(time_range, predicates: @event_predicates)
80
76
  end
81
77
 
82
- def find_location_events time_range
78
+ def find_location_events(time_range)
83
79
  event_repository(type: :location).find(time_range, predicates: @event_predicates)
84
80
  end
85
81
 
86
- def create_location_events time_range, location
82
+ def create_location_events(time_range, location)
87
83
  LocationConfigValidator.valid?(config)
88
84
 
89
- event_set = EventSet::Hash.new(event_repository,{})
85
+ event_set = EventSet::Hash.new(event_repository, {})
90
86
 
91
87
  unique_calendar_ids.each do |calendar_id|
92
88
  event_set[calendar_id] = event_repository(calendar_id, type: :location).create(time_range, location, predicates: @event_predicates)
@@ -95,7 +91,7 @@ class CalendarAssistant
95
91
  event_set
96
92
  end
97
93
 
98
- def event_repository calendar_id=Config::DEFAULT_CALENDAR_ID, type: :base
94
+ def event_repository(calendar_id = Config::DEFAULT_CALENDAR_ID, type: :base)
99
95
  @event_repositories[type] ||= {}
100
96
  @event_repositories[type][calendar_id] ||=
101
97
  @event_repository_factory.new_event_repository(@service, calendar_id, config: config, type: type)
@@ -1,10 +1,10 @@
1
- require_relative 'cli/config'
2
- require_relative 'cli/helpers'
3
- require_relative 'cli/command_service'
4
- require_relative 'cli/printer'
5
- require_relative 'cli/event_presenter'
6
- require_relative 'cli/event_set_presenter'
7
- require_relative 'cli/linter_event_presenter'
8
- require_relative 'cli/linter_event_set_presenter'
9
- require_relative 'cli/authorizer'
10
- require_relative 'cli/commands'
1
+ require_relative "cli/config"
2
+ require_relative "cli/helpers"
3
+ require_relative "cli/command_service"
4
+ require_relative "cli/printer"
5
+ require_relative "cli/event_presenter"
6
+ require_relative "cli/event_set_presenter"
7
+ require_relative "cli/linter_event_presenter"
8
+ require_relative "cli/linter_event_set_presenter"
9
+ require_relative "cli/authorizer"
10
+ require_relative "cli/commands"
@@ -20,17 +20,17 @@
20
20
  class CalendarAssistant
21
21
  module CLI
22
22
  class Authorizer
23
- class NoCredentials < CalendarAssistant::BaseException ; end
24
- class UnauthorizedError < CalendarAssistant::BaseException ; end
23
+ class NoCredentials < CalendarAssistant::BaseException; end
24
+ class UnauthorizedError < CalendarAssistant::BaseException; end
25
25
 
26
- OOB_URI = 'urn:ietf:wg:oauth:2.0:oob'.freeze
26
+ OOB_URI = "urn:ietf:wg:oauth:2.0:oob".freeze
27
27
  APPLICATION_NAME = "Flavorjones Calendar Assistant".freeze
28
- CREDENTIALS_PATH = File.join (ENV['CA_HOME'] || ENV["HOME"]), ".calendar-assistant.client"
28
+ CREDENTIALS_PATH = File.join (ENV["CA_HOME"] || ENV["HOME"]), ".calendar-assistant.client"
29
29
  SCOPE = Google::Apis::CalendarV3::AUTH_CALENDAR
30
30
 
31
31
  attr_reader :profile_name, :config_token_store
32
32
 
33
- def initialize profile_name, config_token_store
33
+ def initialize(profile_name, config_token_store)
34
34
  @profile_name = profile_name
35
35
  @config_token_store = config_token_store
36
36
  end
@@ -50,7 +50,7 @@ class CalendarAssistant
50
50
  end
51
51
  end
52
52
 
53
- private
53
+ private
54
54
 
55
55
  def credentials
56
56
  @credentials ||= authorizer.get_credentials profile_name
@@ -74,15 +74,15 @@ class CalendarAssistant
74
74
 
75
75
  def authorizer
76
76
  @authorizer ||= begin
77
- if ! File.exists?(CREDENTIALS_PATH)
78
- raise NoCredentials, "No credentials found. Please run `calendar-assistant help setup` for instructions"
79
- end
77
+ if !File.exists?(CREDENTIALS_PATH)
78
+ raise NoCredentials, "No credentials found. Please run `calendar-assistant help setup` for instructions"
79
+ end
80
80
 
81
- FileUtils.chmod 0600, CREDENTIALS_PATH
82
- client_id = Google::Auth::ClientId.from_file(CREDENTIALS_PATH)
83
- Google::Auth::UserAuthorizer.new(client_id, SCOPE, config_token_store)
84
- end
81
+ FileUtils.chmod 0600, CREDENTIALS_PATH
82
+ client_id = Google::Auth::ClientId.from_file(CREDENTIALS_PATH)
83
+ Google::Auth::UserAuthorizer.new(client_id, SCOPE, config_token_store)
84
+ end
85
85
  end
86
86
  end
87
87
  end
88
- end
88
+ end