opener-outlet 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: df871ce40445a7412cc20219841a08301b1f6b20
4
+ data.tar.gz: 616bf3d5e1dc8d18dead07853648ff90af349282
5
+ SHA512:
6
+ metadata.gz: 48d8ffb897ff377d690a24a5fe3ac54ec1cce3f5750f9d6aafbc15bdd41317b69c1810a26a5eb794bade1053a5c0507a8fc1cf3a026978d5cdf0296b393530b1
7
+ data.tar.gz: a7003db8bff48097aab1f5afca8f70377174a6c07997eacc5a806faf35467777131dd498209bff78ff50ef24f1127d6e460e0f4b517ab392f0d1e126a6db9011
data/README.md ADDED
@@ -0,0 +1,57 @@
1
+ # Outlet
2
+
3
+ Component that stores results from the Opener Web Services chain into a SQLite
4
+ Database and shows them into your browser.
5
+
6
+
7
+ ## Installation
8
+
9
+ ### As part of a Gemfile in a Ruby application
10
+
11
+ Add this line to your application's Gemfile:
12
+
13
+ gem 'opener-outlet',
14
+ :git=>"git@github.com:opener-project/outlet.git"
15
+
16
+ And then execute:
17
+
18
+ $ bundle install
19
+
20
+ ## Usage
21
+
22
+ The Opener Outlet comes equipped with a simple webservice. To start the
23
+ webservice type:
24
+
25
+ outlet-server
26
+
27
+ This will launch a mini webserver with the webservice. It defaults to port 9292,
28
+ so you can access it at:
29
+
30
+ http://localhost:9292
31
+
32
+ To launch it on a different port provide the ```-p [port-number]``` option like
33
+ this:
34
+
35
+ opinion-detector-server -p 1234
36
+
37
+ It then launches at ```http://localhost:1234```
38
+
39
+ When you run a chain of web services using callbacks, the last URL should be the
40
+ one that points to the Outlet Web Service. A unique id is generated and once the
41
+ chain has finished processing the text, you can view the result in the URL that
42
+ you get.
43
+
44
+ ## Contributing
45
+
46
+ ### Procedure
47
+
48
+ 1. Pull it
49
+ 2. Create your feature branch (`git checkout -b features/my-new-feature`)
50
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
51
+ 4. Push to the branch (`git push origin features/my-new-feature`)
52
+ 5. If you're confident, merge your changes into master.
53
+
54
+ # What's next?
55
+
56
+ If you're interested in the opener-outlet, you also might want to check
57
+ out opener-project/outlet.
data/bin/outlet-server ADDED
@@ -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
data/config.ru ADDED
@@ -0,0 +1,4 @@
1
+ require File.expand_path('../lib/opener/outlet', __FILE__)
2
+ require File.expand_path('../lib/opener/outlet/server', __FILE__)
3
+
4
+ run Opener::Outlet::Server
@@ -0,0 +1,31 @@
1
+ require 'active_record'
2
+ require 'activerecord-jdbcmysql-adapter'
3
+
4
+ DB_PASS = ENV["DB_PASS"]
5
+ DB_NAME = ENV["DB_NAME"]
6
+ DB_USER = ENV["DB_USER"]
7
+ DB_HOST = ENV["DB_HOST"]
8
+
9
+ if ENV["RACK_ENV"] == 'production'
10
+ ActiveRecord::Base.establish_connection(
11
+ adapter: 'mysql2',
12
+ database: DB_NAME,
13
+ host: DB_HOST,
14
+ username: DB_USER,
15
+ password: DB_PASS
16
+ )
17
+ else
18
+ ActiveRecord::Base.establish_connection(
19
+ adapter: 'mysql2',
20
+ host: DB_HOST || 'localhost',
21
+ username: 'root',
22
+ password: DB_PASS || '',
23
+ database: DB_NAME || 'opener_development'
24
+ )
25
+ end
26
+
27
+ ActiveRecord::Base.connection.execute("CREATE TABLE IF NOT EXISTS outputs (uuid varchar(40), text text, created_at timestamp DEFAULT CURRENT_TIMESTAMP);")
28
+
29
+ if ActiveRecord::Base.connection.execute("SHOW INDEX FROM outputs").nil?
30
+ ActiveRecord::Base.connection.execute("CREATE INDEX uuid_index ON outputs(uuid);")
31
+ end
@@ -0,0 +1,14 @@
1
+ require_relative 'outlet/output'
2
+ require_relative 'outlet/version'
3
+ require_relative 'outlet/server'
4
+
5
+ module Opener
6
+ class Outlet
7
+ def run(input, uuid)
8
+ output = Output.new(:uuid=>uuid, :text=>input)
9
+ output.save
10
+
11
+ return input
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,11 @@
1
+ require 'active_record'
2
+
3
+ module Opener
4
+ class Outlet
5
+ class Output < ActiveRecord::Base
6
+ attr_accessible :uuid, :text
7
+
8
+ validates_uniqueness_of :uuid
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,58 @@
1
+ body {
2
+ font-family: arial;
3
+ line-height: 2em;
4
+ font-size: 15px;
5
+ margin-top: 3em;
6
+ }
7
+
8
+ .opener {
9
+ width: 800px;
10
+ margin-left: auto;
11
+ margin-right: auto;
12
+ }
13
+
14
+ .entities {
15
+ margin: 1em;
16
+ }
17
+
18
+ .entities:before {
19
+ font-size: 16px;
20
+ font-weight: bold;
21
+ content: "Entities";
22
+ }
23
+
24
+ .opinions {
25
+ margin: 1em;
26
+ }
27
+
28
+ .opinions:before {
29
+ font-size: 16px;
30
+ font-weight: bold;
31
+ content: "Opinions";
32
+ }
33
+
34
+ .properties {
35
+ margin: 1em;
36
+ }
37
+
38
+ .properties:before {
39
+ font-size: 16px;
40
+ font-weight: bold;
41
+ content: "Aspects";
42
+ }
43
+
44
+ .positive {
45
+ background-color: #AFA;
46
+ }
47
+
48
+ .negative {
49
+ background-color: #FAA;
50
+ }
51
+
52
+ .entity {
53
+ border-bottom: 2px dotted #00F;
54
+ }
55
+
56
+ .property {
57
+ border-bottom: 2px solid orange;
58
+ }
@@ -0,0 +1,79 @@
1
+ require 'sinatra'
2
+ require 'nokogiri'
3
+ require 'stringio'
4
+ require_relative './visualizer'
5
+
6
+ require File.expand_path('../../../../config/database', __FILE__)
7
+
8
+ module Opener
9
+ class Outlet
10
+ class Server < Sinatra::Base
11
+
12
+ post '/' do
13
+ output = Output.new
14
+ output.uuid = params[:request_id]
15
+ output.text = params[:input]
16
+ output.save
17
+ end
18
+
19
+ get '/' do
20
+ if params[:request_id]
21
+ redirect "#{url("/")}#{params[:request_id]}"
22
+ else
23
+ erb :index
24
+ end
25
+ end
26
+
27
+ get '/:request_id' do
28
+ unless params[:request_id] == 'favicon.ico'
29
+ begin
30
+ output = Output.find_by_uuid(params[:request_id])
31
+
32
+ if output
33
+ content_type(:xml)
34
+ body(output.text)
35
+ else
36
+ halt(404, "No record found for ID #{params[:request_id]}")
37
+ end
38
+ rescue => error
39
+ error_callback = params[:error_callback]
40
+
41
+ submit_error(error_callback, error.message) if error_callback
42
+
43
+ raise(error)
44
+ end
45
+ end
46
+ end
47
+
48
+ get '/html/:request_id' do
49
+ unless params[:request_id] == 'favicon.ico'
50
+ begin
51
+ output = Output.find_by_uuid(params[:request_id])
52
+ if output
53
+ output = StringIO.new(output.text)
54
+ parser = Opener::Kaf::Visualizer::Parser.new(output)
55
+ doc = parser.parse
56
+ html = Opener::Kaf::Visualizer::HTMLTextPresenter.new(doc)
57
+ @parsed = html.to_html
58
+ erb :show
59
+ else
60
+ halt(404, "No record found for ID #{params[:request_id]}")
61
+ end
62
+ rescue => error
63
+ error_callback = params[:error_callback]
64
+
65
+ submit_error(error_callback, error.message) if error_callback
66
+
67
+ raise(error)
68
+ end
69
+ end
70
+ end
71
+
72
+ private
73
+
74
+ def submit_error(url, message)
75
+ HTTPClient.post(url, :body => {:error => message})
76
+ end
77
+ end # Server
78
+ end # Outlet
79
+ end # Opener
@@ -0,0 +1,5 @@
1
+ module Opener
2
+ class Outlet
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 Outlet</title>
6
+ </head>
7
+ <body>
8
+ <h1>Opener Outlet</h1>
9
+
10
+ <p>This Web Service, stores the output from a chain of Opener components in a
11
+ SQlite 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 Outlet. 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,10 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <link type="text/css" rel="stylesheet" charset="UTF-8" href="/outlet.css"/>
5
+ <title>Opener Outlet</title>
6
+ </head>
7
+ <body>
8
+ <%= @parsed%>
9
+ </body>
10
+ </html>
@@ -0,0 +1,11 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <link type="text/css" rel="stylesheet" charset="UTF-8" href="markdown.css"/>
5
+ <title>Opener Outlet</title>
6
+ </head>
7
+ <body>
8
+ <h1>Opener Outlet</h1>
9
+ <%=parsed%>
10
+ </body>
11
+ </html>
@@ -0,0 +1,278 @@
1
+ #require "opener/kaf/visualizer/version"
2
+ require 'nokogiri'
3
+
4
+ module Opener
5
+ module Kaf
6
+ module Visualizer
7
+ class Parser
8
+ attr_reader :doc
9
+ attr_reader :words, :terms, :entities, :properties, :opinions, :document
10
+
11
+ def initialize(input_file_handler)
12
+ @doc = Nokogiri::XML(input_file_handler)
13
+ end
14
+
15
+ def parse
16
+ @words = parse_words
17
+ @terms = parse_terms
18
+ @entities = parse_entities
19
+ @properties = parse_properties
20
+ @opinions = parse_opinions
21
+ @document = KAFDocument.new(
22
+ :words => words,
23
+ :terms => terms,
24
+ :entities => entities,
25
+ :properties => properties,
26
+ :opinions => opinions
27
+ )
28
+
29
+ return document
30
+ end
31
+
32
+ def parse_words
33
+ parse_elements("//wf", Word)
34
+ end
35
+
36
+ def parse_terms
37
+ # Of course terms should be words here.
38
+ # Dirty Hack, sufficient for now.
39
+ parse_elements("//term", Term, :terms=>words)
40
+ end
41
+
42
+ def parse_entities
43
+ parse_elements("//entity", Entity, :terms=>terms)
44
+ end
45
+
46
+ def parse_properties
47
+ parse_elements("//property", Property, :terms=>terms)
48
+ end
49
+
50
+ def parse_opinions
51
+ parse_elements("//opinion", Opinion, :terms=>terms)
52
+ end
53
+
54
+ def parse_elements(xpath, klass, opts={})
55
+ elements = doc.xpath(xpath)
56
+ lookup_table = Hash.new
57
+ elements.each do |element|
58
+ instance = klass.new(element, opts)
59
+ lookup_table[instance.id] = instance
60
+ end
61
+ lookup_table
62
+ end
63
+
64
+ end
65
+
66
+ class KAFNode
67
+ attr_reader :content, :targets, :tag, :references
68
+
69
+ def initialize(tag, references)
70
+ @references = references
71
+ @tag = tag
72
+
73
+ set_instance_variables
74
+ set_content
75
+ set_targets
76
+ process_subnodes
77
+ end
78
+
79
+ def set_content
80
+ @content = tag.content
81
+ end
82
+
83
+ def set_instance_variables
84
+ tag.keys.each do |key|
85
+ if respond_to?("#{key}=".to_sym)
86
+ send("#{key}=".to_sym, tag[key])
87
+ else
88
+ instance_variable_set("@#{key}", tag[key])
89
+ end
90
+ end
91
+ end
92
+
93
+ def set_targets
94
+ @targets = []
95
+ tag.css("span target").each do |target|
96
+ id = target["id"]
97
+ @targets << references[:terms][id]
98
+ end
99
+ end
100
+
101
+ def process_subnodes
102
+ end
103
+
104
+ def has_target?(*ids)
105
+ ids.flatten.each do |id|
106
+ return true if target_ids.include?(id)
107
+ end
108
+ return false
109
+ end
110
+
111
+ def to_s
112
+ if targets.size > 0
113
+ return targets.map(&:to_s).join(" ")
114
+ else
115
+ return content
116
+ end
117
+ end
118
+
119
+ def target_ids
120
+ @targets.map(&:id)
121
+ end
122
+
123
+ end
124
+
125
+ class Word < KAFNode
126
+ attr_reader :wid, :sent, :para, :offset
127
+
128
+ def id
129
+ wid
130
+ end
131
+
132
+ def offset=(offset)
133
+ @offset = offset.to_i
134
+ end
135
+
136
+ def length
137
+ content.nil? ? 0 : content.length
138
+ end
139
+
140
+ end
141
+
142
+ class Term < KAFNode
143
+ attr_reader :tid, :type, :lemma, :pos, :morphofeat
144
+
145
+ def id
146
+ tid
147
+ end
148
+ end
149
+
150
+ class Entity < KAFNode
151
+ attr_reader :eid, :type
152
+
153
+ def id
154
+ eid
155
+ end
156
+
157
+ def to_s
158
+ "#{type}: #{targets.map(&:to_s).join(", ")}"
159
+ end
160
+ end
161
+
162
+ class Property < KAFNode
163
+ attr_reader :pid, :lemma
164
+
165
+ def id
166
+ pid
167
+ end
168
+
169
+ def to_s
170
+ "#{lemma}: #{targets.map(&:to_s).join(", ")}"
171
+ end
172
+ end
173
+
174
+ class Opinion < KAFNode
175
+ attr_reader :oid, :expression
176
+
177
+ def id
178
+ oid
179
+ end
180
+
181
+ def process_subnodes
182
+ @expression = tag.xpath("opinion_expression").first["polarity"].to_sym
183
+ end
184
+
185
+ def to_s
186
+ "#{expression}: #{targets.map(&:to_s).join(", ")}"
187
+ end
188
+
189
+ end
190
+
191
+ class KAFDocument
192
+ attr_reader :words, :terms, :entities, :properties, :opinions
193
+
194
+ def initialize(opts={})
195
+ @words = opts.fetch(:words)
196
+ @terms = opts.fetch(:terms)
197
+ @entities = opts.fetch(:entities)
198
+ @properties = opts.fetch(:properties)
199
+ @opinions = opts.fetch(:opinions)
200
+ end
201
+
202
+ end
203
+
204
+ class HTMLTextPresenter
205
+ attr_reader :document
206
+ def initialize(document)
207
+ @document = document
208
+ end
209
+
210
+ def to_html
211
+ offset = 0
212
+ prev = Struct.new(:offset).new(0)
213
+
214
+ builder = Nokogiri::HTML::Builder.new do |html|
215
+ html.div(:class=>"opener") do
216
+ html.p do
217
+ document.words.values.sort_by(&:offset).each do |word|
218
+ if offset < word.offset
219
+ spacer = word.offset - offset
220
+ spacer = Array.new(spacer, " ").join
221
+ html.span(spacer)
222
+ end
223
+
224
+ terms = targets_for(:terms, word.id)
225
+ entities = targets_for(:entities, terms)
226
+ opinions = opinions_for(terms)
227
+ properties = targets_for(:properties, terms)
228
+
229
+ generics = []
230
+ generics << "term" if terms.size > 0
231
+ generics << "entity" if entities.size > 0
232
+ generics << "opinion" if opinions.size > 0
233
+ generics << "property" if properties.size > 0
234
+
235
+ classes = [terms, entities, opinions, properties, generics].flatten.uniq
236
+
237
+ word_annotations = classes.join(" ")
238
+
239
+ html.span(word.content, :class=>word_annotations, :id=>word.id)
240
+ offset = word.offset + word.length
241
+ end
242
+ end
243
+
244
+ [:entities, :opinions, :properties].each do |sym|
245
+ html.div(:class=>sym) do
246
+ document.public_send(sym).values.each do |entity|
247
+ html.div(entity.to_s, :id=>entity.id, :class=>entity.target_ids.join(" "))
248
+ end
249
+ end
250
+ end
251
+ end
252
+ end
253
+
254
+ builder.to_html
255
+ end
256
+
257
+ def targets_for(variable, *ids)
258
+ targets = document.public_send(variable.to_sym).values.select do |value|
259
+ value.has_target?(ids.flatten)
260
+ end
261
+
262
+ targets.map(&:id)
263
+ end
264
+
265
+ def opinions_for(*ids)
266
+ targets = document.opinions.values.select do |value|
267
+ value.has_target?(ids.flatten)
268
+ end
269
+
270
+ ids = targets.map(&:id)
271
+ sentiments = targets.map(&:expression)
272
+ return ids.concat(sentiments).uniq
273
+ end
274
+
275
+ end
276
+ end
277
+ end
278
+ end
@@ -0,0 +1,39 @@
1
+ require File.expand_path('../lib/opener/outlet/version', __FILE__)
2
+
3
+ Gem::Specification.new do |gem|
4
+ gem.name = 'opener-outlet'
5
+ gem.version = Opener::Outlet::VERSION
6
+ gem.authors = ['development@olery.com']
7
+ gem.summary = 'Database 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
+ 'visualizer.rb'
20
+ ]).select { |file| File.file?(file) }
21
+
22
+ gem.executables = Dir.glob('bin/*').map { |file| File.basename(file) }
23
+
24
+ gem.add_dependency 'builder', '~>3.0.0'
25
+ gem.add_dependency 'sinatra', '~>1.4.2'
26
+ gem.add_dependency 'nokogiri'
27
+ gem.add_dependency 'httpclient'
28
+ gem.add_dependency 'uuidtools'
29
+ gem.add_dependency 'jdbc-mysql'
30
+ gem.add_dependency 'activerecord-jdbcmysql-adapter'
31
+ gem.add_dependency 'activerecord', '~>3.2'
32
+ gem.add_dependency 'activesupport', '~>3.2'
33
+ gem.add_dependency 'opener-webservice'
34
+
35
+ gem.add_development_dependency 'rspec'
36
+ gem.add_development_dependency 'cucumber'
37
+ gem.add_development_dependency 'pry'
38
+ gem.add_development_dependency 'rake'
39
+ end
data/visualizer.rb ADDED
@@ -0,0 +1,278 @@
1
+ require 'nokogiri'
2
+
3
+ module Opener
4
+ module Kaf
5
+ module Visualizer
6
+ class Parser
7
+ attr_reader :doc
8
+ attr_reader :words, :terms, :entities, :properties, :opinions, :document
9
+
10
+ def initialize(input_file_handler)
11
+ @doc = Nokogiri::XML(input_file_handler)
12
+ end
13
+
14
+ def parse
15
+ @words = parse_words
16
+ @terms = parse_terms
17
+ @entities = parse_entities
18
+ @properties = parse_properties
19
+ @opinions = parse_opinions
20
+ @document = KAFDocument.new(
21
+ :words => words,
22
+ :terms => terms,
23
+ :entities => entities,
24
+ :properties => properties,
25
+ :opinions => opinions
26
+ )
27
+
28
+ return document
29
+ end
30
+
31
+ def parse_words
32
+ parse_elements("//wf", Word)
33
+ end
34
+
35
+ def parse_terms
36
+ # Of course terms should be words here.
37
+ # Dirty Hack, sufficient for now.
38
+ parse_elements("//term", Term, :terms=>words)
39
+ end
40
+
41
+ def parse_entities
42
+ parse_elements("//entity", Entity, :terms=>terms)
43
+ end
44
+
45
+ def parse_properties
46
+ parse_elements("//property", Property, :terms=>terms)
47
+ end
48
+
49
+ def parse_opinions
50
+ parse_elements("//opinion", Opinion, :terms=>terms)
51
+ end
52
+
53
+ def parse_elements(xpath, klass, opts={})
54
+ elements = doc.xpath(xpath)
55
+ lookup_table = Hash.new
56
+ elements.each do |element|
57
+ instance = klass.new(element, opts)
58
+ lookup_table[instance.id] = instance
59
+ end
60
+ lookup_table
61
+ end
62
+
63
+ end
64
+
65
+ class KAFNode
66
+ attr_reader :content, :targets, :tag, :references
67
+
68
+ def initialize(tag, references)
69
+ @references = references
70
+ @tag = tag
71
+
72
+ set_instance_variables
73
+ set_content
74
+ set_targets
75
+ process_subnodes
76
+ end
77
+
78
+ def set_content
79
+ @content = tag.content
80
+ end
81
+
82
+ def set_instance_variables
83
+ tag.keys.each do |key|
84
+ if respond_to?("#{key}=".to_sym)
85
+ send("#{key}=".to_sym, tag[key])
86
+ else
87
+ instance_variable_set("@#{key}", tag[key])
88
+ end
89
+ end
90
+ end
91
+
92
+ def set_targets
93
+ @targets = []
94
+ tag.css("span target").each do |target|
95
+ id = target["id"]
96
+ @targets << references[:terms][id]
97
+ end
98
+ end
99
+
100
+ def process_subnodes
101
+ end
102
+
103
+ def has_target?(*ids)
104
+ ids.flatten.each do |id|
105
+ return true if target_ids.include?(id)
106
+ end
107
+ return false
108
+ end
109
+
110
+ def to_s
111
+ if targets.size > 0
112
+ return targets.map(&:to_s).join(" ")
113
+ else
114
+ return content
115
+ end
116
+ end
117
+
118
+ def target_ids
119
+ @targets.map(&:id)
120
+ end
121
+
122
+ end
123
+
124
+ class Word < KAFNode
125
+ attr_reader :wid, :sent, :para, :offset
126
+
127
+ def id
128
+ wid
129
+ end
130
+
131
+ def offset=(offset)
132
+ @offset = offset.to_i
133
+ end
134
+
135
+ def length
136
+ content.nil? ? 0 : content.length
137
+ end
138
+
139
+ end
140
+
141
+ class Term < KAFNode
142
+ attr_reader :tid, :type, :lemma, :pos, :morphofeat
143
+
144
+ def id
145
+ tid
146
+ end
147
+ end
148
+
149
+ class Entity < KAFNode
150
+ attr_reader :eid, :type
151
+
152
+ def id
153
+ eid
154
+ end
155
+
156
+ def to_s
157
+ "#{type}: #{targets.map(&:to_s).join(", ")}"
158
+ end
159
+ end
160
+
161
+ class Property < KAFNode
162
+ attr_reader :pid, :lemma
163
+
164
+ def id
165
+ pid
166
+ end
167
+
168
+ def to_s
169
+ "#{lemma}: #{targets.map(&:to_s).join(", ")}"
170
+ end
171
+ end
172
+
173
+ class Opinion < KAFNode
174
+ attr_reader :oid, :expression
175
+
176
+ def id
177
+ oid
178
+ end
179
+
180
+ def process_subnodes
181
+ @expression = tag.xpath("//opinion_expression").first["polarity"].to_sym
182
+ end
183
+
184
+ def to_s
185
+ "#{expression}: #{targets.map(&:to_s).join(", ")}"
186
+ end
187
+
188
+ end
189
+
190
+ class KAFDocument
191
+ attr_reader :words, :terms, :entities, :properties, :opinions
192
+
193
+ def initialize(opts={})
194
+ @words = opts.fetch(:words)
195
+ @terms = opts.fetch(:terms)
196
+ @entities = opts.fetch(:entities)
197
+ @properties = opts.fetch(:properties)
198
+ @opinions = opts.fetch(:opinions)
199
+ end
200
+
201
+ end
202
+
203
+ class HTMLTextPresenter
204
+ attr_reader :document
205
+ def initialize(document)
206
+ @document = document
207
+ end
208
+
209
+ def to_html
210
+ offset = 0
211
+ prev = Struct.new(:offset).new(0)
212
+
213
+ builder = Nokogiri::HTML::Builder.new do |html|
214
+ html.div(:class=>"opener") do
215
+ html.p do
216
+ document.words.values.sort_by(&:offset).each do |word|
217
+ if offset < word.offset
218
+ spacer = word.offset - offset
219
+ spacer = Array.new(spacer, " ").join
220
+ html.span(spacer)
221
+ end
222
+
223
+ terms = targets_for(:terms, word.id)
224
+ entities = targets_for(:entities, terms)
225
+ opinions = opinions_for(terms)
226
+ properties = targets_for(:properties, terms)
227
+
228
+ generics = []
229
+ generics << "term" if terms.size > 0
230
+ generics << "entity" if entities.size > 0
231
+ generics << "opinion" if opinions.size > 0
232
+ generics << "property" if properties.size > 0
233
+
234
+ classes = [terms, entities, opinions, properties, generics].flatten.uniq
235
+
236
+ word_annotations = classes.join(" ")
237
+
238
+ html.span(word.content, :class=>word_annotations, :id=>word.id)
239
+ offset = word.offset + word.length
240
+ end
241
+ end
242
+
243
+ [:entities, :opinions, :properties].each do |sym|
244
+ html.div(:class=>sym) do
245
+ document.public_send(sym).values.each do |entity|
246
+ html.div(entity.to_s, :id=>entity.id, :class=>entity.target_ids.join(" "))
247
+ end
248
+ end
249
+ end
250
+ end
251
+ end
252
+
253
+ builder.to_html
254
+ end
255
+
256
+ def targets_for(variable, *ids)
257
+ targets = document.public_send(variable.to_sym).values.select do |value|
258
+ value.has_target?(ids.flatten)
259
+ end
260
+
261
+ targets.map(&:id)
262
+ end
263
+
264
+ def opinions_for(*ids)
265
+ targets = document.opinions.values.select do |value|
266
+ value.has_target?(ids.flatten)
267
+ end
268
+
269
+ ids = targets.map(&:id)
270
+ sentiments = targets.map(&:expression)
271
+ return ids.concat(sentiments).uniq
272
+ end
273
+
274
+ end
275
+ end
276
+ end
277
+ end
278
+
metadata ADDED
@@ -0,0 +1,255 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: opener-outlet
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: 3.0.0
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: 3.0.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: activesupport
127
+ requirement: !ruby/object:Gem::Requirement
128
+ requirements:
129
+ - - "~>"
130
+ - !ruby/object:Gem::Version
131
+ version: '3.2'
132
+ type: :runtime
133
+ prerelease: false
134
+ version_requirements: !ruby/object:Gem::Requirement
135
+ requirements:
136
+ - - "~>"
137
+ - !ruby/object:Gem::Version
138
+ version: '3.2'
139
+ - !ruby/object:Gem::Dependency
140
+ name: opener-webservice
141
+ requirement: !ruby/object:Gem::Requirement
142
+ requirements:
143
+ - - ">="
144
+ - !ruby/object:Gem::Version
145
+ version: '0'
146
+ type: :runtime
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: rspec
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: cucumber
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: pry
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
+ - !ruby/object:Gem::Dependency
196
+ name: rake
197
+ requirement: !ruby/object:Gem::Requirement
198
+ requirements:
199
+ - - ">="
200
+ - !ruby/object:Gem::Version
201
+ version: '0'
202
+ type: :development
203
+ prerelease: false
204
+ version_requirements: !ruby/object:Gem::Requirement
205
+ requirements:
206
+ - - ">="
207
+ - !ruby/object:Gem::Version
208
+ version: '0'
209
+ description: Database storing for the web services output when using callbacks.
210
+ email:
211
+ executables:
212
+ - outlet-server
213
+ extensions: []
214
+ extra_rdoc_files: []
215
+ files:
216
+ - README.md
217
+ - bin/outlet-server
218
+ - config.ru
219
+ - config/database.rb
220
+ - lib/opener/outlet.rb
221
+ - lib/opener/outlet/output.rb
222
+ - lib/opener/outlet/public/outlet.css
223
+ - lib/opener/outlet/server.rb
224
+ - lib/opener/outlet/version.rb
225
+ - lib/opener/outlet/views/index.erb
226
+ - lib/opener/outlet/views/show.erb
227
+ - lib/opener/outlet/views/show.html.erb
228
+ - lib/opener/outlet/visualizer.rb
229
+ - opener-outlet.gemspec
230
+ - visualizer.rb
231
+ homepage: http://opener-project.github.com/
232
+ licenses: []
233
+ metadata: {}
234
+ post_install_message:
235
+ rdoc_options: []
236
+ require_paths:
237
+ - lib
238
+ required_ruby_version: !ruby/object:Gem::Requirement
239
+ requirements:
240
+ - - ">="
241
+ - !ruby/object:Gem::Version
242
+ version: 1.9.2
243
+ required_rubygems_version: !ruby/object:Gem::Requirement
244
+ requirements:
245
+ - - ">="
246
+ - !ruby/object:Gem::Version
247
+ version: '0'
248
+ requirements: []
249
+ rubyforge_project:
250
+ rubygems_version: 2.2.2
251
+ signing_key:
252
+ specification_version: 4
253
+ summary: Database storing for the web services output when using callbacks.
254
+ test_files: []
255
+ has_rdoc: yard