birds 0.0.0 → 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/ChangeLog +4 -0
- data/README +2 -2
- data/Rakefile +12 -8
- data/lib/birds/app/controllers.rb +20 -12
- data/lib/birds/app/helpers/controller.rb +102 -58
- data/lib/birds/app/helpers/view.rb +125 -99
- data/lib/birds/app/settings.rb +51 -17
- data/lib/birds/app/views/_display.erb +6 -0
- data/lib/birds/app/views/_documents.erb +4 -3
- data/lib/birds/app/views/_facets.erb +20 -3
- data/lib/birds/app/views/_spellcheck.erb +9 -0
- data/lib/birds/app/views/document.erb +4 -9
- data/lib/birds/app/views/index.erb +5 -4
- data/lib/birds/app/views/layout.erb +7 -2
- data/lib/birds/version.rb +1 -1
- metadata +14 -24
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 7fe5c975045c5cd3f27f17496e5fe94c89517e2f
|
4
|
+
data.tar.gz: 125796780f21a82f7266294d4f3e64a4671b25e4
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 6036a21f6e4b8e32d5fec07ce207fddf108e942ca488e2e29690512af14c5447fbd310866170de79307f5ec8730435646f255880ab30db01b96ba5a9ab3ecde9
|
7
|
+
data.tar.gz: f6f880fc852613a626cb2cf8274ab5602bdc05fa03ee62599ce1dd1aa81113df646dfc42dd41066b98478cf3e152be1c6ab6ce6bca7a97c17858f2fd694c4102
|
data/ChangeLog
CHANGED
data/README
CHANGED
@@ -2,7 +2,7 @@
|
|
2
2
|
|
3
3
|
== VERSION
|
4
4
|
|
5
|
-
This documentation refers to Birds version 0.0.
|
5
|
+
This documentation refers to Birds version 0.0.1
|
6
6
|
|
7
7
|
|
8
8
|
== DESCRIPTION
|
@@ -25,7 +25,7 @@ RubyGem:: https://rubygems.org/gems/birds
|
|
25
25
|
|
26
26
|
== LICENSE AND COPYRIGHT
|
27
27
|
|
28
|
-
Copyright (C) 2014-
|
28
|
+
Copyright (C) 2014-2016 Jens Wille
|
29
29
|
|
30
30
|
Birds is free software: you can redistribute it and/or modify it under the
|
31
31
|
terms of the GNU Affero General Public License as published by the Free
|
data/Rakefile
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
|
1
|
+
require_relative 'lib/birds/version'
|
2
2
|
|
3
3
|
begin
|
4
4
|
require 'hen'
|
@@ -15,8 +15,8 @@ begin
|
|
15
15
|
homepage: :blackwinter,
|
16
16
|
extra_files: FileList['*.sample', 'lib/**/{public,views}/*'].to_a,
|
17
17
|
dependencies: {
|
18
|
-
'sinatra-bells' =>
|
19
|
-
'solr4r' =>
|
18
|
+
'sinatra-bells' => '~> 0.4',
|
19
|
+
'solr4r' => '~> 0.3',
|
20
20
|
'unicode' => '~> 0.4'
|
21
21
|
},
|
22
22
|
|
@@ -27,19 +27,23 @@ rescue LoadError => err
|
|
27
27
|
warn "Please install the `hen' gem. (#{err})"
|
28
28
|
end
|
29
29
|
|
30
|
-
|
30
|
+
desc 'Run IRB console'
|
31
|
+
task console: :load do
|
31
32
|
require 'irb'; IRB.start
|
32
33
|
end
|
33
34
|
|
34
|
-
|
35
|
+
desc 'Run rackup command'
|
36
|
+
task rackup: :load do
|
35
37
|
load Gem.bin_path('rack', 'rackup')
|
36
38
|
end
|
37
39
|
|
38
|
-
task :
|
40
|
+
task :load do
|
39
41
|
ARGV.clear
|
40
42
|
|
41
|
-
b =
|
42
|
-
|
43
|
+
b = ENV['DEVEL'] and %w[sinatra-bells solr4r].each { |i|
|
44
|
+
$:.unshift(File.join(File.expand_path(b), i, 'lib')) }
|
45
|
+
|
46
|
+
$:.unshift('lib')
|
43
47
|
|
44
48
|
require 'birds'
|
45
49
|
require 'birds/app'
|
@@ -5,7 +5,7 @@
|
|
5
5
|
# #
|
6
6
|
# Birds -- Bibliographic information retrieval & document search #
|
7
7
|
# #
|
8
|
-
# Copyright (C) 2014-
|
8
|
+
# Copyright (C) 2014-2016 Jens Wille #
|
9
9
|
# #
|
10
10
|
# Birds is free software: you can redistribute it and/or modify it under the #
|
11
11
|
# terms of the GNU Affero General Public License as published by the Free #
|
@@ -28,8 +28,10 @@ require 'unicode'
|
|
28
28
|
class Birds::App
|
29
29
|
|
30
30
|
get '/', render: :index do
|
31
|
-
|
32
|
-
|
31
|
+
@page_title, @page_title_extra = 'Home',
|
32
|
+
'%d documents' % count = settings.solr.count.to_i
|
33
|
+
|
34
|
+
@page_title_query = settings.solr_match_all_query if count > 0
|
33
35
|
end
|
34
36
|
|
35
37
|
get '/search', render: :index do
|
@@ -38,9 +40,14 @@ class Birds::App
|
|
38
40
|
@query, @filter = params[:q] || params[:qq], Array(params[:fq])
|
39
41
|
|
40
42
|
paginate_query(@query, :results,
|
41
|
-
facet_params(debug: 'results', fq: @filter))
|
43
|
+
highlighting_params(facet_params(debug: 'results', fq: @filter)))
|
44
|
+
|
45
|
+
@explain = explain_result
|
46
|
+
@facets = facet_counts
|
47
|
+
@highlighting = highlighting
|
42
48
|
|
43
|
-
@
|
49
|
+
@spellcheck = spellcheck_collations(
|
50
|
+
spell_query(@query, spellcheck_params(fq: @filter))).to_a
|
44
51
|
end
|
45
52
|
|
46
53
|
get '/browse', render: :browse do
|
@@ -100,16 +107,17 @@ class Birds::App
|
|
100
107
|
bad_request if id.empty?
|
101
108
|
not_found unless @document = search_document(id)
|
102
109
|
|
103
|
-
@similar =
|
104
|
-
|
110
|
+
mlt_fields, @similar, @explain = settings.mlt_fields, {}, {}
|
111
|
+
mlt_fields = { nil => mlt_fields } unless mlt_fields.is_a?(Hash)
|
105
112
|
|
106
|
-
|
113
|
+
mlt_fields.each { |key, fields|
|
114
|
+
mlt = @document.more_like_this(fields,
|
115
|
+
debug: 'results', mlt: { boost: true, mintf: 1, minwl: 4 })
|
107
116
|
|
108
|
-
|
109
|
-
|
117
|
+
@similar[key], @explain[key] = mlt, explain_result(mlt) unless mlt.empty?
|
118
|
+
}
|
110
119
|
|
111
|
-
|
112
|
-
erb :schema
|
120
|
+
@page_title, @page_title_extra = 'Document', "##{id}"
|
113
121
|
end
|
114
122
|
|
115
123
|
end
|
@@ -5,7 +5,7 @@
|
|
5
5
|
# #
|
6
6
|
# Birds -- Bibliographic information retrieval & document search #
|
7
7
|
# #
|
8
|
-
# Copyright (C) 2014-
|
8
|
+
# Copyright (C) 2014-2016 Jens Wille #
|
9
9
|
# #
|
10
10
|
# Birds is free software: you can redistribute it and/or modify it under the #
|
11
11
|
# terms of the GNU Affero General Public License as published by the Free #
|
@@ -23,84 +23,128 @@
|
|
23
23
|
###############################################################################
|
24
24
|
#++
|
25
25
|
|
26
|
-
class Birds
|
26
|
+
class Birds
|
27
27
|
|
28
|
-
|
28
|
+
class App
|
29
29
|
|
30
|
-
module
|
30
|
+
module Helpers
|
31
31
|
|
32
|
-
|
33
|
-
settings.solr.json_query(params)
|
34
|
-
end
|
32
|
+
module Controller
|
35
33
|
|
36
|
-
|
37
|
-
|
38
|
-
|
34
|
+
def solr_query(path, params = {}, options = {}, &block)
|
35
|
+
settings.solr.json_query(params, options, path, &block)
|
36
|
+
end
|
39
37
|
|
40
|
-
|
41
|
-
|
42
|
-
|
38
|
+
def search(params)
|
39
|
+
solr_query(settings.solr_select_path, params)
|
40
|
+
end
|
43
41
|
|
44
|
-
|
45
|
-
|
42
|
+
def spell_query(query, params = {})
|
43
|
+
solr_query(settings.solr_spell_path, params.merge(q: query))
|
44
|
+
end
|
46
45
|
|
47
|
-
|
48
|
-
|
46
|
+
def search_query(query, params = {})
|
47
|
+
search(params.merge(q: query, fl: '*,score', defType: 'edismax'))
|
48
|
+
end
|
49
49
|
|
50
|
-
|
50
|
+
def search_document(id)
|
51
|
+
search(q: { id: id }).first
|
52
|
+
end
|
51
53
|
|
52
|
-
|
53
|
-
|
54
|
+
def paginate_query(query, what, query_params = {})
|
55
|
+
return unless query
|
54
56
|
|
55
|
-
|
56
|
-
|
57
|
+
page, per_page = clip(params[:page].to_i),
|
58
|
+
clip(Integer(params[:per_page] || settings.default_per_page))
|
57
59
|
|
58
|
-
|
59
|
-
@next_page = nil if @next_page > @total_pages
|
60
|
-
end
|
60
|
+
@prev_page, @next_page = page - 1, page + 1
|
61
61
|
|
62
|
-
|
63
|
-
|
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
|
62
|
+
@result = search_query(query, query_params.merge(
|
63
|
+
rows: per_page, start: @offset = @prev_page * per_page))
|
71
64
|
|
72
|
-
|
73
|
-
|
74
|
-
end
|
65
|
+
@page_title_extra = '%d %s, page %d of %d' % [@result, what,
|
66
|
+
page, @total_pages = clip(@result.to_i.fdiv(per_page).ceil)]
|
75
67
|
|
76
|
-
|
77
|
-
|
78
|
-
|
68
|
+
@prev_page = nil if @prev_page < 1
|
69
|
+
@next_page = nil if @next_page > @total_pages
|
70
|
+
end
|
71
|
+
|
72
|
+
def facet_params(params = {})
|
73
|
+
params.merge(f: f = params[:f] || {}, facet: {
|
74
|
+
field: fields = [], range: ranges = [], mincount: 1
|
75
|
+
}).tap { settings.facet_fields.each { |facet, (_, options)|
|
76
|
+
options.nil? ? fields << facet : begin ranges << facet
|
77
|
+
((f[facet] ||= {})[:facet] ||= {})[:range] = options
|
78
|
+
end
|
79
|
+
} }
|
80
|
+
end
|
81
|
+
|
82
|
+
def highlighting_params(params = {})
|
83
|
+
params.merge(hl: {
|
84
|
+
fl: settings.highlighting_fields,
|
85
|
+
snippets: settings.highlighting_snippets,
|
86
|
+
fragsize: settings.highlighting_fragsize,
|
87
|
+
mergeContiguous: true,
|
88
|
+
preserveMulti: true,
|
89
|
+
simple: {
|
90
|
+
pre: settings.highlighting_prefix,
|
91
|
+
post: settings.highlighting_postfix
|
92
|
+
}
|
93
|
+
})
|
94
|
+
end
|
79
95
|
|
80
|
-
|
81
|
-
|
96
|
+
def spellcheck_params(params = {})
|
97
|
+
params.merge(spellcheck: { collate: true })
|
98
|
+
end
|
82
99
|
|
83
|
-
|
84
|
-
|
85
|
-
|
100
|
+
def terms(f = @field)
|
101
|
+
settings.solr.json('terms', terms: { fl: f, limit: -1 }).to_h[f].sort
|
102
|
+
end
|
86
103
|
|
87
|
-
|
88
|
-
|
104
|
+
def explain_result(result = @result)
|
105
|
+
result.debug_explain if result && result.debug?
|
106
|
+
end
|
107
|
+
|
108
|
+
def facet_counts(result = @result)
|
109
|
+
return {} unless result && result.to_i > 1 && result.facet_counts?
|
110
|
+
|
111
|
+
exclude, prepare = [@query, *@filter], lambda { |facet_hash, &block|
|
112
|
+
facet_hash.delete_if { |key, hash|
|
113
|
+
gap = block[hash] if block
|
114
|
+
|
115
|
+
hash.delete_if { |term,|
|
116
|
+
exclude.include?(facet_query(key, term, *gap)) }.size < 2
|
117
|
+
}
|
89
118
|
}
|
90
|
-
}
|
91
119
|
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
120
|
+
prepare.(result.facet_fields.to_h).merge(
|
121
|
+
prepare.(result.facet_ranges.to_h) { |hash|
|
122
|
+
counts, before, start, gap = hash
|
123
|
+
.values_at(*%w[counts before start gap])
|
124
|
+
|
125
|
+
hash.clear
|
126
|
+
hash[-start] = before if before && before > 1
|
127
|
+
counts.each { |value, count| hash[value.to_i] = count }
|
128
|
+
|
129
|
+
hash.singleton_class.send(:define_method, :gap) { gap }
|
130
|
+
gap
|
131
|
+
})
|
132
|
+
end
|
133
|
+
|
134
|
+
def highlighting(result = @result)
|
135
|
+
result.highlighting if result && result.highlighting?
|
136
|
+
end
|
137
|
+
|
138
|
+
def spellcheck_collations(result = @result, limit = nil)
|
139
|
+
result.spellcheck_collations(limit) if result && result.spellcheck?
|
140
|
+
end
|
141
|
+
|
142
|
+
private
|
96
143
|
|
97
|
-
|
98
|
-
|
99
|
-
|
144
|
+
def clip(num, min = 1)
|
145
|
+
num < min ? min : num
|
146
|
+
end
|
100
147
|
|
101
|
-
hash.singleton_class.send(:define_method, :gap) { gap }
|
102
|
-
gap
|
103
|
-
})
|
104
148
|
end
|
105
149
|
|
106
150
|
end
|
@@ -5,7 +5,7 @@
|
|
5
5
|
# #
|
6
6
|
# Birds -- Bibliographic information retrieval & document search #
|
7
7
|
# #
|
8
|
-
# Copyright (C) 2014-
|
8
|
+
# Copyright (C) 2014-2016 Jens Wille #
|
9
9
|
# #
|
10
10
|
# Birds is free software: you can redistribute it and/or modify it under the #
|
11
11
|
# terms of the GNU Affero General Public License as published by the Free #
|
@@ -23,105 +23,131 @@
|
|
23
23
|
###############################################################################
|
24
24
|
#++
|
25
25
|
|
26
|
-
class Birds
|
26
|
+
class Birds
|
27
|
+
|
28
|
+
class App
|
29
|
+
|
30
|
+
module Helpers
|
31
|
+
|
32
|
+
module View
|
33
|
+
|
34
|
+
def nav_item(path, name, title = nil)
|
35
|
+
li_(link_to(name, path, title: title), class: active?(path))
|
36
|
+
end
|
37
|
+
|
38
|
+
def hl(text)
|
39
|
+
h(text)
|
40
|
+
.gsub(h(settings.highlighting_prefix), settings.highlighting_prefix)
|
41
|
+
.gsub(h(settings.highlighting_postfix), settings.highlighting_postfix)
|
42
|
+
end
|
43
|
+
|
44
|
+
def cl(text)
|
45
|
+
text
|
46
|
+
.gsub(settings.highlighting_prefix, '')
|
47
|
+
.gsub(settings.highlighting_postfix, '')
|
48
|
+
end
|
49
|
+
|
50
|
+
def link_to_document(document, highlighting = {})
|
51
|
+
label = format_label(settings.document_label) { |k|
|
52
|
+
h, d = highlighting.delete(k), document[k]
|
53
|
+
h ? d.map { |v| h.find { |w| cl(w) == v } || v } : d
|
54
|
+
}
|
55
|
+
|
56
|
+
label = "##{document.id}" if label.empty?
|
57
|
+
|
58
|
+
link_to(hl(label), :document, document.id)
|
59
|
+
end
|
60
|
+
|
61
|
+
def link_to_search(text, query, *filters)
|
62
|
+
options = filters.last.is_a?(Hash) ? filters.pop : {}
|
63
|
+
|
64
|
+
link_to(text, :search, options.merge(params:
|
65
|
+
query ? query_params(query, filters) : { q: filters.first }))
|
66
|
+
end
|
67
|
+
|
68
|
+
def link_to_filter(text, query, filter)
|
69
|
+
link_to_search(hl(text), query, cl(filter), *@filter)
|
70
|
+
end
|
71
|
+
|
72
|
+
def link_to_field(field, value, query = nil)
|
73
|
+
link_to_filter(value, query, field_query(field, value))
|
74
|
+
end
|
75
|
+
|
76
|
+
def link_to_facet(field, value, gap = nil)
|
77
|
+
link_to_filter(
|
78
|
+
gap ? range_label(value, gap) : value,
|
79
|
+
@query, facet_query(field, value, gap))
|
80
|
+
end
|
81
|
+
|
82
|
+
def facet_query(field, value, gap = nil)
|
83
|
+
gap ? range_query(field, value, gap) : field_query(field, value)
|
84
|
+
end
|
85
|
+
|
86
|
+
def field_query(field, value)
|
87
|
+
%Q{#{field}:"#{value}"}
|
88
|
+
end
|
89
|
+
|
90
|
+
def range_query(field, value, gap)
|
91
|
+
value < 0 ?
|
92
|
+
%Q|#{field}:[* TO #{-value}}| :
|
93
|
+
%Q|#{field}:[#{value} TO #{value + gap}}|
|
94
|
+
end
|
95
|
+
|
96
|
+
def range_label(value, gap)
|
97
|
+
value < 0 ? "before #{-value}" : "#{value}–#{value + gap - 1}"
|
98
|
+
end
|
99
|
+
|
100
|
+
def query_params(q = @query, fq = @filter)
|
101
|
+
{ q: q, 'fq[]' => fq }
|
102
|
+
end
|
103
|
+
|
104
|
+
def pagination_for(*args)
|
105
|
+
params = args.last.is_a?(Hash) ?
|
106
|
+
args.pop.reject { |_, v| v.nil? } : {}
|
107
|
+
|
108
|
+
ul_([
|
109
|
+
[@prev_page, :first, 1],
|
110
|
+
[@prev_page, :prev, @prev_page],
|
111
|
+
[@next_page, :next, @next_page],
|
112
|
+
[@next_page, :last, @total_pages]
|
113
|
+
], class: 'pagination') { |condition, type, page|
|
114
|
+
[link_to_if(condition, pagination_icon(type), *args, rel: type,
|
115
|
+
params: params.merge(page: page)), class: disabled?(condition)]
|
116
|
+
}
|
117
|
+
end
|
118
|
+
|
119
|
+
def pagination_icon(type)
|
120
|
+
tag_(:span, glyphicon(*{
|
121
|
+
first: [:fast_backward, title: 'First page'],
|
122
|
+
prev: [:backward, title: 'Previous page'],
|
123
|
+
next: [:forward, title: 'Next page'],
|
124
|
+
last: [:fast_forward, title: 'Last page']
|
125
|
+
}[type]))
|
126
|
+
end
|
127
|
+
|
128
|
+
def glyphicon(name, options = {})
|
129
|
+
name, options = name.to_s.tr('_', '-'), options.dup
|
130
|
+
|
131
|
+
options[:class] = %W[glyphicon glyphicon-#{name}]
|
132
|
+
.push(*options.delete(:class)).compact.join(' ')
|
133
|
+
|
134
|
+
tag_(:span, options)
|
135
|
+
end
|
136
|
+
|
137
|
+
def values_for(key, document = @document)
|
138
|
+
Array(document[key = key.to_s]).dup.tap { |values|
|
139
|
+
return if values.empty?
|
140
|
+
|
141
|
+
if settings.browse_fields.include?(key)
|
142
|
+
values.map! { |v| link_to(v = hl(v), :browse, key, v) }
|
143
|
+
elsif field = settings.linkable_fields[key]
|
144
|
+
values.map! { |v| link_to_field(field, v) }
|
145
|
+
else
|
146
|
+
values.map!(&method(:hl))
|
147
|
+
end
|
148
|
+
}
|
149
|
+
end
|
27
150
|
|
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
151
|
end
|
126
152
|
|
127
153
|
end
|
data/lib/birds/app/settings.rb
CHANGED
@@ -5,7 +5,7 @@
|
|
5
5
|
# #
|
6
6
|
# Birds -- Bibliographic information retrieval & document search #
|
7
7
|
# #
|
8
|
-
# Copyright (C) 2014-
|
8
|
+
# Copyright (C) 2014-2016 Jens Wille #
|
9
9
|
# #
|
10
10
|
# Birds is free software: you can redistribute it and/or modify it under the #
|
11
11
|
# terms of the GNU Affero General Public License as published by the Free #
|
@@ -49,38 +49,72 @@ class Birds::App
|
|
49
49
|
settings.solr_client.new(settings.solr_opts)
|
50
50
|
end
|
51
51
|
|
52
|
-
|
53
|
-
|
52
|
+
set :solr_select_path do
|
53
|
+
settings.solr_client::DEFAULT_SELECT_PATH
|
54
|
+
end
|
55
|
+
|
56
|
+
set :solr_spell_path do
|
57
|
+
settings.solr_client::DEFAULT_SPELL_PATH
|
58
|
+
end
|
59
|
+
|
60
|
+
set :solr_match_all_query do
|
61
|
+
settings.solr_client::MATCH_ALL_QUERY
|
62
|
+
end
|
63
|
+
|
64
|
+
set_hash :display_fields1, %w[
|
65
|
+
editor author title issue source imprint year pages isbn series
|
66
|
+
abstract content footnote theme field form object location area aid
|
54
67
|
], %w[_txt] do |f, l| [f, l.capitalize] end
|
55
68
|
|
69
|
+
set_hash :display_fields2, %w[
|
70
|
+
compass lcsh precis rswk bk asb ddc sbb sfb ghbs kab lcc ssd rvk
|
71
|
+
], %w[_txt] do |f, l| [f, l.upcase] end
|
72
|
+
|
73
|
+
set :display_fields do
|
74
|
+
settings.display_fields1.merge(settings.display_fields2)
|
75
|
+
end
|
76
|
+
|
56
77
|
set_hash :linkable_fields, %w[
|
57
|
-
author
|
78
|
+
editor author
|
79
|
+
theme field object
|
80
|
+
compass lcsh precis rswk bk asb ddc sbb sfb ghbs kab lcc ssd rvk
|
58
81
|
], %w[_txt _ss]
|
59
82
|
|
60
83
|
set_hash :facet_fields, %w[
|
61
|
-
author language theme subject
|
62
|
-
].insert(
|
84
|
+
author language type theme subject classification
|
85
|
+
].insert(1,
|
63
86
|
[:year, start: 1900, end: 2100, gap: 10, other: 'before']
|
64
87
|
) do |f, o| ["#{f}_#{o ? :i : :ss}", ["#{f}s".capitalize, o]] end
|
65
88
|
|
66
|
-
set :mlt_fields,
|
67
|
-
author_txt
|
68
|
-
|
89
|
+
set :mlt_fields,
|
90
|
+
author: %w[author_txt],
|
91
|
+
content: %w[title_txt abstract_txt subject_txt]
|
69
92
|
|
70
93
|
set :browse_fields, {
|
71
|
-
# 'cat' => %w[Category Categories],
|
72
|
-
# 'manu_exact' => %w[Manufacturer Manufacturers]
|
94
|
+
#TODO 'cat' => %w[Category Categories],
|
95
|
+
#TODO 'manu_exact' => %w[Manufacturer Manufacturers]
|
73
96
|
}
|
74
97
|
|
75
98
|
set :scroll_fields, {
|
76
|
-
# 'author_s' => 'Author'
|
99
|
+
#TODO 'author_s' => 'Author'
|
77
100
|
}
|
78
101
|
|
79
|
-
set :
|
80
|
-
|
81
|
-
|
82
|
-
|
102
|
+
set :highlighting_fields, '*'
|
103
|
+
|
104
|
+
set :highlighting_snippets, 4
|
105
|
+
|
106
|
+
set :highlighting_fragsize, 0
|
107
|
+
|
108
|
+
set :highlighting_prefix, '<mark>'
|
109
|
+
|
110
|
+
set :highlighting_postfix, '</mark>'
|
111
|
+
|
112
|
+
set :sample_queries do
|
113
|
+
{ 'All documents' => settings.solr_match_all_query }
|
114
|
+
end
|
115
|
+
|
116
|
+
set :document_label, '{author_txt:%s: }{title_txt( : )}{year_txt: (%s)}'
|
83
117
|
|
84
|
-
set :
|
118
|
+
set :default_per_page, 20
|
85
119
|
|
86
120
|
end
|
@@ -0,0 +1,6 @@
|
|
1
|
+
<dl class="dl-horizontal<%= ' info-display bg-info' if defined?(info) && info %>">
|
2
|
+
<% for key, label in fields; values = values_for(key, document) or next %>
|
3
|
+
<dt class="text-muted"><%=h label || key %></dt>
|
4
|
+
<dd><%= values.join('<br />') %></dd>
|
5
|
+
<% end %>
|
6
|
+
</dl>
|
@@ -1,13 +1,14 @@
|
|
1
|
-
<ol start="<%= defined?(start) && start || 1 %>"
|
2
|
-
<% for document in documents; id, score = document
|
1
|
+
<ol start="<%= defined?(start) && start || 1 %>">
|
2
|
+
<% for document in documents; id, score = document.id, h('%.2f' % document.score) %>
|
3
3
|
<li>
|
4
|
-
<%= link_to_document(document) %>
|
4
|
+
<%= link_to_document(document, hl = defined?(highlighting) == 'local-variable' ? highlighting[id] : {}) %>
|
5
5
|
<% unless defined?(explain) && explain %>
|
6
6
|
<span class="badge"><%= score %></span>
|
7
7
|
<% else; eid = h("explain-#{id}") %>
|
8
8
|
<span class="badge toggle-explain" data-toggle="collapse" data-target="#<%= eid %>" title="Toggle score explanation"><%= score %></span>
|
9
9
|
<pre id="<%= eid %>" class="collapse small"><%=h explain[id] %></pre>
|
10
10
|
<% end %>
|
11
|
+
<%= partial :display, fields: hl.keys.sort.map { |k| [k, settings.display_fields[k] || k.split('_')[0..-2].join('_').capitalize] }.uniq { |_, l| l }, document: hl, info: true unless hl.empty? %>
|
11
12
|
</li>
|
12
13
|
<% end %>
|
13
14
|
</ol>
|
@@ -1,9 +1,26 @@
|
|
1
|
-
<%
|
1
|
+
<%
|
2
|
+
position = 0
|
3
|
+
for facet, (label, _) in settings.facet_fields
|
4
|
+
counts = @facets[facet] or next
|
5
|
+
|
6
|
+
fid = "facet-#{h(facet)}"
|
7
|
+
ffid = fid + '-filter'
|
8
|
+
|
9
|
+
expand = (position += 1) < 4
|
10
|
+
|
11
|
+
collapse = max = nil
|
12
|
+
%>
|
2
13
|
<div class="panel panel-default">
|
3
14
|
<div class="panel-heading">
|
4
|
-
<h4 class="panel-title toggle-facet" data-toggle="collapse" data-target="#<%= fid
|
15
|
+
<h4 class="panel-title toggle-facet" data-toggle="collapse" data-target="#<%= fid %>, #<%= ffid %>" title="Toggle facet list"><%=h label %></h4>
|
16
|
+
<% if counts.size > 8 %>
|
17
|
+
<div class="facet-filter collapse<%= ' in' if expand %>" id="<%= ffid %>">
|
18
|
+
<div class="pull-right"><%= glyphicon(:remove, title: 'Clear facet filter') %></div>
|
19
|
+
<form><input class="form-control" type="text" placeholder="Filter <%=h label.downcase %>" /></form>
|
20
|
+
</div>
|
21
|
+
<% end %>
|
5
22
|
</div>
|
6
|
-
<ul class="list-group compact-list-group collapse<%= ' in' if
|
23
|
+
<ul class="facet-group list-group compact-list-group collapse<%= ' in' if expand %>" id="<%= fid %>">
|
7
24
|
<% if counts.respond_to?(:gap) %>
|
8
25
|
<% for (value, count), index in counts.reverse_each.with_index %>
|
9
26
|
<li class="list-group-item clearfix<%= ' facet-collapse collapse' if collapse ||= index > 2 %>">
|
@@ -1,12 +1,7 @@
|
|
1
|
-
|
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>
|
1
|
+
<%= partial :display, fields: settings.display_fields, document: @document %>
|
7
2
|
|
8
|
-
<%
|
9
|
-
<h2>Similar documents
|
3
|
+
<% for key, mlt in @similar %>
|
4
|
+
<h2>Similar documents<%= " (#{key})" if key %></h2>
|
10
5
|
|
11
|
-
<%=
|
6
|
+
<%= partial :documents, documents: mlt, explain: @explain[key] %>
|
12
7
|
<% end %>
|
@@ -16,9 +16,9 @@
|
|
16
16
|
</form>
|
17
17
|
|
18
18
|
<% if @filter %>
|
19
|
-
<ul class="list-inline"
|
19
|
+
<ul class="filter-list list-inline">
|
20
20
|
<% for filter in @filter %>
|
21
|
-
<li
|
21
|
+
<li>
|
22
22
|
<%= link_to_search('×', @query, *@filter - [filter], class: 'close', title: 'Remove filter') %>
|
23
23
|
<code><%=h filter %></code>
|
24
24
|
</li>
|
@@ -27,11 +27,12 @@
|
|
27
27
|
<% end %>
|
28
28
|
|
29
29
|
<% if @result %>
|
30
|
-
<%=
|
30
|
+
<%= partial :spellcheck unless @spellcheck.empty? %>
|
31
|
+
<%= partial :documents, documents: @result, explain: @explain, highlighting: @highlighting, start: @offset + 1 %>
|
31
32
|
<%= pagination_for(:search, query_params) %>
|
32
33
|
</div>
|
33
34
|
<div class="col-md-3">
|
34
|
-
<%=
|
35
|
+
<%= partial :facets %>
|
35
36
|
<% else %>
|
36
37
|
<h2>Query syntax</h2>
|
37
38
|
|
@@ -39,7 +39,7 @@
|
|
39
39
|
<%= nav_item('/scroll', 'Scroll', 'Scroll through indexes') unless settings.scroll_fields.empty? %>
|
40
40
|
</ul>
|
41
41
|
<ul class="nav navbar-nav navbar-right">
|
42
|
-
<li><a href="<%= url('/help') %>" title="View help pages">Help</a></li>
|
42
|
+
<li><a class="btn disabled" href="<%= url('/help') %>" title="View help pages">Help</a></li>
|
43
43
|
</ul>
|
44
44
|
<form action="<%= url('/search') %>" method="GET" class="navbar-form navbar-right" role="search">
|
45
45
|
<div class="form-group">
|
@@ -56,7 +56,12 @@
|
|
56
56
|
<% if @code && @error %>
|
57
57
|
<p><big>Oops... <%=h @code %> – <%=h @error %></big></p>
|
58
58
|
<% else %>
|
59
|
-
<h1
|
59
|
+
<h1>
|
60
|
+
<%=h @page_title %>
|
61
|
+
<% if pte = @page_title_extra %>
|
62
|
+
<small>(<%= @page_title_query ? link_to_search(h(pte), @page_title_query) : h(pte) %>)</small>
|
63
|
+
<% end %>
|
64
|
+
</h1>
|
60
65
|
<%= yield %>
|
61
66
|
<% end %>
|
62
67
|
</div>
|
data/lib/birds/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: birds
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Jens Wille
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2016-10-12 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: sinatra-bells
|
@@ -16,40 +16,28 @@ dependencies:
|
|
16
16
|
requirements:
|
17
17
|
- - "~>"
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version: '0.
|
20
|
-
- - ">="
|
21
|
-
- !ruby/object:Gem::Version
|
22
|
-
version: 0.0.2
|
19
|
+
version: '0.4'
|
23
20
|
type: :runtime
|
24
21
|
prerelease: false
|
25
22
|
version_requirements: !ruby/object:Gem::Requirement
|
26
23
|
requirements:
|
27
24
|
- - "~>"
|
28
25
|
- !ruby/object:Gem::Version
|
29
|
-
version: '0.
|
30
|
-
- - ">="
|
31
|
-
- !ruby/object:Gem::Version
|
32
|
-
version: 0.0.2
|
26
|
+
version: '0.4'
|
33
27
|
- !ruby/object:Gem::Dependency
|
34
28
|
name: solr4r
|
35
29
|
requirement: !ruby/object:Gem::Requirement
|
36
30
|
requirements:
|
37
31
|
- - "~>"
|
38
32
|
- !ruby/object:Gem::Version
|
39
|
-
version: '0.
|
40
|
-
- - ">="
|
41
|
-
- !ruby/object:Gem::Version
|
42
|
-
version: 0.0.4
|
33
|
+
version: '0.3'
|
43
34
|
type: :runtime
|
44
35
|
prerelease: false
|
45
36
|
version_requirements: !ruby/object:Gem::Requirement
|
46
37
|
requirements:
|
47
38
|
- - "~>"
|
48
39
|
- !ruby/object:Gem::Version
|
49
|
-
version: '0.
|
50
|
-
- - ">="
|
51
|
-
- !ruby/object:Gem::Version
|
52
|
-
version: 0.0.4
|
40
|
+
version: '0.3'
|
53
41
|
- !ruby/object:Gem::Dependency
|
54
42
|
name: unicode
|
55
43
|
requirement: !ruby/object:Gem::Requirement
|
@@ -73,7 +61,7 @@ dependencies:
|
|
73
61
|
version: '0.8'
|
74
62
|
- - ">="
|
75
63
|
- !ruby/object:Gem::Version
|
76
|
-
version: 0.8.
|
64
|
+
version: 0.8.6
|
77
65
|
type: :development
|
78
66
|
prerelease: false
|
79
67
|
version_requirements: !ruby/object:Gem::Requirement
|
@@ -83,7 +71,7 @@ dependencies:
|
|
83
71
|
version: '0.8'
|
84
72
|
- - ">="
|
85
73
|
- !ruby/object:Gem::Version
|
86
|
-
version: 0.8.
|
74
|
+
version: 0.8.6
|
87
75
|
- !ruby/object:Gem::Dependency
|
88
76
|
name: rake
|
89
77
|
requirement: !ruby/object:Gem::Requirement
|
@@ -120,8 +108,10 @@ files:
|
|
120
108
|
- lib/birds/app/helpers/view.rb
|
121
109
|
- lib/birds/app/public/favicon.ico
|
122
110
|
- lib/birds/app/settings.rb
|
111
|
+
- lib/birds/app/views/_display.erb
|
123
112
|
- lib/birds/app/views/_documents.erb
|
124
113
|
- lib/birds/app/views/_facets.erb
|
114
|
+
- lib/birds/app/views/_spellcheck.erb
|
125
115
|
- lib/birds/app/views/browse.erb
|
126
116
|
- lib/birds/app/views/document.erb
|
127
117
|
- lib/birds/app/views/index.erb
|
@@ -135,13 +125,13 @@ licenses:
|
|
135
125
|
metadata: {}
|
136
126
|
post_install_message: |2+
|
137
127
|
|
138
|
-
birds-0.0.
|
128
|
+
birds-0.0.1 [2016-10-12]:
|
139
129
|
|
140
|
-
*
|
130
|
+
* Initial release.
|
141
131
|
|
142
132
|
rdoc_options:
|
143
133
|
- "--title"
|
144
|
-
- birds Application documentation (v0.0.
|
134
|
+
- birds Application documentation (v0.0.1)
|
145
135
|
- "--charset"
|
146
136
|
- UTF-8
|
147
137
|
- "--line-numbers"
|
@@ -162,7 +152,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
162
152
|
version: '0'
|
163
153
|
requirements: []
|
164
154
|
rubyforge_project:
|
165
|
-
rubygems_version: 2.
|
155
|
+
rubygems_version: 2.6.6
|
166
156
|
signing_key:
|
167
157
|
specification_version: 4
|
168
158
|
summary: Bibliographic information retrieval & document search.
|