opener-s3-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: 10341e220c8473c0a1122c20b7ab9dc7b1f8452e
4
+ data.tar.gz: e4a9dcf12a019669c8879838ff5f94ac3fc09a9b
5
+ SHA512:
6
+ metadata.gz: 975df73d82655fe9b781aed0e485a7805c68f3746442dd226d55217abe9acb411b98ea153174035dd242adf346f436ef49cc9b7b2f9a0a5325cebd727eecd073
7
+ data.tar.gz: ab71c20499c3bae2a3ca8a6e35710520786275c59d47b3ef49b2bacecfedbf133e9243206f258a14c1e136a0bbcb99befe467f026af76e335c3782fa0cf39360
data/README.md ADDED
@@ -0,0 +1,57 @@
1
+ # S3-Outlet: Stores the results in an AWS S3 bucket.
2
+
3
+ Component that stores results from the Opener Web Services chain into an S3
4
+ bucket.
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-s3-outlet',
14
+ :git=>"git@github.com:opener-project/s3-outlet.git"
15
+
16
+ And then execute:
17
+
18
+ $ bundle install
19
+
20
+ ## Usage
21
+
22
+ The Opener S3 Outlet comes equipped with a simple webservice. To start the
23
+ webservice type:
24
+
25
+ s3-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
+ s3-outlet-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 S3 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-s3-outlet, you also might want to check
57
+ out opener-project/s3-outlet.
@@ -0,0 +1,9 @@
1
+ #!/usr/bin/env ruby
2
+ #
3
+ require 'rubygems'
4
+ require 'opener/daemons'
5
+
6
+ exec_path = File.expand_path("../../exec/s3-outlet.rb", __FILE__)
7
+ Opener::Daemons::Controller.new(:name=>"s3-outlet",
8
+ :exec_path=>exec_path)
9
+
@@ -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/s3_outlet', __FILE__)
2
+ require File.expand_path('../lib/opener/s3_outlet/server', __FILE__)
3
+
4
+ run Opener::S3Outlet::Server
data/config/aws.rb ADDED
@@ -0,0 +1,8 @@
1
+ # These details are tied to the "opener" AWS user.
2
+ AWS.config(
3
+ :access_key_id => 'AKIAIZH4THHTIHYS22NA',
4
+ :secret_access_key => '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',
5
+ :sqs_region => 'eu-west-1',
6
+ :sqs_endpoint => 'sqs.eu-west-1.amazonaws.com',
7
+ :s3_endpoint => 's3-eu-west-1.amazonaws.com',
8
+ )
data/exec/s3-outlet.rb ADDED
@@ -0,0 +1,33 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'opener/daemons'
4
+ require_relative '../lib/opener/s3_outlet'
5
+
6
+ parser = Opener::Daemons::OptParser.new do |opts, options|
7
+ opts.on("-b", "--bucket NAME", "Bucket name") do |v|
8
+ options[:bucket] = v
9
+ end
10
+ opts.on("-d", "--directory NAME", "Directory name") do |v|
11
+ options[:directory] = v
12
+ end
13
+ end
14
+
15
+ class S3OutletFactory
16
+ attr_reader :dir, :bucket
17
+
18
+ def initialize(options)
19
+ @bucket = options.fetch(:bucket)
20
+ @dir = options.fetch(:directory)
21
+ end
22
+
23
+ def new
24
+ Opener::S3Outlet.new(:directory=>dir, :bucket=>bucket)
25
+ end
26
+ end
27
+
28
+ options = parser.parse!(ARGV)
29
+ factory = S3OutletFactory.new(options)
30
+
31
+ daemon = Opener::Daemons::Daemon.new(factory, options)
32
+ daemon.start
33
+
@@ -0,0 +1,39 @@
1
+ require 'aws-sdk'
2
+
3
+ require_relative '../../config/aws'
4
+ require_relative 's3_outlet/s3_output'
5
+ require_relative 's3_outlet/version'
6
+ require_relative 's3_outlet/server'
7
+
8
+ module Opener
9
+ class S3Outlet
10
+ attr_reader :options
11
+
12
+ def initialize(options={})
13
+ @options = options
14
+ end
15
+
16
+ def run(input)
17
+ options[:text] = input
18
+ S3Output.create(options)
19
+
20
+ return input #Return original input so that we can keep on chaining.
21
+ end
22
+
23
+ def self.s3
24
+ @s3 ||= AWS::S3.new
25
+ end
26
+
27
+ def self.bucket
28
+ @bucket ||= s3.buckets[bucket_name]
29
+ end
30
+
31
+ def self.bucket_name
32
+ return "opener-outlet"
33
+ end
34
+
35
+ def self.dir
36
+ return "webservice"
37
+ end
38
+ end
39
+ 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,59 @@
1
+ require 'uuidtools'
2
+
3
+ module Opener
4
+ class S3Outlet
5
+ class S3Output
6
+ #attr_accessor :params, :bucket
7
+ attr_reader :uuid, :text, :dir, :bucket, :params
8
+
9
+ def initialize(params = {})
10
+ @uuid = params.fetch(:uuid) { UUIDTools::UUID.random_create }
11
+ @text = params.fetch(:text)
12
+ @dir = params.fetch(:directory, S3Outlet.dir)
13
+
14
+ bucket = params[:bucket]
15
+ if bucket.kind_of?(String)
16
+ @bucket = S3Outlet.s3.buckets[bucket]
17
+ else
18
+ @bucket = bucket || S3Outlet.bucket
19
+ end
20
+ end
21
+
22
+ def save
23
+ object = bucket.objects[filename]
24
+ object.write(text)
25
+ end
26
+
27
+ def self.find(uuid, dir=nil, bucket=nil)
28
+ filename = construct_filename(uuid, dir)
29
+ bucket = bucket || default_bucket
30
+
31
+ if bucket.objects[filename].exists?
32
+ file = bucket.objects[filename]
33
+ return file.read
34
+ else
35
+ return nil
36
+ end
37
+ end
38
+
39
+ def self.create(params={})
40
+ new(params).save
41
+ end
42
+
43
+ def filename
44
+ self.class.construct_filename(uuid, dir)
45
+ end
46
+
47
+ private
48
+
49
+ def self.construct_filename(uuid, dir=nil)
50
+ dir = S3Outlet.dir if dir.nil? || dir.empty?
51
+ File.join(dir, "#{uuid}.kaf")
52
+ end
53
+
54
+ def self.default_bucket
55
+ S3Outlet.bucket
56
+ end
57
+ end
58
+ end
59
+ end
@@ -0,0 +1,76 @@
1
+ require 'sinatra'
2
+ require 'nokogiri'
3
+ require 'stringio'
4
+ require_relative './visualizer'
5
+
6
+ require File.expand_path('../../../../config/aws', __FILE__)
7
+
8
+ module Opener
9
+ class S3Outlet
10
+ class Server < Sinatra::Base
11
+
12
+ post '/' do
13
+ output = S3Output.create(:uuid => params[:request_id], :text => params[:input])
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 = S3Output.find(params[:request_id])
28
+
29
+ if output
30
+ content_type(:xml)
31
+ body(output)
32
+ else
33
+ halt(404, "No record found for ID #{params[:request_id]}")
34
+ end
35
+ rescue => error
36
+ error_callback = params[:error_callback]
37
+
38
+ submit_error(error_callback, error.message) if error_callback
39
+
40
+ raise(error)
41
+ end
42
+ end
43
+ end
44
+
45
+ get '/html/:request_id' do
46
+ unless params[:request_id] == 'favicon.ico'
47
+ begin
48
+ output = S3Output.find(params[:request_id])
49
+ if output
50
+ output = StringIO.new(output)
51
+ parser = Opener::Kaf::Visualizer::Parser.new(output)
52
+ doc = parser.parse
53
+ html = Opener::Kaf::Visualizer::HTMLTextPresenter.new(doc)
54
+ @parsed = html.to_html
55
+ erb :show
56
+ else
57
+ halt(404, "No record found for ID #{params[:request_id]}")
58
+ end
59
+ rescue => error
60
+ error_callback = params[:error_callback]
61
+
62
+ submit_error(error_callback, error.message) if error_callback
63
+
64
+ raise(error)
65
+ end
66
+ end
67
+ end
68
+
69
+ private
70
+
71
+ def submit_error(url, message)
72
+ HTTPClient.post(url, :body => {:error => message})
73
+ end
74
+ end # Server
75
+ end # S3Outlet
76
+ end # Opener
@@ -0,0 +1,5 @@
1
+ module Opener
2
+ class S3Outlet
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 S3 Outlet</title>
6
+ </head>
7
+ <body>
8
+ <h1>Opener S3 Outlet</h1>
9
+
10
+ <p>This Web Service, stores the output from a chain of Opener components in a
11
+ S3 bucket.</p>
12
+ <p>When using callbacks, the last Callback URL should be the URL that points
13
+ to S3 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 S3 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 S3 Outlet</title>
6
+ </head>
7
+ <body>
8
+ <h1>Opener S3 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,38 @@
1
+ require File.expand_path('../lib/opener/s3_outlet/version', __FILE__)
2
+
3
+ Gem::Specification.new do |gem|
4
+ gem.name = 'opener-s3-outlet'
5
+ gem.version = Opener::S3Outlet::VERSION
6
+ gem.authors = ['development@olery.com']
7
+ gem.summary = 'S3 Bucket 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
+ 'exec/**/*',
16
+ 'lib/**/*',
17
+ 'config.ru',
18
+ '*.gemspec',
19
+ 'README.md',
20
+ 'visualizer.rb'
21
+ ]).select { |file| File.file?(file) }
22
+
23
+ gem.executables = Dir.glob('bin/*').map { |file| File.basename(file) }
24
+
25
+ gem.add_dependency 'builder'
26
+ gem.add_dependency 'sinatra', '~>1.4.2'
27
+ gem.add_dependency 'nokogiri'
28
+ gem.add_dependency 'httpclient'
29
+ gem.add_dependency 'uuidtools'
30
+ gem.add_dependency 'opener-webservice'
31
+ gem.add_dependency 'opener-daemons'
32
+ gem.add_dependency 'aws-sdk'
33
+
34
+ gem.add_development_dependency 'rspec'
35
+ gem.add_development_dependency 'cucumber'
36
+ gem.add_development_dependency 'pry'
37
+ gem.add_development_dependency 'rake'
38
+ 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,230 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: opener-s3-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: '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: opener-webservice
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: opener-daemons
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: aws-sdk
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - ">="
116
+ - !ruby/object:Gem::Version
117
+ version: '0'
118
+ type: :runtime
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - ">="
123
+ - !ruby/object:Gem::Version
124
+ version: '0'
125
+ - !ruby/object:Gem::Dependency
126
+ name: rspec
127
+ requirement: !ruby/object:Gem::Requirement
128
+ requirements:
129
+ - - ">="
130
+ - !ruby/object:Gem::Version
131
+ version: '0'
132
+ type: :development
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: cucumber
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: pry
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: rake
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
+ description: S3 Bucket data storing for the web services output when using callbacks.
182
+ email:
183
+ executables:
184
+ - s3-outlet-daemon
185
+ - s3-outlet-server
186
+ extensions: []
187
+ extra_rdoc_files: []
188
+ files:
189
+ - README.md
190
+ - bin/s3-outlet-daemon
191
+ - bin/s3-outlet-server
192
+ - config.ru
193
+ - config/aws.rb
194
+ - exec/s3-outlet.rb
195
+ - lib/opener/s3_outlet.rb
196
+ - lib/opener/s3_outlet/public/outlet.css
197
+ - lib/opener/s3_outlet/s3_output.rb
198
+ - lib/opener/s3_outlet/server.rb
199
+ - lib/opener/s3_outlet/version.rb
200
+ - lib/opener/s3_outlet/views/index.erb
201
+ - lib/opener/s3_outlet/views/show.erb
202
+ - lib/opener/s3_outlet/views/show.html.erb
203
+ - lib/opener/s3_outlet/visualizer.rb
204
+ - opener-s3-outlet.gemspec
205
+ - visualizer.rb
206
+ homepage: http://opener-project.github.com/
207
+ licenses: []
208
+ metadata: {}
209
+ post_install_message:
210
+ rdoc_options: []
211
+ require_paths:
212
+ - lib
213
+ required_ruby_version: !ruby/object:Gem::Requirement
214
+ requirements:
215
+ - - ">="
216
+ - !ruby/object:Gem::Version
217
+ version: 1.9.2
218
+ required_rubygems_version: !ruby/object:Gem::Requirement
219
+ requirements:
220
+ - - ">="
221
+ - !ruby/object:Gem::Version
222
+ version: '0'
223
+ requirements: []
224
+ rubyforge_project:
225
+ rubygems_version: 2.2.2
226
+ signing_key:
227
+ specification_version: 4
228
+ summary: S3 Bucket data storing for the web services output when using callbacks.
229
+ test_files: []
230
+ has_rdoc: yard