artisan-core 0.0.1
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.
- data/lib/artisan/activity/activity_interactor.rb +18 -0
- data/lib/artisan/activity/activity_presenter.rb +35 -0
- data/lib/artisan/activity/formatters/diff_changes.rb +41 -0
- data/lib/artisan/activity/formatters/name.rb +22 -0
- data/lib/artisan/activity/formatters/source.rb +16 -0
- data/lib/artisan/activity/formatters/temporal.rb +20 -0
- data/lib/artisan/activity/iteration_auditor.rb +44 -0
- data/lib/artisan/activity/project_auditor.rb +28 -0
- data/lib/artisan/activity/story_auditor.rb +42 -0
- data/lib/artisan/callbacks.rb +27 -0
- data/lib/artisan/crud_strategy.rb +23 -0
- data/lib/artisan/event_mailer.rb +22 -0
- data/lib/artisan/inviter.rb +36 -0
- data/lib/artisan/iterations/iteration_point_calculator.rb +28 -0
- data/lib/artisan/iterations/iteration_presenter.rb +20 -0
- data/lib/artisan/iterations/iteration_workflow_interactor.rb +60 -0
- data/lib/artisan/iterations/iteration_workflow_presenter.rb +11 -0
- data/lib/artisan/iterations/iterations.rb +47 -0
- data/lib/artisan/iterations/iterations_interactor.rb +48 -0
- data/lib/artisan/iterations/move_to_backlog.rb +28 -0
- data/lib/artisan/iterations/story_tags.rb +15 -0
- data/lib/artisan/member.rb +52 -0
- data/lib/artisan/no_op_callbacks.rb +6 -0
- data/lib/artisan/projects/api_key_generator.rb +26 -0
- data/lib/artisan/projects/archive_interactor.rb +29 -0
- data/lib/artisan/projects/completed_stories_presenter.rb +20 -0
- data/lib/artisan/projects/iteration_numberer.rb +15 -0
- data/lib/artisan/projects/project_creator.rb +51 -0
- data/lib/artisan/projects/projects_interactor.rb +58 -0
- data/lib/artisan/projects/projects_presenter.rb +46 -0
- data/lib/artisan/projects/story_point_summer.rb +16 -0
- data/lib/artisan/reports/average_stat_per_iteration_data.rb +55 -0
- data/lib/artisan/reports/burn_up_chart.rb +46 -0
- data/lib/artisan/reports/high_charts_data_retriever.rb +77 -0
- data/lib/artisan/reports/high_charts_interactor.rb +31 -0
- data/lib/artisan/reports/percentage_of_commitments_met_data.rb +47 -0
- data/lib/artisan/reports/signoff/pdf.rb +161 -0
- data/lib/artisan/reports/signoff/report.rb +120 -0
- data/lib/artisan/reports/velocity_report.rb +43 -0
- data/lib/artisan/repository.rb +51 -0
- data/lib/artisan/stories/pert_calculator.rb +62 -0
- data/lib/artisan/stories/stories_interactor.rb +78 -0
- data/lib/artisan/stories/story_collection.rb +66 -0
- data/lib/artisan/stories/story_exporter.rb +45 -0
- data/lib/artisan/stories/story_numberer.rb +15 -0
- data/lib/artisan/stories/story_presenter.rb +23 -0
- data/lib/artisan/stories/story_sorter.rb +37 -0
- data/lib/artisan/story_board.rb +45 -0
- data/lib/artisan/story_column_changer.rb +71 -0
- data/lib/artisan/story_exporter.rb +14 -0
- data/lib/artisan/team.rb +50 -0
- data/spec/crud_strategy_spec.rb +100 -0
- data/spec/event_mailer_spec.rb +86 -0
- data/spec/inviter_spec.rb +58 -0
- data/spec/member_spec.rb +58 -0
- data/spec/repository_spec.rb +32 -0
- data/spec/story_board_spec.rb +36 -0
- data/spec/story_collection_spec.rb +61 -0
- data/spec/story_column_changer_spec.rb +222 -0
- data/spec/story_exporter_spec.rb +25 -0
- data/spec/story_sorter_spec.rb +67 -0
- data/spec/team_spec.rb +107 -0
- metadata +128 -0
@@ -0,0 +1,60 @@
|
|
1
|
+
require "artisan/activity/iteration_auditor"
|
2
|
+
require "artisan/iterations/iterations_interactor"
|
3
|
+
require "artisan/iterations/move_to_backlog"
|
4
|
+
require "artisan/repository"
|
5
|
+
|
6
|
+
module Artisan
|
7
|
+
module Iterations
|
8
|
+
IterationWorkflowPresenter = Struct.new(:iteration_id, :project_id)
|
9
|
+
|
10
|
+
class IterationWorkflowInteractor
|
11
|
+
attr_accessor :iteration, :project, :user
|
12
|
+
|
13
|
+
def initialize(iteration_id, user, iteration_repository = Repository.iteration)
|
14
|
+
self.iteration = iteration_repository.find(iteration_id)
|
15
|
+
self.project = iteration.project
|
16
|
+
self.user = user
|
17
|
+
end
|
18
|
+
|
19
|
+
def complete
|
20
|
+
iteration.update_attributes!(:committed_points_at_completion => total_points)
|
21
|
+
iteration.update_attributes!(:complete => true, :completed_at => Time.now)
|
22
|
+
|
23
|
+
Activity::IterationAuditor.completed(iteration.id, iteration.project.id, user.id)
|
24
|
+
MoveToBacklog.new(iteration).move
|
25
|
+
#
|
26
|
+
# Artisan::Stories.for(project.id, user).backlog.each do |story|
|
27
|
+
# story.position += 1 if story.position
|
28
|
+
# Artisan::Repository.story.save(story)
|
29
|
+
# end
|
30
|
+
|
31
|
+
return IterationWorkflowPresenter.new(iteration.id, iteration.project.id)
|
32
|
+
end
|
33
|
+
|
34
|
+
def resume
|
35
|
+
iteration.update_attributes!(:committed_points_at_completion => nil)
|
36
|
+
iteration.update_attributes!(:complete => false, :completed_at => nil)
|
37
|
+
Activity::IterationAuditor.resumed(iteration.id, iteration.project.id, user.id)
|
38
|
+
return IterationWorkflowPresenter.new(iteration.id, iteration.project.id)
|
39
|
+
end
|
40
|
+
|
41
|
+
def start
|
42
|
+
iteration.update_attributes!(:committed_points => total_points)
|
43
|
+
Activity::IterationAuditor.started(iteration.id, iteration.project.id, user.id)
|
44
|
+
return IterationWorkflowPresenter.new(iteration.id, iteration.project.id)
|
45
|
+
end
|
46
|
+
|
47
|
+
def state
|
48
|
+
return :completed if iteration.complete?
|
49
|
+
return :working if !iteration.committed_points.nil?
|
50
|
+
return :planning
|
51
|
+
end
|
52
|
+
|
53
|
+
private
|
54
|
+
|
55
|
+
def total_points
|
56
|
+
IterationPointCalculator.new(iteration).total_points
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
module Artisan
|
2
|
+
module Iterations
|
3
|
+
class Iterations
|
4
|
+
|
5
|
+
def initialize(project_id)
|
6
|
+
@project = Artisan::Repository.project.find(project_id)
|
7
|
+
end
|
8
|
+
|
9
|
+
def build
|
10
|
+
repository.new(:project => @project)
|
11
|
+
end
|
12
|
+
|
13
|
+
def last
|
14
|
+
return @project.try(:iterations).try(:first)
|
15
|
+
end
|
16
|
+
|
17
|
+
def current
|
18
|
+
return @project.iterations.first
|
19
|
+
end
|
20
|
+
|
21
|
+
def by_start_date
|
22
|
+
return @project.iterations.sort { |a, b| a.start_date <=> b.start_date}
|
23
|
+
end
|
24
|
+
|
25
|
+
def start_date
|
26
|
+
return last.try(:finish_date)
|
27
|
+
end
|
28
|
+
|
29
|
+
def finish_date
|
30
|
+
if !last.nil? && !last.finish_date.nil?
|
31
|
+
return last.finish_date + @project.life_cycle
|
32
|
+
else
|
33
|
+
return nil
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def committed_points
|
38
|
+
return last.try(:committed_points)
|
39
|
+
end
|
40
|
+
|
41
|
+
def repository
|
42
|
+
Artisan::Repository.iteration
|
43
|
+
end
|
44
|
+
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
require "artisan/projects/iteration_numberer"
|
2
|
+
require "artisan/crud_strategy"
|
3
|
+
require "artisan/repository"
|
4
|
+
|
5
|
+
module Artisan
|
6
|
+
module Iterations
|
7
|
+
|
8
|
+
class IterationsInteractor
|
9
|
+
attr_reader :project
|
10
|
+
|
11
|
+
def initialize(project_id, user)
|
12
|
+
@project = project_repository.find(project_id)
|
13
|
+
@current_user = user
|
14
|
+
end
|
15
|
+
|
16
|
+
def create(iteration_attributes, callbacks)
|
17
|
+
iteration_attributes.merge!(:number => Projects::IterationNumberer.new(@project).next_iteration_number)
|
18
|
+
iteration = CrudStrategy.create(@project.iterations.build(iteration_attributes), callbacks, repository)
|
19
|
+
Artisan::Activity::IterationAuditor.created(iteration.id, @project.id, @current_user.id, iteration_attributes)
|
20
|
+
return iteration
|
21
|
+
end
|
22
|
+
|
23
|
+
def update(id, iteration_attributes, callbacks)
|
24
|
+
iteration = find(id)
|
25
|
+
original_attributes = iteration.attributes
|
26
|
+
iteration = CrudStrategy.update(iteration, iteration_attributes, @current_user, callbacks)
|
27
|
+
|
28
|
+
Artisan::Activity::IterationAuditor.updated(iteration.id, @project.id, @current_user.id, original_attributes, iteration_attributes)
|
29
|
+
return iteration
|
30
|
+
end
|
31
|
+
|
32
|
+
#TODO - PWP - check permissions
|
33
|
+
def find(iteration_id)
|
34
|
+
return repository.find(iteration_id)
|
35
|
+
end
|
36
|
+
|
37
|
+
private
|
38
|
+
|
39
|
+
def repository
|
40
|
+
Repository.iteration
|
41
|
+
end
|
42
|
+
|
43
|
+
def project_repository
|
44
|
+
Repository.project
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
module Artisan
|
2
|
+
module Iterations
|
3
|
+
class MoveToBacklog
|
4
|
+
def initialize(iteration)
|
5
|
+
@iteration = iteration
|
6
|
+
end
|
7
|
+
|
8
|
+
def move
|
9
|
+
incomplete_stories.each do |incomplete_story|
|
10
|
+
incomplete_story.iteration = nil
|
11
|
+
incomplete_story.position = 0
|
12
|
+
Artisan::Repository.story.save(incomplete_story)
|
13
|
+
end
|
14
|
+
|
15
|
+
Artisan::Stories.for(@iteration.project.id).backlog.each do |story|
|
16
|
+
story.position += 1 if story.position
|
17
|
+
Artisan::Repository.story.save(story)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
|
22
|
+
def incomplete_stories
|
23
|
+
return @iteration.stories.select {|story| !story.complete?}
|
24
|
+
end
|
25
|
+
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
module Artisan
|
2
|
+
module Iterations
|
3
|
+
class StoryTags
|
4
|
+
def initialize(iteration)
|
5
|
+
@iteration = iteration
|
6
|
+
end
|
7
|
+
|
8
|
+
def tags
|
9
|
+
story_tags = []
|
10
|
+
@iteration.stories.each { |story| story_tags.concat(story.tag_list) }
|
11
|
+
return story_tags.uniq
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
module Artisan
|
2
|
+
class Member
|
3
|
+
|
4
|
+
attr_accessor :member
|
5
|
+
|
6
|
+
def initialize(member)
|
7
|
+
@member = member
|
8
|
+
end
|
9
|
+
|
10
|
+
def add_to_completed_list
|
11
|
+
if !config.nil?
|
12
|
+
config.story_completed_email_list << @member.user.email
|
13
|
+
config.save!
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
def add_to_assigned_list
|
18
|
+
if !config.nil?
|
19
|
+
config.story_assigned_email_list << @member.user.email
|
20
|
+
config.save!
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def remove_from_completed_list
|
25
|
+
if !config.nil?
|
26
|
+
config.story_completed_email_list.delete(@member.user.email)
|
27
|
+
config.save!
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def remove_from_assigned_list
|
32
|
+
if !config.nil?
|
33
|
+
config.story_assigned_email_list.delete(@member.user.email)
|
34
|
+
config.save!
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def remove_from_email_lists
|
39
|
+
if !config.nil?
|
40
|
+
config.story_completed_email_list.delete(@member.user.email)
|
41
|
+
config.story_assigned_email_list.delete(@member.user.email)
|
42
|
+
config.save!
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
private
|
47
|
+
|
48
|
+
def config
|
49
|
+
@config ||= @member.project.project_configuration
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
require "artisan/repository"
|
2
|
+
require "securerandom"
|
3
|
+
|
4
|
+
module Artisan
|
5
|
+
module Projects
|
6
|
+
module ApiKeyGenerator
|
7
|
+
|
8
|
+
module Hexer
|
9
|
+
def self.hex(length)
|
10
|
+
SecureRandom.hex(length)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
def self.unique_api_key
|
15
|
+
repository = Repository.project
|
16
|
+
key = Hexer.hex(6)
|
17
|
+
|
18
|
+
while !repository.find_by_api_key(key).nil?
|
19
|
+
key = Hexer.hex(6)
|
20
|
+
end
|
21
|
+
|
22
|
+
key
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
require "artisan/projects/projects_interactor"
|
2
|
+
|
3
|
+
module Artisan
|
4
|
+
module Projects
|
5
|
+
class ArchiveInteractor
|
6
|
+
attr_reader :project
|
7
|
+
def initialize(project_id, user)
|
8
|
+
@project_id = project_id
|
9
|
+
@user = user
|
10
|
+
end
|
11
|
+
|
12
|
+
def archive
|
13
|
+
@project = find_project
|
14
|
+
@project.update_attribute(:archived, true)
|
15
|
+
end
|
16
|
+
|
17
|
+
def unarchive
|
18
|
+
@project = find_project
|
19
|
+
@project.update_attribute(:archived, false)
|
20
|
+
end
|
21
|
+
|
22
|
+
private
|
23
|
+
|
24
|
+
def find_project
|
25
|
+
return ProjectsInteractor.new(@user).find(@project_id)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
require "artisan/projects/story_point_summer"
|
2
|
+
|
3
|
+
module Artisan
|
4
|
+
module Projects
|
5
|
+
class CompletedStoriesPresenter
|
6
|
+
|
7
|
+
def initialize(completed_stories)
|
8
|
+
@completed_stories = completed_stories
|
9
|
+
end
|
10
|
+
|
11
|
+
def data
|
12
|
+
data = {}
|
13
|
+
data[:total_points] = StoryPointSummer.new(@completed_stories).sum
|
14
|
+
data[:completed_stories] = @completed_stories
|
15
|
+
return data
|
16
|
+
end
|
17
|
+
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
module Artisan
|
2
|
+
module Projects
|
3
|
+
class IterationNumberer
|
4
|
+
def initialize(project)
|
5
|
+
@project = project
|
6
|
+
end
|
7
|
+
|
8
|
+
def next_iteration_number
|
9
|
+
max = 0
|
10
|
+
@project.iterations.each {|iteration| max = iteration.number if iteration.number > max }
|
11
|
+
return max + 1
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
require "artisan/crud_strategy"
|
2
|
+
require "artisan/repository"
|
3
|
+
require "artisan/activity/project_auditor"
|
4
|
+
require "artisan/team"
|
5
|
+
require "artisan/projects/api_key_generator"
|
6
|
+
|
7
|
+
module Artisan
|
8
|
+
module Projects
|
9
|
+
|
10
|
+
class ProjectCreator
|
11
|
+
WEEK_IN_DAYS = 7
|
12
|
+
|
13
|
+
def initialize(user, callbacks)
|
14
|
+
@user = user
|
15
|
+
@callbacks = callbacks
|
16
|
+
end
|
17
|
+
|
18
|
+
def create(project_attributes)
|
19
|
+
api_key = ApiKeyGenerator.unique_api_key
|
20
|
+
project_attributes.merge!({api_key: api_key})
|
21
|
+
project = repository.new(project_attributes)
|
22
|
+
|
23
|
+
if repository.save(project)
|
24
|
+
Activity::ProjectAuditor.created(project.id, @user.id, project_attributes)
|
25
|
+
generate_first_iteration_for project
|
26
|
+
repository.create_project_configuration(project)
|
27
|
+
Team.new(project, @user).add_team_owner(@user)
|
28
|
+
|
29
|
+
@callbacks.call(:success, project)
|
30
|
+
else
|
31
|
+
@callbacks.call(:failure, project)
|
32
|
+
end
|
33
|
+
|
34
|
+
return project
|
35
|
+
end
|
36
|
+
|
37
|
+
private
|
38
|
+
|
39
|
+
def repository
|
40
|
+
Repository.project
|
41
|
+
end
|
42
|
+
|
43
|
+
def generate_first_iteration_for project
|
44
|
+
iteration = Repository.iteration.new(:project_id => project.id, :start_date => Date.today, :finish_date => Date.today + WEEK_IN_DAYS, :number => 1 )
|
45
|
+
repository.add_iteration(project, iteration)
|
46
|
+
repository.save(project)
|
47
|
+
end
|
48
|
+
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
require "artisan/projects/project_creator"
|
2
|
+
require "artisan/activity/project_auditor"
|
3
|
+
require "artisan/stories/stories_interactor"
|
4
|
+
require "artisan/projects/completed_stories_presenter"
|
5
|
+
require "artisan/crud_strategy"
|
6
|
+
|
7
|
+
|
8
|
+
module Artisan
|
9
|
+
module Projects
|
10
|
+
class ProjectsInteractor
|
11
|
+
|
12
|
+
attr_reader :user
|
13
|
+
|
14
|
+
def initialize(user)
|
15
|
+
@user = user
|
16
|
+
end
|
17
|
+
|
18
|
+
def find(project_id)
|
19
|
+
project = repository.find(project_id)
|
20
|
+
check_permissions(project, user)
|
21
|
+
return project
|
22
|
+
end
|
23
|
+
|
24
|
+
def create(project_attributes, callbacks)
|
25
|
+
ProjectCreator.new(user, callbacks).create(project_attributes)
|
26
|
+
end
|
27
|
+
|
28
|
+
def new
|
29
|
+
repository.new
|
30
|
+
end
|
31
|
+
|
32
|
+
def update(id, project_attributes, callbacks)
|
33
|
+
project = find(id)
|
34
|
+
original_attributes = project.attributes
|
35
|
+
project = CrudStrategy.update(project, project_attributes, user, callbacks)
|
36
|
+
Activity::ProjectAuditor.updated(project.id, user.id, original_attributes, project_attributes)
|
37
|
+
return project
|
38
|
+
end
|
39
|
+
|
40
|
+
def completed_stories(project_id)
|
41
|
+
project = find(project_id)
|
42
|
+
completed_stories = Stories::StoriesInteractor.new(project).completed
|
43
|
+
return CompletedStoriesPresenter.new(completed_stories)
|
44
|
+
end
|
45
|
+
|
46
|
+
private
|
47
|
+
|
48
|
+
def check_permissions(project, user)
|
49
|
+
raise "You are not authorized to view this project." if !Team.new(project, user).user?(user)
|
50
|
+
end
|
51
|
+
|
52
|
+
|
53
|
+
def repository
|
54
|
+
Repository.project
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|