ptimelog 0.9.0 → 0.10.0

Sign up to get free protection for your applications and to get access to all the features.
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