opener-scorer 1.0.0

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.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 5c7f17022140c8d6b926ea397e425080cd5e2649
4
+ data.tar.gz: 94e0ff7ab67c6c207cef8fcbbbecf7ea61b6e7e7
5
+ SHA512:
6
+ metadata.gz: 77ef8845ae7ddb33c92f920b431bcda6e966c7dbbfe3356f13495fd292911c68f84232671815268c0e1ed9bce1f42490437d965fccaa4b67da496177820e9149
7
+ data.tar.gz: c9a81adb19075b963e9bf52983be175f2e99a87640f7943123f12a42adfd8117123d541e7d4ddbbdcd1855b25d45f6bd35c3bbb8c8e2e41f1661a8abd6d60a07
@@ -0,0 +1,60 @@
1
+ # Scorer
2
+
3
+ Component that stores results from the Opener Web Services chain into a MySQL
4
+ Database and shows them into your browser.
5
+
6
+ It can be used instead of Opener Outlet, to calculate overall sentiment score
7
+ and sentiment score per topic.
8
+
9
+
10
+ ## Installation
11
+
12
+ ### As part of a Gemfile in a Ruby application
13
+
14
+ Add this line to your application's Gemfile:
15
+
16
+ gem 'opener-scorer',
17
+ :git=>"git@github.com:opener-project/scorer.git"
18
+
19
+ And then execute:
20
+
21
+ $ bundle install
22
+
23
+ ## Usage
24
+
25
+ The Opener Scorer comes equipped with a simple webservice. To start the
26
+ webservice type:
27
+
28
+ scorer-server
29
+
30
+ This will launch a mini webserver with the webservice. It defaults to port 9292,
31
+ so you can access it at:
32
+
33
+ http://localhost:9292
34
+
35
+ To launch it on a different port provide the ```-p [port-number]``` option like
36
+ this:
37
+
38
+ scorer-server -p 1234
39
+
40
+ It then launches at ```http://localhost:1234```
41
+
42
+ When you run a chain of web services using callbacks, the last URL should be the
43
+ one that points to the Scorer Web Service. A unique id is generated and once the
44
+ chain has finished processing the text, you can view the result in the URL that
45
+ you get.
46
+
47
+ ## Contributing
48
+
49
+ ### Procedure
50
+
51
+ 1. Pull it
52
+ 2. Create your feature branch (`git checkout -b features/my-new-feature`)
53
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
54
+ 4. Push to the branch (`git push origin features/my-new-feature`)
55
+ 5. If you're confident, merge your changes into master.
56
+
57
+ # What's next?
58
+
59
+ If you're interested in the opener-scorer, you also might want to check
60
+ out opener-project/scorer.
@@ -0,0 +1,7 @@
1
+ #!/usr/bin/env ruby
2
+ require 'json'
3
+ require_relative '../lib/opener/scorer/output_processor'
4
+
5
+ processor = Opener::Scorer::OutputProcessor.new((STDIN.tty? ? nil : STDIN.read))
6
+
7
+ puts processor.process.to_json
@@ -0,0 +1,10 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'rack'
4
+
5
+ # Without calling `Rack::Server#options` manually the CLI arguments will never
6
+ # be passed, thus the application can't be specified as a constructor argument.
7
+ server = Rack::Server.new
8
+ server.options[:config] = File.expand_path('../../config.ru', __FILE__)
9
+
10
+ server.start
@@ -0,0 +1,4 @@
1
+ require File.expand_path('../lib/opener/scorer', __FILE__)
2
+ require File.expand_path('../lib/opener/scorer/server', __FILE__)
3
+
4
+ run Opener::Scorer::Server
@@ -0,0 +1,30 @@
1
+ require 'active_record'
2
+ require 'activerecord-jdbcmysql-adapter'
3
+
4
+
5
+ DB_PASS = ENV["DB_PASS"]
6
+ DB_NAME = ENV["DB_NAME"]
7
+ DB_USER = ENV["DB_USER"]
8
+
9
+ if ENV["RACK_ENV"] == 'production'
10
+ ActiveRecord::Base.establish_connection(
11
+ :adapter=> 'mysql2',
12
+ :host => "opener-olery.cqlc6ghud3pa.eu-west-1.rds.amazonaws.com",
13
+ :username => "enouch",
14
+ :password => DB_PASS,
15
+ :database => "opener-olery",
16
+ )
17
+ else
18
+ ActiveRecord::Base.establish_connection(
19
+ adapter: 'mysql2',
20
+ host: 'localhost',
21
+ username: 'root',
22
+ password: DB_PASS || '',
23
+ database: DB_NAME || 'opener_development'
24
+ )
25
+ end
26
+
27
+ if ActiveRecord::Base.connection.execute("SHOW INDEX FROM output_scores").nil?
28
+ ActiveRecord::Base.connection.execute("CREATE INDEX uuid_index ON output_scores(uuid);")
29
+ end
30
+ ActiveRecord::Base.connection.execute("CREATE TABLE IF NOT EXISTS output_scores (uuid varchar(40), raw_text longtext, bathroom double,breakfast double,cleanliness double,facilities double,internet double,location double,noise double,parking double,restaurant double,room double,sleeping_comfort double,staff double,swimming_pool double,value_for_money double, overall double);")
@@ -0,0 +1,9 @@
1
+ require_relative 'scorer/output'
2
+ require_relative 'scorer/output_processor'
3
+ require_relative 'scorer/version'
4
+ require_relative 'scorer/server'
5
+
6
+ module Opener
7
+ class Scorer
8
+ end
9
+ end
@@ -0,0 +1,12 @@
1
+ require 'active_record'
2
+
3
+ module Opener
4
+ class Scorer
5
+ class Output < ActiveRecord::Base
6
+ attr_accessible :uuid, :text
7
+
8
+ validates_uniqueness_of :uuid
9
+ end
10
+ end
11
+ end
12
+
@@ -0,0 +1,151 @@
1
+ require 'nokogiri'
2
+
3
+ module Opener
4
+ class Scorer
5
+ ##
6
+ # Class that given a raw xml input, it will calculate the overall sentiment
7
+ # score and the scores per topic, given that it is a valid KAF document.
8
+ #
9
+ # @!attribute [r] input
10
+ # @return [String]
11
+ #
12
+ # @!attribute [r] lemmas_array
13
+ # @return [Array]
14
+ #
15
+ # @!attribute [r] lemmas_hash
16
+ # @return [Hash]
17
+ #
18
+ # @!attribute [r] polarities_hash
19
+ # @return [Hash]
20
+ #
21
+ class OutputProcessor
22
+ attr_accessor :input, :lemmas_array, :lemmas_hash, :polarities_hash
23
+
24
+ ##
25
+ # @param [String] input
26
+ #
27
+ def initialize(input)
28
+ @input = Nokogiri::XML::Document.parse(input)
29
+ @lemmas_array = []
30
+ @lemmas_hash = {}
31
+ @polarities_hash = {}
32
+ end
33
+
34
+ ##
35
+ # Process the document and return the scores for the available topics.
36
+ #
37
+ # @return [Hash]
38
+ #
39
+ def process
40
+ scores = {}
41
+
42
+ build_lemmas_hash
43
+ build_polarities_hash
44
+
45
+ if overall_score = get_overall_score
46
+ scores[:overall] = overall_score
47
+ end
48
+
49
+ lemmas_hash.keys.each do |topic|
50
+ score = get_topic_score(topic)
51
+ if score
52
+ scores[topic] = score
53
+ end
54
+ end
55
+
56
+ return scores
57
+ end
58
+
59
+ protected
60
+
61
+ ##
62
+ # Create a hash with all lemma ids per property and also an array with
63
+ # all lemma ids.
64
+ #
65
+ def build_lemmas_hash
66
+ input.css('features properties property').each do |property|
67
+ lemma = property.attr('lemma').to_sym
68
+ lemmas_hash[lemma] ||= []
69
+ property.css('references target').each do |target|
70
+ lemma_id = target.attr('id')
71
+ lemmas_array << lemma_id
72
+ lemmas_hash[lemma] << lemma_id
73
+ end
74
+ end
75
+ end
76
+
77
+ ##
78
+ # Create a hash with all lemma ids that have a polarity.
79
+ #
80
+ def build_polarities_hash
81
+ input.at('opinions').css('opinion').each do |opinion|
82
+ polarity = opinion.at('opinion_expression').attr('polarity').to_sym
83
+ if opinion.at('opinion_target')
84
+ opinion.at('opinion_target').css('span target').each do |target|
85
+ polarities_hash[target.attr('id')] ||= []
86
+ polarities_hash[target.attr('id')] << polarity
87
+ end
88
+ end
89
+
90
+ opinion.at('opinion_expression').css('span target').each do |target|
91
+ polarities_hash[target.attr('id')] ||= []
92
+ polarities_hash[target.attr('id')] << polarity
93
+ end
94
+ end
95
+ end
96
+
97
+ ##
98
+ # Get the score for all lemmas that have a polarity.
99
+ #
100
+ # @return [Float]
101
+ #
102
+ def get_overall_score
103
+ polarities = []
104
+ input.at('opinions').css('opinion').each do |opinion|
105
+ polarities << opinion.at('opinion_expression').attr('polarity').to_sym
106
+ end
107
+
108
+ positive = polarities.count(:positive)
109
+ negative = polarities.count(:negative)
110
+
111
+ return if (positive + negative) == 0
112
+
113
+ score = ((positive - negative).to_f) / (positive + negative)
114
+
115
+ return score
116
+ end
117
+
118
+ ##
119
+ # Given a topic, return the sentiment score of the lemmas of this topic.
120
+ #
121
+ # @return [Float] || [NilClass]
122
+ #
123
+ def get_topic_score(topic)
124
+ return calculate_score(lemmas_hash[topic]) if lemmas_hash[topic]
125
+ end
126
+
127
+ ##
128
+ # Given an array of lemma ids, calculate the sentiment score.
129
+ #
130
+ # @return [Float]
131
+ #
132
+ def calculate_score(lemma_ids)
133
+ polarities = []
134
+
135
+ lemma_ids.each do |id|
136
+ polarities << polarities_hash[id]
137
+ end
138
+
139
+ positive = polarities.flatten.count(:positive)
140
+ negative = polarities.flatten.count(:negative)
141
+
142
+ return if (positive + negative) == 0
143
+
144
+ score = ((positive - negative).to_f) / (positive + negative)
145
+
146
+ return score
147
+ end
148
+
149
+ end # OutputProcessor
150
+ end # Scorer
151
+ end # Opener
@@ -0,0 +1,53 @@
1
+ require 'sinatra'
2
+ require 'nokogiri'
3
+ require File.expand_path('../../../../config/database', __FILE__)
4
+
5
+ module Opener
6
+ class Scorer
7
+ class Server < Sinatra::Base
8
+
9
+ post '/' do
10
+ output = Output.new
11
+ output.uuid = params[:request_id]
12
+ output.text = OutputProcessor.new(params[:input]).process.to_json
13
+ output.save
14
+ end
15
+
16
+ get '/' do
17
+ if params[:request_id]
18
+ redirect "#{url("/")}#{params[:request_id]}"
19
+ else
20
+ erb :index
21
+ end
22
+ end
23
+
24
+ get '/:request_id' do
25
+ unless params[:request_id] == 'favicon.ico'
26
+ begin
27
+ output = Output.find_by_uuid(params[:request_id])
28
+
29
+ if output
30
+ content_type(:json)
31
+ scores = JSON.parse(output.text)
32
+ body( {:uuid=>output.uuid, :scores=>scores}.to_json)
33
+ else
34
+ halt(404, "No record found for ID #{params[:request_id]}")
35
+ end
36
+ rescue => error
37
+ error_callback = params[:error_callback]
38
+
39
+ submit_error(error_callback, error.message) if error_callback
40
+
41
+ raise(error)
42
+ end
43
+ end
44
+ end
45
+
46
+ private
47
+
48
+ def submit_error(url, message)
49
+ HTTPClient.post(url, :body => {:error => message})
50
+ end
51
+ end # Server
52
+ end # Scorer
53
+ end # Opener
@@ -0,0 +1,5 @@
1
+ module Opener
2
+ class Scorer
3
+ VERSION = "1.0.0"
4
+ end
5
+ end
@@ -0,0 +1,33 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <link type="text/css" rel="stylesheet" charset="UTF-8" href="markdown.css"/>
5
+ <title>Opener Scorer</title>
6
+ </head>
7
+ <body>
8
+ <h1>Opener Scorer</h1>
9
+
10
+ <p>This Web Service, after processing the output, it stores it in a
11
+ MySQL database when using callbacks and lets you view the output.</p>
12
+ <p>When using callbacks, the last Callback URL should be the URL that points
13
+ to Scorer. Then, when you submit your text, you get a URL where the result
14
+ will be shown, when the processing is over.</p>
15
+
16
+ <h2>Search for an output</h2>
17
+
18
+ <p>* required</p>
19
+
20
+
21
+ <form action="<%=url("/")%>" method="GET">
22
+ <div>
23
+ <label for="request_id"/>Type the ID here*</label>
24
+ <br/>
25
+
26
+ <input type="text" name="request_id" id="text" rows="10" cols="50"/>
27
+ </div>
28
+ <input type="submit" value="Submit" />
29
+ </form>
30
+
31
+ </body>
32
+ </html>
33
+
@@ -0,0 +1,37 @@
1
+ require File.expand_path('../lib/opener/scorer/version', __FILE__)
2
+
3
+ Gem::Specification.new do |gem|
4
+ gem.name = 'opener-scorer'
5
+ gem.version = Opener::Scorer::VERSION
6
+ gem.authors = ['development@olery.com']
7
+ gem.summary = 'MySQL data storing for the web services output when using callbacks.'
8
+ gem.description = gem.summary
9
+ gem.homepage = "http://opener-project.github.com/"
10
+ gem.has_rdoc = 'yard'
11
+ gem.required_ruby_version = '>= 1.9.2'
12
+
13
+ gem.files = Dir.glob([
14
+ 'config/**/*',
15
+ 'lib/**/*',
16
+ 'config.ru',
17
+ '*.gemspec',
18
+ 'README.md'
19
+ ]).select { |file| File.file?(file) }
20
+
21
+ gem.executables = Dir.glob('bin/*').map { |file| File.basename(file) }
22
+
23
+ gem.add_dependency 'builder'
24
+ gem.add_dependency 'sinatra', '~>1.4.2'
25
+ gem.add_dependency 'nokogiri'
26
+ gem.add_dependency 'httpclient'
27
+ gem.add_dependency 'uuidtools'
28
+ gem.add_dependency 'jdbc-mysql'
29
+ gem.add_dependency 'activerecord-jdbcmysql-adapter'
30
+ gem.add_dependency 'activerecord', '~>3.2'
31
+ gem.add_dependency 'opener-webservice'
32
+
33
+ gem.add_development_dependency 'rspec'
34
+ gem.add_development_dependency 'cucumber'
35
+ gem.add_development_dependency 'pry'
36
+ gem.add_development_dependency 'rake'
37
+ end
metadata ADDED
@@ -0,0 +1,239 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: opener-scorer
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ platform: ruby
6
+ authors:
7
+ - development@olery.com
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2014-05-20 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: builder
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: sinatra
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: 1.4.2
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: 1.4.2
41
+ - !ruby/object:Gem::Dependency
42
+ name: nokogiri
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: httpclient
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: uuidtools
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :runtime
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: jdbc-mysql
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ">="
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ type: :runtime
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ">="
95
+ - !ruby/object:Gem::Version
96
+ version: '0'
97
+ - !ruby/object:Gem::Dependency
98
+ name: activerecord-jdbcmysql-adapter
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - ">="
102
+ - !ruby/object:Gem::Version
103
+ version: '0'
104
+ type: :runtime
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - ">="
109
+ - !ruby/object:Gem::Version
110
+ version: '0'
111
+ - !ruby/object:Gem::Dependency
112
+ name: activerecord
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - "~>"
116
+ - !ruby/object:Gem::Version
117
+ version: '3.2'
118
+ type: :runtime
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - "~>"
123
+ - !ruby/object:Gem::Version
124
+ version: '3.2'
125
+ - !ruby/object:Gem::Dependency
126
+ name: opener-webservice
127
+ requirement: !ruby/object:Gem::Requirement
128
+ requirements:
129
+ - - ">="
130
+ - !ruby/object:Gem::Version
131
+ version: '0'
132
+ type: :runtime
133
+ prerelease: false
134
+ version_requirements: !ruby/object:Gem::Requirement
135
+ requirements:
136
+ - - ">="
137
+ - !ruby/object:Gem::Version
138
+ version: '0'
139
+ - !ruby/object:Gem::Dependency
140
+ name: rspec
141
+ requirement: !ruby/object:Gem::Requirement
142
+ requirements:
143
+ - - ">="
144
+ - !ruby/object:Gem::Version
145
+ version: '0'
146
+ type: :development
147
+ prerelease: false
148
+ version_requirements: !ruby/object:Gem::Requirement
149
+ requirements:
150
+ - - ">="
151
+ - !ruby/object:Gem::Version
152
+ version: '0'
153
+ - !ruby/object:Gem::Dependency
154
+ name: cucumber
155
+ requirement: !ruby/object:Gem::Requirement
156
+ requirements:
157
+ - - ">="
158
+ - !ruby/object:Gem::Version
159
+ version: '0'
160
+ type: :development
161
+ prerelease: false
162
+ version_requirements: !ruby/object:Gem::Requirement
163
+ requirements:
164
+ - - ">="
165
+ - !ruby/object:Gem::Version
166
+ version: '0'
167
+ - !ruby/object:Gem::Dependency
168
+ name: pry
169
+ requirement: !ruby/object:Gem::Requirement
170
+ requirements:
171
+ - - ">="
172
+ - !ruby/object:Gem::Version
173
+ version: '0'
174
+ type: :development
175
+ prerelease: false
176
+ version_requirements: !ruby/object:Gem::Requirement
177
+ requirements:
178
+ - - ">="
179
+ - !ruby/object:Gem::Version
180
+ version: '0'
181
+ - !ruby/object:Gem::Dependency
182
+ name: rake
183
+ requirement: !ruby/object:Gem::Requirement
184
+ requirements:
185
+ - - ">="
186
+ - !ruby/object:Gem::Version
187
+ version: '0'
188
+ type: :development
189
+ prerelease: false
190
+ version_requirements: !ruby/object:Gem::Requirement
191
+ requirements:
192
+ - - ">="
193
+ - !ruby/object:Gem::Version
194
+ version: '0'
195
+ description: MySQL data storing for the web services output when using callbacks.
196
+ email:
197
+ executables:
198
+ - scorer-server
199
+ - scorer
200
+ extensions: []
201
+ extra_rdoc_files: []
202
+ files:
203
+ - README.md
204
+ - bin/scorer
205
+ - bin/scorer-server
206
+ - config.ru
207
+ - config/database.rb
208
+ - lib/opener/scorer.rb
209
+ - lib/opener/scorer/output.rb
210
+ - lib/opener/scorer/output_processor.rb
211
+ - lib/opener/scorer/server.rb
212
+ - lib/opener/scorer/version.rb
213
+ - lib/opener/scorer/views/index.erb
214
+ - opener-scorer.gemspec
215
+ homepage: http://opener-project.github.com/
216
+ licenses: []
217
+ metadata: {}
218
+ post_install_message:
219
+ rdoc_options: []
220
+ require_paths:
221
+ - lib
222
+ required_ruby_version: !ruby/object:Gem::Requirement
223
+ requirements:
224
+ - - ">="
225
+ - !ruby/object:Gem::Version
226
+ version: 1.9.2
227
+ required_rubygems_version: !ruby/object:Gem::Requirement
228
+ requirements:
229
+ - - ">="
230
+ - !ruby/object:Gem::Version
231
+ version: '0'
232
+ requirements: []
233
+ rubyforge_project:
234
+ rubygems_version: 2.2.2
235
+ signing_key:
236
+ specification_version: 4
237
+ summary: MySQL data storing for the web services output when using callbacks.
238
+ test_files: []
239
+ has_rdoc: yard