lita-standups 1.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.
- checksums.yaml +7 -0
- data/.gitignore +17 -0
- data/.rspec +2 -0
- data/.rubocop.yml +51 -0
- data/.travis.yml +10 -0
- data/Gemfile +3 -0
- data/Guardfile +37 -0
- data/LICENSE +21 -0
- data/README.md +41 -0
- data/Rakefile +6 -0
- data/lib/lita/handlers/standups.rb +155 -0
- data/lib/lita/standups/manager.rb +86 -0
- data/lib/lita/standups/mixins/robot.rb +70 -0
- data/lib/lita/standups/models/standup.rb +31 -0
- data/lib/lita/standups/models/standup_response.rb +72 -0
- data/lib/lita/standups/models/standup_schedule.rb +54 -0
- data/lib/lita/standups/models/standup_session.rb +64 -0
- data/lib/lita/standups/models.rb +12 -0
- data/lib/lita/standups/wizards/create_standup.rb +31 -0
- data/lib/lita/standups/wizards/run_standup.rb +56 -0
- data/lib/lita/standups/wizards/schedule_standup.rb +53 -0
- data/lib/lita/standups/wizards.rb +11 -0
- data/lib/lita-standups.rb +27 -0
- data/lita-standups.gemspec +34 -0
- data/locales/en.yml +4 -0
- data/spec/lita/handlers/standups_spec.rb +17 -0
- data/spec/lita/integration_spec.rb +191 -0
- data/spec/lita/standups/manager_spec.rb +92 -0
- data/spec/lita/standups/mixins/robot_spec.rb +115 -0
- data/spec/lita/standups/models/standup_schedule_spec.rb +27 -0
- data/spec/spec_helper.rb +17 -0
- data/spec/support/fixtures.rb +75 -0
- data/templates/.gitkeep +0 -0
- metadata +309 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 82008368e3b720587006e5591b4bacb7f23d7aa4
|
4
|
+
data.tar.gz: 04a147d56cfba2c231ff58014fd5be92b5ff5bda
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: eff194daa8fc8f234492b4449d0923d541a5c55fa75fd07d239d15d1a93cce3bf7dd6192ada5454387267822ae1db2b7d7b16e043dd947d5764d22ec37562e79
|
7
|
+
data.tar.gz: 3d307792f13acc18a50dc76c83069173cb1ccdfc10b72dba422cfe48f31186290cac7fb16c339e7618a5f796ef1a241efe1969add2d85b5beda5812172a45499
|
data/.gitignore
ADDED
data/.rspec
ADDED
data/.rubocop.yml
ADDED
@@ -0,0 +1,51 @@
|
|
1
|
+
AllCops:
|
2
|
+
DisplayCopNames: true
|
3
|
+
TargetRubyVersion: 2.3
|
4
|
+
|
5
|
+
|
6
|
+
Style/Encoding:
|
7
|
+
Enabled: false
|
8
|
+
|
9
|
+
Style/FrozenStringLiteralComment:
|
10
|
+
Enabled: false
|
11
|
+
|
12
|
+
Style/ClassAndModuleChildren:
|
13
|
+
EnforcedStyle: compact
|
14
|
+
|
15
|
+
Style/Documentation:
|
16
|
+
Enabled: false
|
17
|
+
|
18
|
+
Style/AndOr:
|
19
|
+
EnforcedStyle: conditionals
|
20
|
+
|
21
|
+
Style/EmptyLinesAroundClassBody:
|
22
|
+
EnforcedStyle: empty_lines
|
23
|
+
|
24
|
+
|
25
|
+
Style/MultilineOperationIndentation:
|
26
|
+
EnforcedStyle: indented
|
27
|
+
|
28
|
+
|
29
|
+
Style/PercentLiteralDelimiters:
|
30
|
+
PreferredDelimiters:
|
31
|
+
'%': []
|
32
|
+
'%i': []
|
33
|
+
'%q': ()
|
34
|
+
'%Q': ()
|
35
|
+
'%r': '{}'
|
36
|
+
'%s': []
|
37
|
+
'%w': []
|
38
|
+
'%W': []
|
39
|
+
'%x': ()
|
40
|
+
|
41
|
+
Style/StringLiterals:
|
42
|
+
EnforcedStyle: single_quotes
|
43
|
+
|
44
|
+
Style/StringLiteralsInInterpolation:
|
45
|
+
EnforcedStyle: single_quotes
|
46
|
+
|
47
|
+
Style/GuardClause:
|
48
|
+
MinBodyLength: 1
|
49
|
+
|
50
|
+
Metrics/LineLength:
|
51
|
+
Max: 120
|
data/.travis.yml
ADDED
data/Gemfile
ADDED
data/Guardfile
ADDED
@@ -0,0 +1,37 @@
|
|
1
|
+
guard :bundler do
|
2
|
+
require 'guard/bundler'
|
3
|
+
require 'guard/bundler/verify'
|
4
|
+
helper = Guard::Bundler::Verify.new
|
5
|
+
|
6
|
+
files = ['Gemfile']
|
7
|
+
files += Dir['*.gemspec'] if files.any? { |f| helper.uses_gemspec?(f) }
|
8
|
+
|
9
|
+
# Assume files are symlinked from somewhere
|
10
|
+
files.each { |file| watch(helper.real_path(file)) }
|
11
|
+
end
|
12
|
+
|
13
|
+
# Note: The cmd option is now required due to the increasing number of ways
|
14
|
+
# rspec may be run, below are examples of the most common uses.
|
15
|
+
# * bundler: 'bundle exec rspec'
|
16
|
+
# * bundler binstubs: 'bin/rspec'
|
17
|
+
# * spring: 'bin/rspec' (This will use spring if running and you have
|
18
|
+
# installed the spring binstubs per the docs)
|
19
|
+
# * zeus: 'zeus rspec' (requires the server to be started separately)
|
20
|
+
# * 'just' rspec: 'rspec'
|
21
|
+
|
22
|
+
guard :rspec, cmd: "bundle exec rspec" do
|
23
|
+
require "guard/rspec/dsl"
|
24
|
+
dsl = Guard::RSpec::Dsl.new(self)
|
25
|
+
|
26
|
+
# Feel free to open issues for suggestions and improvements
|
27
|
+
|
28
|
+
# RSpec files
|
29
|
+
rspec = dsl.rspec
|
30
|
+
watch(rspec.spec_helper) { rspec.spec_dir }
|
31
|
+
watch(rspec.spec_support) { rspec.spec_dir }
|
32
|
+
watch(rspec.spec_files)
|
33
|
+
|
34
|
+
# Ruby files
|
35
|
+
ruby = dsl.ruby
|
36
|
+
dsl.watch_spec_files_for(ruby.lib_files)
|
37
|
+
end
|
data/LICENSE
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
MIT License
|
2
|
+
|
3
|
+
Copyright (c) 2016 Cristian Bica
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
13
|
+
copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
21
|
+
SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,41 @@
|
|
1
|
+
# lita-standups
|
2
|
+
|
3
|
+
[](https://travis-ci.org/cristianbica/lita-standups)
|
4
|
+
[](https://coveralls.io/github/cristianbica/lita-standups?branch=master)
|
5
|
+
|
6
|
+
|
7
|
+
lita-standups is a [Lita](http://lita.io) plugin which allows you to run standups with your team.
|
8
|
+
|
9
|
+
## Installation
|
10
|
+
|
11
|
+
Add lita-standups to your Lita instance's Gemfile:
|
12
|
+
|
13
|
+
``` ruby
|
14
|
+
gem "lita-standups"
|
15
|
+
```
|
16
|
+
|
17
|
+
## Usage
|
18
|
+
|
19
|
+
```
|
20
|
+
list standups - list configured standups
|
21
|
+
create standup - create a standup
|
22
|
+
show standup STANDUP_ID - shows details of a standup
|
23
|
+
delete standup STANDUP_ID - deletes a standup
|
24
|
+
schedule standup STANDUP_ID - schedule a standup
|
25
|
+
unschedule standup SCHEDULE_ID - unschedule a standup
|
26
|
+
list standups schedules - shows scheduled standups
|
27
|
+
show standup schedule SCHEDULE_ID - shows a scheduled standup
|
28
|
+
run standup STANDUP_ID with USERS - runs a standup now (users space/eparated)
|
29
|
+
list standup sessions - list all standups sessions
|
30
|
+
show standup session SESSION_ID - show a standups session details
|
31
|
+
```
|
32
|
+
|
33
|
+
## Contributing
|
34
|
+
|
35
|
+
1. Fork it ( https://github.com/cristianbica/lita-standups/fork )
|
36
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
37
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
38
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
39
|
+
5. Create a new Pull Request
|
40
|
+
|
41
|
+
|
data/Rakefile
ADDED
@@ -0,0 +1,155 @@
|
|
1
|
+
module Lita
|
2
|
+
module Handlers
|
3
|
+
class Standups < Handler
|
4
|
+
|
5
|
+
route(/^list standups$/, :list_standups,
|
6
|
+
command: true,
|
7
|
+
help: { 'list standups' => 'list configured standups' })
|
8
|
+
|
9
|
+
route(/^create standup$/, :create_standup,
|
10
|
+
command: true,
|
11
|
+
help: { 'create standup' => 'create a standup' })
|
12
|
+
|
13
|
+
route(/^show standup (\w+)$/, :show_standup,
|
14
|
+
command: true,
|
15
|
+
help: { 'show standup STANDUP_ID' => 'shows details of a standup' })
|
16
|
+
|
17
|
+
route(/^delete standup (\w+)$/, :delete_standup,
|
18
|
+
command: true,
|
19
|
+
help: { 'delete standup STANDUP_ID' => 'deletes a standup' })
|
20
|
+
|
21
|
+
route(/^schedule standup (\w+)$/, :create_standups_schedule,
|
22
|
+
command: true,
|
23
|
+
help: { 'schedule standup STANDUP_ID' => 'schedule a standup' })
|
24
|
+
|
25
|
+
route(/^unschedule standup (\w+)$/, :delete_standups_schedule,
|
26
|
+
command: true,
|
27
|
+
help: { 'unschedule standup SCHEDULE_ID' => 'unschedule a standup' })
|
28
|
+
|
29
|
+
route(/^list standups schedules$/, :show_standups_schedule,
|
30
|
+
command: true,
|
31
|
+
help: { 'list standups schedules' => 'shows scheduled standups' })
|
32
|
+
|
33
|
+
route(/^show standup schedule (\w+)$/, :show_standup_schedule,
|
34
|
+
command: true,
|
35
|
+
help: { 'show standup schedule SCHEDULE_ID' => 'shows a scheduled standup' })
|
36
|
+
|
37
|
+
route(/^run standup (.+) with (.*)$/, :run_standup,
|
38
|
+
command: true,
|
39
|
+
help: { 'run standup STANDUP_ID with USERS' => 'runs a standup now (users space/comma separated)' })
|
40
|
+
|
41
|
+
route(/^list standup sessions$/, :list_standup_sessions,
|
42
|
+
command: true,
|
43
|
+
help: { 'list standup sessions' => 'list all standups sessions' })
|
44
|
+
|
45
|
+
route(/^show standup session (\w+)$/, :show_standup_session,
|
46
|
+
command: true,
|
47
|
+
help: { 'show standup session SESSION_ID' => 'show a standups session details' })
|
48
|
+
|
49
|
+
def list_standups(request)
|
50
|
+
standups = Models::Standup.all.to_a
|
51
|
+
message = "Standups found: #{standups.size}."
|
52
|
+
message << " Here they are: \n" if standups.size>0
|
53
|
+
message << standups.map(&:summary).join("\n")
|
54
|
+
request.reply message
|
55
|
+
end
|
56
|
+
|
57
|
+
def create_standup(request)
|
58
|
+
start_wizard Wizards::CreateStandup, request.message
|
59
|
+
end
|
60
|
+
|
61
|
+
def show_standup(request)
|
62
|
+
standup = Models::Standup[request.matches[0][0]]
|
63
|
+
if standup
|
64
|
+
request.reply "Here are the details of your standup: \n>>>\n#{standup.description}"
|
65
|
+
else
|
66
|
+
request.reply "I couldn't find a standup with ID=#{request.matches[0][0]}"
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
def delete_standup(request)
|
71
|
+
standup = Models::Standup[request.matches[0][0]]
|
72
|
+
if standup
|
73
|
+
standup.delete
|
74
|
+
request.reply "Standup with ID #{standup.id} has been deleted"
|
75
|
+
else
|
76
|
+
request.reply "I couldn't find a standup with ID=#{request.matches[0][0]}"
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
def create_standups_schedule(request)
|
81
|
+
standup = Models::Standup[request.matches[0][0]]
|
82
|
+
if standup
|
83
|
+
start_wizard Wizards::ScheduleStandup, request.message, 'standup_id' => standup.id
|
84
|
+
else
|
85
|
+
request.reply "I couldn't find a standup with ID=#{request.matches[0][0]}"
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
def show_standups_schedule(request)
|
90
|
+
schedules = Models::StandupSchedule.all.to_a
|
91
|
+
message = "Scheduled standups found: #{schedules.size}."
|
92
|
+
message << " Here they are: \n" if schedules.size>0
|
93
|
+
message << schedules.map(&:summary).join("\n")
|
94
|
+
request.reply message
|
95
|
+
end
|
96
|
+
|
97
|
+
def delete_standups_schedule(request)
|
98
|
+
schedule = Models::StandupSchedule[request.matches[0][0]]
|
99
|
+
if schedule
|
100
|
+
robot.unschedule_standup(schedule)
|
101
|
+
schedule.delete
|
102
|
+
request.reply "Schedule with ID #{schedule.id} has been deleted"
|
103
|
+
else
|
104
|
+
request.reply "I couldn't find a scheduled standup with ID=#{request.matches[0][0]}"
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
def show_standup_schedule(request)
|
109
|
+
schedule = Models::StandupSchedule[request.matches[0][0]]
|
110
|
+
if schedule
|
111
|
+
request.reply "Here are the details: \n>>>\n#{schedule.description}"
|
112
|
+
else
|
113
|
+
request.reply "I couldn't find a scheduled standup with ID=#{request.matches[0][0]}"
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
def run_standup(request)
|
118
|
+
standup = Models::Standup[request.matches[0][0]]
|
119
|
+
recipients = request.matches[0][1].to_s.gsub("@", "").split(/[\s,\n]/m).map(&:strip).map(&:presence).compact
|
120
|
+
if standup
|
121
|
+
request.reply "I'll run the standup shortly and post the results here. Thanks"
|
122
|
+
robot.run_standup standup.id, recipients, request.message.source.room
|
123
|
+
else
|
124
|
+
request.reply "I couldn't find a standup with ID=#{request.matches[0][0]}"
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
def list_standup_sessions(request)
|
129
|
+
sessions = Models::StandupSession.all.to_a
|
130
|
+
message = "Sessions found: #{sessions.size}."
|
131
|
+
message << " Here they are: \n" if sessions.size>0
|
132
|
+
message << sessions.map(&:summary).join("\n")
|
133
|
+
request.reply message
|
134
|
+
end
|
135
|
+
|
136
|
+
def show_standup_session(request)
|
137
|
+
session = Models::StandupSession[request.matches[0][0]]
|
138
|
+
if session
|
139
|
+
message = "Here are the standup session details: \n #{session.description}\n"
|
140
|
+
message << "\n*Responses:*\n"
|
141
|
+
message << session.report_message
|
142
|
+
request.reply message
|
143
|
+
else
|
144
|
+
request.reply "I couldn't find a standup session with ID=#{request.matches[0][0]}"
|
145
|
+
end
|
146
|
+
end
|
147
|
+
|
148
|
+
def self.const_missing(name)
|
149
|
+
Lita::Standups.const_defined?(name) ? Lita::Standups.const_get(name) : super
|
150
|
+
end
|
151
|
+
|
152
|
+
Lita.register_handler(self)
|
153
|
+
end
|
154
|
+
end
|
155
|
+
end
|
@@ -0,0 +1,86 @@
|
|
1
|
+
module Lita
|
2
|
+
module Standups
|
3
|
+
class Manager
|
4
|
+
|
5
|
+
EXPIRATION_TIME = 3600
|
6
|
+
|
7
|
+
def self.run(robot:, standup_id:, recipients:, room:)
|
8
|
+
session = Models::StandupSession.create(
|
9
|
+
standup_id: standup_id,
|
10
|
+
recipients: recipients,
|
11
|
+
room: room
|
12
|
+
)
|
13
|
+
new(robot: robot, session: session).run
|
14
|
+
end
|
15
|
+
|
16
|
+
def self.run_schedule(robot:, schedule_id:)
|
17
|
+
schedule = Models::StandupSchedule[schedule_id]
|
18
|
+
session = Models::StandupSession.create(
|
19
|
+
standup_id: schedule.standup_id,
|
20
|
+
standup_schedule_id: schedule.id,
|
21
|
+
recipients: schedule.recipients,
|
22
|
+
room: schedule.channel
|
23
|
+
)
|
24
|
+
new(robot: robot, session: session).run
|
25
|
+
end
|
26
|
+
|
27
|
+
def self.abort_expired_standups(robot:)
|
28
|
+
Lita::Standups::Models::StandupResponse.find(status: "pending").union(status: "running").each do |response|
|
29
|
+
next unless Time.current - response.created_at > EXPIRATION_TIME
|
30
|
+
response.expired!
|
31
|
+
response.save
|
32
|
+
Lita::Wizard.cancel_wizard(response.user.id)
|
33
|
+
target = Lita::Source.new(user: response.user, room: nil, private_message: true)
|
34
|
+
robot.send_message target, "Expired. See you next time!"
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def self.complete_finished_standups(robot:)
|
39
|
+
Lita::Standups::Models::StandupSession.find(status: "completed", results_sent: false).each do |session|
|
40
|
+
new(robot: robot, session: session).post_results
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
attr_accessor :robot, :session
|
45
|
+
|
46
|
+
def initialize(robot:, session:)
|
47
|
+
@robot = robot
|
48
|
+
@session = session
|
49
|
+
end
|
50
|
+
|
51
|
+
def standup
|
52
|
+
session.standup
|
53
|
+
end
|
54
|
+
|
55
|
+
def room
|
56
|
+
@room ||= Lita::Source.new(user: nil, room: session.room)
|
57
|
+
end
|
58
|
+
|
59
|
+
def run
|
60
|
+
session.running!
|
61
|
+
session.save
|
62
|
+
session.recipients.each { |recipient| ask_questions(recipient) }
|
63
|
+
end
|
64
|
+
|
65
|
+
def ask_questions(recipient)
|
66
|
+
user = Lita::User.fuzzy_find(recipient)
|
67
|
+
response = Models::StandupResponse.create(
|
68
|
+
standup_session_id: session.id,
|
69
|
+
user_id: user.id
|
70
|
+
)
|
71
|
+
dummy_source = Lita::Source.new(user: user, room: nil, private_message: true)
|
72
|
+
dummy_message = Lita::Message.new(robot, '', dummy_source)
|
73
|
+
Wizards::RunStandup.start robot, dummy_message, 'response_id' => response.id
|
74
|
+
end
|
75
|
+
|
76
|
+
def post_results
|
77
|
+
return if session.results_sent
|
78
|
+
message = "The standup '#{standup.name}' has finished. Here's what everyone posted:\n\n#{session.report_message}"
|
79
|
+
robot.send_message room, message
|
80
|
+
session.results_sent = true
|
81
|
+
session.save
|
82
|
+
end
|
83
|
+
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
@@ -0,0 +1,70 @@
|
|
1
|
+
module Lita
|
2
|
+
module Standups
|
3
|
+
module Mixins
|
4
|
+
module Robot
|
5
|
+
def initialize(*args)
|
6
|
+
@scheduler = Rufus::Scheduler.new if scheduler_enabled?
|
7
|
+
super
|
8
|
+
end
|
9
|
+
|
10
|
+
# :nocov:
|
11
|
+
def scheduler
|
12
|
+
@scheduler
|
13
|
+
end
|
14
|
+
# :nocov:
|
15
|
+
|
16
|
+
def scheduler_enabled?
|
17
|
+
ENV['TEST'].nil?
|
18
|
+
end
|
19
|
+
|
20
|
+
def schedule_standups
|
21
|
+
return unless scheduler_enabled?
|
22
|
+
scheduler.jobs.each(&:unschedule)
|
23
|
+
scheduler.cron '* * * * *', tags: [:standup_schedules, :abort_expired] do |job|
|
24
|
+
Lita::Standups::Manager.abort_expired_standups(robot: self)
|
25
|
+
end
|
26
|
+
scheduler.cron '* * * * *', tags: [:standup_schedules, :complete_finished] do |job|
|
27
|
+
Lita::Standups::Manager.complete_finished_standups(robot: self)
|
28
|
+
end
|
29
|
+
Models::StandupSchedule.all.each do |standup_schedule|
|
30
|
+
schedule_standup(standup_schedule)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def schedule_standup(standup_schedule)
|
35
|
+
return unless scheduler_enabled?
|
36
|
+
scheduler.cron standup_schedule.cron_line, schedule_id: standup_schedule.id,
|
37
|
+
tags: [:standup_schedules, "standup_schedule_#{standup_schedule.id}"] do |job|
|
38
|
+
Lita::Standups::Manager.run_schedule(robot: self, schedule_id: job.opts[:schedule_id])
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def unschedule_standup(standup_schedule)
|
43
|
+
return unless scheduler_enabled?
|
44
|
+
scheduler.jobs(tags: [:standup_schedules, "standup_schedule_#{standup_schedule.id}"]).each(&:unschedule)
|
45
|
+
end
|
46
|
+
|
47
|
+
def run_standup(standup_id, recipients, room_id)
|
48
|
+
if scheduler_enabled?
|
49
|
+
scheduler.in "5s", tags: [:standup_schedules, :run_standup] do |job|
|
50
|
+
Lita::Standups::Manager.run(robot: self, standup_id: standup_id, recipients: recipients, room: room_id)
|
51
|
+
end
|
52
|
+
else
|
53
|
+
Lita::Standups::Manager.run(robot: self, standup_id: standup_id, recipients: recipients, room: room_id)
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
# :nocov:
|
58
|
+
def run
|
59
|
+
schedule_standups
|
60
|
+
super
|
61
|
+
end
|
62
|
+
# :nocov:
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
class Robot
|
68
|
+
prepend ::Lita::Standups::Mixins::Robot
|
69
|
+
end
|
70
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
module Lita
|
2
|
+
module Standups
|
3
|
+
module Models
|
4
|
+
class Standup < Ohm::Model
|
5
|
+
|
6
|
+
include Ohm::Callbacks
|
7
|
+
include Ohm::Timestamps
|
8
|
+
include Ohm::DataTypes
|
9
|
+
|
10
|
+
attribute :name
|
11
|
+
attribute :questions, Type::Array
|
12
|
+
|
13
|
+
collection :schedules, StandupSchedule, :standup
|
14
|
+
|
15
|
+
def summary
|
16
|
+
"#{name} (ID: #{id}) - #{questions.size} question(s)"
|
17
|
+
end
|
18
|
+
|
19
|
+
def description
|
20
|
+
[
|
21
|
+
"ID: #{id}",
|
22
|
+
"Name: #{name}",
|
23
|
+
"Questions:",
|
24
|
+
questions.join("\n")
|
25
|
+
].join("\n")
|
26
|
+
end
|
27
|
+
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,72 @@
|
|
1
|
+
module Lita
|
2
|
+
module Standups
|
3
|
+
module Models
|
4
|
+
class StandupResponse < Ohm::Model
|
5
|
+
|
6
|
+
include Ohm::Callbacks
|
7
|
+
include Ohm::Timestamps
|
8
|
+
include Ohm::DataTypes
|
9
|
+
|
10
|
+
attribute :status
|
11
|
+
attribute :user_id
|
12
|
+
attribute :answers, Type::Array
|
13
|
+
|
14
|
+
reference :standup_session, StandupSession
|
15
|
+
|
16
|
+
index :status
|
17
|
+
index :user_id
|
18
|
+
|
19
|
+
def user
|
20
|
+
@user ||= Lita::User.fuzzy_find(user_id)
|
21
|
+
end
|
22
|
+
|
23
|
+
def standup
|
24
|
+
standup_session.standup
|
25
|
+
end
|
26
|
+
|
27
|
+
def questions
|
28
|
+
standup.questions
|
29
|
+
end
|
30
|
+
|
31
|
+
def before_create
|
32
|
+
self.status ||= 'pending'
|
33
|
+
end
|
34
|
+
|
35
|
+
def after_save
|
36
|
+
standup_session.update_status if finished?
|
37
|
+
end
|
38
|
+
|
39
|
+
%w(pending running completed aborted expired).each do |status_name|
|
40
|
+
define_method("#{status_name}?") do
|
41
|
+
status == status_name
|
42
|
+
end
|
43
|
+
define_method("#{status_name}!") do
|
44
|
+
self.status = status_name
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def finished?
|
49
|
+
completed? || aborted? || expired?
|
50
|
+
end
|
51
|
+
|
52
|
+
def report_message
|
53
|
+
message = "#{user.name} (a.k.a @#{user.mention_name})\n"
|
54
|
+
if answers.is_a?(Array)
|
55
|
+
questions.map.with_index do |question, index|
|
56
|
+
if answers[index]
|
57
|
+
message << "> *#{question}*\n"
|
58
|
+
answers[index].split("\n").each do |line|
|
59
|
+
message << "> #{line}\n"
|
60
|
+
end
|
61
|
+
end
|
62
|
+
message << "> \n" if index < questions.count - 1
|
63
|
+
end
|
64
|
+
else
|
65
|
+
message << "> *Expired*\n"
|
66
|
+
end
|
67
|
+
message
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
module Lita
|
2
|
+
module Standups
|
3
|
+
module Models
|
4
|
+
class StandupSchedule < Ohm::Model
|
5
|
+
|
6
|
+
include Ohm::Callbacks
|
7
|
+
include Ohm::Timestamps
|
8
|
+
include Ohm::DataTypes
|
9
|
+
|
10
|
+
attribute :repeat
|
11
|
+
attribute :day_of_week
|
12
|
+
attribute :time, Type::Time
|
13
|
+
attribute :recipients, Type::Array
|
14
|
+
attribute :channel
|
15
|
+
|
16
|
+
reference :standup, Standup
|
17
|
+
|
18
|
+
def cron_line
|
19
|
+
[
|
20
|
+
time.min,
|
21
|
+
time.hour,
|
22
|
+
"*",
|
23
|
+
"*",
|
24
|
+
(weekly? ? day_of_week_index : "*")
|
25
|
+
].join(" ")
|
26
|
+
end
|
27
|
+
|
28
|
+
def day_of_week_index
|
29
|
+
%w(sunday monday tuesday wednesday thursday friday saturday).index(day_of_week)
|
30
|
+
end
|
31
|
+
|
32
|
+
def summary
|
33
|
+
day_text = weekly? ? " on #{day_of_week}" : ""
|
34
|
+
"ID: #{id} - running standup #{standup.name} (ID: #{standup.id}) #{repeat}#{day_text} at #{time.strftime("%H:%M")}"
|
35
|
+
end
|
36
|
+
|
37
|
+
def description
|
38
|
+
[
|
39
|
+
"ID: #{id}",
|
40
|
+
"Standup: #{standup.name} (ID: #{standup.id})",
|
41
|
+
"Recipients: #{recipients.join(", ")}",
|
42
|
+
"Running #{repeat} " + (weekly? ? "on #{day_of_week} " : "") + "at #{time.strftime("%H:%M")}",
|
43
|
+
"Sending the result on #{channel}"
|
44
|
+
].join("\n")
|
45
|
+
end
|
46
|
+
|
47
|
+
def weekly?
|
48
|
+
repeat == "weekly"
|
49
|
+
end
|
50
|
+
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|