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.
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