gpuzzletime 0.3.2 → 0.4.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 +4 -4
- data/.rubocop.yml +6 -4
- data/.rubocop_todo.yml +25 -0
- data/README.md +11 -3
- data/Rakefile +3 -1
- data/lib/gpuzzletime.rb +1 -0
- data/lib/gpuzzletime/app.rb +44 -39
- data/lib/gpuzzletime/timelog.rb +48 -0
- data/lib/gpuzzletime/version.rb +1 -1
- metadata +4 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: da04f6e2158de538ed579e152023e1577f4743eaf869148398cad63cc4602e7e
|
4
|
+
data.tar.gz: 1cbf496d462a395a0466e7fe0d94ef54ef3bd59c64caccfa7c3a77e5ca013fe7
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: e35352a95c6bc0bda0ce852f7b95c9681ca508d094239b754d5081fdb625c0d8e0eafd50983206b70ef2fbd7656164f3965d4d730612eec786c6dd1fe580ae4d
|
7
|
+
data.tar.gz: 8ca1ea92a1036ce3f9a791d2d565728dcc7ecd2afe510cb04f60579e57d0ce3eaf78d7e5c7fb9f3208ec58cbac01c8117e5b3361befc8b4c2b8599869d14d860
|
data/.rubocop.yml
CHANGED
@@ -1,6 +1,12 @@
|
|
1
|
+
inherit_from: .rubocop_todo.yml
|
2
|
+
|
1
3
|
Metrics/LineLength:
|
2
4
|
Max: 120
|
3
5
|
|
6
|
+
Metrics/BlockLength:
|
7
|
+
Exclude:
|
8
|
+
- spec/**/*
|
9
|
+
|
4
10
|
Style/TrailingCommaInArrayLiteral:
|
5
11
|
EnforcedStyleForMultiline: consistent_comma
|
6
12
|
|
@@ -11,7 +17,3 @@ Layout/AlignHash:
|
|
11
17
|
EnforcedHashRocketStyle: table
|
12
18
|
EnforcedColonStyle: table
|
13
19
|
EnforcedLastArgumentHashStyle: always_inspect # default
|
14
|
-
|
15
|
-
Metrics/BlockLength:
|
16
|
-
Exclude:
|
17
|
-
- spec/**/*
|
data/.rubocop_todo.yml
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
# This configuration was generated by
|
2
|
+
# `rubocop --auto-gen-config`
|
3
|
+
# on 2019-06-11 08:46:25 +0200 using RuboCop version 0.65.0.
|
4
|
+
# The point is for the user to remove these configuration records
|
5
|
+
# one by one as the offenses are removed from the code base.
|
6
|
+
# Note that changes in the inspected code, or installation of new
|
7
|
+
# versions of RuboCop, may require this file to be generated again.
|
8
|
+
|
9
|
+
# Offense count: 1
|
10
|
+
Metrics/AbcSize:
|
11
|
+
Max: 28
|
12
|
+
|
13
|
+
# Offense count: 1
|
14
|
+
# Configuration parameters: CountComments.
|
15
|
+
Metrics/ClassLength:
|
16
|
+
Max: 153
|
17
|
+
|
18
|
+
# Offense count: 1
|
19
|
+
Metrics/CyclomaticComplexity:
|
20
|
+
Max: 8
|
21
|
+
|
22
|
+
# Offense count: 3
|
23
|
+
# Configuration parameters: CountComments, ExcludedMethods.
|
24
|
+
Metrics/MethodLength:
|
25
|
+
Max: 27
|
data/README.md
CHANGED
@@ -25,7 +25,7 @@ small tooling to transfer timelog-entries from gtimelog's timelog.txt to the Puz
|
|
25
25
|
- [ ] open day in browser for review
|
26
26
|
- [ ] avoid duplicate entries
|
27
27
|
- [ ] start/end time as indicator?
|
28
|
-
- [
|
28
|
+
- [x] offer rounding times to the next 5, 10 or 15 minutes
|
29
29
|
- [ ] allow to add entries from the command-line
|
30
30
|
- [ ] handle time-account and billable better
|
31
31
|
- [ ] import time-accounts from ptime (https://time.puzzle.ch/work_items/search.json?q=search%20terms)
|
@@ -83,19 +83,27 @@ Otherwise, a script to determine the time-account is loaded.
|
|
83
83
|
gpuzzletime can prefill the account-number and billable-state of an entry.
|
84
84
|
|
85
85
|
The tags are used to determine a script that helps infer the time-account.
|
86
|
-
These scripts should be located in
|
86
|
+
These scripts should be located in `~/.config/gpuzzletime/parsers/` and be named
|
87
87
|
like the first tag used. The script gets the ticket, the description and all
|
88
88
|
remaining tags passed as arguments. The output of the script should only be the
|
89
89
|
ID of the time-account.
|
90
90
|
|
91
91
|
In order to infer the billable-state of an entry, a script
|
92
|
-
|
92
|
+
`~/.config/gpuzzletime/billable` is called. It only gets the previously infered
|
93
93
|
account-id as argument and is expected to output "true" or "false".
|
94
94
|
|
95
95
|
Since these script are called a lot, it is better to write them in a compiled
|
96
96
|
language. If you only like ruby, take a look at crystal. For such simple
|
97
97
|
scripts, the code is almost identical and "just" needs to be compiled.
|
98
98
|
|
99
|
+
## Configuration
|
100
|
+
|
101
|
+
A config-file is read from `$HOME/.config/gpuzzletime/config`. It is expected
|
102
|
+
to be a YAML-file. Currently, it supports the following keys:
|
103
|
+
|
104
|
+
- rounding: [integer or false, default 15]
|
105
|
+
- base_url: [url to your puzzletime-installation, default https://time.puzzle.ch]
|
106
|
+
|
99
107
|
## Development
|
100
108
|
|
101
109
|
After checking out the repo, run `bin/setup` to install dependencies. Then, run
|
data/Rakefile
CHANGED
data/lib/gpuzzletime.rb
CHANGED
data/lib/gpuzzletime/app.rb
CHANGED
@@ -7,9 +7,14 @@ require 'pathname'
|
|
7
7
|
module Gpuzzletime
|
8
8
|
# Wrapper for everything
|
9
9
|
class App
|
10
|
-
|
11
|
-
|
10
|
+
CONFIGURATION_DEFAULTS = {
|
11
|
+
base_url: 'https://time.puzzle.ch',
|
12
|
+
rounding: 15,
|
13
|
+
dir: Pathname.new('~/.config/gpuzzletime').expand_path,
|
14
|
+
}.freeze
|
12
15
|
|
16
|
+
def initialize(args)
|
17
|
+
@config = load_config(CONFIGURATION_DEFAULTS[:dir].join('config'))
|
13
18
|
@command = (args[0] || :show).to_sym
|
14
19
|
|
15
20
|
case @command
|
@@ -48,22 +53,32 @@ module Gpuzzletime
|
|
48
53
|
|
49
54
|
private
|
50
55
|
|
56
|
+
def load_config(config_fn)
|
57
|
+
user_config = config_fn.exist? ? YAML.load_file(config_fn) : {}
|
58
|
+
|
59
|
+
CONFIGURATION_DEFAULTS.merge(user_config)
|
60
|
+
end
|
61
|
+
|
51
62
|
def entries
|
52
63
|
@entries ||= {}
|
53
64
|
end
|
54
65
|
|
66
|
+
def timelog
|
67
|
+
Timelog.load
|
68
|
+
end
|
69
|
+
|
55
70
|
def fill_entries(purpose)
|
56
|
-
|
71
|
+
timelog.each do |date, lines|
|
57
72
|
# this is mixing preparation, assembly and output, but gets the job done
|
58
73
|
next unless date # guard against the machine
|
59
74
|
next unless @date == :all || @date == date # limit to one day if passed
|
60
75
|
|
61
76
|
entries[date] = []
|
62
77
|
|
63
|
-
start = nil
|
78
|
+
start = nil # at the start of the day, we have no previous end
|
64
79
|
|
65
80
|
lines.each do |entry|
|
66
|
-
finish = entry[:time] # we use that twice
|
81
|
+
finish = round_time(entry[:time], @config[:rounding]) # we use that twice
|
67
82
|
hidden = entry[:description].match(/\*\*$/) # hide lunch and breaks
|
68
83
|
|
69
84
|
if start && !hidden
|
@@ -88,12 +103,25 @@ module Gpuzzletime
|
|
88
103
|
end
|
89
104
|
end
|
90
105
|
|
106
|
+
def round_time(time, interval)
|
107
|
+
return time unless interval
|
108
|
+
|
109
|
+
hour, minute = time.split(':')
|
110
|
+
minute = (minute.to_i / interval.to_f).round * interval.to_i
|
111
|
+
|
112
|
+
if minute == 60
|
113
|
+
[hour.succ, 0]
|
114
|
+
else
|
115
|
+
[hour, minute]
|
116
|
+
end.map { |part| part.to_s.rjust(2, '0') }.join(':')
|
117
|
+
end
|
118
|
+
|
91
119
|
def open_browser(start, entry)
|
92
|
-
xdg_open "'#{@base_url}/ordertimes/new?#{url_options(start, entry)}'", silent: true
|
120
|
+
xdg_open "'#{@config[:base_url]}/ordertimes/new?#{url_options(start, entry)}'", silent: true
|
93
121
|
end
|
94
122
|
|
95
123
|
def xdg_open(args, silent: false)
|
96
|
-
opener = 'xdg-open'
|
124
|
+
opener = 'xdg-open' # could be configurable, but is already a proxy
|
97
125
|
silencer = '> /dev/null 2> /dev/null'
|
98
126
|
|
99
127
|
if system("which #{opener} #{silencer}")
|
@@ -104,6 +132,9 @@ module Gpuzzletime
|
|
104
132
|
|
105
133
|
This binary is needed to launch a webbrowser and open the page
|
106
134
|
to enter the worktime-entry into puzzletime.
|
135
|
+
|
136
|
+
If this needs to be configurable, please open an issue at
|
137
|
+
https://github.com/kronn/gpuzzletime/issues/new
|
107
138
|
ERRORMESSAGE
|
108
139
|
end
|
109
140
|
end
|
@@ -111,7 +142,7 @@ module Gpuzzletime
|
|
111
142
|
def launch_editor
|
112
143
|
editor = `which $EDITOR`.chomp
|
113
144
|
|
114
|
-
file = @file.nil? ? timelog_txt : parser_file(@file)
|
145
|
+
file = @file.nil? ? Timelog.timelog_txt : parser_file(@file)
|
115
146
|
|
116
147
|
exec "#{editor} #{file}"
|
117
148
|
end
|
@@ -123,7 +154,7 @@ module Gpuzzletime
|
|
123
154
|
'ordertime[ticket]': entry[:ticket],
|
124
155
|
'ordertime[description]': entry[:description],
|
125
156
|
'ordertime[from_start_time]': start,
|
126
|
-
'ordertime[to_end_time]': entry[:time],
|
157
|
+
'ordertime[to_end_time]': round_time(entry[:time], @config[:rounding]),
|
127
158
|
'ordertime[account_id]': account,
|
128
159
|
'ordertime[billable]': infer_billable(account),
|
129
160
|
}
|
@@ -135,40 +166,14 @@ module Gpuzzletime
|
|
135
166
|
case date
|
136
167
|
when 'yesterday' then Date.today.prev_day.to_s
|
137
168
|
when 'today' then Date.today.to_s
|
138
|
-
when 'last' then
|
169
|
+
when 'last' then timelog.to_h.keys.compact.sort[-2] || Date.today.prev_day.to_s
|
139
170
|
when /\d{4}(-\d{2}){2}/ then date
|
140
171
|
end
|
141
172
|
end
|
142
173
|
|
143
|
-
def timelog_txt
|
144
|
-
Pathname.new('~/.local/share/gtimelog/timelog.txt').expand_path
|
145
|
-
end
|
146
|
-
|
147
|
-
def read
|
148
|
-
timelog_txt.read
|
149
|
-
end
|
150
|
-
|
151
|
-
def parse(data)
|
152
|
-
data.split("\n")
|
153
|
-
.map { |line| tokenize(line) }
|
154
|
-
.group_by { |match| match && match[:date] }
|
155
|
-
.to_a
|
156
|
-
end
|
157
|
-
|
158
|
-
def tokenize(line)
|
159
|
-
re_date = /(?<date>\d{4}-\d{2}-\d{2})/
|
160
|
-
re_time = /(?<time>\d{2}:\d{2})/
|
161
|
-
re_tick = /(?:(?<ticket>.*?): )/
|
162
|
-
re_desc = /(?<description>.*?)/
|
163
|
-
re_tags = /(?: -- (?<tags>.*)?)/
|
164
|
-
|
165
|
-
regexp = /^#{re_date} #{re_time}: #{re_tick}?#{re_desc}#{re_tags}?$/
|
166
|
-
line.match(regexp)
|
167
|
-
end
|
168
|
-
|
169
174
|
def parser_file(parser_name)
|
170
|
-
|
171
|
-
|
175
|
+
@config[:dir].join("parsers/#{parser_name}") # FIXME: security-hole, prevent relative paths!
|
176
|
+
.expand_path
|
172
177
|
end
|
173
178
|
|
174
179
|
def infer_account(entry)
|
@@ -186,7 +191,7 @@ module Gpuzzletime
|
|
186
191
|
end
|
187
192
|
|
188
193
|
def infer_billable(account)
|
189
|
-
script =
|
194
|
+
script = @config[:dir].join('billable')
|
190
195
|
|
191
196
|
return 1 unless script.exist?
|
192
197
|
|
@@ -0,0 +1,48 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'pathname'
|
4
|
+
|
5
|
+
module Gpuzzletime
|
6
|
+
# Load and tokenize the data from gtimelog
|
7
|
+
class Timelog
|
8
|
+
class << self
|
9
|
+
def load
|
10
|
+
new.load
|
11
|
+
end
|
12
|
+
|
13
|
+
def timelog_txt
|
14
|
+
Pathname.new('~/.local/share/gtimelog/timelog.txt').expand_path
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def load
|
19
|
+
parse(read)
|
20
|
+
end
|
21
|
+
|
22
|
+
def timelog_txt
|
23
|
+
self.class.timelog_txt
|
24
|
+
end
|
25
|
+
|
26
|
+
def read
|
27
|
+
timelog_txt.read
|
28
|
+
end
|
29
|
+
|
30
|
+
def parse(data)
|
31
|
+
data.split("\n")
|
32
|
+
.map { |line| tokenize(line) }
|
33
|
+
.group_by { |match| match && match[:date] }
|
34
|
+
.to_a
|
35
|
+
end
|
36
|
+
|
37
|
+
def tokenize(line)
|
38
|
+
re_date = /(?<date>\d{4}-\d{2}-\d{2})/
|
39
|
+
re_time = /(?<time>\d{2}:\d{2})/
|
40
|
+
re_tick = /(?:(?<ticket>.*?): )/
|
41
|
+
re_desc = /(?<description>.*?)/
|
42
|
+
re_tags = /(?: -- (?<tags>.*)?)/
|
43
|
+
|
44
|
+
regexp = /^#{re_date} #{re_time}: #{re_tick}?#{re_desc}#{re_tags}?$/
|
45
|
+
line.match(regexp)
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
data/lib/gpuzzletime/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: gpuzzletime
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.4.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: 2019-
|
11
|
+
date: 2019-07-09 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -120,6 +120,7 @@ files:
|
|
120
120
|
- ".overcommit.yml"
|
121
121
|
- ".rspec"
|
122
122
|
- ".rubocop.yml"
|
123
|
+
- ".rubocop_todo.yml"
|
123
124
|
- ".ruby-gemset"
|
124
125
|
- ".ruby-version"
|
125
126
|
- ".travis.yml"
|
@@ -133,6 +134,7 @@ files:
|
|
133
134
|
- gpuzzletime.gemspec
|
134
135
|
- lib/gpuzzletime.rb
|
135
136
|
- lib/gpuzzletime/app.rb
|
137
|
+
- lib/gpuzzletime/timelog.rb
|
136
138
|
- lib/gpuzzletime/version.rb
|
137
139
|
homepage: https://github.com/kronn/gpuzzletime
|
138
140
|
licenses:
|