birds 0.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.
- checksums.yaml +7 -0
- data/COPYING +663 -0
- data/ChangeLog +7 -0
- data/README +41 -0
- data/Rakefile +46 -0
- data/config.ru.sample +4 -0
- data/lib/birds/app/controllers.rb +115 -0
- data/lib/birds/app/helpers/controller.rb +110 -0
- data/lib/birds/app/helpers/view.rb +131 -0
- data/lib/birds/app/helpers.rb +31 -0
- data/lib/birds/app/public/favicon.ico +0 -0
- data/lib/birds/app/settings.rb +86 -0
- data/lib/birds/app/views/_documents.erb +13 -0
- data/lib/birds/app/views/_facets.erb +30 -0
- data/lib/birds/app/views/browse.erb +22 -0
- data/lib/birds/app/views/document.erb +12 -0
- data/lib/birds/app/views/index.erb +64 -0
- data/lib/birds/app/views/layout.erb +83 -0
- data/lib/birds/app/views/scroll.erb +23 -0
- data/lib/birds/app.rb +52 -0
- data/lib/birds/rack_app.rb +2 -0
- data/lib/birds/version.rb +27 -0
- data/lib/birds.rb +67 -0
- metadata +169 -0
data/README
ADDED
@@ -0,0 +1,41 @@
|
|
1
|
+
= Birds - Bibliographic information retrieval & document search
|
2
|
+
|
3
|
+
== VERSION
|
4
|
+
|
5
|
+
This documentation refers to Birds version 0.0.0
|
6
|
+
|
7
|
+
|
8
|
+
== DESCRIPTION
|
9
|
+
|
10
|
+
Experimental information retrieval system for bibliographic data.
|
11
|
+
|
12
|
+
|
13
|
+
== LINKS
|
14
|
+
|
15
|
+
Demo:: http://ixtrieve.fh-koeln.de/birds/litie
|
16
|
+
Documentation:: https://blackwinter.github.com/birds
|
17
|
+
Source code:: https://github.com/blackwinter/birds
|
18
|
+
RubyGem:: https://rubygems.org/gems/birds
|
19
|
+
|
20
|
+
|
21
|
+
== AUTHORS
|
22
|
+
|
23
|
+
* Jens Wille <mailto:jens.wille@gmail.com>
|
24
|
+
|
25
|
+
|
26
|
+
== LICENSE AND COPYRIGHT
|
27
|
+
|
28
|
+
Copyright (C) 2014-2015 Jens Wille
|
29
|
+
|
30
|
+
Birds is free software: you can redistribute it and/or modify it under the
|
31
|
+
terms of the GNU Affero General Public License as published by the Free
|
32
|
+
Software Foundation, either version 3 of the License, or (at your option)
|
33
|
+
any later version.
|
34
|
+
|
35
|
+
Birds is distributed in the hope that it will be useful, but WITHOUT ANY
|
36
|
+
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
37
|
+
FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for
|
38
|
+
more details.
|
39
|
+
|
40
|
+
You should have received a copy of the GNU Affero General Public License
|
41
|
+
along with Birds. If not, see <http://www.gnu.org/licenses/>.
|
data/Rakefile
ADDED
@@ -0,0 +1,46 @@
|
|
1
|
+
require File.expand_path(%q{../lib/birds/version}, __FILE__)
|
2
|
+
|
3
|
+
begin
|
4
|
+
require 'hen'
|
5
|
+
|
6
|
+
Hen.lay! {{
|
7
|
+
gem: {
|
8
|
+
name: %q{birds},
|
9
|
+
version: Birds::VERSION,
|
10
|
+
summary: %q{Bibliographic information retrieval & document search.},
|
11
|
+
description: %q{Experimental information retrieval system for bibliographic data.},
|
12
|
+
author: %q{Jens Wille},
|
13
|
+
email: %q{jens.wille@gmail.com},
|
14
|
+
license: %q{AGPL-3.0},
|
15
|
+
homepage: :blackwinter,
|
16
|
+
extra_files: FileList['*.sample', 'lib/**/{public,views}/*'].to_a,
|
17
|
+
dependencies: {
|
18
|
+
'sinatra-bells' => ['~> 0.0', '>= 0.0.2'],
|
19
|
+
'solr4r' => ['~> 0.0', '>= 0.0.4'],
|
20
|
+
'unicode' => '~> 0.4'
|
21
|
+
},
|
22
|
+
|
23
|
+
required_ruby_version: '>= 1.9.3'
|
24
|
+
}
|
25
|
+
}}
|
26
|
+
rescue LoadError => err
|
27
|
+
warn "Please install the `hen' gem. (#{err})"
|
28
|
+
end
|
29
|
+
|
30
|
+
task c: :_ do
|
31
|
+
require 'irb'; IRB.start
|
32
|
+
end
|
33
|
+
|
34
|
+
task r: :_ do
|
35
|
+
load Gem.bin_path('rack', 'rackup')
|
36
|
+
end
|
37
|
+
|
38
|
+
task :_ do
|
39
|
+
ARGV.clear
|
40
|
+
|
41
|
+
b = File.expand_path('~/devel') # XXX
|
42
|
+
$:.unshift(*%w[birds solr4r sinatra-bells].map { |i| File.join(b, i, 'lib') })
|
43
|
+
|
44
|
+
require 'birds'
|
45
|
+
require 'birds/app'
|
46
|
+
end
|
data/config.ru.sample
ADDED
@@ -0,0 +1,115 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
#--
|
4
|
+
###############################################################################
|
5
|
+
# #
|
6
|
+
# Birds -- Bibliographic information retrieval & document search #
|
7
|
+
# #
|
8
|
+
# Copyright (C) 2014-2015 Jens Wille #
|
9
|
+
# #
|
10
|
+
# Birds is free software: you can redistribute it and/or modify it under the #
|
11
|
+
# terms of the GNU Affero General Public License as published by the Free #
|
12
|
+
# Software Foundation, either version 3 of the License, or (at your option) #
|
13
|
+
# any later version. #
|
14
|
+
# #
|
15
|
+
# Birds is distributed in the hope that it will be useful, but WITHOUT ANY #
|
16
|
+
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS #
|
17
|
+
# FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for #
|
18
|
+
# more details. #
|
19
|
+
# #
|
20
|
+
# You should have received a copy of the GNU Affero General Public License #
|
21
|
+
# along with Birds. If not, see <http://www.gnu.org/licenses/>. #
|
22
|
+
# #
|
23
|
+
###############################################################################
|
24
|
+
#++
|
25
|
+
|
26
|
+
require 'unicode'
|
27
|
+
|
28
|
+
class Birds::App
|
29
|
+
|
30
|
+
get '/', render: :index do
|
31
|
+
result = settings.solr.count
|
32
|
+
@page_title, @page_title_extra = 'Home', "#{result.to_i} documents"
|
33
|
+
end
|
34
|
+
|
35
|
+
get '/search', render: :index do
|
36
|
+
@page_title = 'Search'
|
37
|
+
|
38
|
+
@query, @filter = params[:q] || params[:qq], Array(params[:fq])
|
39
|
+
|
40
|
+
paginate_query(@query, :results,
|
41
|
+
facet_params(debug: 'results', fq: @filter))
|
42
|
+
|
43
|
+
@explain, @facets = explain_result(@result), facet_counts(@result)
|
44
|
+
end
|
45
|
+
|
46
|
+
get '/browse', render: :browse do
|
47
|
+
@page_title, @fields = 'Browse', settings.browse_fields
|
48
|
+
end
|
49
|
+
|
50
|
+
get '/browse/:field', render: :browse do
|
51
|
+
@page_title, @field = 'Browse', params[:field]
|
52
|
+
bad_request unless labels = settings.browse_fields[@field]
|
53
|
+
|
54
|
+
@hierarchy, @page_title_extra = terms, labels.last
|
55
|
+
end
|
56
|
+
|
57
|
+
get '/browse/:field/*', render: :browse do
|
58
|
+
@page_title, @field = 'Browse', params[:field]
|
59
|
+
bad_request unless labels = settings.browse_fields[@field]
|
60
|
+
|
61
|
+
category = params[:splat].join('/')
|
62
|
+
bad_request if category.empty?
|
63
|
+
|
64
|
+
@page_title_extra = "#{labels.first}: #{category}"
|
65
|
+
|
66
|
+
@result = search_query(@field => category)
|
67
|
+
not_found if @result.empty?
|
68
|
+
end
|
69
|
+
|
70
|
+
get '/scroll', render: :scroll do
|
71
|
+
@page_title, @fields = 'Scroll', settings.scroll_fields
|
72
|
+
end
|
73
|
+
|
74
|
+
get '/scroll/:field', render: :scroll do
|
75
|
+
@page_title, @field = 'Scroll', params[:field]
|
76
|
+
bad_request unless label = settings.scroll_fields[@field]
|
77
|
+
|
78
|
+
@letters = terms.group_by { |term,|
|
79
|
+
Unicode.upcase(term[0])
|
80
|
+
}.map { |letter, values|
|
81
|
+
[letter, values.map(&:last).inject(:+)] if letter =~ /\p{Letter}/
|
82
|
+
}.compact
|
83
|
+
|
84
|
+
@page_title_extra = label
|
85
|
+
end
|
86
|
+
|
87
|
+
get '/scroll/:field/:letter', render: :scroll do
|
88
|
+
@page_title, @field = 'Scroll', params[:field]
|
89
|
+
bad_request unless label = settings.scroll_fields[@field]
|
90
|
+
|
91
|
+
@letter = params[:letter]
|
92
|
+
paginate_query({ @field => "#{@letter}*" }, :documents)
|
93
|
+
|
94
|
+
@page_title_extra = "#{label}: #{@letter}"
|
95
|
+
end
|
96
|
+
|
97
|
+
get '/document/*', render: :document do
|
98
|
+
id = params[:splat].join('/')
|
99
|
+
|
100
|
+
bad_request if id.empty?
|
101
|
+
not_found unless @document = search_document(id)
|
102
|
+
|
103
|
+
@similar = @document.more_like_this(settings.mlt_fields,
|
104
|
+
debugQuery: true, mlt: { boost: true, mintf: 1, minwl: 4 })
|
105
|
+
|
106
|
+
@explain = explain_result(@similar)
|
107
|
+
|
108
|
+
@page_title, @page_title_extra = 'Document', "##{id}"
|
109
|
+
end
|
110
|
+
|
111
|
+
get 'schema.xml' do
|
112
|
+
erb :schema
|
113
|
+
end
|
114
|
+
|
115
|
+
end
|
@@ -0,0 +1,110 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
#--
|
4
|
+
###############################################################################
|
5
|
+
# #
|
6
|
+
# Birds -- Bibliographic information retrieval & document search #
|
7
|
+
# #
|
8
|
+
# Copyright (C) 2014-2015 Jens Wille #
|
9
|
+
# #
|
10
|
+
# Birds is free software: you can redistribute it and/or modify it under the #
|
11
|
+
# terms of the GNU Affero General Public License as published by the Free #
|
12
|
+
# Software Foundation, either version 3 of the License, or (at your option) #
|
13
|
+
# any later version. #
|
14
|
+
# #
|
15
|
+
# Birds is distributed in the hope that it will be useful, but WITHOUT ANY #
|
16
|
+
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS #
|
17
|
+
# FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for #
|
18
|
+
# more details. #
|
19
|
+
# #
|
20
|
+
# You should have received a copy of the GNU Affero General Public License #
|
21
|
+
# along with Birds. If not, see <http://www.gnu.org/licenses/>. #
|
22
|
+
# #
|
23
|
+
###############################################################################
|
24
|
+
#++
|
25
|
+
|
26
|
+
class Birds::App
|
27
|
+
|
28
|
+
module Helpers
|
29
|
+
|
30
|
+
module Controller
|
31
|
+
|
32
|
+
def search(params)
|
33
|
+
settings.solr.json_query(params)
|
34
|
+
end
|
35
|
+
|
36
|
+
def search_query(query, params = {})
|
37
|
+
search(params.merge(q: query, fl: '*,score', defType: 'edismax'))
|
38
|
+
end
|
39
|
+
|
40
|
+
def search_document(id)
|
41
|
+
search(q: { id: id }).first
|
42
|
+
end
|
43
|
+
|
44
|
+
def paginate_query(query, what, query_params = {}, per_page = 20)
|
45
|
+
return unless query
|
46
|
+
|
47
|
+
page = params[:page].to_i
|
48
|
+
page = 1 if page < 1
|
49
|
+
|
50
|
+
@prev_page, @next_page = page - 1, page + 1
|
51
|
+
|
52
|
+
@result = search_query(query, query_params.merge(
|
53
|
+
rows: per_page, start: @offset = @prev_page * per_page))
|
54
|
+
|
55
|
+
@page_title_extra = '%d %s, page %d of %d' % [
|
56
|
+
@result, what, page, @total_pages = (@result.to_i / per_page.to_f).ceil]
|
57
|
+
|
58
|
+
@prev_page = nil if @prev_page < 1
|
59
|
+
@next_page = nil if @next_page > @total_pages
|
60
|
+
end
|
61
|
+
|
62
|
+
def facet_params(params = {})
|
63
|
+
params.merge(f: f = params[:f] || {}, facet: {
|
64
|
+
field: fields = [], range: ranges = [], mincount: 1
|
65
|
+
}).tap { settings.facet_fields.each { |facet, (_, options)|
|
66
|
+
options.nil? ? fields << facet : begin ranges << facet
|
67
|
+
((f[facet] ||= {})[:facet] ||= {})[:range] = options
|
68
|
+
end
|
69
|
+
} }
|
70
|
+
end
|
71
|
+
|
72
|
+
def terms(f = @field)
|
73
|
+
settings.solr.json('terms', terms: { fl: f, limit: -1 }).to_h[f].sort
|
74
|
+
end
|
75
|
+
|
76
|
+
def explain_result(result)
|
77
|
+
result % %w[debug explain] if result
|
78
|
+
end
|
79
|
+
|
80
|
+
def facet_counts(result)
|
81
|
+
return {} unless result && result.to_i > 1
|
82
|
+
|
83
|
+
exclude, prepare = [@query, *@filter], lambda { |facet_hash, &block|
|
84
|
+
facet_hash.delete_if { |key, hash|
|
85
|
+
gap = block[hash] if block
|
86
|
+
|
87
|
+
hash.delete_if { |term,|
|
88
|
+
exclude.include?(facet_query(key, term, *gap)) }.size < 2
|
89
|
+
}
|
90
|
+
}
|
91
|
+
|
92
|
+
prepare.(result.facet_fields.to_h).merge(
|
93
|
+
prepare.(result.facet_ranges.to_h) { |hash|
|
94
|
+
counts, before, start, gap = hash
|
95
|
+
.values_at(*%w[counts before start gap])
|
96
|
+
|
97
|
+
hash.clear
|
98
|
+
hash[-start] = before if before > 1
|
99
|
+
counts.each { |value, count| hash[value.to_i] = count }
|
100
|
+
|
101
|
+
hash.singleton_class.send(:define_method, :gap) { gap }
|
102
|
+
gap
|
103
|
+
})
|
104
|
+
end
|
105
|
+
|
106
|
+
end
|
107
|
+
|
108
|
+
end
|
109
|
+
|
110
|
+
end
|
@@ -0,0 +1,131 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
#--
|
4
|
+
###############################################################################
|
5
|
+
# #
|
6
|
+
# Birds -- Bibliographic information retrieval & document search #
|
7
|
+
# #
|
8
|
+
# Copyright (C) 2014-2015 Jens Wille #
|
9
|
+
# #
|
10
|
+
# Birds is free software: you can redistribute it and/or modify it under the #
|
11
|
+
# terms of the GNU Affero General Public License as published by the Free #
|
12
|
+
# Software Foundation, either version 3 of the License, or (at your option) #
|
13
|
+
# any later version. #
|
14
|
+
# #
|
15
|
+
# Birds is distributed in the hope that it will be useful, but WITHOUT ANY #
|
16
|
+
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS #
|
17
|
+
# FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for #
|
18
|
+
# more details. #
|
19
|
+
# #
|
20
|
+
# You should have received a copy of the GNU Affero General Public License #
|
21
|
+
# along with Birds. If not, see <http://www.gnu.org/licenses/>. #
|
22
|
+
# #
|
23
|
+
###############################################################################
|
24
|
+
#++
|
25
|
+
|
26
|
+
class Birds::App
|
27
|
+
|
28
|
+
module Helpers
|
29
|
+
|
30
|
+
module View
|
31
|
+
|
32
|
+
def nav_item(path, name, title = nil)
|
33
|
+
li_(link_to(name, path, title: title), class: active?(path))
|
34
|
+
end
|
35
|
+
|
36
|
+
def link_to_document(document)
|
37
|
+
label = format_label(settings.document_label, document)
|
38
|
+
link_to(h(label), :document, document['id'])
|
39
|
+
end
|
40
|
+
|
41
|
+
def link_to_search(text, query, *filters)
|
42
|
+
options = filters.last.is_a?(Hash) ? filters.pop : {}
|
43
|
+
|
44
|
+
link_to(text, :search, options.merge(params:
|
45
|
+
query ? query_params(query, filters) : { q: filters.first }))
|
46
|
+
end
|
47
|
+
|
48
|
+
def link_to_filter(text, query, filter)
|
49
|
+
link_to_search(h(text), query, filter, *@filter)
|
50
|
+
end
|
51
|
+
|
52
|
+
def link_to_field(field, value, query = nil)
|
53
|
+
link_to_filter(value, query, field_query(field, value))
|
54
|
+
end
|
55
|
+
|
56
|
+
def link_to_facet(field, value, gap = nil)
|
57
|
+
link_to_filter(
|
58
|
+
gap ? range_label(value, gap) : value,
|
59
|
+
@query, facet_query(field, value, gap))
|
60
|
+
end
|
61
|
+
|
62
|
+
def facet_query(field, value, gap = nil)
|
63
|
+
gap ? range_query(field, value, gap) : field_query(field, value)
|
64
|
+
end
|
65
|
+
|
66
|
+
def field_query(field, value)
|
67
|
+
%Q{#{field}:"#{value}"}
|
68
|
+
end
|
69
|
+
|
70
|
+
def range_query(field, value, gap)
|
71
|
+
value < 0 ?
|
72
|
+
%Q|#{field}:[* TO #{-value}}| :
|
73
|
+
%Q|#{field}:[#{value} TO #{value + gap}}|
|
74
|
+
end
|
75
|
+
|
76
|
+
def range_label(value, gap)
|
77
|
+
value < 0 ? "before #{-value}" : "#{value}–#{value + gap - 1}"
|
78
|
+
end
|
79
|
+
|
80
|
+
def query_params(q = @query, fq = @filter)
|
81
|
+
{ q: q, 'fq[]' => fq }
|
82
|
+
end
|
83
|
+
|
84
|
+
def pagination_for(*args)
|
85
|
+
params = args.last.is_a?(Hash) ? args.pop.reject { |_, v| v.nil? } : {}
|
86
|
+
|
87
|
+
ul_([
|
88
|
+
[@prev_page, :first, 1],
|
89
|
+
[@prev_page, :prev, @prev_page],
|
90
|
+
[@next_page, :next, @next_page],
|
91
|
+
[@next_page, :last, @total_pages]
|
92
|
+
], class: 'pagination') { |condition, type, page|
|
93
|
+
li_(link_to_if(condition, pagination_icon(type),
|
94
|
+
*args, params: params.merge(page: page)),
|
95
|
+
class: disabled?(condition))
|
96
|
+
}
|
97
|
+
end
|
98
|
+
|
99
|
+
def pagination_icon(type)
|
100
|
+
tag_(:span, glyphicon(*{
|
101
|
+
first: [:fast_backward, 'First page'],
|
102
|
+
prev: [:backward, 'Previous page'],
|
103
|
+
next: [:forward, 'Next page'],
|
104
|
+
last: [:fast_forward, 'Last page']
|
105
|
+
}[type]))
|
106
|
+
end
|
107
|
+
|
108
|
+
def glyphicon(name, title = nil)
|
109
|
+
name = name.to_s.tr('_', '-')
|
110
|
+
tag_(:span, class: "glyphicon glyphicon-#{name}", title: title)
|
111
|
+
end
|
112
|
+
|
113
|
+
def values_for(key, document = @document)
|
114
|
+
Array(document[key = key.to_s]).dup.tap { |values|
|
115
|
+
return if values.empty?
|
116
|
+
|
117
|
+
if settings.browse_fields.include?(key)
|
118
|
+
values.map! { |v| link_to(v = h(v), :browse, key, v) }
|
119
|
+
elsif field = settings.linkable_fields[key]
|
120
|
+
values.map! { |v| link_to_field(field, v) }
|
121
|
+
else
|
122
|
+
values.map!(&method(:h))
|
123
|
+
end
|
124
|
+
}
|
125
|
+
end
|
126
|
+
|
127
|
+
end
|
128
|
+
|
129
|
+
end
|
130
|
+
|
131
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
#--
|
4
|
+
###############################################################################
|
5
|
+
# #
|
6
|
+
# Birds -- Bibliographic information retrieval & document search #
|
7
|
+
# #
|
8
|
+
# Copyright (C) 2014-2015 Jens Wille #
|
9
|
+
# #
|
10
|
+
# Birds is free software: you can redistribute it and/or modify it under the #
|
11
|
+
# terms of the GNU Affero General Public License as published by the Free #
|
12
|
+
# Software Foundation, either version 3 of the License, or (at your option) #
|
13
|
+
# any later version. #
|
14
|
+
# #
|
15
|
+
# Birds is distributed in the hope that it will be useful, but WITHOUT ANY #
|
16
|
+
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS #
|
17
|
+
# FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for #
|
18
|
+
# more details. #
|
19
|
+
# #
|
20
|
+
# You should have received a copy of the GNU Affero General Public License #
|
21
|
+
# along with Birds. If not, see <http://www.gnu.org/licenses/>. #
|
22
|
+
# #
|
23
|
+
###############################################################################
|
24
|
+
#++
|
25
|
+
|
26
|
+
require_relative 'helpers/controller'
|
27
|
+
require_relative 'helpers/view'
|
28
|
+
|
29
|
+
class Birds::App
|
30
|
+
helpers *Helpers.constants.map { |mod| Helpers.const_get(mod) }
|
31
|
+
end
|
Binary file
|
@@ -0,0 +1,86 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
#--
|
4
|
+
###############################################################################
|
5
|
+
# #
|
6
|
+
# Birds -- Bibliographic information retrieval & document search #
|
7
|
+
# #
|
8
|
+
# Copyright (C) 2014-2015 Jens Wille #
|
9
|
+
# #
|
10
|
+
# Birds is free software: you can redistribute it and/or modify it under the #
|
11
|
+
# terms of the GNU Affero General Public License as published by the Free #
|
12
|
+
# Software Foundation, either version 3 of the License, or (at your option) #
|
13
|
+
# any later version. #
|
14
|
+
# #
|
15
|
+
# Birds is distributed in the hope that it will be useful, but WITHOUT ANY #
|
16
|
+
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS #
|
17
|
+
# FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for #
|
18
|
+
# more details. #
|
19
|
+
# #
|
20
|
+
# You should have received a copy of the GNU Affero General Public License #
|
21
|
+
# along with Birds. If not, see <http://www.gnu.org/licenses/>. #
|
22
|
+
# #
|
23
|
+
###############################################################################
|
24
|
+
#++
|
25
|
+
|
26
|
+
class Birds::App
|
27
|
+
|
28
|
+
set :site_title,
|
29
|
+
'Birds – Bibliographic information retrieval & document search'
|
30
|
+
|
31
|
+
set :bootstrap_url,
|
32
|
+
'//netdna.bootstrapcdn.com/bootstrap/3.1.1'
|
33
|
+
|
34
|
+
set :solr_client do
|
35
|
+
require 'solr4r'
|
36
|
+
Solr4R::Client
|
37
|
+
end
|
38
|
+
|
39
|
+
solr_opts = Hash[%w[host port path core].map { |key| [key, "solr_#{key}"] }]
|
40
|
+
|
41
|
+
set :solr_opts do
|
42
|
+
Hash[solr_opts.map { |key, opt| [key.to_sym, settings.send(opt)] }]
|
43
|
+
end
|
44
|
+
|
45
|
+
solr_opts.each { |key, opt| set(opt) {
|
46
|
+
settings.solr_client.const_get("DEFAULT_#{key.upcase}") } }
|
47
|
+
|
48
|
+
set :solr do
|
49
|
+
settings.solr_client.new(settings.solr_opts)
|
50
|
+
end
|
51
|
+
|
52
|
+
set_hash :display_fields, %w[
|
53
|
+
title author year abstract language theme subject
|
54
|
+
], %w[_txt] do |f, l| [f, l.capitalize] end
|
55
|
+
|
56
|
+
set_hash :linkable_fields, %w[
|
57
|
+
author theme subject
|
58
|
+
], %w[_txt _ss]
|
59
|
+
|
60
|
+
set_hash :facet_fields, %w[
|
61
|
+
author language theme subject
|
62
|
+
].insert(2,
|
63
|
+
[:year, start: 1900, end: 2100, gap: 10, other: 'before']
|
64
|
+
) do |f, o| ["#{f}_#{o ? :i : :ss}", ["#{f}s".capitalize, o]] end
|
65
|
+
|
66
|
+
set :mlt_fields, %w[
|
67
|
+
author_txt title_txt abstract_txt subject_txt
|
68
|
+
]
|
69
|
+
|
70
|
+
set :browse_fields, {
|
71
|
+
# 'cat' => %w[Category Categories],
|
72
|
+
# 'manu_exact' => %w[Manufacturer Manufacturers]
|
73
|
+
}
|
74
|
+
|
75
|
+
set :scroll_fields, {
|
76
|
+
# 'author_s' => 'Author'
|
77
|
+
}
|
78
|
+
|
79
|
+
set :sample_queries, {
|
80
|
+
'Sample query 1' => 'author_txt:fugmann',
|
81
|
+
'Sample query 2' => 'fugmann -author_txt:fugmann'
|
82
|
+
}
|
83
|
+
|
84
|
+
set :document_label, '{author_ss:%s: }{title_ss( : )}{year_ss: (%s)}'
|
85
|
+
|
86
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
<ol start="<%= defined?(start) && start || 1 %>" style="margin-top: 1em"><!-- XXX -->
|
2
|
+
<% for document in documents; id, score = document['id'], h('%.2f' % document['score']); next if @document && id == @document['id'] %>
|
3
|
+
<li>
|
4
|
+
<%= link_to_document(document) %>
|
5
|
+
<% unless defined?(explain) && explain %>
|
6
|
+
<span class="badge"><%= score %></span>
|
7
|
+
<% else; eid = h("explain-#{id}") %>
|
8
|
+
<span class="badge toggle-explain" data-toggle="collapse" data-target="#<%= eid %>" title="Toggle score explanation"><%= score %></span>
|
9
|
+
<pre id="<%= eid %>" class="collapse small"><%=h explain[id] %></pre>
|
10
|
+
<% end %>
|
11
|
+
</li>
|
12
|
+
<% end %>
|
13
|
+
</ol>
|
@@ -0,0 +1,30 @@
|
|
1
|
+
<% position = 0; for facet, (label, _) in settings.facet_fields; counts = @facets[facet] or next; collapse = max = nil %>
|
2
|
+
<div class="panel panel-default">
|
3
|
+
<div class="panel-heading">
|
4
|
+
<h4 class="panel-title toggle-facet" data-toggle="collapse" data-target="#<%= fid = "facet-#{h(facet)}" %>" title="Toggle facet list"><%=h label %></h4>
|
5
|
+
</div>
|
6
|
+
<ul class="list-group compact-list-group collapse<%= ' in' if (position += 1) < 4 %>" id="<%= fid %>">
|
7
|
+
<% if counts.respond_to?(:gap) %>
|
8
|
+
<% for (value, count), index in counts.reverse_each.with_index %>
|
9
|
+
<li class="list-group-item clearfix<%= ' facet-collapse collapse' if collapse ||= index > 2 %>">
|
10
|
+
<%= link_to_facet(facet, value, counts.gap) %>
|
11
|
+
<span class="badge"><%= count %></span>
|
12
|
+
</li>
|
13
|
+
<% end %>
|
14
|
+
<% else %>
|
15
|
+
<% for (value, count), index in counts.each_with_index; max ||= count %>
|
16
|
+
<li class="list-group-item clearfix<%= ' facet-collapse collapse' if collapse ||= index > 5 || count < max / 10 %>">
|
17
|
+
<%= link_to_facet(facet, value) %>
|
18
|
+
<span class="badge"><%= count %></span>
|
19
|
+
</li>
|
20
|
+
<% end %>
|
21
|
+
<% end %>
|
22
|
+
<% if collapse %>
|
23
|
+
<li class="list-group-item toggle-facet" data-toggle="collapse" data-target="#<%= fid %> .facet-collapse.collapse">
|
24
|
+
<span class="facet-collapse collapse in">More…</span>
|
25
|
+
<span class="facet-collapse collapse">Less…</span>
|
26
|
+
</li>
|
27
|
+
<% end %>
|
28
|
+
</ul>
|
29
|
+
</div>
|
30
|
+
<% end %>
|
@@ -0,0 +1,22 @@
|
|
1
|
+
<% if @result %>
|
2
|
+
<ol>
|
3
|
+
<% for document in @result %>
|
4
|
+
<li><%= link_to_document(document) %></li>
|
5
|
+
<% end %>
|
6
|
+
</ol>
|
7
|
+
<% elsif @fields %>
|
8
|
+
<ul>
|
9
|
+
<% for field, labels in @fields %>
|
10
|
+
<li><%= link_to(h(labels.last), :browse, field) %></li>
|
11
|
+
<% end %>
|
12
|
+
</ul>
|
13
|
+
<% else %>
|
14
|
+
<ul>
|
15
|
+
<% for category, count in @hierarchy %>
|
16
|
+
<li>
|
17
|
+
<%= link_to(h(category), :browse, @field, category) %>
|
18
|
+
<span class="badge"><%=h count %></span>
|
19
|
+
</li>
|
20
|
+
<% end %>
|
21
|
+
</ul>
|
22
|
+
<% end %>
|
@@ -0,0 +1,12 @@
|
|
1
|
+
<dl class="dl-horizontal">
|
2
|
+
<% for key, label in settings.display_fields; values = values_for(key) or next %>
|
3
|
+
<dt class="text-muted" title="<%=h key %>:"><%=h label || key %></dt>
|
4
|
+
<dd><%= values.join('<br />') %></dd>
|
5
|
+
<% end %>
|
6
|
+
</dl>
|
7
|
+
|
8
|
+
<% unless @similar.empty? %>
|
9
|
+
<h2>Similar documents</h2>
|
10
|
+
|
11
|
+
<%= erb :_documents, locals: { documents: @similar, explain: @explain } %>
|
12
|
+
<% end %>
|