artisan-core 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (63) hide show
  1. data/lib/artisan/activity/activity_interactor.rb +18 -0
  2. data/lib/artisan/activity/activity_presenter.rb +35 -0
  3. data/lib/artisan/activity/formatters/diff_changes.rb +41 -0
  4. data/lib/artisan/activity/formatters/name.rb +22 -0
  5. data/lib/artisan/activity/formatters/source.rb +16 -0
  6. data/lib/artisan/activity/formatters/temporal.rb +20 -0
  7. data/lib/artisan/activity/iteration_auditor.rb +44 -0
  8. data/lib/artisan/activity/project_auditor.rb +28 -0
  9. data/lib/artisan/activity/story_auditor.rb +42 -0
  10. data/lib/artisan/callbacks.rb +27 -0
  11. data/lib/artisan/crud_strategy.rb +23 -0
  12. data/lib/artisan/event_mailer.rb +22 -0
  13. data/lib/artisan/inviter.rb +36 -0
  14. data/lib/artisan/iterations/iteration_point_calculator.rb +28 -0
  15. data/lib/artisan/iterations/iteration_presenter.rb +20 -0
  16. data/lib/artisan/iterations/iteration_workflow_interactor.rb +60 -0
  17. data/lib/artisan/iterations/iteration_workflow_presenter.rb +11 -0
  18. data/lib/artisan/iterations/iterations.rb +47 -0
  19. data/lib/artisan/iterations/iterations_interactor.rb +48 -0
  20. data/lib/artisan/iterations/move_to_backlog.rb +28 -0
  21. data/lib/artisan/iterations/story_tags.rb +15 -0
  22. data/lib/artisan/member.rb +52 -0
  23. data/lib/artisan/no_op_callbacks.rb +6 -0
  24. data/lib/artisan/projects/api_key_generator.rb +26 -0
  25. data/lib/artisan/projects/archive_interactor.rb +29 -0
  26. data/lib/artisan/projects/completed_stories_presenter.rb +20 -0
  27. data/lib/artisan/projects/iteration_numberer.rb +15 -0
  28. data/lib/artisan/projects/project_creator.rb +51 -0
  29. data/lib/artisan/projects/projects_interactor.rb +58 -0
  30. data/lib/artisan/projects/projects_presenter.rb +46 -0
  31. data/lib/artisan/projects/story_point_summer.rb +16 -0
  32. data/lib/artisan/reports/average_stat_per_iteration_data.rb +55 -0
  33. data/lib/artisan/reports/burn_up_chart.rb +46 -0
  34. data/lib/artisan/reports/high_charts_data_retriever.rb +77 -0
  35. data/lib/artisan/reports/high_charts_interactor.rb +31 -0
  36. data/lib/artisan/reports/percentage_of_commitments_met_data.rb +47 -0
  37. data/lib/artisan/reports/signoff/pdf.rb +161 -0
  38. data/lib/artisan/reports/signoff/report.rb +120 -0
  39. data/lib/artisan/reports/velocity_report.rb +43 -0
  40. data/lib/artisan/repository.rb +51 -0
  41. data/lib/artisan/stories/pert_calculator.rb +62 -0
  42. data/lib/artisan/stories/stories_interactor.rb +78 -0
  43. data/lib/artisan/stories/story_collection.rb +66 -0
  44. data/lib/artisan/stories/story_exporter.rb +45 -0
  45. data/lib/artisan/stories/story_numberer.rb +15 -0
  46. data/lib/artisan/stories/story_presenter.rb +23 -0
  47. data/lib/artisan/stories/story_sorter.rb +37 -0
  48. data/lib/artisan/story_board.rb +45 -0
  49. data/lib/artisan/story_column_changer.rb +71 -0
  50. data/lib/artisan/story_exporter.rb +14 -0
  51. data/lib/artisan/team.rb +50 -0
  52. data/spec/crud_strategy_spec.rb +100 -0
  53. data/spec/event_mailer_spec.rb +86 -0
  54. data/spec/inviter_spec.rb +58 -0
  55. data/spec/member_spec.rb +58 -0
  56. data/spec/repository_spec.rb +32 -0
  57. data/spec/story_board_spec.rb +36 -0
  58. data/spec/story_collection_spec.rb +61 -0
  59. data/spec/story_column_changer_spec.rb +222 -0
  60. data/spec/story_exporter_spec.rb +25 -0
  61. data/spec/story_sorter_spec.rb +67 -0
  62. data/spec/team_spec.rb +107 -0
  63. 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,11 @@
1
+ module Artisan
2
+ module Iterations
3
+ class IterationWorkflowPresenter
4
+ attr_reader :iteration_id, :project_id
5
+ def initialize(iteration_id, project_id)
6
+ @iteration_id = iteration_id
7
+ @project_id = project_id
8
+ end
9
+ end
10
+ end
11
+ 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,6 @@
1
+ module Artisan
2
+ class NoOpCallbacks
3
+ def call(name, *args)
4
+ end
5
+ end
6
+ 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