gpuzzletime 0.4.3 → 0.5.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: 33cb7358466e5954e4207ca89ade24fa8f4ac6692df88049e1ed9dfd6dc4e4ba
4
- data.tar.gz: 6db80db69c4b1405a9b04462d2702bd105b4e597e64c68af347dd1e5cc173600
3
+ metadata.gz: e079956f32fec893e670321a6f454c4f15fdd4583b15ff276f963a07697dee15
4
+ data.tar.gz: 15fffcd7e114645e7e9462ca6966405760cfc735c11b5226a74126c8d7ac9161
5
5
  SHA512:
6
- metadata.gz: df09d81d902a96ec785169a3114677b8f398ca143a7e53be153498dbecec598d9fad2a512efd86dec577db945667720a313c9d53a83c12a4a4af3a3c57a4bb19
7
- data.tar.gz: 2ff1bc61d9ff6c404bbefa50fffabaac124115eb1a4da668f22d8b99c5c6bf2e117fff3b6a76f1f855a3e2b2b4da7bf8010ab98695bd9afca5ca64f8338adfaf
6
+ metadata.gz: d1ffd70676c71b9440d016547ca06f532bd80c0ee104547e262678e7eb190ed0c6cfe56794b57ec2b3defc8830ef4fdb47355b490ae2caa198595bb65037a3b1
7
+ data.tar.gz: 075a999e3b6ce106f7c0a6f0051dc62bfd8b7f5080271f6d117408aaf5a8c3259312118c9ca240cbf77ba7be96fbd48f827399158c57e4a30e5ec9e96ced1ddc
metadata CHANGED
@@ -1,149 +1,37 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: gpuzzletime
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.3
4
+ version: 0.5.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Matthias Viehweger
8
8
  autorequire:
9
- bindir: exe
9
+ bindir: bin
10
10
  cert_chain: []
11
- date: 2019-07-26 00:00:00.000000000 Z
11
+ date: 2019-08-07 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
- name: bundler
14
+ name: ptimelog
15
15
  requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
17
  - - ">="
18
18
  - !ruby/object:Gem::Version
19
19
  version: '0'
20
- type: :development
20
+ type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
24
  - - ">="
25
25
  - !ruby/object:Gem::Version
26
26
  version: '0'
27
- - !ruby/object:Gem::Dependency
28
- name: overcommit
29
- requirement: !ruby/object:Gem::Requirement
30
- requirements:
31
- - - "~>"
32
- - !ruby/object:Gem::Version
33
- version: '0.45'
34
- type: :development
35
- prerelease: false
36
- version_requirements: !ruby/object:Gem::Requirement
37
- requirements:
38
- - - "~>"
39
- - !ruby/object:Gem::Version
40
- version: '0.45'
41
- - !ruby/object:Gem::Dependency
42
- name: pry
43
- requirement: !ruby/object:Gem::Requirement
44
- requirements:
45
- - - "~>"
46
- - !ruby/object:Gem::Version
47
- version: '0.12'
48
- type: :development
49
- prerelease: false
50
- version_requirements: !ruby/object:Gem::Requirement
51
- requirements:
52
- - - "~>"
53
- - !ruby/object:Gem::Version
54
- version: '0.12'
55
- - !ruby/object:Gem::Dependency
56
- name: rake
57
- requirement: !ruby/object:Gem::Requirement
58
- requirements:
59
- - - "~>"
60
- - !ruby/object:Gem::Version
61
- version: '10.0'
62
- type: :development
63
- prerelease: false
64
- version_requirements: !ruby/object:Gem::Requirement
65
- requirements:
66
- - - "~>"
67
- - !ruby/object:Gem::Version
68
- version: '10.0'
69
- - !ruby/object:Gem::Dependency
70
- name: rspec
71
- requirement: !ruby/object:Gem::Requirement
72
- requirements:
73
- - - "~>"
74
- - !ruby/object:Gem::Version
75
- version: '3.0'
76
- type: :development
77
- prerelease: false
78
- version_requirements: !ruby/object:Gem::Requirement
79
- requirements:
80
- - - "~>"
81
- - !ruby/object:Gem::Version
82
- version: '3.0'
83
- - !ruby/object:Gem::Dependency
84
- name: rubocop
85
- requirement: !ruby/object:Gem::Requirement
86
- requirements:
87
- - - "~>"
88
- - !ruby/object:Gem::Version
89
- version: '0.50'
90
- type: :development
91
- prerelease: false
92
- version_requirements: !ruby/object:Gem::Requirement
93
- requirements:
94
- - - "~>"
95
- - !ruby/object:Gem::Version
96
- version: '0.50'
97
- - !ruby/object:Gem::Dependency
98
- name: timecop
99
- requirement: !ruby/object:Gem::Requirement
100
- requirements:
101
- - - "~>"
102
- - !ruby/object:Gem::Version
103
- version: '0.9'
104
- type: :development
105
- prerelease: false
106
- version_requirements: !ruby/object:Gem::Requirement
107
- requirements:
108
- - - "~>"
109
- - !ruby/object:Gem::Version
110
- version: '0.9'
111
27
  description:
112
28
  email:
113
29
  - kronn@kronn.de
114
- executables:
115
- - gpuzzletime
30
+ executables: []
116
31
  extensions: []
117
32
  extra_rdoc_files: []
118
- files:
119
- - ".gitignore"
120
- - ".overcommit.yml"
121
- - ".rspec"
122
- - ".rubocop.yml"
123
- - ".rubocop_todo.yml"
124
- - ".ruby-gemset"
125
- - ".ruby-version"
126
- - ".travis.yml"
127
- - Gemfile
128
- - LICENSE.txt
129
- - README.md
130
- - Rakefile
131
- - bin/console
132
- - bin/setup
133
- - exe/gpuzzletime
134
- - gpuzzletime.gemspec
135
- - lib/gpuzzletime.rb
136
- - lib/gpuzzletime/app.rb
137
- - lib/gpuzzletime/command/edit.rb
138
- - lib/gpuzzletime/command/show.rb
139
- - lib/gpuzzletime/command/upload.rb
140
- - lib/gpuzzletime/configuration.rb
141
- - lib/gpuzzletime/entry.rb
142
- - lib/gpuzzletime/named_date.rb
143
- - lib/gpuzzletime/script.rb
144
- - lib/gpuzzletime/timelog.rb
145
- - lib/gpuzzletime/version.rb
146
- homepage: https://github.com/kronn/gpuzzletime
33
+ files: []
34
+ homepage: https://github.com/kronn/ptimelog
147
35
  licenses:
148
36
  - MIT
149
37
  metadata: {}
@@ -165,5 +53,5 @@ requirements: []
165
53
  rubygems_version: 3.0.3
166
54
  signing_key:
167
55
  specification_version: 4
168
- summary: Move time-entries from gTimelog to PuzzleTime
56
+ summary: The gem formerly known as gpuzzletime is now ptimelog
169
57
  test_files: []
data/.gitignore DELETED
@@ -1,9 +0,0 @@
1
- /.bundle/
2
- /.yardoc
3
- /Gemfile.lock
4
- /_yardoc/
5
- /coverage/
6
- /doc/
7
- /pkg/
8
- /spec/reports/
9
- /tmp/
data/.overcommit.yml DELETED
@@ -1,26 +0,0 @@
1
- # Use this file to configure the Overcommit hooks you wish to use. This will
2
- # extend the default configuration defined in:
3
- # https://github.com/brigade/overcommit/blob/master/config/default.yml
4
- #
5
- # For a complete list of hooks, see:
6
- # https://github.com/brigade/overcommit/tree/master/lib/overcommit/hook
7
- #
8
- # For a complete list of options that you can use to customize hooks, see:
9
- # https://github.com/brigade/overcommit#configuration
10
-
11
- PreCommit:
12
- RuboCop:
13
- enabled: true
14
- on_warn: fail # Treat all warnings as failures
15
-
16
- TrailingWhitespace:
17
- enabled: true
18
- # exclude:
19
- # - '**/db/structure.sql' # Ignore trailing whitespace in generated files
20
-
21
- #PostCheckout:
22
- # ALL: # Special hook name that customizes all hooks of this type
23
- # quiet: true # Change all post-checkout hooks to only display output on failure
24
- #
25
- # IndexTags:
26
- # enabled: true # Generate a tags file with `ctags` each time HEAD changes
data/.rspec DELETED
@@ -1,2 +0,0 @@
1
- --format documentation
2
- --color
data/.rubocop.yml DELETED
@@ -1,33 +0,0 @@
1
- inherit_from: .rubocop_todo.yml
2
-
3
- Metrics/LineLength:
4
- Max: 120
5
-
6
- Metrics/BlockLength:
7
- Exclude:
8
- - spec/**/*
9
-
10
- Style/TrailingCommaInArrayLiteral:
11
- EnforcedStyleForMultiline: consistent_comma
12
-
13
- Style/TrailingCommaInHashLiteral:
14
- EnforcedStyleForMultiline: consistent_comma
15
-
16
- Layout/AlignHash:
17
- EnforcedHashRocketStyle: table
18
- EnforcedColonStyle: table
19
- EnforcedLastArgumentHashStyle: always_inspect # default
20
-
21
- Naming/UncommunicativeMethodParamName:
22
- AllowedNames:
23
- - fn
24
- # default allowed names
25
- - io
26
- - id
27
- - to
28
- - by
29
- - on
30
- - in
31
- - at
32
- - ip
33
- - db
data/.rubocop_todo.yml DELETED
@@ -1,25 +0,0 @@
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/.ruby-gemset DELETED
@@ -1 +0,0 @@
1
- gpuzzletime
data/.ruby-version DELETED
@@ -1 +0,0 @@
1
- 2.6.3
data/.travis.yml DELETED
@@ -1,7 +0,0 @@
1
- language: ruby
2
- rvm:
3
- - 2.3.1
4
- - 2.4.2
5
- - 2.5.3
6
- - 2.6.1
7
- before_install: gem install bundler
data/Gemfile DELETED
@@ -1,6 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- source 'https://rubygems.org'
4
-
5
- # Specify your gem's dependencies in gpuzzletime.gemspec
6
- gemspec
data/LICENSE.txt DELETED
@@ -1,21 +0,0 @@
1
- The MIT License (MIT)
2
-
3
- Copyright (c) 2017-2018 Matthias Viehweger
4
-
5
- Permission is hereby granted, free of charge, to any person obtaining a copy
6
- of this software and associated documentation files (the "Software"), to deal
7
- in the Software without restriction, including without limitation the rights
8
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
- copies of the Software, and to permit persons to whom the Software is
10
- furnished to do so, subject to the following conditions:
11
-
12
- The above copyright notice and this permission notice shall be included in
13
- all copies or substantial portions of the Software.
14
-
15
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
- THE SOFTWARE.
data/README.md DELETED
@@ -1,128 +0,0 @@
1
- # gPuzzleTime
2
-
3
- small tooling to transfer timelog-entries from gtimelog's timelog.txt to the PuzzleTime TimeTracking Website.
4
-
5
- ## Approach
6
-
7
- - [x] read timelog.txt
8
- - [x] from known location
9
- - [x] later: configure location?
10
- - [ ] later: auto-detect location?
11
- - [x] parse out last day
12
- - [x] especially start/end-times for each entry
13
- - [x] date - ticket - description
14
- - [x] later: parse out specific day
15
- - [x] open N browser instances with the time entry data
16
- - [x] without selected account
17
- - [x] infer time-account from ticket-format
18
- - [x] support user-supplied ticket-parsers
19
- - [x] get ticket-parser from tags
20
- - [ ] merge equal adjacent entries into one
21
- - [ ] complete login/entry automation
22
- - [ ] handle authentication
23
- - [ ] login and store cookie
24
- - [ ] send user and pwd with every request
25
- - [ ] make entries
26
- - [ ] open day in browser for review
27
- - [ ] avoid duplicate entries
28
- - [ ] start/end time as indicator?
29
- - [x] offer rounding times to the next 5, 10 or 15 minutes
30
- - [ ] allow to add entries from the command-line
31
- - [ ] handle time-account and billable better
32
- - [ ] import time-accounts from ptime (https://time.puzzle.ch/work_items/search.json?q=search%20terms)
33
- - [ ] with a dedicated cli?
34
- - [ ] from a REST-Endpoint of PTime?
35
- - [ ] automatically prefill billable
36
- - [x] from time-accounts
37
- - [ ] from *-notation
38
- - [ ] allow to have a list of "favourite" time-accounts
39
- - [ ] select best-matching time-account according to tags, possibly limited to the favourites
40
- - [ ] add cli-help
41
- - [ ] use commander for CLI?
42
-
43
- ## Installation
44
-
45
- Install it with:
46
-
47
- $ gem install gpuzzletime
48
-
49
- ## Usage
50
-
51
- $ gpuzzletime ACTION DATE
52
-
53
- ### Actions
54
-
55
- Currently supported actions are
56
-
57
- - show
58
- - upload
59
- - edit
60
-
61
- ### Date-Identifier
62
-
63
- 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.
64
-
65
- For reusability in a shell-history the following keywords are supported:
66
-
67
- - today
68
- - yesterday
69
- - last
70
- - all
71
-
72
- If nothing is specified, the action is applied to entries of the last day.
73
-
74
- ### Edit-Identifier
75
-
76
- When the action is "edit", the next argument is treated as script that should be edited.
77
-
78
- If nothing is passed, the main timelog.txt is loaded.
79
-
80
- Otherwise, a script to determine the time-account is loaded.
81
-
82
- ## Helper-Scripts
83
-
84
- gpuzzletime can prefill the account-number and billable-state of an entry.
85
-
86
- The tags are used to determine a script that helps infer the time-account.
87
- These scripts should be located in `~/.config/gpuzzletime/parsers/` and be named
88
- like the first tag used. The script gets the ticket, the description and all
89
- remaining tags passed as arguments. The output of the script should only be the
90
- ID of the time-account.
91
-
92
- In order to infer the billable-state of an entry, a script
93
- `~/.config/gpuzzletime/billable` is called. It only gets the previously infered
94
- account-id as argument and is expected to output "true" or "false".
95
-
96
- Since these script are called a lot, it is better to write them in a compiled
97
- language. If you only like ruby, take a look at crystal. For such simple
98
- scripts, the code is almost identical and "just" needs to be compiled.
99
-
100
- ## Configuration
101
-
102
- A config-file is read from `$HOME/.config/gpuzzletime/config`. It is expected
103
- to be a YAML-file. Currently, it supports the following keys:
104
-
105
- - rounding: [integer or false, default 15]
106
- - base_url: [url to your puzzletime-installation, default https://time.puzzle.ch]
107
-
108
- ## Development
109
-
110
- After checking out the repo, run `bin/setup` to install dependencies. Then, run
111
- `rake spec` to run the tests. You can also run `bin/console` for an interactive
112
- prompt that will allow you to experiment. Run `bundle exec gpuzzletime` to use
113
- the gem in this directory, ignoring other installed copies of this gem.
114
-
115
- To install this gem onto your local machine, run `bundle exec rake install`. To
116
- release a new version, update the version number in `version.rb`, and then run
117
- `bundle exec rake release`, which will create a git tag for the version, push
118
- git commits and tags, and push the `.gem` file to
119
- [rubygems.org](https://rubygems.org).
120
-
121
- ## Contributing
122
-
123
- Bug reports and pull requests are welcome on GitHub at https://github.com/kronn/gpuzzletime.
124
-
125
-
126
- ## License
127
-
128
- The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
data/Rakefile DELETED
@@ -1,10 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'bundler/gem_tasks'
4
- require 'rspec/core/rake_task'
5
- require 'rubocop/rake_task'
6
-
7
- RSpec::Core::RakeTask.new(:spec)
8
- RuboCop::RakeTask.new
9
-
10
- task default: %i[rubocop spec]
data/bin/console DELETED
@@ -1,15 +0,0 @@
1
- #!/usr/bin/env ruby
2
- # frozen_string_literal: true
3
-
4
- require 'bundler/setup'
5
- require 'gpuzzletime'
6
-
7
- # You can add fixtures and/or initialization code here to make experimenting
8
- # with your gem easier. You can also use a different console, if you like.
9
-
10
- # (If you use this, don't forget to add pry to your Gemfile!)
11
- # require "pry"
12
- # Pry.start
13
-
14
- require 'irb'
15
- IRB.start
data/bin/setup DELETED
@@ -1,8 +0,0 @@
1
- #!/usr/bin/env bash
2
- set -euo pipefail
3
- IFS=$'\n\t'
4
- set -vx
5
-
6
- bundle install
7
-
8
- # Do any other automated setup that you need to do here
data/exe/gpuzzletime DELETED
@@ -1,6 +0,0 @@
1
- #!/usr/bin/env ruby
2
- # frozen_string_literal: true
3
-
4
- require_relative '../lib/gpuzzletime'
5
-
6
- Gpuzzletime::App.new(ARGV).run
data/gpuzzletime.gemspec DELETED
@@ -1,30 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- lib = File.expand_path('lib', __dir__)
4
- $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
5
- require 'gpuzzletime/version'
6
-
7
- Gem::Specification.new do |spec|
8
- spec.name = 'gpuzzletime'
9
- spec.version = Gpuzzletime::VERSION
10
- spec.authors = ['Matthias Viehweger']
11
- spec.email = ['kronn@kronn.de']
12
-
13
- spec.summary = 'Move time-entries from gTimelog to PuzzleTime'
14
- # spec.description = %q{}
15
- spec.homepage = 'https://github.com/kronn/gpuzzletime'
16
- spec.license = 'MIT'
17
-
18
- spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
19
- spec.bindir = 'exe'
20
- spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
21
- spec.require_paths = ['lib']
22
-
23
- spec.add_development_dependency 'bundler'
24
- spec.add_development_dependency 'overcommit', '~> 0.45'
25
- spec.add_development_dependency 'pry', '~> 0.12'
26
- spec.add_development_dependency 'rake', '~> 10.0'
27
- spec.add_development_dependency 'rspec', '~> 3.0'
28
- spec.add_development_dependency 'rubocop', '~> 0.50'
29
- spec.add_development_dependency 'timecop', '~> 0.9'
30
- end
data/lib/gpuzzletime.rb DELETED
@@ -1,21 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- $LOAD_PATH.unshift File.dirname(__FILE__)
4
-
5
- # Autoloading and such
6
- module Gpuzzletime
7
- autoload :App, 'gpuzzletime/app'
8
- autoload :Configuration, 'gpuzzletime/configuration'
9
- autoload :Entry, 'gpuzzletime/entry'
10
- autoload :NamedDate, 'gpuzzletime/named_date'
11
- autoload :Script, 'gpuzzletime/script'
12
- autoload :Timelog, 'gpuzzletime/timelog'
13
- autoload :VERSION, 'gpuzzletime/version'
14
-
15
- # Collection of commands available at the CLI
16
- module Command
17
- autoload :Edit, 'gpuzzletime/command/edit'
18
- autoload :Show, 'gpuzzletime/command/show'
19
- autoload :Upload, 'gpuzzletime/command/upload'
20
- end
21
- end
@@ -1,66 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'date'
4
- require 'erb'
5
- require 'pathname'
6
-
7
- module Gpuzzletime
8
- # Wrapper for everything
9
- class App
10
- def initialize(args)
11
- @config = Configuration.instance
12
- command = (args[0] || :show).to_sym
13
-
14
- @command = case command
15
- when :show
16
- @date = NamedDate.new.date(args[1])
17
- Gpuzzletime::Command::Show.new(@config)
18
- when :upload
19
- @date = NamedDate.new.date(args[1])
20
- Gpuzzletime::Command::Upload.new(@config)
21
- when :edit
22
- Gpuzzletime::Command::Edit.new(@config, args[1])
23
- else
24
- raise ArgumentError, "Unsupported Command #{@command}"
25
- end
26
- end
27
-
28
- def run
29
- if @command.needs_entries?
30
- fill_entries
31
- @command.entries = entries
32
- end
33
-
34
- @command.run
35
- end
36
-
37
- private
38
-
39
- def entries
40
- @entries ||= {}
41
- end
42
-
43
- def timelog
44
- Timelog.load
45
- end
46
-
47
- def fill_entries
48
- timelog.each do |date, lines|
49
- next unless date # guard against the machine
50
- next unless @date == :all || @date == date # limit to one day if passed
51
-
52
- entries[date] = []
53
- start = nil # at the start of the day, we have no previous end
54
-
55
- lines.each do |line|
56
- entry = Entry.from_timelog(line)
57
- entry.start_time = start
58
-
59
- entries[date] << entry if entry.valid?
60
-
61
- start = entry.finish_time # store previous ending for nice display of next entry
62
- end
63
- end
64
- end
65
- end
66
- end
@@ -1,33 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Gpuzzletime
4
- module Command
5
- # edit one file. without argument, it will edit the timelog, otherwise a
6
- # parser-script is loaded
7
- class Edit
8
- def initialize(config, file)
9
- @config = config
10
- @script = Script.new(@config[:dir])
11
- @file = file
12
- end
13
-
14
- def needs_entries?
15
- false
16
- end
17
-
18
- def run
19
- launch_editor(@file)
20
- end
21
-
22
- private
23
-
24
- def launch_editor(file)
25
- editor = `which $EDITOR`.chomp
26
-
27
- file = file.nil? ? Timelog.timelog_txt : @script.parser(@file)
28
-
29
- exec "#{editor} #{file}"
30
- end
31
- end
32
- end
33
- end
@@ -1,45 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Gpuzzletime
4
- module Command
5
- # show entries of one day or all of them
6
- class Show
7
- def initialize(config)
8
- @config = config
9
- @entries = {}
10
- end
11
-
12
- def needs_entries?
13
- true
14
- end
15
-
16
- def run
17
- @entries.each do |date, list|
18
- puts date, '----------'
19
- list.each do |entry|
20
- puts entry
21
- end
22
- puts nil
23
- end
24
- end
25
-
26
- def entries=(entries)
27
- entries.each do |date, list|
28
- @entries[date] = []
29
-
30
- list.each do |entry|
31
- @entries[date] << [
32
- entry.start_time, '-', entry.finish_time,
33
- [
34
- entry.ticket,
35
- entry.description,
36
- entry.tags,
37
- entry.account,
38
- ].compact.join(' ∴ '),
39
- ].compact.join(' ')
40
- end
41
- end
42
- end
43
- end
44
- end
45
- end
@@ -1,67 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Gpuzzletime
4
- module Command
5
- # Upload entries to puzzletime
6
- class Upload
7
- attr_writer :entries
8
-
9
- def initialize(config)
10
- @config = config
11
- @entries = {}
12
- end
13
-
14
- def needs_entries?
15
- true
16
- end
17
-
18
- def run
19
- @entries.each do |date, list|
20
- puts "Uploading #{date}"
21
- list.each do |entry|
22
- open_browser(entry)
23
- end
24
- end
25
- end
26
-
27
- private
28
-
29
- def open_browser(entry)
30
- xdg_open "'#{@config[:base_url]}/ordertimes/new?#{url_options(entry)}'", silent: true
31
- end
32
-
33
- def xdg_open(args, silent: false)
34
- opener = 'xdg-open' # could be configurable, but is already a proxy
35
- silencer = '> /dev/null 2> /dev/null'
36
-
37
- if system("which #{opener} #{silencer}")
38
- system "#{opener} #{args} #{silencer if silent}"
39
- else
40
- abort <<~ERRORMESSAGE
41
- #{opener} not found
42
-
43
- This binary is needed to launch a webbrowser and open the page
44
- to enter the worktime-entry into puzzletime.
45
-
46
- If this needs to be configurable, please open an issue at
47
- https://github.com/kronn/gpuzzletime/issues/new
48
- ERRORMESSAGE
49
- end
50
- end
51
-
52
- def url_options(entry)
53
- {
54
- work_date: entry.date,
55
- 'ordertime[ticket]': entry.ticket,
56
- 'ordertime[description]': entry.description,
57
- 'ordertime[from_start_time]': entry.start_time,
58
- 'ordertime[to_end_time]': entry.finish_time,
59
- 'ordertime[account_id]': entry.account,
60
- 'ordertime[billable]': entry.billable,
61
- }
62
- .map { |key, value| [key, ERB::Util.url_encode(value)].join('=') }
63
- .join('&')
64
- end
65
- end
66
- end
67
- end
@@ -1,54 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'singleton'
4
-
5
- module Gpuzzletime
6
- # Wrapper around configuration-options and -loading
7
- class Configuration
8
- include Singleton
9
-
10
- CONFIGURATION_DEFAULTS = {
11
- base_url: 'https://time.puzzle.ch',
12
- rounding: 15,
13
- dir: '~/.config/gpuzzletime',
14
- timelog: '~/.local/share/gtimelog/timelog.txt',
15
- }.freeze
16
-
17
- def initialize
18
- reset
19
- end
20
-
21
- def reset
22
- @config = load_config(
23
- Pathname.new(CONFIGURATION_DEFAULTS[:dir]).join('config')
24
- )
25
- wrap_with_pathname(:dir)
26
- wrap_with_pathname(:timelog)
27
- end
28
-
29
- def load_config(fn)
30
- user_config = fn.exist? ? YAML.load_file(fn) : {}
31
-
32
- CONFIGURATION_DEFAULTS.merge(user_config)
33
- end
34
-
35
- def [](key)
36
- @config[key.to_sym]
37
- end
38
-
39
- def []=(key, value)
40
- @config[key.to_sym] = value
41
-
42
- wrap_with_pathname(key.to_sym) if %w[dir timelog].include?(key.to_s)
43
- end
44
-
45
- private
46
-
47
- def wrap_with_pathname(key)
48
- return unless @config.key?(key)
49
- return @config[key] if @config[key].is_a? Pathname
50
-
51
- @config[key] = Pathname.new(@config[key]).expand_path
52
- end
53
- end
54
- end
@@ -1,109 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Gpuzzletime
4
- # Dataclass to wrap an entry
5
- class Entry
6
- # allow to read everything
7
- attr_reader :date, :start_time, :finish_time, :ticket, :description,
8
- :tags, :billable, :account
9
-
10
- # define only trivial writers, omit special and derived values
11
- attr_writer :date, :start_time, :ticket, :description
12
-
13
- def initialize(config = Configuration.instance)
14
- @config = config
15
- @script = Script.new(@config[:dir])
16
- end
17
-
18
- class << self
19
- def from_timelog(matched_line)
20
- entry = new
21
- entry.from_timelog(matched_line)
22
- entry
23
- end
24
- end
25
-
26
- def from_timelog(matched_line)
27
- self.date = matched_line[:date]
28
- self.ticket = matched_line[:ticket]
29
- self.description = matched_line[:description]
30
- self.finish_time = matched_line[:time]
31
- self.tags = matched_line[:tags]
32
-
33
- infer_ptime_settings
34
- end
35
-
36
- def finish_time=(time)
37
- @finish_time = round_time(time, @config[:rounding])
38
- end
39
-
40
- def tags=(tags)
41
- return unless tags
42
-
43
- @tags = tags.split
44
- end
45
-
46
- def valid?
47
- @start_time && !hidden?
48
- end
49
-
50
- def hidden?
51
- @description.match(/\*\*$/) # hide lunch and breaks
52
- end
53
-
54
- def infer_ptime_settings
55
- @account = infer_account
56
- @billable = infer_billable
57
- end
58
-
59
- def to_s
60
- [
61
- @start_time, '-', @finish_time,
62
- [
63
- @ticket,
64
- @description,
65
- @tags,
66
- @account,
67
- ].compact.join(' : '),
68
- ].compact.join(' ')
69
- end
70
-
71
- # make sortable/def <=>
72
- # duration if start and finish is set
73
-
74
- private
75
-
76
- def round_time(time, interval)
77
- return time unless interval
78
-
79
- hour, minute = time.split(':')
80
- minute = (minute.to_i / interval.to_f).round * interval.to_i
81
-
82
- if minute == 60
83
- [hour.succ, 0]
84
- else
85
- [hour, minute]
86
- end.map { |part| part.to_s.rjust(2, '0') }.join(':')
87
- end
88
-
89
- def infer_account
90
- return unless @tags
91
-
92
- parser_name = tags.shift
93
- parser = @script.parser(parser_name)
94
-
95
- return unless parser.exist?
96
-
97
- cmd = %(#{parser} "#{@ticket}" "#{@description}" #{tags.map(&:inspect).join(' ')})
98
- `#{cmd}`.chomp # maybe only execute if parser is in correct dir?
99
- end
100
-
101
- def infer_billable
102
- script = @script.billable
103
-
104
- return 1 unless script.exist?
105
-
106
- `#{script} #{@account}`.chomp == 'true' ? 1 : 0
107
- end
108
- end
109
- end
@@ -1,25 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Gpuzzletime
4
- # Mapping between semantic/relative names and absolute dates
5
- class NamedDate
6
- def date(arg = 'last')
7
- named_date(arg) || :all
8
- end
9
-
10
- def named_date(date)
11
- case date
12
- when 'yesterday' then Date.today.prev_day.to_s
13
- when 'today' then Date.today.to_s
14
- when 'last' then timelog.to_h.keys.compact.sort[-2] || Date.today.prev_day.to_s
15
- when /\d{4}(-\d{2}){2}/ then date
16
- end
17
- end
18
-
19
- private
20
-
21
- def timelog
22
- Timelog.load
23
- end
24
- end
25
- end
@@ -1,20 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Gpuzzletime
4
- # Wrapper around all external scripts that might be called to get more
5
- # information about the time-entries
6
- class Script
7
- def initialize(config_dir)
8
- @config_dir = config_dir
9
- end
10
-
11
- def parser(parser_name)
12
- @config_dir.join("parsers/#{parser_name}") # FIXME: security-hole, prevent relative paths!
13
- .expand_path
14
- end
15
-
16
- def billable
17
- @config_dir.join('billable').expand_path
18
- end
19
- end
20
- end
@@ -1,50 +0,0 @@
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
- include Singleton
9
-
10
- class << self
11
- def load
12
- instance.load
13
- end
14
-
15
- def timelog_txt
16
- Pathname.new(Configuration.instance[:timelog]).expand_path
17
- end
18
- end
19
-
20
- def load
21
- @load ||= parse(read)
22
- end
23
-
24
- def timelog_txt
25
- self.class.timelog_txt
26
- end
27
-
28
- def read
29
- timelog_txt.read
30
- end
31
-
32
- def parse(data)
33
- data.split("\n")
34
- .map { |line| tokenize(line) }
35
- .group_by { |match| match && match[:date] }
36
- .to_a
37
- end
38
-
39
- def tokenize(line)
40
- re_date = /(?<date>\d{4}-\d{2}-\d{2})/
41
- re_time = /(?<time>\d{2}:\d{2})/
42
- re_tick = /(?:(?<ticket>.*?): )/
43
- re_desc = /(?<description>.*?)/
44
- re_tags = /(?: -- (?<tags>.*)?)/
45
-
46
- regexp = /^#{re_date} #{re_time}: #{re_tick}?#{re_desc}#{re_tags}?$/
47
- line.match(regexp)
48
- end
49
- end
50
- end
@@ -1,6 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- # Version
4
- module Gpuzzletime
5
- VERSION = '0.4.3'
6
- end