time-sheet 0.8.0 → 0.11.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 3c7858f7734163ab5688073e5ea9fe214a0994bfbf9644dd6a6b8e6d872bdf1c
4
- data.tar.gz: 4dc69782f6f4ec97664b5f2cabc941ebc34396a859612c00ccab75e20c314c8e
3
+ metadata.gz: f5e2d47f0a17abcab7e6b134fd87b176dd753a5dc8022a66d59dcd8b16b3393b
4
+ data.tar.gz: 3335117c3db218920b623aa6759d231f77867b41db80a4bf532a2ba65d334451
5
5
  SHA512:
6
- metadata.gz: e44ab0c69ce3c6131259571e8692b54304404a10e1bcc1be20a52b42ec36e08ec239d87ead3429e02c47c2f7a977e0447dc244df74d439401b0475441282f4b3
7
- data.tar.gz: 78c49a26b85cce835ef3f8f96b944a2932ea683f7762320981ec8ce60ffcbf9f085fa77edd311a6eb0f1b012cdd3f7eb2ae4543305ec2d60c3fdeeb6552ccf64
6
+ metadata.gz: 449c5648f9734732fe4f7c89bdfbcba97b847c213460ac66ecaf69601fd90524ba451cc2abed6b3cb87c1ef1a519218843046b63783e01496e4662f80cb9bb42
7
+ data.tar.gz: cc06940f56f3c29aa6f1dadde4f3c171f9589ffb28abcd3ca1adebfacb5df3b88477d9527b49e5ea7dc44b73a149330489ef84dcf570570e1d6a965ae881543f
data/README.md CHANGED
@@ -3,6 +3,14 @@
3
3
  This gem allows you to parse a spreadsheet with time tracking information to
4
4
  generate various statistics suitable for invoices and project effort estimates.
5
5
 
6
+ ## Changelog
7
+
8
+ v0.9.0
9
+
10
+ * introduced employee data column: To use it, add a column "employee" to the
11
+ spreadsheets and use flag `-e` to filter by it. If employee is not set, it
12
+ falls back to "Me".
13
+
6
14
  ## Installation
7
15
 
8
16
  ```bash
data/lib/time_sheet.rb CHANGED
@@ -1,5 +1,12 @@
1
1
  require 'time_sheet/version'
2
2
 
3
+ # silence HTTPClient
4
+ module Warning
5
+ def warn(mgs)
6
+ # drop
7
+ end
8
+ end
9
+
3
10
  if defined?(Bundler)
4
11
  begin
5
12
  require 'pry'
@@ -15,4 +22,12 @@ module TimeSheet
15
22
  def self.root
16
23
  @root ||= File.expand_path(File.dirname(__FILE__) + '/..')
17
24
  end
25
+
26
+ def self.options=(options)
27
+ @options = options
28
+ end
29
+
30
+ def self.options
31
+ @options
32
+ end
18
33
  end
@@ -41,7 +41,7 @@ module TimeSheet::Time
41
41
  unless results['entries'].empty?
42
42
  time = (
43
43
  (options[:to] || Util.day_end) -
44
- (options[:from] || results['entries'].first[0].to_time)
44
+ (options[:from] || results['entries'].first[1].to_time)
45
45
  ).to_i
46
46
  days = (time.to_f / 60 / 60 / 24).round
47
47
  end
@@ -3,6 +3,8 @@ require 'time'
3
3
 
4
4
  class TimeSheet::Time::Cmd
5
5
  def run
6
+ TimeSheet.options = options
7
+
6
8
  if d = options[:from]
7
9
  if d.match(/^\d\d?-\d\d?$/)
8
10
  d = "#{TimeSheet::Time::Util.now.year}-#{d}"
@@ -64,6 +66,10 @@ class TimeSheet::Time::Cmd
64
66
  options[:to] = TimeSheet::Time::Util.month_end(-1)
65
67
  options[:summary] = true
66
68
  report
69
+ when 'year-to-day', 'year'
70
+ options[:from] = TimeSheet::Time::Util.year_start(-1)
71
+ options[:summary] = true
72
+ report
67
73
  when 'overview'
68
74
  overview
69
75
  else
@@ -111,11 +117,14 @@ class TimeSheet::Time::Cmd
111
117
  o.string '-t', '--to', 'ignore entries more recent than the date given'
112
118
  o.string '-p', '--project', 'take only entries of this project into account'
113
119
  o.string '-a', '--activity', 'take only entries of this activity into account'
120
+ o.string '--tags', 'take only entries with these tags into account (comma separated, not case sensitive)'
114
121
  o.string '-d', '--description', 'consider only entries matching this description'
122
+ o.string '-e', '--employee', 'consider only entries for this employee'
115
123
  o.float '-r', '--rate', 'use an alternative hourly rate (default: 80.0)', default: 80.00
116
124
  o.boolean '-s', '--summary', 'when reporting, add summary section'
117
125
  o.boolean '--trim', 'compact the output for processing as CSV', default: false
118
126
  o.boolean '-v', '--verbose', 'be more verbose'
127
+ o.boolean '--debug', 'drop into a REPL on errors'
119
128
  o.separator "\n invoice options:"
120
129
  o.integer '--package', 'for invoice output: build packages of this duration in hours', default: 0
121
130
  o.integer '--petty', 'fold records under a certain threshold into a "misc" activity', default: 0
@@ -41,6 +41,10 @@ class TimeSheet::Time::Entry
41
41
  )
42
42
  end
43
43
 
44
+ def employee
45
+ @employee ||= @data['employee'] || (self.prev ? self.prev.employee : 'Me')
46
+ end
47
+
44
48
  # Experiment to add timezone support. However, this would complicate every day
45
49
  # handing because of daylight saving time changes.
46
50
  # def start_zone
@@ -74,7 +78,7 @@ class TimeSheet::Time::Entry
74
78
  end
75
79
 
76
80
  def tags
77
- (@data['tags'] || '').split(/\s*,\s*/)
81
+ self.class.parse_tags(@data['tags'])
78
82
  end
79
83
 
80
84
  def working_day?
@@ -86,7 +90,10 @@ class TimeSheet::Time::Entry
86
90
  from = from.to_time if from.is_a?(Date)
87
91
  to = (filters[:to] ? filters[:to] : nil)
88
92
  to = (to + 1).to_time if to.is_a?(Date)
93
+ tags = self.class.parse_tags(filters[:tags])
89
94
 
95
+ has_tags?(tags) &&
96
+ self.class.attrib_matches_any?(employee, filters[:employee]) &&
90
97
  self.class.attrib_matches_any?(description, filters[:description]) &&
91
98
  self.class.attrib_matches_any?(project, filters[:project]) &&
92
99
  self.class.attrib_matches_any?(activity, filters[:activity]) &&
@@ -94,12 +101,23 @@ class TimeSheet::Time::Entry
94
101
  (!to || to >= self.end)
95
102
  end
96
103
 
104
+ def has_tags?(tags)
105
+ return true if tags.empty?
106
+
107
+ tags.all? do |tag|
108
+ self.tags.include?(tag)
109
+ end
110
+ end
111
+
97
112
  def valid?
98
113
  valid!
99
114
  true
100
115
  rescue TimeSheet::Time::Exception => e
101
116
  self.exception = e
102
117
  false
118
+ rescue StandardError => e
119
+ binding.pry if Timesheet.options[:debug]
120
+ false
103
121
  end
104
122
 
105
123
  def valid!
@@ -114,14 +132,22 @@ class TimeSheet::Time::Entry
114
132
  if (self.start >= self.end) && self.next
115
133
  raise TimeSheet::Time::Exception.new('time entry has no end')
116
134
  end
135
+
136
+ if !employee
137
+ raise TimeSheet::Time::Exception.new('no employee set')
138
+ end
117
139
  end
118
140
 
119
141
  def to_row
120
- [date, start, self.end, duration.to_i, project, activity, description]
142
+ [
143
+ employee, date, start, self.end, duration.to_i, project, activity,
144
+ description
145
+ ]
121
146
  end
122
147
 
123
148
  def to_s
124
149
  values = [
150
+ employee,
125
151
  date.strftime('%Y-%m-%d'),
126
152
  start.strftime('%H:%M'),
127
153
  self.end.strftime('%H:%M'),
@@ -134,13 +160,15 @@ class TimeSheet::Time::Entry
134
160
 
135
161
  def to_hash
136
162
  return {
163
+ 'employee' => employee,
137
164
  'date' => date,
138
165
  'start' => start,
139
166
  'end' => self.end,
140
167
  'duration' => duration,
141
168
  'project' => project,
142
169
  'activity' => activity,
143
- 'description' => description
170
+ 'description' => description,
171
+ 'tags' => tags
144
172
  }
145
173
  end
146
174
 
@@ -156,4 +184,8 @@ class TimeSheet::Time::Entry
156
184
  end
157
185
  end
158
186
 
187
+ def self.parse_tags(string)
188
+ (string || '').to_s.downcase.split(/\s*,\s*/).map{|t| t.strip}
189
+ end
190
+
159
191
  end
@@ -68,10 +68,11 @@ class TimeSheet::Time::Parser
68
68
 
69
69
  Spreadsheet.open(filename).worksheets.each do |sheet|
70
70
  headers = sheet.rows.first.to_a
71
- sheet.rows[1..-1].each do |row|
71
+ sheet.rows[1..-1].each.with_index do |row, i|
72
72
  # TODO find a way to guard against xls sheets with 65535 (empty)
73
73
  # lines, perhaps:
74
74
  # break if row[1].nil?
75
+ next if row.all?{|cell| [nil, ''].include?(cell)}
75
76
 
76
77
  record = {}
77
78
  row.each_with_index do |value, i|
@@ -84,16 +85,16 @@ class TimeSheet::Time::Parser
84
85
  results
85
86
  end
86
87
 
87
- def parse_google_doc(share_url)
88
- id = share_url.match(/\/d\/([^\/]+)\//)[1]
89
- url = "https://docs.google.com/spreadsheets/d/#{id}/export?exportFormat=tsv"
88
+ def parse_google_doc(url)
89
+ # Chart Tools datasource protocol, see
90
+ # https://developers.google.com/chart/interactive/docs/querylanguage
90
91
  response = HTTPClient.get(url)
91
92
 
92
93
  if response.status == 200
93
- data = CSV.parse(response.body, col_sep: "\t")
94
+ data = CSV.parse(response.body, liberal_parsing: true)
94
95
  headers = data.shift
95
96
  data.map do |row|
96
- record = headers.zip(row).to_h
97
+ record = nullify_empties(headers.zip(row).to_h)
97
98
  parse_date_and_time(record)
98
99
  end
99
100
  else
@@ -108,9 +109,16 @@ class TimeSheet::Time::Parser
108
109
  'end' => (record['end'] ? DateTime.parse(record['end']) : nil)
109
110
  )
110
111
  rescue ArgumentError => e
112
+ binding.pry if TimeSheet.options[:debug]
111
113
  puts "current record: #{record.inspect}"
112
114
  return {}
113
115
  # raise e
114
116
  end
115
117
 
118
+ def nullify_empties(record)
119
+ record.transform_values do |v|
120
+ v == '' ? nil : v
121
+ end
122
+ end
123
+
116
124
  end
@@ -1,3 +1,3 @@
1
1
  module TimeSheet
2
- VERSION = "0.8.0"
2
+ VERSION = "0.11.1"
3
3
  end
data/time_sheet.gemspec CHANGED
@@ -28,8 +28,8 @@ Gem::Specification.new do |spec|
28
28
  spec.add_dependency 'slop'
29
29
  spec.add_dependency 'httpclient'
30
30
 
31
- spec.add_development_dependency "bundler", "~> 1.15"
32
- spec.add_development_dependency "rake", "~> 10.0"
31
+ spec.add_development_dependency "bundler", "~> 2.2.16"
32
+ spec.add_development_dependency "rake", "~> 13.0"
33
33
  spec.add_development_dependency "rspec", "~> 3.0"
34
34
  spec.add_development_dependency 'pry'
35
35
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: time-sheet
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.8.0
4
+ version: 0.11.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Moritz Schepp
8
- autorequire:
8
+ autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2019-12-03 00:00:00.000000000 Z
11
+ date: 2021-06-25 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: spreadsheet
@@ -58,28 +58,28 @@ dependencies:
58
58
  requirements:
59
59
  - - "~>"
60
60
  - !ruby/object:Gem::Version
61
- version: '1.15'
61
+ version: 2.2.16
62
62
  type: :development
63
63
  prerelease: false
64
64
  version_requirements: !ruby/object:Gem::Requirement
65
65
  requirements:
66
66
  - - "~>"
67
67
  - !ruby/object:Gem::Version
68
- version: '1.15'
68
+ version: 2.2.16
69
69
  - !ruby/object:Gem::Dependency
70
70
  name: rake
71
71
  requirement: !ruby/object:Gem::Requirement
72
72
  requirements:
73
73
  - - "~>"
74
74
  - !ruby/object:Gem::Version
75
- version: '10.0'
75
+ version: '13.0'
76
76
  type: :development
77
77
  prerelease: false
78
78
  version_requirements: !ruby/object:Gem::Requirement
79
79
  requirements:
80
80
  - - "~>"
81
81
  - !ruby/object:Gem::Version
82
- version: '10.0'
82
+ version: '13.0'
83
83
  - !ruby/object:Gem::Dependency
84
84
  name: rspec
85
85
  requirement: !ruby/object:Gem::Requirement
@@ -108,7 +108,7 @@ dependencies:
108
108
  - - ">="
109
109
  - !ruby/object:Gem::Version
110
110
  version: '0'
111
- description:
111
+ description:
112
112
  email:
113
113
  - moritz.schepp@gmail.com
114
114
  executables:
@@ -134,13 +134,13 @@ files:
134
134
  - lib/time_sheet/time/util.rb
135
135
  - lib/time_sheet/version.rb
136
136
  - time_sheet.gemspec
137
- homepage:
137
+ homepage:
138
138
  licenses:
139
139
  - GPL-3.0-only
140
140
  metadata:
141
141
  bug_tracker_uri: https://github.com/moritzschepp/time-sheet/issues
142
142
  documentation_uri: https://github.com/moritzschepp/time-sheet
143
- post_install_message:
143
+ post_install_message:
144
144
  rdoc_options: []
145
145
  require_paths:
146
146
  - lib
@@ -155,9 +155,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
155
155
  - !ruby/object:Gem::Version
156
156
  version: '0'
157
157
  requirements: []
158
- rubyforge_project:
159
- rubygems_version: 2.7.6.2
160
- signing_key:
158
+ rubygems_version: 3.1.6
159
+ signing_key:
161
160
  specification_version: 4
162
161
  summary: a time tracking solution based on spreadsheets
163
162
  test_files: []