timetrello 1.0.0
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.
- checksums.yaml +7 -0
- data/lib/time_trello/activity_record.rb +68 -0
- data/lib/time_trello/duration.rb +97 -0
- data/lib/time_trello/parser.rb +125 -0
- data/lib/time_trello/report.rb +60 -0
- data/lib/time_trello/trello_driver.rb +55 -0
- data/lib/time_trello/version.rb +3 -0
- data/lib/time_trello.rb +46 -0
- metadata +73 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 29ddfa611b55bd19230b313aa9d1e0967ad0ebe4
|
4
|
+
data.tar.gz: acd5a81c5aa88d3f767d1f8fc9590bbee00c6295
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 729ca5285e2259d674dce181730c3e28547c0cd75f62d111a89da221c5d18e535676646b88a64d1229edc36f4e5877840c49e796e182bc575a8ec53da86fbbd9
|
7
|
+
data.tar.gz: cdbf70e22bc99c46d0841dc7769e668a913f80f97e62d5f7a50dbbeeb14eb150c2a966fe1769853c447e498c01864e4592fb93f404a186a9e21e99fc92cbbcd6
|
@@ -0,0 +1,68 @@
|
|
1
|
+
# -*-ruby-*-
|
2
|
+
# Copyright (c) 2016 - Formaweb - All rights reserved
|
3
|
+
#
|
4
|
+
# Author:: Ronaldo Faria Lima
|
5
|
+
# Created:: 2016-03-21
|
6
|
+
#
|
7
|
+
# Defines the structure that holds the activity record grabbed from a trello
|
8
|
+
# board.
|
9
|
+
|
10
|
+
require 'time_trello/duration';
|
11
|
+
|
12
|
+
module TimeTrello
|
13
|
+
|
14
|
+
# Public: An activity record identifies completely an activity done in
|
15
|
+
# trello. Its main ideia is to be a standard record that can be compared,
|
16
|
+
# sorted and collected, used for reporting purposes.
|
17
|
+
class ActivityRecord
|
18
|
+
include Comparable
|
19
|
+
|
20
|
+
# Public: Task duration
|
21
|
+
attr_accessor :duration
|
22
|
+
# Public: Task owner
|
23
|
+
attr_accessor :owner
|
24
|
+
# Public: Project name (i.e., Trello board)
|
25
|
+
attr_accessor :project
|
26
|
+
# Public: Task start date
|
27
|
+
attr_accessor :start_date
|
28
|
+
# Public: Task comment
|
29
|
+
attr_accessor :task_description
|
30
|
+
|
31
|
+
# Public: Initializes this class with proper information about a given task
|
32
|
+
#
|
33
|
+
# project - Project name (i.e., the board name)
|
34
|
+
# owner - Name of the duration owner
|
35
|
+
# start_date - When the task started
|
36
|
+
# duration - The task duration
|
37
|
+
def initialize(*args)
|
38
|
+
if args.size != 0
|
39
|
+
@duration = args[0]
|
40
|
+
@owner = args[1]
|
41
|
+
@project = args[2]
|
42
|
+
@start_date = args[3]
|
43
|
+
@task_description = args[4]
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
# Public: Implementation of Comparable mixin. The comparison is done
|
48
|
+
# following this attributes hierarchy:
|
49
|
+
# - project
|
50
|
+
# - owner
|
51
|
+
# - start_date
|
52
|
+
#
|
53
|
+
# other - The other instance of ActivityRecord to compare with.
|
54
|
+
def <=>(other)
|
55
|
+
if other == nil
|
56
|
+
return -1
|
57
|
+
end
|
58
|
+
result = @project <=> other.project
|
59
|
+
result = @owner <=> other.owner if result == 0
|
60
|
+
result = @start_date <=> other.start_date if result == 0
|
61
|
+
|
62
|
+
result
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
end
|
67
|
+
|
68
|
+
|
@@ -0,0 +1,97 @@
|
|
1
|
+
# -*-ruby-*-
|
2
|
+
# Copyright (c) 2016 - Formaweb - All rights reserved
|
3
|
+
#
|
4
|
+
# Author:: Ronaldo Faria Lima
|
5
|
+
# Created:: 2016-03-21
|
6
|
+
#
|
7
|
+
# Describes a duration in hours and minutes and proper operations using those.
|
8
|
+
|
9
|
+
module TimeTrello
|
10
|
+
|
11
|
+
# Public: Describes a duration in terms of hours and minutes, representing
|
12
|
+
# data internaly as seconds.
|
13
|
+
class Duration
|
14
|
+
protected
|
15
|
+
attr_accessor :internal_seconds # Internal representation of a duration
|
16
|
+
|
17
|
+
# Public: Initializes this class with hours, minutes and seconds. You can
|
18
|
+
# provide two different sets of arguments in order to initialize this class.
|
19
|
+
#
|
20
|
+
# hours - Hours as an integer.
|
21
|
+
# minutes - Minutes as an integer using sexagesimal representation.
|
22
|
+
# seconds - Seconds as an integer, using sexagesimal representation.
|
23
|
+
#
|
24
|
+
# or:
|
25
|
+
#
|
26
|
+
# a string containing the duration using the format hh:mm.ss
|
27
|
+
public
|
28
|
+
def initialize(*args)
|
29
|
+
@internal_seconds = 0
|
30
|
+
time_components = args
|
31
|
+
if args.size == 1 && args[0].class.equal?(String)
|
32
|
+
time_components = args[0].split(/[:.]/)
|
33
|
+
end
|
34
|
+
factor = 3600
|
35
|
+
time_components.each do |component|
|
36
|
+
@internal_seconds += factor * component.to_i
|
37
|
+
factor /= 60
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
# Public: Getter. Returns the number of hours from a given duration
|
42
|
+
def hours
|
43
|
+
@internal_seconds.abs / 3600
|
44
|
+
end
|
45
|
+
|
46
|
+
# Public: Getter. Returns the number of minutes from the internal representation
|
47
|
+
def minutes
|
48
|
+
(@internal_seconds.abs / 60) % 60
|
49
|
+
end
|
50
|
+
|
51
|
+
# Public: Getter. Returns the number of seconds of a given duration
|
52
|
+
def seconds
|
53
|
+
@internal_seconds.abs % 60
|
54
|
+
end
|
55
|
+
|
56
|
+
# Public: Getter. Returns the number of raw minutes of a given duration
|
57
|
+
#
|
58
|
+
# Important: This is a float value, since it is a raw value
|
59
|
+
def raw_minutes
|
60
|
+
@internal_seconds.abs.to_f / 60.0
|
61
|
+
end
|
62
|
+
|
63
|
+
# Public: Operator overload. Sums up two different instances of Duration
|
64
|
+
#
|
65
|
+
# other - The other operand
|
66
|
+
def +(other)
|
67
|
+
duration = Duration.new(0)
|
68
|
+
duration.internal_seconds = @internal_seconds + other.internal_seconds
|
69
|
+
|
70
|
+
duration
|
71
|
+
end
|
72
|
+
|
73
|
+
# Public: Operator overload. Subtracts two different instances of Duration.
|
74
|
+
#
|
75
|
+
# other - The other operand
|
76
|
+
#
|
77
|
+
# Important: The resultant duration will have its components described
|
78
|
+
# always as positive numbers, even if other is greater than this
|
79
|
+
# instance. It is because the way ruby operates over negative numbers in an
|
80
|
+
# integer division.
|
81
|
+
def -(other)
|
82
|
+
duration = Duration.new(0)
|
83
|
+
duration.internal_seconds = @internal_seconds - other.internal_seconds
|
84
|
+
|
85
|
+
duration
|
86
|
+
end
|
87
|
+
|
88
|
+
# Public: Converts an instance of this class to a string
|
89
|
+
# representation. This is a simple facility for fast representation
|
90
|
+
# on-screen.
|
91
|
+
#
|
92
|
+
def to_s
|
93
|
+
"#{hours}:#{minutes}.#{seconds}"
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
end
|
@@ -0,0 +1,125 @@
|
|
1
|
+
# -*-ruby-*-
|
2
|
+
# Copyright (c) 2016 - Formaweb - All rights reserved
|
3
|
+
#
|
4
|
+
# Author:: Ronaldo Faria Lima
|
5
|
+
# Created:: 2016-03-26
|
6
|
+
#
|
7
|
+
# Parser for ActivityRecord conversion from Trello data
|
8
|
+
|
9
|
+
require 'date'
|
10
|
+
|
11
|
+
require 'time_trello/activity_record'
|
12
|
+
|
13
|
+
module TimeTrello
|
14
|
+
|
15
|
+
# Public: Parser for Trello::Action to ActivityRecord conversion. See
|
16
|
+
# ruby-trello for Trello::Action class specification.
|
17
|
+
class Parser
|
18
|
+
# Private: Instance of Trello::Action to analize
|
19
|
+
private
|
20
|
+
attr :action_record
|
21
|
+
|
22
|
+
# Private: Prefix to use in detecting if a given comment is really an
|
23
|
+
# expected one
|
24
|
+
attr :prefix
|
25
|
+
|
26
|
+
# Private: Class variable used to hold the workflow pieces that parses the
|
27
|
+
# Trello::Action into TimeTrello::ActivityRecord. Each workflow step is a
|
28
|
+
# block that takes two parameters:
|
29
|
+
#
|
30
|
+
# action_record - Hash containing Trello::Action and Trello::Member instances
|
31
|
+
# record - TimeTrello::ActivityRecord instance to build
|
32
|
+
#
|
33
|
+
# Return:: true if parsing was flawless and false otherwise
|
34
|
+
#
|
35
|
+
protected
|
36
|
+
def workflow
|
37
|
+
[
|
38
|
+
# Detects if a given action_record is the one we are looking for.
|
39
|
+
lambda do |action_record, activity|
|
40
|
+
return action_record[:action].data['text'].starts_with?(@prefix) unless action_record[:action].data['text'] == nil
|
41
|
+
|
42
|
+
false
|
43
|
+
end,
|
44
|
+
|
45
|
+
# Parses the duration
|
46
|
+
lambda do |action_record, activity|
|
47
|
+
txt_duration = action_record[:action].data['text'].scan(/[0-9]+:[0-9]+/)
|
48
|
+
activity.duration = Duration.new(txt_duration[0]) unless txt_duration.size == 0
|
49
|
+
|
50
|
+
activity.duration != nil
|
51
|
+
end,
|
52
|
+
|
53
|
+
# Parses the comment owner
|
54
|
+
lambda do |action_record, activity|
|
55
|
+
activity.owner = action_record[:member].full_name
|
56
|
+
|
57
|
+
activity.owner != nil
|
58
|
+
end,
|
59
|
+
|
60
|
+
# Parses the project
|
61
|
+
lambda do |action_record, activity|
|
62
|
+
activity.project = action_record[:action].data["board"]["name"]
|
63
|
+
|
64
|
+
activity.project != nil
|
65
|
+
end,
|
66
|
+
|
67
|
+
# Parses the start date
|
68
|
+
lambda do |action_record, activity|
|
69
|
+
activity.start_date = action_record[:action].date
|
70
|
+
txt_date = action_record[:action].data["text"].scan(/\[[A-Z0-9:. -]+\]/)
|
71
|
+
activity.start_date = DateTime.parse(txt_date[0].to_s).to_time unless txt_date.size == 0
|
72
|
+
|
73
|
+
activity.start_date != nil
|
74
|
+
end,
|
75
|
+
|
76
|
+
# Parses the task comment
|
77
|
+
lambda do |action_record, activity|
|
78
|
+
txt_comment = action_record[:action].data["text"].scan(/"[a-zA-Z0-9]+"/)
|
79
|
+
if txt_comment.size != 0
|
80
|
+
activity.task_description = txt_comment[0]
|
81
|
+
else
|
82
|
+
activity.task_description = action_record[:action].card.name
|
83
|
+
end
|
84
|
+
|
85
|
+
activity.task_description != nil
|
86
|
+
end
|
87
|
+
]
|
88
|
+
end
|
89
|
+
|
90
|
+
# Private: List of steps that parses the action_record, loading fields of a
|
91
|
+
# provided ActivityRecord instance. It is used as a workflow.
|
92
|
+
|
93
|
+
# Public: Initializes the parser, setting it to the proper state.
|
94
|
+
#
|
95
|
+
# action_record - Hash containing the following elements:
|
96
|
+
# - :action_record - An instance of Trello::Action_Record class
|
97
|
+
# - :member - An instance of Trello::Member class, which is the
|
98
|
+
# creator of the action_record with details.
|
99
|
+
# prefix - Prefix to use for comment detection
|
100
|
+
public
|
101
|
+
def initialize(action_record, prefix)
|
102
|
+
@action_record = action_record
|
103
|
+
@prefix = prefix
|
104
|
+
end
|
105
|
+
|
106
|
+
# Public: Parses the action_record, building an instance of ActivityRecord. This
|
107
|
+
# parser is based on a workflow defined by a constant array of blocks. Each
|
108
|
+
# block is a step that will be executed in sequence.
|
109
|
+
def parse
|
110
|
+
record = ActivityRecord.new()
|
111
|
+
|
112
|
+
for step in self.workflow
|
113
|
+
if !step.call(@action_record, record)
|
114
|
+
# Workflow was interrupted. Not necessarily an error. Possibly the
|
115
|
+
# action_record was not the one we are looking for.
|
116
|
+
return nil
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
record
|
121
|
+
end
|
122
|
+
|
123
|
+
end
|
124
|
+
|
125
|
+
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
# -*-ruby-*-
|
2
|
+
# Copyright (c) 2016 - Formaweb - All rights reserved
|
3
|
+
#
|
4
|
+
# Author:: Ronaldo Faria Lima
|
5
|
+
# Created:: 2016-03-21
|
6
|
+
#
|
7
|
+
# Reporting engine. Used to select and build a result set to the caller.
|
8
|
+
|
9
|
+
require 'time_trello/trello_driver'
|
10
|
+
|
11
|
+
module TimeTrello
|
12
|
+
|
13
|
+
# Public: This class represents a report manager on the time trello
|
14
|
+
# structure. It coordinates efforts with the persistence manager in order to
|
15
|
+
# collect data from a given trello board.
|
16
|
+
class Report
|
17
|
+
# Public: Prefix for proper filtering
|
18
|
+
attr_accessor :prefix
|
19
|
+
# Public: Report start date
|
20
|
+
attr_accessor :start_date
|
21
|
+
# Public: Report end date
|
22
|
+
attr_accessor :end_date
|
23
|
+
# Public: board identification for reporting
|
24
|
+
attr_accessor :board_id
|
25
|
+
|
26
|
+
# Public: Initializes this class providing initial filter information
|
27
|
+
#
|
28
|
+
# start_date - Used to limit the result set. It is the start of time records
|
29
|
+
# end_date - Used to limit the result set. It is the end of time records
|
30
|
+
# board_id - Identification of the given board. Used to grab information
|
31
|
+
# from an specific board.
|
32
|
+
def initialize(start_date, end_date, board_id, prefix)
|
33
|
+
@start_date = start_date
|
34
|
+
@end_date = end_date
|
35
|
+
@board_id = board_id
|
36
|
+
@prefix = prefix
|
37
|
+
end
|
38
|
+
|
39
|
+
# Public: Generates the report based on a filter, if provided. The filter is
|
40
|
+
# a block that will filter the result set accordingly. The result set is
|
41
|
+
# always an array containing instances of ActivityRecord. See::
|
42
|
+
# ActivityRecord
|
43
|
+
#
|
44
|
+
# filter - Block used to filter the result set even further. It must follow
|
45
|
+
# the same format for the block passed as parameter to Array.find_all
|
46
|
+
# method. Each element on the array is, in fact, an instance of
|
47
|
+
# TimeTrello::ActivityRecord
|
48
|
+
def find_all &filter
|
49
|
+
driver = TrelloDriver.new(@board_id, @prefix)
|
50
|
+
result_set = driver.activities.find_all { |activity| activity.start_date >= @start_date && activity.start_date <= @end_date }
|
51
|
+
|
52
|
+
if filter
|
53
|
+
return result_set.find_all &filter
|
54
|
+
end
|
55
|
+
|
56
|
+
result_set
|
57
|
+
end
|
58
|
+
|
59
|
+
end
|
60
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
# -*-ruby-*-
|
2
|
+
# Copyright (c) 2016 - Formaweb - All rights reserved
|
3
|
+
#
|
4
|
+
# Author:: Ronaldo Faria Lima
|
5
|
+
# Created:: 2016-03-22
|
6
|
+
#
|
7
|
+
# Trello driver, used to access trello and convert records to an internal
|
8
|
+
# representation
|
9
|
+
|
10
|
+
require 'trello'
|
11
|
+
|
12
|
+
require 'time_trello/activity_record'
|
13
|
+
require 'time_trello/parser'
|
14
|
+
|
15
|
+
module TimeTrello
|
16
|
+
|
17
|
+
# Public: Driver responsible to convert data gathered from Trello to an
|
18
|
+
# internal representation.
|
19
|
+
class TrelloDriver
|
20
|
+
attr_accessor :board_id
|
21
|
+
attr_accessor :prefix
|
22
|
+
|
23
|
+
def initialize(board_id, prefix)
|
24
|
+
@board_id = board_id
|
25
|
+
@prefix = prefix
|
26
|
+
@activities = []
|
27
|
+
@board = nil
|
28
|
+
end
|
29
|
+
|
30
|
+
# Public: Getter. Gets a board, based on a board id.
|
31
|
+
def board
|
32
|
+
@board = Trello::Board.find(@board_id) if @board == nil
|
33
|
+
|
34
|
+
@board
|
35
|
+
end
|
36
|
+
|
37
|
+
# Public: Getter. Gets all activities for a given board.
|
38
|
+
def activities
|
39
|
+
return @activities if @activities != nil && @activities.length > 0
|
40
|
+
|
41
|
+
@activities = []
|
42
|
+
self.board.cards.each do |card|
|
43
|
+
card.actions.each do |action|
|
44
|
+
member = Trello::Member.find(action.member_creator_id)
|
45
|
+
action_record = {action: action, member: member}
|
46
|
+
activity = (Parser.new(action_record, @prefix)).parse
|
47
|
+
@activities.push(activity) unless activity == nil
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
@activities
|
52
|
+
end
|
53
|
+
|
54
|
+
end
|
55
|
+
end
|
data/lib/time_trello.rb
ADDED
@@ -0,0 +1,46 @@
|
|
1
|
+
# -*-ruby-*-
|
2
|
+
# Copyright (c) 2016 - Formaweb - All rights reserved
|
3
|
+
#
|
4
|
+
# Author:: Ronaldo Faria Lima
|
5
|
+
# Created:: 2016-03-21
|
6
|
+
#
|
7
|
+
# Main gem module
|
8
|
+
|
9
|
+
require 'trello'
|
10
|
+
|
11
|
+
require 'time_trello/version'
|
12
|
+
require 'time_trello/report'
|
13
|
+
require 'time_trello/duration'
|
14
|
+
require 'time_trello/activity_record'
|
15
|
+
|
16
|
+
module TimeTrello
|
17
|
+
# Private: Prefix for comment detection/parsing
|
18
|
+
private
|
19
|
+
attr_accessor :prefix
|
20
|
+
|
21
|
+
# Public: Initializes this module with the proper trello client configuration.
|
22
|
+
#
|
23
|
+
# public_key - Trello public key used for authentication.
|
24
|
+
# member_token - Trello member token, used for authentication.
|
25
|
+
public
|
26
|
+
def self.initialize(public_key, member_token, prefix = ':clock12:')
|
27
|
+
@prefix = prefix
|
28
|
+
Trello.configure do |config|
|
29
|
+
config.developer_public_key = public_key
|
30
|
+
config.member_token = member_token
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
# Public: Generates the report based on the provided parameters.
|
35
|
+
#
|
36
|
+
# start_date - The start date to limit the results.
|
37
|
+
# end_date - The end date to limit the results.
|
38
|
+
# board_id - Identification of the board that should be parsed in order to
|
39
|
+
# return results.
|
40
|
+
# filter - A block containing a filter for the results. The block must receive
|
41
|
+
# a parameter which is an instance of ActivityRecord.
|
42
|
+
def self.find_all(start_date, end_date, board_id, &filter)
|
43
|
+
(Report.new(start_date, end_date, board_id, @prefix)).find_all(&filter)
|
44
|
+
end
|
45
|
+
|
46
|
+
end
|
metadata
ADDED
@@ -0,0 +1,73 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: timetrello
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 1.0.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Caio Tarifa
|
8
|
+
- Ronaldo Faria Lima
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2016-03-29 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: ruby-trello
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
requirements:
|
18
|
+
- - "~>"
|
19
|
+
- !ruby/object:Gem::Version
|
20
|
+
version: '1.4'
|
21
|
+
- - ">="
|
22
|
+
- !ruby/object:Gem::Version
|
23
|
+
version: 1.4.1
|
24
|
+
type: :runtime
|
25
|
+
prerelease: false
|
26
|
+
version_requirements: !ruby/object:Gem::Requirement
|
27
|
+
requirements:
|
28
|
+
- - "~>"
|
29
|
+
- !ruby/object:Gem::Version
|
30
|
+
version: '1.4'
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: 1.4.1
|
34
|
+
description: Trello time tracking.
|
35
|
+
email:
|
36
|
+
- caiotarifa@gmail.com
|
37
|
+
- ronaldo@nineteen.com.br
|
38
|
+
executables: []
|
39
|
+
extensions: []
|
40
|
+
extra_rdoc_files: []
|
41
|
+
files:
|
42
|
+
- lib/time_trello.rb
|
43
|
+
- lib/time_trello/activity_record.rb
|
44
|
+
- lib/time_trello/duration.rb
|
45
|
+
- lib/time_trello/parser.rb
|
46
|
+
- lib/time_trello/report.rb
|
47
|
+
- lib/time_trello/trello_driver.rb
|
48
|
+
- lib/time_trello/version.rb
|
49
|
+
homepage: http://www.formaweb.com.br
|
50
|
+
licenses:
|
51
|
+
- MIT
|
52
|
+
metadata: {}
|
53
|
+
post_install_message:
|
54
|
+
rdoc_options: []
|
55
|
+
require_paths:
|
56
|
+
- lib
|
57
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ">="
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
63
|
+
requirements:
|
64
|
+
- - ">="
|
65
|
+
- !ruby/object:Gem::Version
|
66
|
+
version: '0'
|
67
|
+
requirements: []
|
68
|
+
rubyforge_project:
|
69
|
+
rubygems_version: 2.4.8
|
70
|
+
signing_key:
|
71
|
+
specification_version: 4
|
72
|
+
summary: Trello time tracking with Ruby.
|
73
|
+
test_files: []
|