ontopia-tldr 0.0.1-java

Sign up to get free protection for your applications and to get access to all the features.
data/ChangeLog ADDED
@@ -0,0 +1,7 @@
1
+ # markup: rd
2
+
3
+ = Revision history for ontopia-tldr
4
+
5
+ == 0.0.1 [2013-07-30]
6
+
7
+ * Birthday :-)
data/README ADDED
@@ -0,0 +1,112 @@
1
+ = ontopia-tldr - Tolog Document Retrieval with Ontopia
2
+
3
+ == VERSION
4
+
5
+ This documentation refers to ontopia-tldr version 0.0.1
6
+
7
+
8
+ == DESCRIPTION
9
+
10
+ Ontopia::TLDR is an attempt at bridging the gap between the worlds of formal
11
+ knowledge representation (ontologies, topic maps, etc.) and bibliographic document
12
+ retrieval (bibliographic databases). It allows for retrieving documents from
13
+ bibliographic databases (currently, only Midos[http://progris.de] databases are
14
+ supported) by means of tolog[http://ontopia.net/omnigator/docs/query/tutorial.html],
15
+ Ontopia's[http://ontopia.net] topic map query language.
16
+
17
+ === Deployment
18
+
19
+ Ontopia::TLDR comes as a Sinatra application, so all the standard deployment
20
+ options apply (rackup, Passenger[https://www.phusionpassenger.com/], etc.).
21
+ However, in order to allow for maximum flexibility, you need to supply your
22
+ own <tt>config.ru</tt> file; e.g. (see Ontopia::TLDR for available options):
23
+
24
+ require 'ontopia/tldr'
25
+
26
+ Ontopia::TLDR.set(
27
+ dbm_file: File.expand_path('../tldr.dbm', __FILE__),
28
+ xtm_file: File.expand_path('../tldr.xtm', __FILE__),
29
+
30
+ document_keys: %w[YOUR DOCUMENT KEYS],
31
+ topic_keys: %w[YOUR TOPIC KEYS],
32
+
33
+ title: 'YOUR TITLE'
34
+ )
35
+
36
+ run Ontopia::TLDR
37
+
38
+ Assuming the following directory layout:
39
+
40
+ /srv/tldr
41
+ |
42
+ +-- config.ru
43
+ |
44
+ +-- tldr.dbm
45
+ |
46
+ +-- tldr.xtm
47
+ |
48
+ +-- tmp/
49
+
50
+ Place your database and topic map files there and adjust their paths in the
51
+ <tt>config.ru</tt> file. The <tt>tmp/</tt> directory is used by Passenger
52
+ for the <tt>restart.txt</tt> file.
53
+
54
+ To deploy Ontopia::TLDR with Passenger on Apache, create a symlink in the
55
+ DocumentRoot pointing to the app's <tt>public/</tt> directory (this example
56
+ makes use of current_gem[http://blackwinter.github.com/current_gem]; adjust
57
+ the paths according to your environment):
58
+
59
+ /var/www
60
+ |
61
+ +-- tldr -> /usr/local/jruby/lib/ruby/gems/shared/current/ontopia-tldr/lib/ontopia/tldr/public
62
+
63
+ Then put the following snippet in Apache's VirtualHost configuration:
64
+
65
+ <VirtualHost *:80>
66
+ ...
67
+
68
+ RackBaseURI /tldr
69
+ <Directory /var/www/tldr>
70
+ Options -MultiViews
71
+ PassengerAppRoot /srv/tldr # <-- This (non-standard) line is important
72
+ </Directory>
73
+ </VirtualHost>
74
+
75
+
76
+ == SUPPORTED PLATFORMS
77
+
78
+ Ontopia::TLDR requires JRuby[http://jruby.org]. It has been tested with jruby
79
+ 1.7.4 (1.9.3p392) on OpenJDK 64-Bit Server VM 1.6.0_27-b27 [linux-amd64].
80
+
81
+
82
+ == LINKS
83
+
84
+ <b></b>
85
+ Documentation:: http://blackwinter.github.com/ontopia-tldr
86
+ Source code:: http://github.com/blackwinter/ontopia-tldr
87
+ RubyGem:: http://rubygems.org/gems/ontopia-tldr
88
+ Ontopia:: http://ontopia.net/
89
+ Demo:: http://ixtrieve.fh-koeln.de/ghn
90
+
91
+
92
+ == AUTHORS
93
+
94
+ * Jens Wille <mailto:jens.wille@gmail.com>
95
+
96
+
97
+ == LICENSE AND COPYRIGHT
98
+
99
+ Copyright (C) 2013 Jens Wille
100
+
101
+ ontopia-tldr is free software: you can redistribute it and/or modify it
102
+ under the terms of the GNU Affero General Public License as published by
103
+ the Free Software Foundation, either version 3 of the License, or (at your
104
+ option) any later version.
105
+
106
+ ontopia-tldr is distributed in the hope that it will be useful, but
107
+ WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
108
+ or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
109
+ License for more details.
110
+
111
+ You should have received a copy of the GNU Affero General Public License
112
+ along with ontopia-tldr. If not, see <http://www.gnu.org/licenses/>.
data/Rakefile ADDED
@@ -0,0 +1,22 @@
1
+ require File.expand_path(%q{../lib/ontopia/tldr/version}, __FILE__)
2
+
3
+ begin
4
+ require 'hen'
5
+
6
+ Hen.lay! {{
7
+ :gem => {
8
+ :name => %q{ontopia-tldr},
9
+ :version => Ontopia::TLDR::VERSION,
10
+ :summary => %q{Tolog Document Retrieval with Ontopia.},
11
+ :author => %q{Jens Wille},
12
+ :email => %q{jens.wille@gmail.com},
13
+ :license => %q{AGPL},
14
+ :homepage => :blackwinter,
15
+ :platform => 'java',
16
+ :dependencies => %w[json ontopia-topicmaps ruby-nuggets sinatra],
17
+ :extra_files => FileList['*.sample', 'lib/ontopia/tldr/{public,views}/*'].to_a
18
+ }
19
+ }}
20
+ rescue LoadError => err
21
+ warn "Please install the `hen' gem. (#{err})"
22
+ end
data/config.ru.sample ADDED
@@ -0,0 +1,13 @@
1
+ require 'ontopia/tldr'
2
+
3
+ Ontopia::TLDR.set(
4
+ #xtm_file: '...',
5
+ #dbm_file: '...',
6
+
7
+ #document_keys: %w[...],
8
+ #topic_keys: %w[...],
9
+
10
+ #title: '...'
11
+ )
12
+
13
+ run Ontopia::TLDR
@@ -0,0 +1 @@
1
+ require 'ontopia/tldr'
@@ -0,0 +1,378 @@
1
+ # encoding: utf-8
2
+
3
+ #--
4
+ ###############################################################################
5
+ # #
6
+ # ontopia-tldr -- Tolog Document Retrieval with Ontopia. #
7
+ # #
8
+ # Copyright (C) 2013 Jens Wille #
9
+ # #
10
+ # ontopia-tldr is free software: you can redistribute it and/or modify it #
11
+ # under the terms of the GNU Affero General Public License as published by #
12
+ # the Free Software Foundation, either version 3 of the License, or (at your #
13
+ # option) any later version. #
14
+ # #
15
+ # ontopia-tldr is distributed in the hope that it will be useful, but WITHOUT #
16
+ # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or #
17
+ # FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License #
18
+ # for more details. #
19
+ # #
20
+ # You should have received a copy of the GNU Affero General Public License #
21
+ # along with ontopia-tldr. If not, see <http://www.gnu.org/licenses/>. #
22
+ # #
23
+ ###############################################################################
24
+ #++
25
+
26
+ require 'json'
27
+ require 'sinatra/base'
28
+ require 'nuggets/midos'
29
+ require 'ontopia/topicmaps'
30
+
31
+ module Ontopia
32
+ class TLDR < Sinatra::Base
33
+
34
+ class << self
35
+
36
+ private
37
+
38
+ def jget(*a, &b)
39
+ jroute(:get, *a, &b)
40
+ end
41
+
42
+ def jpost(*a, &b)
43
+ jroute(:post, *a, &b)
44
+ end
45
+
46
+ def jroute(m, r, t, &b)
47
+ e = 'json'; e.prepend('.') unless r.end_with?('/')
48
+ send(m, "#{r}#{e}") { instance_eval(&b); do_json }
49
+ send(m, r, provides: :html) { instance_eval(&b); erb(t) }
50
+ send(m, r, provides: :json) { instance_eval(&b); do_json }
51
+ end
52
+
53
+ end
54
+
55
+ set :root, __FILE__.chomp('.rb')
56
+
57
+ set :otm do
58
+ @__otm__ ||= Ontopia::Topicmaps::Topicmap.new(settings.xtm_file).tap {
59
+ Ontopia::Topicmaps.default_stringifier = :id
60
+ }
61
+ end
62
+
63
+ set :dbm do
64
+ @__dbm__ ||= begin
65
+ dbm, topic_keys = {}, settings.topic_keys
66
+
67
+ topic_index = Hash.new { |h, k| h[k] = {} }
68
+ dbm.define_singleton_method(:topic_index) { topic_index }
69
+
70
+ Nuggets::Midos::Parser.parse_file(settings.dbm_file, settings.dbm_opts) { |id, doc|
71
+ unless (topics = doc.values_at(*topic_keys).compact).empty?
72
+ dbm[id] = doc
73
+ topics.flatten.each { |topic| topic_index[topic][id] = doc }
74
+ end
75
+ }
76
+
77
+ dbm
78
+ end
79
+ end
80
+
81
+ set :topics do
82
+ @__topics__ ||= settings.otm.topics(:name)
83
+ end
84
+
85
+ set :topic_index do
86
+ @__topic_index__ ||= settings.dbm.topic_index
87
+ end
88
+
89
+ set :document_keys, %w[]
90
+ set :topic_keys, %w[]
91
+
92
+ set :title, 'TLDR'
93
+
94
+ set :xtm_file, File.expand_path('../tldr.xtm', __FILE__)
95
+ set :dbm_file, File.expand_path('../tldr.dbm', __FILE__)
96
+ set :dbm_opts, { encoding: 'utf-8', vs: '|' }
97
+
98
+ set :tolog, <<-EOT
99
+ import "http://psi.ontopia.net/tolog/string/" as s
100
+ %s
101
+ select %s from %s?
102
+ EOT
103
+
104
+ set :title_topic, 'TOPIC'
105
+ set :title_query, 'YOUR QUERY'
106
+ set :title_rules, 'CUSTOM INFERENCE RULES (optional)'
107
+
108
+ set :tolog_sample, {
109
+ q: <<-EOT,
110
+ Production($_ : isProducing, $TOPIC : isProductOf),
111
+ topic-name($TOPIC, $PRODUCTNAME),
112
+ value($PRODUCTNAME, $PRODUCTSTRING),
113
+ s:contains($PRODUCTSTRING, "service")
114
+ EOT
115
+ r: <<-EOT
116
+ direct-narrower-term($A, $B) :-
117
+ HierarchicalRelation($A : broaderTermMember,
118
+ $B : narrowerTermMember).
119
+
120
+ strictly-narrower-term($A, $B) :- {
121
+ direct-narrower-term($A, $B) |
122
+ direct-narrower-term($A, $C), strictly-narrower-term($C, $B)
123
+ }.
124
+
125
+ narrower-term($A, $B) :- {
126
+ $A = $B | strictly-narrower-term($A, $B)
127
+ }.
128
+
129
+ narrower-term-1($A, $B) :- {
130
+ $A = $B | direct-narrower-term($A, $B)
131
+ }.
132
+
133
+ narrower-term-2($A, $B) :- {
134
+ narrower-term-1($A, $B) |
135
+ narrower-term-1($A, $C), narrower-term-1($C, $B)
136
+ }.
137
+
138
+ narrower-term-3($A, $B) :- {
139
+ narrower-term-2($A, $B) |
140
+ narrower-term-2($A, $C), narrower-term-1($C, $B)
141
+ }.
142
+
143
+ direct-broader-term($A, $B) :-
144
+ direct-narrower-term($B, $A).
145
+
146
+ strictly-broader-term($A, $B) :-
147
+ strictly-narrower-term($B, $A).
148
+
149
+ broader-term($A, $B) :-
150
+ narrower-term($B, $A).
151
+
152
+ broader-term-1($A, $B) :-
153
+ narrower-term-1($B, $A).
154
+
155
+ broader-term-2($A, $B) :-
156
+ narrower-term-2($B, $A).
157
+
158
+ broader-term-3($A, $B) :-
159
+ narrower-term-3($B, $A).
160
+ EOT
161
+ }
162
+
163
+ tolog_maps_base = <<-EOT
164
+ association-role($ASSOC, $ROLE),
165
+ association-role($ASSOC, $ROLE2),
166
+ role-player($ROLE, $TOPIC2),
167
+ role-player($ROLE2, $TOPIC),
168
+ type($ASSOC, $TYPE),
169
+ $TOPIC /= $TOPIC2
170
+ EOT
171
+
172
+ set :tolog_maps, {
173
+ relations: <<-EOT,
174
+ select $TYPE, $ROLE, $TOPIC from
175
+ #{tolog_maps_base}, $TOPIC2 = %s?
176
+ EOT
177
+ roles: <<-EOT,
178
+ select $ROLE, $TOPIC from
179
+ #{tolog_maps_base}, type($ASSOC, %s)?
180
+ EOT
181
+ types: <<-EOT
182
+ select $TYPE, $TOPIC from
183
+ #{tolog_maps_base}, type($ROLE, %s)?
184
+ EOT
185
+ }
186
+
187
+ helpers ERB::Util
188
+
189
+ not_found do
190
+ @error = 'Not found!'
191
+ erb ''
192
+ end
193
+
194
+ get '/' do
195
+ erb :index
196
+ end
197
+
198
+ jpost '/', :index do
199
+ @query = params[:q]
200
+ @rules = params[:r] || ''
201
+ @param = params[:p] || TITLE_TOPIC
202
+
203
+ if !@query || @query.strip.empty?
204
+ @error = 'Query missing!'
205
+ elsif !@param || @param.strip.empty? || @param =~ /\W/
206
+ @error = 'Invalid projection!'
207
+ elsif (@topics = get_topics).empty?
208
+ @topics = nil
209
+ elsif (@documents = get_documents).empty?
210
+ @documents = nil
211
+ else
212
+ @filter = :documents
213
+ end
214
+ end
215
+
216
+ get '/xtm' do
217
+ do_file(settings.xtm_file, 'xml')
218
+ end
219
+
220
+ jget '/topics', :topics do
221
+ @title = "Topics (#{settings.topics.size})"
222
+ @topics, @filter = settings.topics.keys, :topics
223
+ end
224
+
225
+ jget '/topic/:i', :topic do
226
+ @title = topic_to_s(@topic = params[:i])
227
+ not_found unless settings.topics.include?(@topic)
228
+
229
+ @documents, @filter = settings.topic_index[@topic], :documents
230
+ @relations, @roles, @types = {}, {}, {}
231
+
232
+ get_topic_maps(:relations)
233
+ get_topic_maps(:roles) if @relations.empty?
234
+ get_topic_maps(:types) if @roles.empty?
235
+ end
236
+
237
+ get '/dbm' do
238
+ do_file(settings.dbm_file, 'txt')
239
+ end
240
+
241
+ jget '/documents', :documents do
242
+ @title = "Documents (#{settings.dbm.size})"
243
+ @documents, @filter = settings.dbm, :documents
244
+ end
245
+
246
+ jget '/document/:i', :document do
247
+ @title = "##{@id = params[:i].to_i}"
248
+ not_found unless @document = settings.dbm[@id]
249
+ end
250
+
251
+ private
252
+
253
+ def do_json
254
+ content_type :json
255
+
256
+ JSON.fast_generate({
257
+ d: @document || @documents,
258
+ e: @error,
259
+ i: @id,
260
+ l: @relations,
261
+ o: @roles,
262
+ p: @param,
263
+ q: @query,
264
+ r: @rules,
265
+ t: @topic || @topics,
266
+ y: @types
267
+ }.delete_if { |_, v| !v })
268
+ end
269
+
270
+ def do_file(file, type)
271
+ File.readable?(file) ? send_file(file, type: type) : not_found
272
+ end
273
+
274
+ def get_topics(query = @query, param = @param, rules = @rules)
275
+ settings.otm.query(settings.tolog % [rules, "$#{param}", query])
276
+ rescue => err
277
+ @error = err.to_s
278
+ []
279
+ end
280
+
281
+ def get_topic_maps(name, topic = @topic, hash = nil, str = nil)
282
+ hash ||= instance_variable_get("@#{name}")
283
+ str ||= Ontopia::Topicmaps.id_stringifier
284
+
285
+ query = settings.tolog_maps[name] % topic
286
+ keys = settings.otm.extract_query_projection(query)
287
+
288
+ settings.otm.query_maps(query).each { |map|
289
+ values = map.values_at(*keys).map!(&str)
290
+ topic, key = values.pop, values.pop
291
+
292
+ (values.inject(hash) { |h, k| h[k] ||= {} }[key] ||= []) << topic
293
+ }
294
+ rescue => err
295
+ @error = err.to_s
296
+ end
297
+
298
+ def get_documents(topics = @topics)
299
+ settings.topic_index.values_at(*topics).compact.inject(&:merge)
300
+ end
301
+
302
+ def render_topics(topics = @topics)
303
+ _ul(topics.sort, id: :topics) { |topic|
304
+ _li(link_to_topic(topic), ' (', settings.topic_index[topic].size, ')')
305
+ }
306
+ end
307
+
308
+ def render_documents(documents = @documents)
309
+ _ul(documents.sort_by { |k,| k }, id: :documents) { |id, doc|
310
+ _li(link_to_document(id, doc))
311
+ }
312
+ end
313
+
314
+ def render_topics_hash(hash)
315
+ _ul(hash.map { |k, v| [topic_to_s(k), k, v] }.sort) { |name, key, value|
316
+ _li(link_to_topic(key, name),
317
+ value.is_a?(Hash) ? render_topics_hash(value) :
318
+ _ul(value.sort.uniq) { |topic| _li(link_to_topic(topic)) })
319
+ }
320
+ end
321
+
322
+ def topic_to_s(topic)
323
+ h(settings.topics[topic] || topic.tr('_', ' '))
324
+ end
325
+
326
+ def doc_to_s(id, doc)
327
+ a, t, u, y = doc.values_at(*settings.document_keys)
328
+
329
+ a = a.join('; ') if a.is_a?(Array)
330
+ s = [a, t].compact.join(': ')
331
+
332
+ if s.empty?
333
+ s = "##{id}"
334
+ elsif u
335
+ s << " – #{u}"
336
+ end
337
+
338
+ s << " (#{y})" if y
339
+
340
+ h(s)
341
+ end
342
+
343
+ def link_to_topic(topic, text = topic_to_s(topic))
344
+ _a(text, href: url("/topic/#{h(topic)}"))
345
+ end
346
+
347
+ def link_to_document(id, doc)
348
+ _a(doc_to_s(id, doc), href: url("/document/#{h(id)}"))
349
+ end
350
+
351
+ def _a(*args)
352
+ _tag(:a, *args)
353
+ end
354
+
355
+ def _ul(list, *args)
356
+ _tag(:ul, *args) { |t| list.each { |*i| t << yield(*i) } }
357
+ end
358
+
359
+ def _li(*args)
360
+ _tag(:li, *args)
361
+ end
362
+
363
+ def _tag(name, *args)
364
+ a = args.pop.map { |k, v| %Q{#{h(k)}="#{h(v)}"} } if args.last.is_a?(Hash)
365
+
366
+ t = ["<#{name}#{a.unshift(nil).join(' ') if a}>"]
367
+
368
+ args.each { |s| t << s }
369
+ yield t if block_given?
370
+
371
+ t << "</#{name}>"
372
+ t.join
373
+ end
374
+
375
+ end
376
+ end
377
+
378
+ require_relative 'tldr/version'