xfel-timew-jira 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: b33fb2e86a1142e590c4031f4be634c61a4fdd08cd4fd938b8cf8082247a288b
4
+ data.tar.gz: bb36a9c53779cc2795ee732b7871532d4368caf5650b91bea105bae76e28fc83
5
+ SHA512:
6
+ metadata.gz: c7a3c2af52b6a0fdc66821a6b93f6543bdf3b0a6a599ec913d4427b2378704ad6cedc070c0c159303a443c42a8871f0478e42d4ccb3c65e1cfcd04f671987411
7
+ data.tar.gz: deba1336520f3fbc02951a1d0303140e6e72086c47e569fa3f7d500b37045dfd6bc0cca5bf046cfd290dbf01789d44aee77e630c3784d7cf482db8c2f3db79bc
@@ -0,0 +1,87 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'net/http'
4
+
5
+ module Xfel
6
+ module Timew
7
+ # Class for REST interaction with Jira servers.
8
+ class Jira
9
+ @worklogs = {}
10
+
11
+ class << self
12
+ attr_reader :worklogs
13
+ end
14
+
15
+ def initialize(worklog)
16
+ @start = worklog[:start]
17
+ @duration = worklog[:duration]
18
+ @key = worklog[:key]
19
+ return unless ENV['XFEL_JIRA_SYNC']
20
+
21
+ if ENV['JIRA_HOST'] && ENV['JIRA_USER'] && ENV['JIRA_PASS']
22
+ @uri = "#{ENV['JIRA_HOST']}/rest/api/2/issue/#{@key}/worklog"
23
+ sync
24
+ else
25
+ log 'Missing required env vars: JIRA_HOST, JIRA_USER, JIRA_PASS'
26
+ end
27
+ end
28
+
29
+ def log(msg)
30
+ puts "#{@key} | #{msg}"
31
+ end
32
+
33
+ def vars
34
+ 'notifyUsers=false&adjustEstimate=leave&overrideEditableFlag=true'
35
+ end
36
+
37
+ def sync
38
+ log "#{@start} sync..."
39
+ if worklogs.any? { |w| duplicated?(w) }
40
+ log "#{@start} already present, skipping it."
41
+ else
42
+ res = req_for_sync
43
+ log "#{@start} error: #{res.code}. #{res.body}" unless req_success?(res)
44
+ end
45
+ end
46
+
47
+ def req_success?(response)
48
+ code_int = response.code.to_i
49
+ code_int > 199 && code_int < 300
50
+ end
51
+
52
+ def req_for_sync
53
+ uri = URI("#{@uri}?#{vars}")
54
+ req = Net::HTTP::Post.new(uri)
55
+ req['Content-Type'] = 'application/json'
56
+ req.body = {
57
+ comment: '', started: @start, timeSpentSeconds: @duration
58
+ }.to_json
59
+ execute(req, uri)
60
+ end
61
+
62
+ def duplicated?(worklog)
63
+ @start == worklog['started']
64
+ end
65
+
66
+ def execute(req, uri)
67
+ req.basic_auth ENV['JIRA_USER'], ENV['JIRA_PASS']
68
+ Net::HTTP.start(uri.hostname, uri.port, { use_ssl: true }) { |http| http.request(req) }
69
+ end
70
+
71
+ def fetch_worklogs
72
+ uri = URI(@uri)
73
+ res = execute(Net::HTTP::Get.new(uri), uri)
74
+ unless req_success?(res)
75
+ log "Error getting worklogs. #{res.code}: #{res.msg}"
76
+ exit
77
+ end
78
+ self.class.worklogs[@key] = JSON.parse(res.body)['worklogs']
79
+ end
80
+
81
+ def worklogs
82
+ fetch_worklogs unless self.class.worklogs[@key]
83
+ self.class.worklogs[@key]
84
+ end
85
+ end
86
+ end
87
+ end
@@ -0,0 +1,68 @@
1
+ # frozen_string_literal true
2
+
3
+ require 'date'
4
+ require 'json'
5
+ require_relative 'jira'
6
+ require_relative 'table'
7
+
8
+ module Xfel
9
+ module Timew
10
+ # Main entry point for TimeWarrior report
11
+ class Report
12
+ def initialize
13
+ table = Table.new
14
+ read.each do |x|
15
+ worklog = convert(x)
16
+ next if worklog.nil?
17
+
18
+ Jira.new(worklog)
19
+ table.add(worklog)
20
+ end
21
+ table.render
22
+ end
23
+
24
+ def task_rc
25
+ 'rc.report.list.columns="description" rc.report.list.labels="Notes" rc.verbose=label'
26
+ end
27
+
28
+ def read
29
+ header_finished = false
30
+ json_str = ''
31
+ while (line = gets)
32
+ if !header_finished
33
+ header_finished = line.length == 1
34
+ else
35
+ json_str += line
36
+ end
37
+ end
38
+ JSON.parse(json_str)
39
+ end
40
+
41
+ def key_from_tags(tags)
42
+ return unless tags
43
+
44
+ tags.each do |x|
45
+ return x unless (x =~ /^[A-Z]+-[0-9]+$/).nil?
46
+ end
47
+ nil
48
+ end
49
+
50
+ def project_from_key(key)
51
+ key.split('-')[0]
52
+ end
53
+
54
+ def convert(item)
55
+ key = key_from_tags(item['tags'])
56
+ return unless key && item['end']
57
+
58
+ start = DateTime.parse(item['start'])
59
+ st = start.strftime('%Y-%m-%dT%H:%M:%S.%L%z')
60
+ finish = DateTime.parse(item['end'])
61
+ duration = finish.to_time.to_i - start.to_time.to_i
62
+ project = project_from_key(key)
63
+
64
+ { project: project, key: key, start: st, duration: duration }
65
+ end
66
+ end
67
+ end
68
+ end
@@ -0,0 +1,57 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'terminal-table'
4
+
5
+ module Xfel
6
+ module Timew
7
+ # Report's terminal output.
8
+ class Table
9
+ def initialize
10
+ @data = {}
11
+ @table = Terminal::Table.new({ headings: %w[Project Hours Total] })
12
+ end
13
+
14
+ def time_str(seconds)
15
+ hours = seconds / 3600
16
+ minutes = seconds / 60 % 60
17
+ format("#{hours}h %02dm", minutes)
18
+ end
19
+
20
+ def project_to_table(project, tickets)
21
+ @table.add_row([project, '', ''])
22
+ project_total = 0
23
+ tickets.each do |key, duration|
24
+ project_total += duration
25
+ @table.add_row(["└── #{key}", time_str(duration), ''])
26
+ end
27
+ @table.add_row(['', '', time_str(project_total)])
28
+ project_total
29
+ end
30
+
31
+ def data_to_table
32
+ total = 0
33
+ @data.each do |project, tickets|
34
+ total += project_to_table(project, tickets)
35
+ @table.add_separator
36
+ end
37
+ @table.add_row(['', '', time_str(total)])
38
+ end
39
+
40
+ def render
41
+ data_to_table
42
+ @table.align_column(1, :right)
43
+ @table.align_column(2, :right)
44
+ puts @table
45
+ end
46
+
47
+ def add(worklog)
48
+ project = worklog[:project]
49
+ ticket = worklog[:key]
50
+
51
+ @data[project] = {} unless @data.key?(project)
52
+ @data[project][ticket] = 0 unless @data[project].key?(ticket)
53
+ @data[project][ticket] += worklog[:duration]
54
+ end
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,5 @@
1
+ module Xfel
2
+ module Timew
3
+ VERSION = "0.1.0"
4
+ end
5
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative './xfel/timew/report'
4
+
5
+ Xfel::Timew::Report.new
metadata ADDED
@@ -0,0 +1,119 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: xfel-timew-jira
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Fernando Jascovich
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2020-10-29 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: 2.1.4
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: 2.1.4
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '10.0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '10.0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rspec
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '3.0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '3.0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: simplecov
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: 0.19.0
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: 0.19.0
69
+ - !ruby/object:Gem::Dependency
70
+ name: terminal-table
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: 1.8.0
76
+ type: :runtime
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: 1.8.0
83
+ description: " This gem generates a Jira ticket oriented summary and it syncs TimeWarrior
84
+ entries with Jira ticket's worklog.\n"
85
+ email:
86
+ - fernando.ej@gmail.com
87
+ executables: []
88
+ extensions: []
89
+ extra_rdoc_files: []
90
+ files:
91
+ - lib/xfel/timew/jira.rb
92
+ - lib/xfel/timew/report.rb
93
+ - lib/xfel/timew/table.rb
94
+ - lib/xfel/timew/version.rb
95
+ - lib/xfel_timew_jira.rb
96
+ homepage: https://github.com/fernando-jascovich/xfel-timew-jira
97
+ licenses:
98
+ - MIT
99
+ metadata: {}
100
+ post_install_message:
101
+ rdoc_options: []
102
+ require_paths:
103
+ - lib
104
+ required_ruby_version: !ruby/object:Gem::Requirement
105
+ requirements:
106
+ - - ">="
107
+ - !ruby/object:Gem::Version
108
+ version: '2.6'
109
+ required_rubygems_version: !ruby/object:Gem::Requirement
110
+ requirements:
111
+ - - ">="
112
+ - !ruby/object:Gem::Version
113
+ version: '0'
114
+ requirements: []
115
+ rubygems_version: 3.0.3
116
+ signing_key:
117
+ specification_version: 4
118
+ summary: Jira sync and report for TimeWarrior
119
+ test_files: []