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,18 @@
1
+ require "artisan/activity/activity_presenter"
2
+
3
+ module Artisan
4
+ module Activity
5
+ class ActivityInteractor
6
+ def initialize(project_id, user)
7
+ @project_id = project_id
8
+ @user = user
9
+ end
10
+
11
+ def changes
12
+ project_changes = ::Change.find_all_by_project_id(@project_id)
13
+ project = Projects::ProjectsInteractor.new(@user).find(@project_id)
14
+ return ActivityPresenter.new(project_changes, project)
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,35 @@
1
+ require 'artisan/activity/formatters/temporal'
2
+ require 'artisan/activity/formatters/name'
3
+ require 'artisan/activity/formatters/source'
4
+ require 'artisan/activity/formatters/diff_changes'
5
+
6
+ module Artisan
7
+ module Activity
8
+ class ActivityPresenter
9
+ attr_reader :project
10
+
11
+ def initialize(changes, project)
12
+ @changes = changes
13
+ @project = project
14
+ end
15
+
16
+ def changes
17
+ changes = []
18
+ @changes.each do |change|
19
+ temporal = Formatters::Temporal.new(change)
20
+
21
+ changes << {
22
+ :date => temporal.date,
23
+ :time => temporal.time,
24
+ :who => Formatters::Name.new(change).full_name,
25
+ :what => Formatters::Source.new(change).type,
26
+ :diffs => Formatters::DiffChanges.new(change).diffs,
27
+ :event => change.event
28
+ }
29
+ end
30
+ return changes
31
+ end
32
+
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,41 @@
1
+ module Artisan
2
+ module Activity
3
+ module Formatters
4
+ class DiffChanges
5
+
6
+ def initialize(change)
7
+ @change = change
8
+ end
9
+
10
+ def diffs
11
+ diffs = []
12
+ return diffs if @change.changed_fields.nil? || @change.original_fields.nil?
13
+
14
+ changed_fields = YAML::load(@change.changed_fields)
15
+ original_fields = YAML::load(@change.original_fields)
16
+
17
+ changed_fields.keys.each do |field|
18
+ if fields_different?(changed_fields, original_fields, field)
19
+ diffs << "#{field} changed from '#{display_nil(original_fields[field])}' to '#{display_nil(changed_fields[field])}'"
20
+ end
21
+ end
22
+ return diffs
23
+ end
24
+
25
+ private
26
+
27
+ def display_nil(value)
28
+ return "no value" if value.nil?
29
+ return value
30
+ end
31
+
32
+ def fields_different?(changed_fields, original_fields, field)
33
+ return false if field == "updated_at"
34
+ return false if changed_fields[field] == 'f' && original_fields[field].nil?
35
+ return false if changed_fields[field].nil? && original_fields[field] == 'f'
36
+ return changed_fields[field] != original_fields[field]
37
+ end
38
+ end
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,22 @@
1
+ module Artisan
2
+ module Activity
3
+ module Formatters
4
+
5
+ class Name
6
+
7
+ def initialize(change)
8
+ @change = change
9
+ end
10
+
11
+ def full_name
12
+ unless @change.user_id == nil
13
+ return User.find(@change.user_id).full_name
14
+ else
15
+ return "Unknown"
16
+ end
17
+ end
18
+
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,16 @@
1
+ module Artisan
2
+ module Activity
3
+ module Formatters
4
+ class Source
5
+
6
+ def initialize(change)
7
+ @change = change
8
+ end
9
+
10
+ def type
11
+ return @change.type.gsub(/Change/, '')
12
+ end
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,20 @@
1
+ module Artisan
2
+ module Activity
3
+ module Formatters
4
+ class Temporal
5
+
6
+ def initialize(change)
7
+ @change = change
8
+ end
9
+
10
+ def date
11
+ return @change.created_at.strftime("%m/%d/%Y")
12
+ end
13
+
14
+ def time
15
+ return @change.created_at.strftime("%I:%M:%S %p")
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,44 @@
1
+ module Artisan
2
+ module Activity
3
+ class IterationAuditor
4
+
5
+ def self.created(iteration_id, project_id, user_id, new_attributes)
6
+ create_change(:source_id => iteration_id, :project_id => project_id, :user_id => user_id, :event => :create,
7
+ :changed_fields => new_attributes)
8
+ end
9
+
10
+ def self.updated(iteration_id, project_id, user_id, original_attributes, new_attributes)
11
+ create_change(:source_id => iteration_id, :project_id => project_id, :user_id => user_id, :event => :update,
12
+ :original_fields => original_attributes, :changed_fields => new_attributes)
13
+ end
14
+
15
+ def self.deleted(iteration_id, project_id, user_id)
16
+ create_change(:source_id => iteration_id, :project_id => project_id, :user_id => user_id, :event => :delete,
17
+ :original_fields => {}, :changed_fields => {})
18
+ end
19
+
20
+ def self.completed(iteration_id, project_id, user_id)
21
+ create_change(:source_id => iteration_id, :project_id => project_id, :user_id => user_id, :event => :complete,
22
+ :original_fields => {}, :changed_fields => {})
23
+ end
24
+
25
+ def self.resumed(iteration_id, project_id, user_id)
26
+ create_change(:source_id => iteration_id, :project_id => project_id, :user_id => user_id, :event => :resume,
27
+ :original_fields => {}, :changed_fields => {})
28
+ end
29
+
30
+ def self.started(iteration_id, project_id, user_id)
31
+ create_change(:source_id => iteration_id, :project_id => project_id, :user_id => user_id, :event => :start,
32
+ :original_fields => {}, :changed_fields => {})
33
+ end
34
+
35
+
36
+ def self.create_change(attributes)
37
+ Repository.change.create_iteration_change(attributes)
38
+ end
39
+
40
+
41
+
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,28 @@
1
+ module Artisan
2
+ module Activity
3
+ class ProjectAuditor
4
+
5
+ def self.created( project_id, user_id, new_attributes)
6
+ create_change(:source_id => project_id, :project_id => project_id, :user_id => user_id, :event => :create,
7
+ :changed_fields => new_attributes)
8
+ end
9
+
10
+ def self.updated(project_id, user_id, original_attributes, new_attributes)
11
+ create_change(:source_id => project_id, :project_id => project_id, :user_id => user_id, :event => :update,
12
+ :changed_fields => new_attributes, :original_fields => original_attributes)
13
+ end
14
+
15
+ def self.deleted(project_id, user_id)
16
+ create_change(:source_id => project_id, :project_id => project_id, :user_id => user_id, :event => :delete,
17
+ :changed_fields => {}, :original_fields => {})
18
+ end
19
+
20
+
21
+ def self.create_change(attrs)
22
+ Repository.change.create_project_change(attrs)
23
+ end
24
+
25
+
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,42 @@
1
+ module Artisan
2
+ module Activity
3
+ class StoryAuditor
4
+
5
+ def self.created(story_id, project_id, user_id, new_attributes)
6
+ story_change(story_id, project_id, user_id, :create, {}, new_attributes)
7
+ end
8
+
9
+ def self.updated(story_id, project_id, user_id, original_attributes, new_attributes)
10
+ story_change(story_id, project_id, user_id, :update, original_attributes, new_attributes)
11
+ end
12
+
13
+ def self.deleted(story_id, project_id, user_id)
14
+ story_change(story_id, project_id, user_id, :delete)
15
+ end
16
+
17
+ def self.backlogged(story_id, project_id, user_id)
18
+ story_change(story_id, project_id, user_id, :move_to_backlog)
19
+ end
20
+
21
+ def self.readied(story_id, project_id, user_id)
22
+ story_change(story_id, project_id, user_id, :move_to_ready)
23
+ end
24
+
25
+ def self.worked(story_id, project_id, user_id)
26
+ story_change(story_id, project_id, user_id, :move_to_working)
27
+ end
28
+
29
+ def self.completed(story_id, project_id, user_id)
30
+ story_change(story_id, project_id, user_id, :move_to_complete)
31
+ end
32
+
33
+ private
34
+
35
+ def self.story_change(story_id, project_id, user_id, event, original_fields = {}, changed_fields = {})
36
+ Repository.change.create_story_change(:source_id => story_id, :project_id => project_id, :user_id => user_id, :event => event,
37
+ :original_fields => original_fields, :changed_fields => changed_fields)
38
+ end
39
+
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,27 @@
1
+ module Artisan
2
+ class Callbacks
3
+ attr_reader :callbacks
4
+
5
+ def initialize
6
+ @callbacks = Hash.new
7
+ end
8
+
9
+ def self.build(&block)
10
+ builder = Callbacks.new
11
+ block.call(builder)
12
+ builder
13
+ end
14
+
15
+ def on(name, &block)
16
+ @callbacks[name] = Proc.new(&block)
17
+ end
18
+
19
+ def call(name, *args)
20
+ unless @callbacks.has_key?(name)
21
+ raise ArgumentError, "You are missing one of the expected callbacks. Missing: #{name} callback"
22
+ end
23
+
24
+ @callbacks[name].call(*args)
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,23 @@
1
+ module Artisan
2
+ class CrudStrategy
3
+
4
+ def self.create(model, callbacks, repository)
5
+ if repository.save(model)
6
+ callbacks.call(:success, model)
7
+ else
8
+ callbacks.call(:failure, model)
9
+ end
10
+ return model
11
+ end
12
+
13
+ def self.update(model, model_attributes, user, callbacks)
14
+ if model.update_attributes(model_attributes)
15
+ callbacks.call(:success, model)
16
+ else
17
+ callbacks.call(:failure, model)
18
+ end
19
+ return model
20
+ end
21
+
22
+ end
23
+ end
@@ -0,0 +1,22 @@
1
+ class EventMailer
2
+
3
+ def initialize(project)
4
+ @project_name = project.name
5
+ @configuration = project.project_configuration
6
+ @archived = project.archived
7
+ end
8
+
9
+ def new_assigned_user(story)
10
+ if !@archived
11
+ email_list = @configuration.story_assigned_email_list.join(", ")
12
+ Notifier.new_assigned_user(@project_name, story.assigned_user.full_name, story.name, email_list).deliver if !email_list.empty?
13
+ end
14
+ end
15
+
16
+ def completed_story(story)
17
+ if !@archived
18
+ email_list = @configuration.story_completed_email_list.join(", ")
19
+ Notifier.completed_story(@project_name, story.name, email_list).deliver if !email_list.empty?
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,36 @@
1
+ require 'artisan/repository'
2
+
3
+ class Inviter
4
+ EMAIL_REGEX = /^[a-zA-Z][\w\.-]*[a-zA-Z0-9]@[a-zA-Z0-9][\w\.-]*[a-zA-Z0-9]\.[a-zA-Z][a-zA-Z\.]*[a-zA-Z]$/
5
+
6
+ def initialize(name, email)
7
+ @name = name
8
+ @email = email
9
+ end
10
+
11
+ def invite
12
+ if valid_name? && valid_email?
13
+ mail_invite
14
+ else
15
+ raise "Name and email fields are required, please fill out all fields and try again."
16
+ end
17
+ end
18
+
19
+ def add_to_future_users(project)
20
+ Artisan::Repository.future_user.create(:email => @email, :project_id => project.id)
21
+ end
22
+
23
+ private
24
+
25
+ def valid_name?
26
+ return !@name.nil? && !@name.empty?
27
+ end
28
+
29
+ def valid_email?
30
+ return @email =~ EMAIL_REGEX
31
+ end
32
+
33
+ def mail_invite
34
+ Invites.invite(@name, @email).deliver
35
+ end
36
+ end
@@ -0,0 +1,28 @@
1
+ require "artisan/repository"
2
+ require "artisan/stories/pert_calculator"
3
+ module Artisan
4
+ module Iterations
5
+ class IterationPointCalculator
6
+
7
+ def initialize(iteration)
8
+ @iteration = iteration
9
+ end
10
+
11
+ def total_points
12
+ return total_of Repository.story.all(@iteration)
13
+ end
14
+
15
+ def completed_points
16
+ return total_of Repository.story.completed(@iteration)
17
+ end
18
+
19
+ private
20
+
21
+ def total_of(stories)
22
+ total = stories.inject(0){|total, story| total + story.estimate }
23
+ return total.round(2)
24
+ end
25
+
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,20 @@
1
+ module Artisan
2
+ module Iterations
3
+ class IterationPresenter
4
+
5
+ def initialize(iteration)
6
+ @iteration = iteration
7
+ end
8
+
9
+ def team_names
10
+ @iteration.users.collect { |user| user.full_name }.join(", ")
11
+ end
12
+
13
+ def total_billed_points
14
+ total = 0
15
+ @iteration.stories.completed.each { |story| total += story.estimate if !story.nonbillable?}
16
+ return total
17
+ end
18
+ end
19
+ end
20
+ end