primo_central_counter 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: e47cedcc636353d5b8623c3693ace12af43eea52
4
+ data.tar.gz: 34f1cd2abf6c872a71cd69c23a79b6411b7ba312
5
+ SHA512:
6
+ metadata.gz: cfea6a88d35200f722856f356e709fa2e70912f4a39c07a71b5b8cf3414d238c27e24c044b14a3a67451860bcfe1f9b1b19acff9cf973e967de84c7ba425ded8
7
+ data.tar.gz: 7c0a88a1b36ada00437370da2928ae0790ff673db98af980d4125faf1f5a780818e8147f7ac9291a8a0a163232c7fefb7ce09dfe74dd9177730c57cf7f660dcc
data/.gitignore ADDED
@@ -0,0 +1,9 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --format documentation
2
+ --color
data/.travis.yml ADDED
@@ -0,0 +1,4 @@
1
+ language: ruby
2
+ rvm:
3
+ - 2.3.0
4
+ before_install: gem install bundler -v 1.11.2
data/Gemfile ADDED
@@ -0,0 +1,14 @@
1
+ source "https://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in your gemspec
4
+ gemspec
5
+
6
+ if !ENV["CI"] && RUBY_ENGINE == "ruby"
7
+ group :development do
8
+ gem "pry", "~> 0.9.12.6"
9
+ gem "pry-byebug", "<= 1.3.2"
10
+ gem "pry-rescue", "~> 1.4.1"
11
+ gem "pry-stack_explorer", "~> 0.4.9.1"
12
+ gem "pry-syntax-hacks", "~> 0.0.6"
13
+ end
14
+ end
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2016 Michael Sievers
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,47 @@
1
+ # PrimoCentralCounter
2
+
3
+ This utility tries to count the number of records in Primo Central. Therefor it issues search requests which should result in disjunct results (e.g. `a*`, `b*` in field `sid`) and retrieves `TOTALHITS` for each search result.
4
+
5
+ ## Installation
6
+
7
+ $ gem install primo_central_counter
8
+
9
+ ## Usage
10
+
11
+ ```
12
+ Usage: primo_central_counter [options]
13
+
14
+ -u, --base-url=BASE_URL Primo base url (e.g. http://primo.kobv.de) *required*
15
+ -i, --institution=INSTITUTION Institution (e.g. PAD) *required*
16
+ -c, --on-campus=ON_CAMPUS Set the on_campus flag (default: true)
17
+ -f, --index-field=INDEX_FIELD Index field to use (default: sid)
18
+ --log-level=LOG_LEVEL Enable logging (e.g. info or debug)
19
+ -v, --version Display version
20
+ -h, --help Display usage informations
21
+ ```
22
+
23
+ The output is (Excel) CSV on STDOUT. If you enable logging, this will be outputted on STDERR.
24
+
25
+ ### Examples
26
+
27
+ ```
28
+ primo_central_counter -u http://primo.kobv.de -i PAD > ./output.csv
29
+ ```
30
+
31
+ ```
32
+ primo_central_counter -u http://primo.kobv.de -i PAD -l info 1> ./output.csv 2> ./messages.txt
33
+ ```
34
+
35
+ ## Development
36
+
37
+ After checking out the repo, run `bin/setup` to install dependencies. You can run `bin/console` for an interactive prompt that will allow you to experiment.
38
+
39
+ To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
40
+
41
+ ## Contributing
42
+
43
+ Bug reports and pull requests are welcome on GitHub at https://github.com/ubpb/primo_central_counter.
44
+
45
+ ## License
46
+
47
+ The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
data/Rakefile ADDED
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
data/bin/console ADDED
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "primo_central_counter"
5
+
6
+ # You can add fixtures and/or initialization code here to make experimenting
7
+ # with your gem easier. You can also use a different console, if you like.
8
+
9
+ # (If you use this, don't forget to add pry to your Gemfile!)
10
+ # require "pry"
11
+ # Pry.start
12
+
13
+ require "irb"
14
+ IRB.start
data/bin/setup ADDED
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
@@ -0,0 +1,6 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "primo_central_counter"
5
+
6
+ PrimoCentralCounter::Cli.new(ARGV)
@@ -0,0 +1,77 @@
1
+ require "logger"
2
+ require_relative "../primo_central_counter"
3
+ require_relative "./counter"
4
+ require_relative "./option_parser"
5
+ require_relative "./soap_discoverer"
6
+
7
+ class PrimoCentralCounter::Cli
8
+ def initialize(argv = [])
9
+ @options = {
10
+ index_field: "sid",
11
+ log_level: "error",
12
+ on_campus: true
13
+ }
14
+
15
+ option_parser(@options).parse!(argv)
16
+
17
+ @logger = ::Logger.new(STDERR).tap do |_new_logger|
18
+ _new_logger.level = ::Logger.const_get @options[:log_level].upcase
19
+ end
20
+
21
+ if @options[:base_url] && @options[:institution] && !@options[:help] && !@options[:version]
22
+ call
23
+ elsif @options[:version]
24
+ puts "#{PrimoCentralCounter::VERSION}"
25
+ else
26
+ puts option_parser.help
27
+ end
28
+ end
29
+
30
+ def call
31
+ searcher_url = PrimoCentralCounter::SoapDiscoverer.call(@options[:base_url], logger: @logger)
32
+ queries = [("a".."z").to_a, (0..9).to_a.map(&:to_s)].flatten(1).map { |_char| "#{_char}*" }
33
+ result = PrimoCentralCounter::Counter.call(searcher_url, queries, @options.merge(logger: @logger))
34
+ sum = result.inject(0) do |_sum, (_query, _count)|
35
+ _sum + _count
36
+ end
37
+
38
+ puts sum
39
+ end
40
+
41
+ #
42
+ private
43
+ #
44
+ def option_parser(result = {})
45
+ PrimoCentralCounter::OptionParser.new(nil, 38) do |_option_parser|
46
+ _option_parser.banner = "Usage: primo_central_counter [options]\n\n"
47
+
48
+ _option_parser.on("-u", "--base-url=BASE_URL", "Primo base url (e.g. http://primo.kobv.de) *required*") do |_value|
49
+ result[:base_url] = _value
50
+ end
51
+
52
+ _option_parser.on("-i", "--institution=INSTITUTION", "Institution (e.g. PAD) *required*") do |_value|
53
+ result[:institution] = _value
54
+ end
55
+
56
+ _option_parser.on("-c", "--on-campus=ON_CAMPUS", "Set the on_campus flag (default: #{@options[:on_campus]})") do |_value|
57
+ result[:index_field] = ["true", "yes", "ja", "1"].include?(value.to_s.downcase) ? true : false
58
+ end
59
+
60
+ _option_parser.on("-f", "--index-field=INDEX_FIELD", "Index field to use (default: #{@options[:index_field]})") do |_value|
61
+ result[:index_field] = _value
62
+ end
63
+
64
+ _option_parser.on("--log-level=LOG_LEVEL", "Enable logging (e.g. info or debug)") do |_value|
65
+ result[:log_level] = _value
66
+ end
67
+
68
+ _option_parser.on("-v", "--version", "Display version") do |_value|
69
+ result[:version] = true
70
+ end
71
+
72
+ _option_parser.on("-h", "--help", "Display usage informations") do
73
+ result[:help] = true
74
+ end
75
+ end
76
+ end
77
+ end
@@ -0,0 +1,76 @@
1
+ require "net/http"
2
+ require "rexml/document"
3
+ require "uri"
4
+ require_relative "../primo_central_counter"
5
+
6
+ class PrimoCentralCounter::Counter
7
+ def self.call(searcher_url, queries, options = {})
8
+ index_field = options[:index_field]
9
+ institution = options[:institution]
10
+ logger = options[:logger]
11
+ on_campus = options[:on_campus]
12
+ uri = URI.parse(searcher_url)
13
+
14
+ # http://stackoverflow.com/questions/11269224/ruby-https-post-with-headers
15
+ http = Net::HTTP.new(uri.host, uri.port)
16
+
17
+ queries.each_with_object({}) do |_query, _hash|
18
+ request = Net::HTTP::Post.new(uri.path)
19
+ request["Content-Type"] = "application/xml" # necessary since new soap endpoint (else -> premature end of file error)
20
+ request["SOAPAction"] = "searchBrief"
21
+ request.body = soap_request(
22
+ index_field: index_field,
23
+ query: _query,
24
+ institution: institution,
25
+ on_campus: on_campus
26
+ )
27
+
28
+ response = http.request(request)
29
+ total_hits = response.body.match(/TOTALHITS[^\s]+/)[0][/\d+/].to_i
30
+
31
+ log_info(logger, "Retriving TOTALHITS for #{institution} #{index_field}:\"#{_query}\" (on_campus: #{on_campus}) ... #{total_hits.to_s.rjust(9)}")
32
+ _hash[_query] = total_hits
33
+ end
34
+ end
35
+
36
+ private
37
+
38
+ def self.log_info(logger, message)
39
+ logger.info(message) if logger
40
+ end
41
+
42
+ def self.soap_request(options = {})
43
+ <<-xml
44
+ <?xml version="1.0" encoding="UTF-8"?>
45
+ <env:Envelope xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:impl="http://primo.kobv.de/PrimoWebServices/services/searcher" xmlns:env="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ins0="http://xml.apache.org/xml-soap">
46
+ <env:Body>
47
+ <impl:searchBrief>
48
+ <searchRequestStr>
49
+ <![CDATA[
50
+ <searchRequest xmlns="http://www.exlibris.com/primo/xsd/wsRequest" xmlns:uic="http://www.exlibris.com/primo/xsd/primoview/uicomponents">
51
+ <PrimoSearchRequest xmlns="http://www.exlibris.com/primo/xsd/search/request">
52
+ <QueryTerms>
53
+ <BoolOpeator>AND</BoolOpeator>
54
+ <QueryTerm>
55
+ <IndexField>#{options[:index_field]}</IndexField>
56
+ <PrecisionOperator>contains</PrecisionOperator>
57
+ <Value>#{options[:query]}</Value>
58
+ </QueryTerm>
59
+ </QueryTerms>
60
+ <StartIndex>1</StartIndex>
61
+ <BulkSize>0</BulkSize>
62
+ <Locations>
63
+ <uic:Location type="adaptor" value="primo_central_multiple_fe"/>
64
+ </Locations>
65
+ </PrimoSearchRequest>
66
+ <institution>#{options[:institution]}</institution>
67
+ <onCampus>#{options[:on_campus]}</onCampus>
68
+ </searchRequest>
69
+ ]]>
70
+ </searchRequestStr>
71
+ </impl:searchBrief>
72
+ </env:Body>
73
+ </env:Envelope>
74
+ xml
75
+ end
76
+ end
@@ -0,0 +1,33 @@
1
+ require "optparse"
2
+ require_relative "../primo_central_counter"
3
+
4
+ # This class wraps the original OptionParser to "repair" some bad behaviour.
5
+ class PrimoCentralCounter::OptionParser < ::OptionParser
6
+ # Usually, OptionParser adds default handlers for help, version, etc. This is
7
+ # a undocumented behavoir and some cases not intended, because the default
8
+ # handler also stops execution immediately. Thatswhy we remove some of them.
9
+ def add_officious
10
+ super
11
+ base.long.reject! { |_key, _| _key == "help" || _key == "version" }
12
+ end
13
+
14
+ # The original option parser stops processing if it sees any options it does
15
+ # not know. This implementation uses order!, which is a lower level OptionParser
16
+ # method to get the requested behavior. Because #parse uses #parse! internally,
17
+ # this automatically fixes #parse.
18
+ def parse!(argv)
19
+ unprocessed_options = []
20
+
21
+ begin
22
+ order!(argv) do |_non_option|
23
+ unprocessed_options << _non_option
24
+ end
25
+ rescue InvalidOption => e
26
+ unprocessed_options << e.args.first
27
+ retry
28
+ end
29
+ .tap do
30
+ argv.concat(unprocessed_options)
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,22 @@
1
+ require "net/http"
2
+ require "rexml/document"
3
+ require "uri"
4
+ require_relative "../primo_central_counter"
5
+
6
+ class PrimoCentralCounter::SoapDiscoverer
7
+ def self.call(primo_base_url, options = {})
8
+ logger = options[:logger]
9
+
10
+ log_info(logger, "Retriving WSDL")
11
+ wsdl_url = URI.parse("#{primo_base_url}/PrimoWebServices/services/searcher?wsdl")
12
+ response = Net::HTTP.get_response(wsdl_url)
13
+ doc = REXML::Document.new response.body
14
+ doc.elements["//wsdl:service//wsdlsoap:address"].attributes["location"]
15
+ end
16
+
17
+ private
18
+
19
+ def self.log_info(logger, message)
20
+ logger.info(message) if logger
21
+ end
22
+ end
@@ -0,0 +1,3 @@
1
+ module PrimoCentralCounter
2
+ VERSION = "0.2.0"
3
+ end
@@ -0,0 +1,10 @@
1
+ require "primo_central_counter/version"
2
+
3
+ begin
4
+ require "pry"
5
+ rescue LoadError
6
+ end
7
+
8
+ module PrimoCentralCounter
9
+ require_relative "./primo_central_counter/cli"
10
+ end
@@ -0,0 +1,22 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path("../lib", __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require "primo_central_counter/version"
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "primo_central_counter"
8
+ spec.version = PrimoCentralCounter::VERSION
9
+ spec.authors = ["Michael Sievers"]
10
+
11
+ spec.summary = %q{A small utility which tries to count the number of records in Primo Central.}
12
+ spec.homepage = "https://github.com/ubpb/primo_central_counter"
13
+ spec.license = "MIT"
14
+
15
+ spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
16
+ spec.bindir = "exe"
17
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
18
+ spec.require_paths = ["lib"]
19
+
20
+ spec.add_development_dependency "bundler", "~> 1.11"
21
+ spec.add_development_dependency "rake", "~> 10.0"
22
+ end
metadata ADDED
@@ -0,0 +1,89 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: primo_central_counter
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.2.0
5
+ platform: ruby
6
+ authors:
7
+ - Michael Sievers
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2016-01-20 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.11'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.11'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '10.0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '10.0'
41
+ description:
42
+ email:
43
+ executables:
44
+ - primo_central_counter
45
+ extensions: []
46
+ extra_rdoc_files: []
47
+ files:
48
+ - ".gitignore"
49
+ - ".rspec"
50
+ - ".travis.yml"
51
+ - Gemfile
52
+ - LICENSE.txt
53
+ - README.md
54
+ - Rakefile
55
+ - bin/console
56
+ - bin/setup
57
+ - exe/primo_central_counter
58
+ - lib/primo_central_counter.rb
59
+ - lib/primo_central_counter/cli.rb
60
+ - lib/primo_central_counter/counter.rb
61
+ - lib/primo_central_counter/option_parser.rb
62
+ - lib/primo_central_counter/soap_discoverer.rb
63
+ - lib/primo_central_counter/version.rb
64
+ - primo_central_counter.gemspec
65
+ homepage: https://github.com/ubpb/primo_central_counter
66
+ licenses:
67
+ - MIT
68
+ metadata: {}
69
+ post_install_message:
70
+ rdoc_options: []
71
+ require_paths:
72
+ - lib
73
+ required_ruby_version: !ruby/object:Gem::Requirement
74
+ requirements:
75
+ - - ">="
76
+ - !ruby/object:Gem::Version
77
+ version: '0'
78
+ required_rubygems_version: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ requirements: []
84
+ rubyforge_project:
85
+ rubygems_version: 2.5.1
86
+ signing_key:
87
+ specification_version: 4
88
+ summary: A small utility which tries to count the number of records in Primo Central.
89
+ test_files: []