22
33require 'dotenv/load'
44require 'thor'
5+ require 'snaky_hash'
56require_relative '../lib/freeagent'
67
78def match type , collection , needle , *values
@@ -34,6 +35,29 @@ module Freeagent
3435 def api
3536 @api ||= API . new
3637 end
38+
39+ def users
40+ @users ||= Hash . new { |cache , url | cache [ url ] = api . user url . split ( '/' ) . last }
41+ end
42+
43+ def projects
44+ @projects ||= Hash . new { |cache , url | cache [ url ] = api . project url . split ( '/' ) . last }
45+ end
46+
47+ def tasks
48+ @tasks ||= Hash . new { |cache , url | cache [ url ] = api . task url . split ( '/' ) . last }
49+ end
50+
51+ def human timeslip
52+ [
53+ Date ::parse ( timeslip . dated_on ) . strftime ( '%a %d %b %Y' ) ,
54+ users [ timeslip . user ] . email ,
55+ projects [ timeslip . project ] . name ,
56+ tasks [ timeslip . task ] . name ,
57+ timeslip . hours ,
58+ timeslip . comment
59+ ] . join ( "\t " )
60+ end
3761 end
3862
3963 desc 'list [-U USER] [-P PROJECT [-T TASK]] [-f FROM] [-t TO]' , "List timeslips"
@@ -42,6 +66,7 @@ module Freeagent
4266 method_option :task , aliases : '-T' , type : :string , default : nil , desc : 'Name of a task within the project'
4367 method_option :from , aliases : '-f' , type : :string , default : nil , desc : 'Date to list timeslips from'
4468 method_option :to , aliases : '-t' , type : :string , default : nil , desc : 'Date to list timeslips to'
69+ method_option :format , type : :string , default : 'human' , desc : 'Output format, one of human or json'
4570 def list
4671 raise ArgumentError , '--project must be specified if --task if used' if options [ :project ] . nil? && !options [ :task ] . nil?
4772
@@ -51,83 +76,108 @@ module Freeagent
5176 project = options [ :project ] . nil? ? nil : match ( :project , api . projects , options [ :project ] , :name )
5277 task = options [ :task ] . nil? ? nil : match ( :task , api . tasks ( project ) , options [ :task ] , :name )
5378
54- users = Hash . new { |cache , url | cache [ url ] = api . user url . split ( '/' ) . last }
5579 users [ user . url ] = user unless user . nil?
56- projects = Hash . new { |cache , url | cache [ url ] = api . project url . split ( '/' ) . last }
5780 projects [ project . url ] = project unless project . nil?
58- tasks = Hash . new { |cache , url | cache [ url ] = api . task url . split ( '/' ) . last }
5981 tasks [ task . url ] = task unless task . nil?
6082
6183 api . timeslips ( user : user , project : project , task : task , from : from , to : to ) . each do |timeslip |
62- puts [
63- Date ::parse ( timeslip . dated_on ) . strftime ( '%a %d %b %Y' ) ,
64- users [ timeslip . user ] . email ,
65- projects [ timeslip . project ] . name ,
66- tasks [ timeslip . task ] . name ,
67- timeslip . hours ,
68- timeslip . comment
69- ] . join ( "\t " )
84+ puts case options [ :format ] . downcase
85+ when 'human' ; human ( timeslip )
86+ when 'json' ; timeslip . to_json
87+ else raise ArgumentError , "unknown output format #{ options [ :format ] } "
88+ end
7089 end
7190 end
7291
7392 desc 'create USER PROJECT [TASK [FROM [TO]]]' , "Create timeslips"
7493 method_option :hours , aliases : '-h' , type : :numeric , default : 8 , desc : "Number of hours per day"
7594 method_option :weekends , aliases : '-w' , type : :boolean , default : false , desc : "Create timeslips on Saturdays and Sundays"
7695 method_option :comment , aliases : '-c' , type : :string , default : nil , desc : "Comment to add to any created timeslips"
96+ method_option :format , type : :string , default : 'human' , desc : 'Output format, one of human or json'
7797 def create user , project , task = nil , from = nil , to = nil
7898 from = from . nil? ? Date ::today : Date ::parse ( from )
7999 to = to . nil? ? from : Date ::parse ( to )
80100 user = match :user , api . users , user , :first_name , :last_name , :email
81101 project = match :project , api . projects , project , :name
82102 task = select_task project , task
83103
84- puts "Creating timeslips for #{ user . first_name } #{ user . last_name } for task '#{ task . name } ' in project '#{ project . name } ' between #{ from } and #{ to } inclusive"
104+ users [ user . url ] = user unless user . nil?
105+ projects [ project . url ] = project unless project . nil?
106+ tasks [ task . url ] = task unless task . nil?
107+
85108 timeslips = from . step ( to ) . map do |date |
86109 next if ( date . saturday? || date . sunday? ) && !options [ :weekends ]
87- { ' task' => task . url , ' project' => project . url , ' user' => user . url , ' dated_on' => date . to_s , ' hours' => options [ :hours ] , ' comment' => options [ :comment ] }
110+ SnakyHash :: StringKeyed . new ( task : task . url , project : project . url , user : user . url , dated_on : date . to_s , hours : options [ :hours ] , comment : options [ :comment ] )
88111 end . reject ( &:nil? )
89112 api . batch_create_timeslips timeslips
113+ timeslips . each do |timeslip |
114+ puts case options [ :format ] . downcase
115+ when 'human' ; human ( timeslip )
116+ when 'json' ; timeslip . to_json
117+ else raise ArgumentError , "unknown output format #{ options [ :format ] } "
118+ end
119+ end
90120 end
91121
92122 desc 'fill USER PROJECT [TASK [FROM [TO]]]' , 'Fill remaining hours with new timeslips'
93123 method_option :hours , aliases : '-h' , type : :numeric , default : 8 , desc : "Number of hours per day"
94124 method_option :weekends , aliases : '-w' , type : :boolean , default : false , desc : "Create timeslips on Saturdays and Sundays"
95125 method_option :comment , aliases : '-c' , type : :string , default : nil , desc : "Comment to add to any created timeslips"
126+ method_option :format , type : :string , default : 'human' , desc : 'Output format, one of human or json'
96127 def fill user , project , task = nil , from = nil , to = nil
97128 from = from . nil? ? Date ::today : Date ::parse ( from )
98129 to = to . nil? ? from : Date ::parse ( to )
99130 user = match :user , api . users , user , :first_name , :last_name , :email
100131 project = match :project , api . projects , project , :name
101132 task = select_task project , task
102133
134+ users [ user . url ] = user unless user . nil?
135+ projects [ project . url ] = project unless project . nil?
136+ tasks [ task . url ] = task unless task . nil?
137+
103138 existing = api . timeslips ( user : user , from : from , to : to ) . reduce ( Hash . new ) do |hash , timeslip |
104139 date = Date ::parse timeslip . dated_on
105140 hash . update ( { date => ( hash [ date ] || 0 ) + timeslip . hours . to_f } )
106141 end
107142
108- puts "Filling timeslips for #{ user . first_name } #{ user . last_name } up to #{ options [ :hours ] } hrs using task '#{ task . name } ' in project '#{ project . name } ' between #{ from } and #{ to } inclusive"
109143 timeslips = from . step ( to ) . map do |date |
110144 next if ( date . saturday? || date . sunday? ) && !options [ :weekends ]
111145 existing_hours = existing [ date ] || 0
112146 adding_hours = options [ :hours ] - existing_hours
113147 next if adding_hours <= 0
114148
115- { ' task' => task . url , ' project' => project . url , ' user' => user . url , ' dated_on' => date . to_s , ' hours' => adding_hours . to_s , ' comment' => options [ :comment ] }
149+ SnakyHash :: StringKeyed . new ( task : task . url , project : project . url , user : user . url , dated_on : date . to_s , hours : adding_hours . to_s , comment : options [ :comment ] )
116150 end . reject ( &:nil? )
117151 api . batch_create_timeslips timeslips
152+ timeslips . each do |timeslip |
153+ puts case options [ :format ] . downcase
154+ when 'human' ; human ( timeslip )
155+ when 'json' ; timeslip . to_json
156+ else raise ArgumentError , "unknown output format #{ options [ :format ] } "
157+ end
158+ end
118159 end
119160
120161 desc 'delete USER PROJECT TASK [FROM [TO]]' , 'Delete timeslips'
162+ method_option :format , type : :string , default : 'human' , desc : 'Output format, one of human or json'
121163 def delete user , project , task , from = nil , to = nil
122164 from = from . nil? ? Date ::today : Date ::parse ( from )
123165 to = to . nil? ? from : Date ::parse ( to )
124166 user = match :user , api . users , user , :first_name , :last_name , :email
125167 project = match :project , api . projects , project , :name
126168 task = match :task , api . tasks ( project ) , task , :name
127169
128- puts "Deleting timeslips for #{ user . first_name } #{ user . last_name } for task '#{ task . name } ' in project '#{ project . name } ' between #{ from } and #{ to } inclusive"
170+ users [ user . url ] = user unless user . nil?
171+ projects [ project . url ] = project unless project . nil?
172+ tasks [ task . url ] = task unless task . nil?
173+
129174 api . timeslips ( user : user , project : project , task : task , from : from , to : to ) . each do |timeslip |
130175 api . delete_timeslip ( timeslip )
176+ puts case options [ :format ] . downcase
177+ when 'human' ; human ( timeslip )
178+ when 'json' ; timeslip . to_json
179+ else raise ArgumentError , "unknown output format #{ options [ :format ] } "
180+ end
131181 end
132182 end
133183 end
0 commit comments