trackinator 0.0.1

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.
data/.gitignore ADDED
@@ -0,0 +1,18 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
18
+ .idea
data/.rvmrc ADDED
@@ -0,0 +1,6 @@
1
+ if [[ -d "$rvm_path/environments" && -s "$rvm_path/environments/ruby-1.9.3-p194@trackinator" ]] ; then
2
+ \. "$rvm_path/environments/ruby-1.9.3-p194@trackinator"
3
+ else
4
+ rvm --create use "ruby-1.9.3-p194@trackinator"
5
+ fi
6
+
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in trackinator.gemspec
4
+ gemspec
data/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2012 Justin Beck
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,89 @@
1
+ # Trackinator
2
+
3
+ This gem, when used correctly, will import a Google spreadsheet
4
+ into YouTrack as tickets. The spreadsheet is a test plan which
5
+ defines features (and how to use them). There are numerous
6
+ benefits to this approach:
7
+
8
+ - Scope is defined up front which reduces scope creep
9
+ - Creating the test plan forces developers to be appropriately familiar
10
+ with the project
11
+ - A test plan is written up front (TDD at a macro level)
12
+
13
+ The format of the spreadsheet is as follows (columns can be in any order):
14
+
15
+ ####project
16
+ The abbreviation for the project in YouTrack. The project must
17
+ exist in YouTrack already, this gem will not create the project for you
18
+
19
+ ####id
20
+ Similar to a version number. Must be unique in this test plan. Follows
21
+ the format x.y.z (this example is 3 levels deep) and there is no limit
22
+ to nesting depth. Each level denotes sub-tickets that will be created
23
+ and associated to the level above.
24
+
25
+ ####type
26
+ "Story" for top level items, "Features" and "Tasks" for items one or more
27
+ levels below "Story". “Features” are user facing and "Tasks" are
28
+ implementation details. "Tasks" will not be acceptance tested.
29
+
30
+ ####summary
31
+ A one-line summarizing of the feature.
32
+
33
+ ####description
34
+ A description of the feature including steps to use the feature.
35
+
36
+ ####outcome
37
+ The expected outcome if the feature is used per the steps in the description.
38
+
39
+ ####notes
40
+ Any additional notes (which will show up as a comment) that might be useful
41
+ for the developer or tester.
42
+
43
+ ####references
44
+ A reference to the design document. The format should be wf/c-<page>-<screen
45
+ (or range)>. E.g. wf-12-5 or c-9-2-3 (where "wf" refers to wireframe and "c"
46
+ refers to composition)
47
+
48
+ ####platform
49
+ This is usually one of iOS, iPhone, iPad, Android, Desktop Web, Mobile Web
50
+
51
+ ####priority
52
+ One of Low, Normal, High, Show-stopper
53
+
54
+ ## Installation
55
+
56
+ Add this line to your application's Gemfile:
57
+
58
+ gem 'trackinator'
59
+
60
+ And then execute:
61
+
62
+ $ bundle
63
+
64
+ Or install it yourself as:
65
+
66
+ $ gem install trackinator
67
+
68
+ ## Usage
69
+
70
+ <pre>
71
+ Options:
72
+ --youtrack-username, -y <s>: Your YouTrack username
73
+ --youtrack-password, -p <s>: Your YouTrack password
74
+ --google-username, -g <s>: Your Google username
75
+ --google-password, -a <s>: Your Google password
76
+ --youtrack-host, -o <s>: YouTrack host
77
+ --youtrack-port, -r <i>: YouTrack port (default: 80)
78
+ --youtrack-path-prefix, -e <s>: YouTrack path prefix (e.g. '/youtrack/') (default: /)
79
+ --help, -h: Show this message
80
+ filename: File name in Google Docs
81
+ </pre>
82
+
83
+ ## Contributing
84
+
85
+ 1. Fork it
86
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
87
+ 3. Commit your changes (`git commit -am 'Added some feature'`)
88
+ 4. Push to the branch (`git push origin my-new-feature`)
89
+ 5. Create new Pull Request
data/Rakefile ADDED
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env rake
2
+ require "bundler/gem_tasks"
data/bin/trackinate ADDED
@@ -0,0 +1,25 @@
1
+ #!/usr/bin/ruby
2
+
3
+ require 'trackinator/importer'
4
+ require 'trollop'
5
+
6
+ opts = Trollop::options do
7
+ opt :youtrack_username, "Your YouTrack username", :type => :string, :short => "y"
8
+ opt :youtrack_password, "Your YouTrack password", :type => :string, :short => "p"
9
+ opt :google_username, "Your Google username", :type => :string, :short => "g"
10
+ opt :google_password, "Your Google password", :type => :string, :short => "a"
11
+ opt :youtrack_host, "YouTrack host", :type => :string, :short => "o"
12
+ opt :youtrack_port, "YouTrack port", :type => :int, :default => 80, :short => "r"
13
+ opt :youtrack_path_prefix, "YouTrack path prefix (e.g. '/youtrack/')", :type => :string, :default => "/", :short =>"e"
14
+ end
15
+
16
+ p opts
17
+
18
+ Trollop::die :youtrack_username, "is required" if opts[:youtrack_username].empty?
19
+ Trollop::die :youtrack_password, "is required" if opts[:youtrack_password].empty?
20
+ Trollop::die :google_username, "is required" if opts[:google_username].empty?
21
+ Trollop::die :google_password, "is required" if opts[:google_password].empty?
22
+ Trollop::die :youtrack_host, "is required" if opts[:youtrack_host].empty?
23
+
24
+ importer = Trackinator::Importer.new opts
25
+ importer.import ARGV[0]
@@ -0,0 +1,176 @@
1
+ require 'net/http'
2
+ require 'CSV'
3
+
4
+ require 'gdata'
5
+
6
+ module Trackinator
7
+ class Importer
8
+ @youtrack_cookie
9
+ @youtrack_connection
10
+
11
+ @google_headers
12
+ @stack
13
+
14
+ @col_count
15
+
16
+ @youtrack_host
17
+ @youtrack_port
18
+ @youtrack_path_prefix
19
+
20
+ def initialize opts
21
+ @stack = []
22
+ @google_connection = Net::HTTP
23
+ @google_client = GData::Client::Spreadsheets.new
24
+
25
+ google_login = opts[:google_username]
26
+ youtrack_login = opts[:youtrack_username]
27
+
28
+ @youtrack_host = opts[:youtrack_host]
29
+ @youtrack_port = opts[:youtrack_port]
30
+ @youtrack_path_prefix = opts[:youtrack_path_prefix]
31
+
32
+ google_password = params[:google_password]
33
+ youtrack_password = params[:youtrack_password]
34
+
35
+ login_youtrack youtrack_login, youtrack_password
36
+
37
+ @google_client.clientlogin(google_login, google_password)
38
+ end
39
+
40
+ def import file_name
41
+ spreadsheet_feed = @google_client.get("http://spreadsheets.google.com/feeds/worksheets/#{get_spreadsheet_key(file_name)}/private/full").to_xml
42
+ spreadsheet_list_data = @google_client.get(spreadsheet_feed.elements[1, 'entry'].elements[1, 'content'].attributes['src']).to_xml
43
+
44
+ spreadsheet_list_data.elements.each('entry') do |entry|
45
+ ticket_data = get_ticket_data entry
46
+ issue_id = is_issue_exists? ticket_data
47
+ false unless !issue_id.nil? ? update_ticket(issue_id, ticket_data) : create_ticket(ticket_data)
48
+ end
49
+ end
50
+
51
+ private
52
+
53
+ # Google API methods
54
+
55
+ def get_spreadsheet_key file_name
56
+ doc_feed = @google_client.get("http://spreadsheets.google.com/feeds/spreadsheets/private/full").to_xml
57
+
58
+ doc_feed.elements.each ('entry') do |entry|
59
+ if entry.elements['title'].text.eql? file_name
60
+ return entry.elements[1].text[/spreadsheets\/(.*)/, 1]
61
+ end
62
+ end
63
+ end
64
+
65
+ def get_ticket_data entry
66
+ data = {}
67
+
68
+ REXML::XPath.match(entry, 'gsx:*').each do |col|
69
+ data[col.name] = URI.escape(col.text) unless col.text.nil?
70
+ end
71
+
72
+ data
73
+ end
74
+
75
+ # YouTrack API methods
76
+
77
+ def login_youtrack user_name, password
78
+ @youtrack_connection = Net::HTTP.new @youtrack_host, @youtrack_port
79
+ response = @youtrack_connection.post "#{@youtrack_path_prefix}rest/user/login", "login=#{user_name}&password=#{password}"
80
+ @youtrack_cookie = response.response['Set-Cookie'].split('; ')[0]
81
+ end
82
+
83
+ def create_ticket data
84
+ issue_id, create_response = create_youtrack_ticket data
85
+ success = create_response.eql? "Created"
86
+
87
+ success ? (success = update_ticket(issue_id, data)) : (return success)
88
+ success ? update_dependencies([issue_id, data['id']]) : success
89
+ end
90
+
91
+ def update_ticket issue_id, data
92
+ success = set_platform(issue_id, data['platform'])
93
+ success ? (success = set_summary_and_description(issue_id, data['summary'], data['description'])) : (return success)
94
+ success ? (success = set_type(issue_id, data['type'])) : (return success)
95
+ success ? (success = set_import_identifier(issue_id, "#{data['project']}-#{data['id']}")) : (return success)
96
+ success ? (success = set_design_reference(issue_id, "#{data['references']}")) : (return success) unless data['references'].nil?
97
+
98
+ success
99
+ end
100
+
101
+ def update_dependencies issue
102
+ issue_id = issue[0]
103
+ issue_level = issue[1]
104
+
105
+ if @stack.empty?
106
+ @stack.push [issue_id, issue_level]
107
+ else
108
+ last_issue = @stack.last
109
+ last_issue_id = last_issue[0]
110
+ last_issue_level = last_issue[1]
111
+
112
+ if issue_level.length <= last_issue_level.length
113
+ @stack.pop
114
+ update_dependencies issue
115
+ else
116
+ success = create_dependency last_issue_id, issue_id
117
+ @stack.push issue
118
+
119
+ success
120
+ end
121
+ end
122
+ end
123
+
124
+ # YouTrack API calls
125
+
126
+ def is_issue_exists? data
127
+ find_response_xml = REXML::Document.new(@youtrack_connection.get("#{@youtrack_path_prefix}rest/issue/byproject/#{data['project']}?filter=Import+Identifier:+#{data['project']}-#{data['id']}", { 'Cookie' => @youtrack_cookie, 'Content-Type' => 'text/plain; charset=utf-8' }).body)
128
+ find_response_xml.elements['issues'].length > 0 ? find_response_xml.elements['issues/issue'].attributes['id'] : nil
129
+ end
130
+
131
+ def create_youtrack_ticket data
132
+ response = @youtrack_connection.put("#{@youtrack_path_prefix}rest/issue?project=#{data['project']}&summary=#{data['summary']}&description=#{data['description']}&priority=#{data['priority']}", nil, { 'Cookie' => @youtrack_cookie, 'Content-Type' => 'text/plain; charset=utf-8' })
133
+ return response.header["Location"].split("/").last, response.header.msg
134
+ end
135
+
136
+ def create_dependency parent_id, child_id
137
+ response = @youtrack_connection.post("#{@youtrack_path_prefix}rest/issue/#{parent_id}/execute?command=parent+for+#{child_id}&disableNotifications=true", nil, { 'Cookie' => @youtrack_cookie })
138
+ response.header.msg.eql? "OK"
139
+ end
140
+
141
+ def set_summary_and_description issue_id, summary, description
142
+ return true if summary.nil?
143
+
144
+ response = @youtrack_connection.post("#{@youtrack_path_prefix}rest/issue/#{issue_id}/?summary=#{summary}&description=#{description}&disableNotifications=true", nil, { 'Cookie' => @youtrack_cookie })
145
+ response.header.msg.eql? "OK"
146
+ end
147
+
148
+ def set_platform issue_id, platform
149
+ return true if platform.nil?
150
+
151
+ response = @youtrack_connection.post("#{@youtrack_path_prefix}rest/issue/#{issue_id}/execute?command=Platform+#{platform}&disableNotifications=true", nil, { 'Cookie' => @youtrack_cookie })
152
+ response.header.msg.eql? "OK"
153
+ end
154
+
155
+ def set_type issue_id, type
156
+ return true if type.nil?
157
+
158
+ response = @youtrack_connection.post("#{@youtrack_path_prefix}rest/issue/#{issue_id}/execute?command=Type+#{type}&disableNotifications=true", nil, { 'Cookie' => @youtrack_cookie })
159
+ response.header.msg.eql? "OK"
160
+ end
161
+
162
+ def set_import_identifier issue_id, import_id
163
+ return true if import_id.nil?
164
+
165
+ response = @youtrack_connection.post("#{@youtrack_path_prefix}rest/issue/#{issue_id}/execute?command=Import+Identifier+#{import_id}&disableNotifications=true", nil, { 'Cookie' => @youtrack_cookie })
166
+ response.header.msg.eql? "OK"
167
+ end
168
+
169
+ def set_design_reference issue_id, reference
170
+ return true if reference.nil?
171
+
172
+ response = @youtrack_connection.post("#{@youtrack_path_prefix}rest/issue/#{issue_id}/execute?command=Design+Reference+#{reference}&disableNotifications=true", nil, { 'Cookie' => @youtrack_cookie })
173
+ response.header.msg.eql? "OK"
174
+ end
175
+ end
176
+ end
@@ -0,0 +1,3 @@
1
+ module Trackinator
2
+ VERSION = "0.0.1"
3
+ end
@@ -0,0 +1,5 @@
1
+ require "trackinator/version"
2
+
3
+ module Trackinator
4
+ # Your code goes here...
5
+ end
@@ -0,0 +1,26 @@
1
+ # -*- encoding: utf-8 -*-
2
+ require File.expand_path('../lib/trackinator/version', __FILE__)
3
+
4
+ Gem::Specification.new do |gem|
5
+ gem.authors = ["Justin Beck"]
6
+ gem.email = %w{justinbeck@mac.com}
7
+ gem.description = <<-EOF
8
+ In order to introduce in to my main development process
9
+ I decided that creating a test plan prior to development
10
+ and then using that as the basis for my YouTrack tickets
11
+ would be a good approach. Think of it as TDD at a macro
12
+ level.
13
+ EOF
14
+ gem.summary = %q{Imports a spreadsheet in to YouTrack}
15
+ gem.homepage = "https://github.com/justincbeck/trackinator"
16
+
17
+ gem.files = `git ls-files`.split($\)
18
+ gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
19
+ gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
20
+ gem.name = "trackinator"
21
+ gem.require_paths = %w{lib}
22
+ gem.version = Trackinator::VERSION
23
+
24
+ gem.add_dependency "gdata_19"
25
+ gem.add_dependency "trollop"
26
+ end
metadata ADDED
@@ -0,0 +1,92 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: trackinator
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Justin Beck
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2012-07-31 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: gdata_19
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: '0'
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ! '>='
28
+ - !ruby/object:Gem::Version
29
+ version: '0'
30
+ - !ruby/object:Gem::Dependency
31
+ name: trollop
32
+ requirement: !ruby/object:Gem::Requirement
33
+ none: false
34
+ requirements:
35
+ - - ! '>='
36
+ - !ruby/object:Gem::Version
37
+ version: '0'
38
+ type: :runtime
39
+ prerelease: false
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ! '>='
44
+ - !ruby/object:Gem::Version
45
+ version: '0'
46
+ description: ! " In order to introduce in to my main development process\n I
47
+ decided that creating a test plan prior to development\n and then using that
48
+ as the basis for my YouTrack tickets\n would be a good approach. Think of it
49
+ as TDD at a macro\n level.\n"
50
+ email:
51
+ - justinbeck@mac.com
52
+ executables:
53
+ - trackinate
54
+ extensions: []
55
+ extra_rdoc_files: []
56
+ files:
57
+ - .gitignore
58
+ - .rvmrc
59
+ - Gemfile
60
+ - LICENSE
61
+ - README.md
62
+ - Rakefile
63
+ - bin/trackinate
64
+ - lib/trackinator.rb
65
+ - lib/trackinator/importer.rb
66
+ - lib/trackinator/version.rb
67
+ - trackinator.gemspec
68
+ homepage: https://github.com/justincbeck/trackinator
69
+ licenses: []
70
+ post_install_message:
71
+ rdoc_options: []
72
+ require_paths:
73
+ - lib
74
+ required_ruby_version: !ruby/object:Gem::Requirement
75
+ none: false
76
+ requirements:
77
+ - - ! '>='
78
+ - !ruby/object:Gem::Version
79
+ version: '0'
80
+ required_rubygems_version: !ruby/object:Gem::Requirement
81
+ none: false
82
+ requirements:
83
+ - - ! '>='
84
+ - !ruby/object:Gem::Version
85
+ version: '0'
86
+ requirements: []
87
+ rubyforge_project:
88
+ rubygems_version: 1.8.24
89
+ signing_key:
90
+ specification_version: 3
91
+ summary: Imports a spreadsheet in to YouTrack
92
+ test_files: []