trajectory 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (70) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +18 -0
  3. data/.rspec +2 -0
  4. data/.ruby-version +1 -0
  5. data/.travis.yml +6 -0
  6. data/Gemfile +19 -0
  7. data/LICENSE.txt +22 -0
  8. data/README.md +57 -0
  9. data/Rakefile +18 -0
  10. data/config/env.yml +3 -0
  11. data/lib/trajectory/client.rb +24 -0
  12. data/lib/trajectory/core_ext/hash.rb +8 -0
  13. data/lib/trajectory/core_ext.rb +1 -0
  14. data/lib/trajectory/data_access/api.rb +54 -0
  15. data/lib/trajectory/data_access/data_store.rb +70 -0
  16. data/lib/trajectory/data_access.rb +2 -0
  17. data/lib/trajectory/domain/idea.rb +70 -0
  18. data/lib/trajectory/domain/ideas.rb +23 -0
  19. data/lib/trajectory/domain/iteration.rb +85 -0
  20. data/lib/trajectory/domain/iterations.rb +46 -0
  21. data/lib/trajectory/domain/project.rb +168 -0
  22. data/lib/trajectory/domain/projects.rb +55 -0
  23. data/lib/trajectory/domain/stories.rb +56 -0
  24. data/lib/trajectory/domain/story.rb +119 -0
  25. data/lib/trajectory/domain/update.rb +17 -0
  26. data/lib/trajectory/domain/user.rb +28 -0
  27. data/lib/trajectory/domain/users.rb +30 -0
  28. data/lib/trajectory/domain.rb +11 -0
  29. data/lib/trajectory/exceptions/bad_environment_error.rb +7 -0
  30. data/lib/trajectory/exceptions/missing_attribute_error.rb +12 -0
  31. data/lib/trajectory/exceptions/velocity_equal_to_zero_error.rb +11 -0
  32. data/lib/trajectory/exceptions.rb +3 -0
  33. data/lib/trajectory/version.rb +3 -0
  34. data/lib/trajectory.rb +15 -0
  35. data/spec/fabricators/iteration_fabricator.rb +18 -0
  36. data/spec/fabricators/project_fabricator.rb +11 -0
  37. data/spec/fabricators/story_fabricator.rb +18 -0
  38. data/spec/fixtures/vcr_cassettes/client.yml +853 -0
  39. data/spec/fixtures/vcr_cassettes/projects_and_ideas.yml +98 -0
  40. data/spec/fixtures/vcr_cassettes/projects_and_ideas_association.yml +52 -0
  41. data/spec/fixtures/vcr_cassettes/projects_and_iterations.yml +49 -0
  42. data/spec/fixtures/vcr_cassettes/projects_and_iterations_association.yml +49 -0
  43. data/spec/fixtures/vcr_cassettes/projects_and_stories.yml +101 -0
  44. data/spec/fixtures/vcr_cassettes/projects_and_stories_association.yml +147 -0
  45. data/spec/integration/client_spec.rb +96 -0
  46. data/spec/spec_helper.rb +13 -0
  47. data/spec/support/env.rb +11 -0
  48. data/spec/support/fabrication.rb +1 -0
  49. data/spec/support/simple_cov.rb +11 -0
  50. data/spec/support/timecop.rb +1 -0
  51. data/spec/support/vcr.rb +8 -0
  52. data/spec/unit/client_spec.rb +27 -0
  53. data/spec/unit/core_ext/hash_spec.rb +11 -0
  54. data/spec/unit/data_access/api_spec.rb +100 -0
  55. data/spec/unit/data_access/data_store_spec.rb +86 -0
  56. data/spec/unit/domain/idea_spec.rb +46 -0
  57. data/spec/unit/domain/ideas_spec.rb +22 -0
  58. data/spec/unit/domain/iteration_spec.rb +51 -0
  59. data/spec/unit/domain/iterations_spec.rb +47 -0
  60. data/spec/unit/domain/project_spec.rb +163 -0
  61. data/spec/unit/domain/projects_spec.rb +84 -0
  62. data/spec/unit/domain/stories_spec.rb +84 -0
  63. data/spec/unit/domain/story_spec.rb +71 -0
  64. data/spec/unit/domain/user_spec.rb +29 -0
  65. data/spec/unit/domain/users_spec.rb +36 -0
  66. data/spec/unit/exceptions/bad_environment_error_spec.rb +9 -0
  67. data/spec/unit/exceptions/missing_attribute_error_spec.rb +10 -0
  68. data/spec/unit/exceptions/velocity_equal_to_zero_error_spec.rb +10 -0
  69. data/trajectory.gemspec +21 -0
  70. metadata +174 -0
@@ -0,0 +1,168 @@
1
+ module Trajectory
2
+ class Project
3
+ include Virtus
4
+
5
+ NUMBER_OF_WORKING_DAYS_BY_WEEK = 5.0
6
+
7
+ attr_writer :stories, :users_collection
8
+
9
+ # @return [Integer] the unique identifier of the project.
10
+ # @raise [MissingAttributeError] if id is nil
11
+ attribute :id, Integer, default: lambda { |project, attribute| raise MissingAttributeError.new(project, :id) }
12
+
13
+ # @return [String] the project name
14
+ attribute :name, String
15
+ # @!method archived?
16
+ # @return [true, false] true if the project has been archived, false otherwise
17
+ attribute :archived, Boolean
18
+ # @return [DateTime] creation date of the project
19
+ attribute :created_at, DateTime
20
+ # @return [Integer] the current velocity of the project
21
+ attribute :estimated_velocity, Integer
22
+ # @return [Array<Integer>] the velocities of past iterations
23
+ attribute :historic_velocity, Array[Integer]
24
+ # @return [String] project keyword identifier
25
+ attribute :keyword, String
26
+ # @return [DateTime] last modification date of the project
27
+ attribute :updated_at, DateTime
28
+ # @return [Integer] number of completed iterations in the project
29
+ attribute :completed_iterations_count, Integer
30
+ # @return [Integer] number of completed stories in the project
31
+ attribute :completed_stories_count, Integer
32
+
33
+ # Returns true if two projects are the sames i.e they share the same id
34
+ # attribute
35
+ #
36
+ # @param other [Project] the other object to compare
37
+ # @return [true, false]
38
+ def ==(other)
39
+ id == other.id
40
+ end
41
+
42
+ # Fetch all stories that belongs to the project
43
+ #
44
+ # @return [Stories] the stories collection
45
+ def stories
46
+ @stories ||= DataStore.stories_for_project(self)
47
+ end
48
+
49
+ # Fetch all iterations that belongs to the project
50
+ #
51
+ # @return [Iterations] the iterations collection
52
+ def iterations
53
+ @iterations ||= DataStore.iterations_for_project(self)
54
+ end
55
+
56
+ # Fetch all ideas that belongs to the project
57
+ #
58
+ # @return [Ideas] the ideas collection
59
+ def ideas
60
+ @ideas ||= DataStore.ideas_for_project(self)
61
+ end
62
+
63
+ # Fetch all users that belongs to the project
64
+ #
65
+ # @return [Users] the users collection
66
+ def users
67
+ @users_collection ||= DataStore.users_for_project(self)
68
+ end
69
+
70
+ # Fetch updates that belongs to the project since a given date
71
+ #
72
+ # @param since [DateTime] the date
73
+ # @return [Updates] the updates collection
74
+ def updates(since = DateTime.now)
75
+ DataStore.updates_for_project(self, since)
76
+ end
77
+
78
+ # Fetch a user from the project given its id or false if it does not exist
79
+ #
80
+ # @return [User, false] the user or false
81
+ def find_user_by_id(id)
82
+ users.find_by_id(id)
83
+ end
84
+
85
+ # Fetch the stories in a given iteration of a project
86
+ #
87
+ # @param iteration [Iteration] the iteration
88
+ # @return [Stories] the user
89
+ def stories_in_iteration(iteration)
90
+ stories.in_iteration(iteration)
91
+ end
92
+
93
+ # Returns the sum of all points of each story of the project
94
+ #
95
+ # @return [Integer] the points accumulation
96
+ def total_points
97
+ stories.inject(0) do |accumulator, story|
98
+ accumulator += story.points
99
+ end
100
+ end
101
+
102
+ # Returns estimated end date of the project with the actual estimated velocity
103
+ #
104
+ # @return [Date] the estimated date
105
+ def estimated_end_date
106
+ Date.today + remaining_days
107
+ end
108
+
109
+ # Returns the estimated number of days (weekend included) remaining before the end of the project.
110
+ #
111
+ # This is usefull to estimate the project end date.
112
+ #
113
+ # @return [Integer] the number of days
114
+ # @raise [VelocityEqualToZeroError] if estimated velocity is equal to zero (i.e the project can't be finished because no one actually works on it)
115
+ def remaining_days
116
+ raise VelocityEqualToZeroError.new(self) if estimated_velocity_per_day == 0
117
+ (remaining_points / estimated_velocity_per_day).ceil
118
+ end
119
+
120
+ # Returns sum of points of not completed stories
121
+ #
122
+ # @return [Integer] the number of points
123
+ def remaining_points
124
+ stories.not_completed.inject(0) do |accumulator, story|
125
+ accumulator += story.points
126
+ end
127
+ end
128
+
129
+ # Returns the estimated velocity by day over a week
130
+ #
131
+ # @return [Integer] the estimated velocity
132
+ def estimated_velocity_per_day
133
+ estimated_velocity / 7.0
134
+ end
135
+
136
+ # Returns the estimated number of working days (weekend excluded) remaining before the end of the project.
137
+ #
138
+ # This is usefull to be able to evaluate project budget as not all days are billable.
139
+ #
140
+ # @return [Integer] the number of days
141
+ # @raise [VelocityEqualToZeroError] if estimated velocity is equal to zero (i.e the project can't be finished because no one actually works on it)
142
+ def remaining_working_days
143
+ raise VelocityEqualToZeroError.new(self) if estimated_velocity_per_working_day == 0
144
+ (remaining_points / estimated_velocity_per_working_day).ceil
145
+ end
146
+
147
+ # Returns the estimated velocity by day over billable days (actually 5 days)
148
+ #
149
+ # @return [Integer] the estimated velocity
150
+ def estimated_velocity_per_working_day
151
+ estimated_velocity / NUMBER_OF_WORKING_DAYS_BY_WEEK
152
+ end
153
+
154
+ # Returns the completion percentage of the project
155
+ #
156
+ # @return [Float] the percentage
157
+ def percent_complete
158
+ (accepted_points.to_f / total_points * 100.0).round(1)
159
+ end
160
+
161
+ # Returns the sum of accepted story points
162
+ #
163
+ # @return [Integer] the number of points
164
+ def accepted_points
165
+ total_points - remaining_points
166
+ end
167
+ end
168
+ end
@@ -0,0 +1,55 @@
1
+ require 'delegate'
2
+
3
+ module Trajectory
4
+ class Projects < SimpleDelegator
5
+ alias :projects :__getobj__
6
+
7
+ # Creates a new collection of {Projects}
8
+ #
9
+ # @param projects [Array<Project>] a arbitrary lenght list of {Project} objects
10
+ def initialize(*projects)
11
+ super(projects)
12
+ end
13
+
14
+ # Create a new collection of {Project} from a JSON array of attributes from trajectory API
15
+ #
16
+ # @param json_attributes [Hash] the hash of attributes of each project of the collection
17
+ def self.from_json(json_attributes)
18
+ new(*json_attributes.map do |attributes|
19
+ Project.new(attributes.symbolize_keys!)
20
+ end)
21
+ end
22
+
23
+ # Fetch the project with the given id in the collection. If it is not found,
24
+ # it returns false
25
+ #
26
+ # @param id [Integer] the project id
27
+ # @return [Project, false] the found project or false
28
+ def find_by_id(id)
29
+ projects.find { |project| project.id == id } || false
30
+ end
31
+
32
+ # Fetch the project with the given keyword in the collection. If it is not found,
33
+ # it returns false
34
+ #
35
+ # @param keyword [String] the project keyword
36
+ # @return [Project, false] the found project or false
37
+ def find_by_keyword(keyword)
38
+ projects.find { |project| project.keyword == keyword } || false
39
+ end
40
+
41
+ # Returns the archived projects of the collection
42
+ #
43
+ # @return [Projects] the filtered collection
44
+ def archived
45
+ projects.select { |project| project.archived? }
46
+ end
47
+
48
+ # Returns the active projects of the collection
49
+ #
50
+ # @return [Projects] the filtered collection
51
+ def active
52
+ projects.select { |project| !project.archived? }
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,56 @@
1
+ require 'delegate'
2
+
3
+ module Trajectory
4
+ class Stories < SimpleDelegator
5
+ alias :stories :__getobj__
6
+
7
+ # Creates a new collection of {Story}
8
+ #
9
+ # @param stories [Array<Story>] a arbitrary lenght list of {Story} objects
10
+ def initialize(*stories)
11
+ super(stories)
12
+ end
13
+
14
+ # Create a new collection of {Story} from a JSON array of attributes from trajectory API
15
+ #
16
+ # @param project [Project] the project the stories belongs to
17
+ # @param json_attributes [Hash] the hash of attributes of each story of the collection
18
+ def self.from_json(project, json_attributes)
19
+ new(*json_attributes.map do |attributes|
20
+ attributes = attributes.symbolize_keys!.merge({project_id: project.id})
21
+ Story.new(attributes)
22
+ end)
23
+ end
24
+
25
+ # Returns started stories of the collection
26
+ #
27
+ # @return [Stories] started stories collection
28
+ def started
29
+ stories.select(&:started?)
30
+ end
31
+
32
+ # Returns unstarted stories of the collection
33
+ #
34
+ # @return [Stories] unstarted stories collection
35
+ def unstarted
36
+ stories.select(&:unstarted?)
37
+ end
38
+
39
+ # Returns not completed stories of the collection
40
+ #
41
+ # @return [Stories] not completed stories collection
42
+ def not_completed
43
+ stories.reject(&:completed?)
44
+ end
45
+
46
+ # Returns stories of the collection that are in the given iteration
47
+ #
48
+ # @param iteration [Iteration] an iteration
49
+ # @return [Stories] stories collection in iteration
50
+ def in_iteration(iteration)
51
+ stories.select do |story|
52
+ story.in_iteration?(iteration)
53
+ end
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,119 @@
1
+ module Trajectory
2
+ class Story
3
+ include Virtus
4
+
5
+ # @return [Integer] the unique identifier of the story
6
+ # @raise [MissingAttributeError] if id is nil
7
+ attribute :id, Integer, default: lambda { |project, attribute| raise MissingAttributeError.new(project, :id) }
8
+ # @return [String] the name of the user assigned to the story
9
+ attribute :assignee_name, String
10
+ # @return [String] the type of story as "Feature", "Bug", "Todo" or "Milestone"
11
+ attribute :task_type, String
12
+ # The Integer position of the story in the backlog.
13
+ # Lower is higher.
14
+ # @return [Integer] the position of the story
15
+ attribute :position, Integer
16
+ # @return [DateTime] the creation date of the story
17
+ attribute :created_at, DateTime
18
+ # @return [Array<String>] the valid states the story can transition to
19
+ # @todo Replace String by Symbol
20
+ attribute :state_events, Array[String]
21
+ # @return [String] the title of the story
22
+ attribute :title, String
23
+ # @return [true, false] true if design is needed for the story, false otherwise
24
+ attribute :design_needed, Boolean
25
+ # @return [DateTime] the last modification date of the story
26
+ attribute :updated_at, DateTime
27
+ # @return [String] the subject of the idea the story is attached to
28
+ attribute :idea_subject, String
29
+ # @!method archived?
30
+ # @return [true, false] true if the story has been archived, false otherwise
31
+ attribute :archived, Boolean
32
+ # @return [Integer] estimation in points of the story complexity
33
+ attribute :points, Integer
34
+ # @!method development_needed?
35
+ # @return [true, false] true if development is needed for the story, false otherwise
36
+ attribute :development_needed, Boolean
37
+ # @!method deleted?
38
+ # @return [ture, false] true if the story has been deleted, false otherwise
39
+ attribute :deleted, Boolean
40
+ # @return [String] name of the user that created the story
41
+ attribute :user_name, String
42
+ # @return [Integer] id of the user that created the story
43
+ # @see #user
44
+ attribute :user_id, Integer
45
+ # @return [Integer] number of comments of the story
46
+ attribute :comments_count, Integer
47
+ # @return [Symbol] state of the story in [:started, :unstarted, :delivered, :accepted, :rejected]
48
+ attribute :state, Symbol
49
+ # @return [Integer] project id the story belongs to
50
+ # @see #project
51
+ attribute :project_id, Integer
52
+ # @return [Integer] iteration id the story belongs to
53
+ # @see #iteration
54
+ attribute :iteration_id, Integer
55
+
56
+ # Returns true if two stories are the sames i.e they share the same id
57
+ # attribute
58
+ #
59
+ # @param other [Story] the other object to compare
60
+ # @return [true, false]
61
+ def ==(other)
62
+ id == other.id
63
+ end
64
+
65
+ # Fetch the project the story belongs to
66
+ #
67
+ # @return [Project]
68
+ def project
69
+ @project ||= DataStore.find_project_by_id(project_id)
70
+ end
71
+
72
+ # Returns true if the story is started i.e in :started state
73
+ #
74
+ # @return [true, false]
75
+ def started?
76
+ state == :started
77
+ end
78
+
79
+ # Returns true if the story is not started i.e in :unstarted state
80
+ #
81
+ # @return [true, false]
82
+ def unstarted?
83
+ state == :unstarted
84
+ end
85
+
86
+ # Returns true if the story is not completed i.e not in :accepted state
87
+ #
88
+ # @return [true, false]
89
+ def not_completed?
90
+ !completed?
91
+ end
92
+
93
+ # Returns true if the story is completed i.e in :accepted state
94
+ #
95
+ # @return [true, false]
96
+ def completed?
97
+ state == :accepted
98
+ end
99
+
100
+ # Fetch the user that created the story
101
+ #
102
+ # @return [User]
103
+ def user
104
+ @user ||= DataStore.find_user_of_project_with_id(project, user_id)
105
+ end
106
+
107
+ # Returns true if the story belongs to the given iteration, false otherwise
108
+ #
109
+ # @param iteration [Iteration] an iteration
110
+ # @return [true, false]
111
+ def in_iteration?(iteration)
112
+ iteration_id == iteration.id
113
+ end
114
+
115
+ # @todo Not Yet Implemented
116
+ def iteration
117
+ end
118
+ end
119
+ end
@@ -0,0 +1,17 @@
1
+ module Trajectory
2
+ class Update
3
+ # @attr_reader stories [Stories] the updated stories by the update
4
+ # @attr_reader iterations [iterations] the updated iterations by the update
5
+ attr_reader :stories, :iterations
6
+
7
+ # Creates a new update with given updated stories and iterations
8
+ #
9
+ # @param stories [Stories] collection of updated stories
10
+ # @param iterations [Iterations] collection of updated iterations
11
+ # :nocov:
12
+ def initialize(stories, iterations)
13
+ @stories = stories
14
+ @iterations = iterations
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,28 @@
1
+ module Trajectory
2
+ class User
3
+ include Virtus
4
+
5
+ # @return [Integer] the unique identifier of the User
6
+ # @raise [MissingAttributeError] if id is nil
7
+ attribute :id, Integer, default: lambda { |project, attribute| raise MissingAttributeError.new(project, :id) }
8
+ # @return [String] the full name of the user
9
+ attribute :name, String
10
+ # @return [DateTime] the creation date of the user account
11
+ attribute :created_at, DateTime
12
+ # @return [DateTime] the last modification date of the user account
13
+ attribute :updated_at, DateTime
14
+ # @return [String] the url to the avatar image of the user (uses gravatar)
15
+ attribute :gravatar_url, String
16
+ # @return [String] the email of the user
17
+ attribute :email, String
18
+
19
+ # Returns true if two users are the sames i.e they share the same id
20
+ # attribute
21
+ #
22
+ # @param other [User] the other object to compare
23
+ # @return [true, false]
24
+ def ==(other)
25
+ id == other.id
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,30 @@
1
+ module Trajectory
2
+ class Users < SimpleDelegator
3
+ alias :users :__getobj__
4
+
5
+ # Creates a new collection of {User}
6
+ #
7
+ # @param users [Array<User>] a arbitrary lenght list of {User} objects
8
+ def initialize(*users)
9
+ super(users)
10
+ end
11
+
12
+ # Create a new collection of {User} from a JSON array of attributes from trajectory API
13
+ #
14
+ # @param json_attributes [Hash] the hash of attributes of each user of the collection
15
+ def self.from_json(json_attributes)
16
+ new(*json_attributes.map do |attributes|
17
+ User.new(attributes.symbolize_keys!)
18
+ end)
19
+ end
20
+
21
+ # Returns the the first user with the given id in the collection or false if
22
+ # no user can be found with the id
23
+ #
24
+ # @param id [Integer] the id of the user to find
25
+ # @return [User, false] the user with the given id
26
+ def find_by_id(id)
27
+ users.find { |user| user.id == id } || false
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,11 @@
1
+ require 'trajectory/domain/story'
2
+ require 'trajectory/domain/stories'
3
+ require 'trajectory/domain/project'
4
+ require 'trajectory/domain/projects'
5
+ require 'trajectory/domain/iteration'
6
+ require 'trajectory/domain/iterations'
7
+ require 'trajectory/domain/idea'
8
+ require 'trajectory/domain/ideas'
9
+ require 'trajectory/domain/user'
10
+ require 'trajectory/domain/users'
11
+ require 'trajectory/domain/update'
@@ -0,0 +1,7 @@
1
+ module Trajectory
2
+ class BadEvnrionmentError < RuntimeError
3
+ def to_s
4
+ "Specify trajectory API environment variables : TRAJECTORY_API_KEY and TRAJECTORY_ACCOUNT_KEYWORD"
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,12 @@
1
+ module Trajectory
2
+ class MissingAttributeError < RuntimeError
3
+ def initialize(object, attribute)
4
+ @object = object
5
+ @attribute = attribute.to_sym
6
+ end
7
+
8
+ def to_s
9
+ "Attribute #{@attribute} of #{@object.inspect} is nil."
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,11 @@
1
+ module Trajectory
2
+ class VelocityEqualToZeroError < RuntimeError
3
+ def initialize(project)
4
+ @project = project
5
+ end
6
+
7
+ def to_s
8
+ "Estimated velocity of #{@project.inspect} is equal to 0."
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,3 @@
1
+ require 'trajectory/exceptions/missing_attribute_error'
2
+ require 'trajectory/exceptions/velocity_equal_to_zero_error'
3
+ require 'trajectory/exceptions/bad_environment_error'
@@ -0,0 +1,3 @@
1
+ module Trajectory
2
+ VERSION = "0.1.0"
3
+ end
data/lib/trajectory.rb ADDED
@@ -0,0 +1,15 @@
1
+ require 'virtus'
2
+ require 'httparty'
3
+ require 'json'
4
+
5
+ require "trajectory/version"
6
+
7
+ require 'trajectory/core_ext'
8
+ require 'trajectory/exceptions'
9
+ require 'trajectory/domain'
10
+ require 'trajectory/data_access'
11
+
12
+ require 'trajectory/client'
13
+
14
+ module Trajectory
15
+ end
@@ -0,0 +1,18 @@
1
+ Fabricator(:iteration, class_name: Trajectory::Iteration) do
2
+ id { sequence }
3
+ percent_complete 90.0
4
+ started_stories_count 4
5
+ created_at DateTime.now
6
+ estimated_velocity 12
7
+ delivered_stories_count 28
8
+ updated_at DateTime.now
9
+ unstarted_stories_count 31
10
+ starts_on DateTime.now
11
+ current false
12
+ stories_count 59
13
+ complete false
14
+ accepted_points 51
15
+ estimated_points 72
16
+ comments_count 8
17
+ accepted_stories_count 24
18
+ end
@@ -0,0 +1,11 @@
1
+ Fabricator(:project, class_name: Trajectory::Project) do
2
+ archived false
3
+ created_at DateTime.now
4
+ estimated_velocity 20
5
+ historic_velocity [10, 9, 12, 13, 18]
6
+ id { sequence(:id, 1) }
7
+ keyword 'keyword'
8
+ updated_at DateTime.now
9
+ completed_iterations_count 10
10
+ completed_stories_count 42
11
+ end
@@ -0,0 +1,18 @@
1
+ Fabricator(:story, class_name: Trajectory::Story) do
2
+ assignee_name 'An asignee'
3
+ task_type 'Feature'
4
+ position { sequence }
5
+ created_at DateTime.now
6
+ state_events []
7
+ title 'A story title'
8
+ design_needed true
9
+ updated_at DateTime.now
10
+ idea_subject 'the related idea subject'
11
+ archived false
12
+ points 1
13
+ id { sequence }
14
+ development_needed true
15
+ deleted false
16
+ user_name 'A user'
17
+ comments_count 24
18
+ end