artisan-core 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|