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