ptimelog 0.9.0 → 0.10.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: efde9baef775527079705c4a87dfa656a8df4f186381a88c6ebd51e0cfdfd4c8
4
- data.tar.gz: d68a4f17937165e47fe381dedade07ab625ae98c426a30c6cf09d806a2b77466
3
+ metadata.gz: 3a7ad17dbf09bc8d7694f9261ffcc8216af2f1ec04be03e62ccabadf9732e8b0
4
+ data.tar.gz: 4ee34a775b9300e50971421adedee472fbfa5f5c0ec226b93e299d17474df411
5
5
  SHA512:
6
- metadata.gz: 439357c8e138db02f373cdefedbcd3abd79e16aa5997a8140449327352e0efbdb436b293989b0a19086fcbe41a07c09e7743a3e68b9945cd885080e31b06f056
7
- data.tar.gz: 2ca4a1333cd8f21fc07deb995f2ab31b62ada7a8b95286f050bb3f481adbddf85471df28be2c80b5a838c0a91d0e770dbf144830f1349298157161284d545a6f
6
+ metadata.gz: 198b9c6aefbab6f64b617a6b2ad125f9e9921c829e1740ca08faa41c69c797fda1cca1181665adec6b68b51560b3a5bed88ad3b23a8bceba6382dd4b222bb129
7
+ data.tar.gz: d37ed829daa68c7527caafe929874c6523986609661f9a9155463f0307ac2fb9d14d3237878d6c6659f799ca59c9536e8cae46770f21cdb13916753277beaf2c
@@ -1,5 +1,14 @@
1
1
  inherit_from: .rubocop_todo.yml
2
2
 
3
+ # require:
4
+ # - rubocop-rake
5
+ # - rubocop-rspec
6
+ # - rubocop-packaging
7
+ # - rubocop-performance
8
+
9
+ AllCops:
10
+ NewCops: enable
11
+
3
12
  Layout/LineLength:
4
13
  Max: 120
5
14
 
@@ -1 +1 @@
1
- 2.6.3
1
+ 2.7.0
@@ -0,0 +1 @@
1
+ ruby 2.7.2
data/README.md CHANGED
@@ -1,6 +1,8 @@
1
1
  # pTimeLog
2
2
 
3
- small tooling to transfer timelog-entries from gtimelog's timelog.txt to the PuzzleTime TimeTracking Website.
3
+ small tooling to transfer timelog-entries from gtimelog's timelog.txt to the PuzzleTime Timetracking Website.
4
+
5
+ [![Build Status](https://travis-ci.org/kronn/ptimelog.svg?branch=master)](https://travis-ci.org/kronn/ptimelog)
4
6
 
5
7
  ## Approach
6
8
 
@@ -24,8 +26,8 @@ small tooling to transfer timelog-entries from gtimelog's timelog.txt to the Puz
24
26
  - [ ] send user and pwd with every request
25
27
  - [ ] make entries
26
28
  - [ ] open day in browser for review
27
- - [ ] avoid duplicate entries
28
- - [ ] start/end time as indicator?
29
+ - [x] avoid duplicate entries
30
+ - [x] start/end time as indicator?
29
31
  - [x] offer rounding times to the next 5, 10 or 15 minutes
30
32
  - [x] allow to add entries from the command-line
31
33
  - [ ] handle time-account and billable better
@@ -58,10 +60,13 @@ Currently supported actions are
58
60
  - show
59
61
  - upload
60
62
  - edit
63
+ - add
64
+ - version
61
65
 
62
66
  ### Date-Identifier
63
67
 
64
- To handle a specific date, the format YYYY-MM-DD is expected, e.g. 2017-12-25. Please note that you should not work on that day, unless you bring presents.
68
+ To handle a specific date, the format YYYY-MM-DD is expected, e.g. 2017-12-25.
69
+ Please note that you should not work on that day, unless you bring presents.
65
70
 
66
71
  For reusability in a shell-history the following keywords are supported:
67
72
 
@@ -74,12 +79,44 @@ If nothing is specified, the action is applied to entries of the last day.
74
79
 
75
80
  ### Edit-Identifier
76
81
 
77
- When the action is "edit", the next argument is treated as script that should be edited.
82
+ When the action is "edit", the next argument is treated as script that should
83
+ be edited.
78
84
 
79
85
  If nothing is passed, the main timelog.txt is loaded.
80
86
 
81
87
  Otherwise, a script to determine the time-account is loaded.
82
88
 
89
+ ### Adding entries
90
+
91
+ In order to add entries with the ptimelog-cli, the complete entry needs to be
92
+ quoted on the command-line to count as one argument.
93
+
94
+ $ ptimelog add 'ticket 1337: Implement requirements -- client coding'
95
+
96
+ While this requires some knowledge of the file-format, it is no different than
97
+ entering the same string in gTimelog. For now, the entry is added to the
98
+ timelog.txt as it is passed. By default, the date/time added to the entry is
99
+ the one when the command is executed.
100
+
101
+ You can prefix a positive or negative signed number to slightly skew the entry
102
+ (think: '-5 meeting' or '+5 lunch \*\*') or even set a precise time ('10:30
103
+ meeting').
104
+
105
+ $ ptimelog add '-5 meeting: Discuss requirements -- client planning'
106
+
107
+ ### Showing the Version
108
+
109
+ I got tired of asking rubygems which version I installed, so I took on the
110
+ herculean task of letting ptimelog show its own version.
111
+
112
+ ### Formatting the Output
113
+
114
+ In order to format the output of the show-action into a table, a hopefully
115
+ convienient field-marker has been chosen. I think it is unlikely, that ∴ is
116
+ being used in a time-entry. Therefore, you can pipe the output into `column`:
117
+
118
+ ptimelog show today | column -t -s ∴
119
+
83
120
  ## Helper-Scripts
84
121
 
85
122
  ptimelog can prefill the account-number and billable-state of an entry.
@@ -102,8 +139,8 @@ scripts, the code is almost identical and "just" needs to be compiled.
102
139
  A config-file is read from `$HOME/.config/ptimelog/config`. It is expected
103
140
  to be a YAML-file. Currently, it supports the following keys:
104
141
 
105
- - rounding: [integer or false, default 15]
106
- - base_url: [url to your puzzletime-installation, default https://time.puzzle.ch]
142
+ - rounding: [integer or false, default 15]
143
+ - base_url: [url to your puzzletime-installation, default https://time.puzzle.ch]
107
144
 
108
145
  ## Development
109
146
 
@@ -7,7 +7,7 @@ module Ptimelog
7
7
  @config = Configuration.instance
8
8
  command = (args[0] || 'show')
9
9
 
10
- constant_name = command.to_s[0].upcase + command[1..-1].downcase
10
+ constant_name = command.to_s[0].upcase + command[1..].downcase
11
11
  command_class = Command.const_get(constant_name.to_sym)
12
12
  raise ArgumentError, "Unsupported Command '#{command}'" if command_class.nil?
13
13
 
@@ -11,6 +11,7 @@ module Ptimelog
11
11
 
12
12
  @task = task
13
13
  @timelog = Ptimelog::Timelog.instance
14
+ @new_lines = []
14
15
  end
15
16
 
16
17
  def needs_entries?
@@ -18,12 +19,47 @@ module Ptimelog
18
19
  end
19
20
 
20
21
  def run
21
- date_time = Time.now.strftime('%F %R')
22
+ add_empty_line if @timelog.previous_entry.date == yesterday
23
+ add_entry(*parse_task(@task))
22
24
 
25
+ save_file
26
+ end
27
+
28
+ private
29
+
30
+ def parse_task(line)
31
+ matches = line.match('(?<time>\d{1,2}:\d{2} )?(?<offset>[+-]\d+ )?(?<task>.*)')
32
+ formatted_time = if matches[:time]
33
+ Time.parse(matches[:time])
34
+ else
35
+ Time.now
36
+ end
37
+ .localtime
38
+ .then { |time| time + (matches[:offset].to_i * 60) }
39
+ .strftime('%F %R')
40
+
41
+ [formatted_time, matches[:task]]
42
+ end
43
+
44
+ def add_entry(date_time, task)
45
+ @new_lines << "#{date_time}: #{task}"
46
+ end
47
+
48
+ def add_empty_line
49
+ @new_lines << ''
50
+ end
51
+
52
+ def save_file
23
53
  @timelog.timelog_txt.open('a') do |log|
24
- log << "#{date_time}: #{@task}\n"
54
+ @new_lines.each do |line|
55
+ log << "#{line}\n"
56
+ end
25
57
  end
26
58
  end
59
+
60
+ def yesterday
61
+ NamedDate.new.named_date('yesterday')
62
+ end
27
63
  end
28
64
  end
29
65
  end
@@ -18,6 +18,9 @@ module Ptimelog
18
18
  @entries.each do |date, list|
19
19
  puts date,
20
20
  '----------'
21
+
22
+ next if list.empty?
23
+
21
24
  list.each do |entry|
22
25
  puts entry
23
26
  end
@@ -2,6 +2,7 @@
2
2
 
3
3
  require 'singleton'
4
4
  require 'pathname'
5
+ require 'yaml'
5
6
 
6
7
  module Ptimelog
7
8
  # Wrapper around configuration-options and -loading
@@ -9,10 +10,10 @@ module Ptimelog
9
10
  include Singleton
10
11
 
11
12
  CONFIGURATION_DEFAULTS = {
12
- base_url: 'https://time.puzzle.ch',
13
- rounding: 15,
14
- dir: '~/.config/ptimelog',
15
- timelog: '~/.local/share/gtimelog/timelog.txt',
13
+ 'base_url' => 'https://time.puzzle.ch',
14
+ 'rounding' => 15,
15
+ 'dir' => '~/.config/ptimelog',
16
+ 'timelog' => '~/.local/share/gtimelog/timelog.txt',
16
17
  }.freeze
17
18
 
18
19
  def initialize
@@ -21,10 +22,12 @@ module Ptimelog
21
22
 
22
23
  def reset
23
24
  @config = load_config(
24
- Pathname.new(CONFIGURATION_DEFAULTS[:dir]).join('config')
25
+ Pathname.new(CONFIGURATION_DEFAULTS['dir'])
26
+ .expand_path
27
+ .join('config')
25
28
  )
26
- wrap_with_pathname(:dir)
27
- wrap_with_pathname(:timelog)
29
+ wrap_with_pathname('dir')
30
+ wrap_with_pathname('timelog')
28
31
  end
29
32
 
30
33
  def load_config(fn)
@@ -34,13 +37,13 @@ module Ptimelog
34
37
  end
35
38
 
36
39
  def [](key)
37
- @config[key.to_sym]
40
+ @config[key.to_s]
38
41
  end
39
42
 
40
43
  def []=(key, value)
41
- @config[key.to_sym] = value
44
+ @config[key.to_s] = value
42
45
 
43
- wrap_with_pathname(key.to_sym) if %w[dir timelog].include?(key.to_s)
46
+ wrap_with_pathname(key.to_s) if %w[dir timelog].include?(key.to_s)
44
47
  end
45
48
 
46
49
  private
@@ -9,10 +9,10 @@ module Ptimelog
9
9
 
10
10
  def entries
11
11
  timelog.each_with_object({}) do |(date, lines), entries|
12
- next unless date # guard against the machine
13
- next unless @date == :all || @date == date # limit to one day if passed
12
+ next unless date # guard against the machine
13
+ next unless @date.to_s == 'all' || @date == date # limit to one day if passed
14
14
 
15
- entries[date] = join_similar(entries_of_day(lines)) # lines |> entries_of_day |> join_similar"
15
+ entries[date] = join_similar(entries_of_day(lines)) # lines |> entries_of_day |> join_similar
16
16
  end
17
17
  end
18
18
 
@@ -5,12 +5,11 @@ require 'time'
5
5
  module Ptimelog
6
6
  # Dataclass to wrap an entry
7
7
  class Entry
8
- # allow to read everything
9
- attr_reader :date, :start_time, :finish_time, :ticket, :description,
10
- :tags, :billable, :account
11
-
12
8
  # define only trivial writers, omit special and derived values
13
- attr_writer :date, :ticket, :description
9
+ attr_accessor :date, :ticket, :description
10
+
11
+ # allow to read everything else
12
+ attr_reader :start_time, :finish_time, :tags, :billable, :account
14
13
 
15
14
  BILLABLE = 1
16
15
  NON_BILLABLE = 0
@@ -54,7 +53,7 @@ module Ptimelog
54
53
  end
55
54
 
56
55
  def valid?
57
- @start_time && !hidden?
56
+ @start_time && duration.positive? && !hidden?
58
57
  end
59
58
 
60
59
  def hidden?
@@ -77,10 +76,16 @@ module Ptimelog
77
76
  end
78
77
 
79
78
  def to_s
80
- billable = billable? ? '$' : nil
79
+ billable = billable? ? '($)' : nil
80
+ tag_list = Array(@tags).compact
81
+
82
+ tags = tag_list.join(' ') if tag_list.any?
83
+ desc = [@ticket, @description].compact.join(': ')
84
+ acc = [@account, billable].compact.join(' ') if @account
85
+
81
86
  [
82
- @start_time, '-', @finish_time,
83
- [@ticket, @description, @tags, @account, billable].compact.join(' ∴ '),
87
+ @start_time, '-', @finish_time, '∴',
88
+ [desc, tags, acc].compact.join(' ∴ '),
84
89
  ].compact.join(' ')
85
90
  end
86
91
 
@@ -107,7 +112,7 @@ module Ptimelog
107
112
  end
108
113
 
109
114
  def script_args
110
- @script_args ||= @tags.to_a[1..-1].to_a.map(&:inspect).join(' ')
115
+ @script_args ||= @tags.to_a[1..].to_a.map(&:inspect).join(' ')
111
116
  end
112
117
 
113
118
  def infer_account_and_billable
@@ -9,17 +9,39 @@ module Ptimelog
9
9
  named_date(arg) || :all
10
10
  end
11
11
 
12
- def named_date(date)
12
+ def named_date(date) # rubocop:disable Metrics/CyclomaticComplexity,Metrics/AbcSize
13
13
  case date.to_s
14
- when 'yesterday' then Date.today.prev_day.to_s
14
+ when 'yesterday' then yesterday
15
15
  when 'today' then Date.today.to_s
16
- when 'last', '' then timelog.to_h.keys.compact.sort[-2] || Date.today.prev_day.to_s
17
- when /\d{4}(-\d{2}){2}/ then date
16
+ when 'last', '' then last_entry.to_s || yesterday
17
+ when 'mon', 'monday' then previous_weekday('monday')
18
+ when 'tue', 'tuesday' then previous_weekday('tuesday')
19
+ when 'wed', 'wednesday' then previous_weekday('wednesday')
20
+ when 'thu', 'thursday' then previous_weekday('thursday')
21
+ when 'fri', 'friday' then previous_weekday('friday')
22
+ when 'sat', 'saturday' then previous_weekday('saturday')
23
+ when 'sun', 'sunday' then previous_weekday('sunday')
24
+ when /\d{4}(-\d{2}){2}/ then date.to_s
18
25
  end
19
26
  end
20
27
 
21
28
  private
22
29
 
30
+ def previous_weekday(date)
31
+ Date.today.prev_day(7)
32
+ .step(Date.today.prev_day)
33
+ .find { |d| d.send(:"#{date}?") }
34
+ .to_s
35
+ end
36
+
37
+ def last_entry
38
+ timelog.to_h.keys.compact.sort[-2]
39
+ end
40
+
41
+ def yesterday
42
+ Date.today.prev_day.to_s
43
+ end
44
+
23
45
  def timelog
24
46
  Timelog.load
25
47
  end
@@ -15,6 +15,14 @@ module Ptimelog
15
15
  def timelog_txt
16
16
  Pathname.new(Configuration.instance[:timelog]).expand_path
17
17
  end
18
+
19
+ def previous_entry
20
+ lines = timelog_txt.readlines.last(2)
21
+ last_line = lines.map(&:chomp).delete_if(&:empty?).last
22
+ last_entry = instance.tokenize(last_line)
23
+
24
+ Entry.from_timelog(last_entry)
25
+ end
18
26
  end
19
27
 
20
28
  def load
@@ -25,6 +33,10 @@ module Ptimelog
25
33
  self.class.timelog_txt
26
34
  end
27
35
 
36
+ def previous_entry
37
+ self.class.previous_entry
38
+ end
39
+
28
40
  def read
29
41
  timelog_txt.read
30
42
  end
@@ -16,5 +16,5 @@
16
16
  # with an improvement. Keep in mind that this is also covered by rspec so I
17
17
  # expect (pun intended) 100% test-coverage for any additional code.
18
18
  module Ptimelog
19
- VERSION = '0.9.0'
19
+ VERSION = '0.10.0'
20
20
  end
@@ -22,6 +22,8 @@ Gem::Specification.new do |spec|
22
22
  spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
23
23
  spec.require_paths = ['lib']
24
24
 
25
+ spec.required_ruby_version = '>= 2.7.0'
26
+
25
27
  spec.add_dependency 'naught'
26
28
 
27
29
  spec.add_development_dependency 'bundler'
@@ -30,5 +32,9 @@ Gem::Specification.new do |spec|
30
32
  spec.add_development_dependency 'rake', '>= 12.3.3'
31
33
  spec.add_development_dependency 'rspec', '~> 3.0'
32
34
  spec.add_development_dependency 'rubocop', '~> 0.50'
35
+ # spec.add_development_dependency 'rubocop-rake'
36
+ # spec.add_development_dependency 'rubocop-rspec'
37
+ # spec.add_development_dependency 'rubocop-packaging'
38
+ # spec.add_development_dependency 'rubocop-performance'
33
39
  spec.add_development_dependency 'timecop', '~> 0.9'
34
40
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ptimelog
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.9.0
4
+ version: 0.10.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Matthias Viehweger
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2020-05-08 00:00:00.000000000 Z
11
+ date: 2021-01-27 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: naught
@@ -136,8 +136,8 @@ files:
136
136
  - ".rspec"
137
137
  - ".rubocop.yml"
138
138
  - ".rubocop_todo.yml"
139
- - ".ruby-gemset"
140
139
  - ".ruby-version"
140
+ - ".tool-versions"
141
141
  - ".travis.yml"
142
142
  - Gemfile
143
143
  - LICENSE.txt
@@ -176,14 +176,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
176
176
  requirements:
177
177
  - - ">="
178
178
  - !ruby/object:Gem::Version
179
- version: '0'
179
+ version: 2.7.0
180
180
  required_rubygems_version: !ruby/object:Gem::Requirement
181
181
  requirements:
182
182
  - - ">="
183
183
  - !ruby/object:Gem::Version
184
184
  version: '0'
185
185
  requirements: []
186
- rubygems_version: 3.1.2
186
+ rubygems_version: 3.1.4
187
187
  signing_key:
188
188
  specification_version: 4
189
189
  summary: Move time-entries from gTimelog to PuzzleTime
@@ -1 +0,0 @@
1
- gpuzzletime