birds 0.0.0 → 0.0.1
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 +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.
|