trackinator 0.0.1

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