Skip to content

Commit

Permalink
Merge pull request #17 from UNC-Libraries/sequelify
Browse files Browse the repository at this point in the history
Sequelify
  • Loading branch information
kspurgin authored May 21, 2019
2 parents abf678a + 784d84c commit daf4433
Show file tree
Hide file tree
Showing 129 changed files with 5,370 additions and 3,654 deletions.
109 changes: 60 additions & 49 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# sierra-postgres-utilities

Ruby connection to iii Sierra ILS postgres database / SierraDNA, meant to simplify making queries, exporting results, and some lookup / manipulation / transformation of MARC or non-MARC Sierra data and records.
Ruby connection and ORM for iii Sierra ILS postgres database / SierraDNA, meant to simplify making querying, manipulating, and exporting MARC or non-MARC Sierra data and records.

__NOTE: This is in early development and future changes may well not be backwards compatible.__

Expand All @@ -13,24 +13,34 @@ __NOTE: Some sites may have iii setups that store different data in different pl
```ruby
require 'sierra_postgres_utilities'

# Retrieve record by bnum/rnum
bnum = 'b9256886a'
bib = SierraBib.new(bnum)
bib = Sierra::Record.get(bnum)

# or retrieve by id
bib = Sierra::Record.get(id: 420916051894)

bib.suppressed? #=> false
bib.deleted? #=> false
bib.mat_type #=> "a"

# Get data from sierra_view.bib_record as a hash
bib.bib_record #=> {:id=>420916051894,
# Get data from record_metadata and bib_record/item_record/etc. as a hash
bib.values #=> {:id=>420916051894,
# :record_num=>9256886
# :record_id=>420916051894,
# :language_code=>"eng",
# :creation_date_gmt=>2018-07-24 16:02:39 -0400,
# :deletion_date_gmt=>nil,
# ...
# :bcode1=>"m",
# :bcode2=>"a",
# :bcode3=>"-",
# ....
# :is_suppressed=>false}

# Get bib as a ruby-marc object (https://github.com/ruby-marc/ruby-marc/)
# All of those hash keys are also available as methods
bib.bcode1 #=> "m"

# Get rec's MARC as a ruby-marc object (https://github.com/ruby-marc/ruby-marc/)
bib.marc

# Write MARC to binary file (as per normal ruby-marc)
Expand All @@ -51,67 +61,88 @@ puts bib.marc.to_mrk
# ...

# Get an array of item records attached to the bib
bib.items #=> [#<SierraItem:i11736082a>]
bib.items #=> [#<Sierra::Data::Item i11736082a ...>]

item = bib.items.first
item.status_code #=> "-"
item.status_description #=> "Available"
item.barcodes #=> ["00053203834"]
```

### Sequel Datasets, Associations, and Querying

Many record-types, fields, properties (e.g. itype) have Sequel models and associations available under Sierra::Data (though some have not been implemented due to lack of local relevance or use).

See <http://sequel.jeremyevans.net/> for Sequel documentation

```ruby

b = Sierra::Data::Bib.first #=> #<Sierra::Data::Bib b1370009a...

i = b.items.first #=> #<Sierra::Data::Item i1869459a...

# 856 fields with 'hathitrust.org'
v = Sierra::Data::Varfield.where(marc_tag: '856',
field_content: /hathitrust\.org/)

# bibs for those 856s
v.bibs.distinct

# items with location code 'ddda' on unsuppressed bibs
Sierra::Data::Bib.exclude(:is_suppressed).
items.
where(location_code: 'ddda')
```

### Run arbitrary queries against the PostgresDB / SierraDNA

```ruby
query = "select * from sierra_view.subfield limit 1"

# Execute query and return a PG::Result object
SierraDB.make_query(query)
# Execute query and return a Sequel::Dataset object
Sierra::DB.query(query)

SierraDB.results # reference the same PG::Result object
Sierra::DB.results # reference the same Dataset object

SierraDB.results.entries # results as array of record hashes
#=> [{"record_id"=>"425206113029", "varfield_id"=>"57093780", ...}]
Sierra::DB.results.sql

SierraDB.results.values # results as array of record arrays
#=> [["425206113029", "57093780", ...]]
Sierra::DB.results.all # results as array of record hashes
#=> [{"record_id"=>"425206113029", "varfield_id"=>"57093780", ...}]

# Write results to file
SierraDB.write_results('output.tsv')
SierraDB.write_results('output.csv', format: 'csv')
SierraDB.write_results('output.xlsx', format: 'xlsx') # Windows only. Maybe Mac. Not Linux.
Sierra::DB.write_results('output.tsv')
Sierra::DB.write_results('output.csv', format: 'csv')
Sierra::DB.write_results('output.xlsx', format: 'xlsx') # Requires WIN32OLE so probably Windows-only.

# Send results as attachment
details = {:from => '[email protected]',
:to => '[email protected]',
:cc => '[email protected]',
:subject => 'that query',
:body => 'Attached.'}
SierraDB.mail_results('output.tsv', mail_details: details)
Sierra::DB.mail_results('output.tsv', mail_details: details)
```

### Retrieve arbitrary views
Retrieve arbitrary views as arrays of OpenStruct objects via ```SierraDB.[view_name]```.

Retrieve arbitrary views as arrays of hashes via ```Sierra::DB.db[:view_name]```.

```ruby
SierraDB.item_status_property_myuser.
map { |r| [r.code, r.name] }.
to_h
#=> {"!"=>"ON HOLDSHELF", "$"=>"LOST AND PAID", ...
Sierra::DB.db[:request_rule].first
#=> {:id=>6265, :record_type_code=>"i", :query ...
```

### Retrieve defined views in the context of a particular record
Retrieve records related to a specific object via object.[view_name]. E.g.:
```bib.bib_record_item_record_link``` and ```bib.bib_record_property```
return any of ```bib```'s entries in those two views.

## SETUP

* git clone https://github.com/UNC-Libraries/sierra-postgres-utilities
* git clone <https://github.com/UNC-Libraries/sierra-postgres-utilities>
* cd sierra-postgres-utilities
* bundle install
* bundle exec rake install
* supply the Sierra postgres credentials per the below
* optionally supply smtp server address

When possible, it is recommended that you also install the ```sequel_pg``` gem which makes database access significantly faster. See <https://github.com/jeremyevans/sequel_pg> for installation details / requirements.

### Credentials

Create a yaml file in the base directory like so:
Expand Down Expand Up @@ -142,11 +173,6 @@ ENV['SIERRA_INIT_CREDS'] = 'my/path/file.yaml'
require 'sierra_postgres_utilities'
```

Once connected to the Sierra DB, you can close the connection and reconnect
under alternate creds using:
- ```SierraDB.connect_as(creds: filename)```, or
- ```SierraDB.connect_as(creds: cred_hash)```

### SMTP connection / email address storage

Define an smtp connection (that does not require authentication) if you'll use this to send emails.
Expand All @@ -156,18 +182,3 @@ Create ```smtp.secret``` in the working directory:
address: smtp.example.com
port: 25
```

You can create a YAML file ```email.secret```. Example contents:

```yaml
default_email: [email protected]
other_email: [email protected]
```

And then in ruby:

```ruby
c.yield_email # => [email protected]
c.yield_email(index: 'default_email') # => [email protected]
c.yield_email(index: 'other_email') # => [email protected]
```
10 changes: 9 additions & 1 deletion ext/marc/controlfield.rb → lib/ext_spu/marc/controlfield.rb
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
require_relative 'xml_helper'

module MARC
# Extend the MARC::Controlfield class with some UNC-specific helpers
# Extends MARC::Controlfield.
class ControlField
include MARC::XMLHelper

# Convert field to a mrk-type string
# e.g. ControlField.new('001', 'ocm12345').to_mrk
Expand All @@ -10,6 +13,11 @@ def to_mrk
"=#{tag} #{value}"
end

def xml_string(_)
data = escape_xml_reserved(value)
" <controlfield tag='#{tag}'>#{data}</controlfield>"
end

# Returns Sierra style content string.
# It's less silly than this for DataFields, but useful to also define
# here so we can call field_content on any field.
Expand Down
23 changes: 20 additions & 3 deletions ext/marc/datafield.rb → lib/ext_spu/marc/datafield.rb
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
require_relative 'xml_helper'

module MARC
#Extend the MARC::Datafield class with some UNC-specific helpers
# Extends MARC::Datafield.
class DataField
include MARC::XMLHelper

def add_subfields!(sf_tag, new_data)
# add a string or array of string to datafield as subfields
Expand All @@ -9,7 +12,7 @@ def add_subfields!(sf_tag, new_data)
new_data = [new_data] unless new_data.is_a?(Array)
new_data.each do |sf_data|
next unless sf_data
sf = MARC::Subfield.new(sf_tag, sf_data )
sf = MARC::Subfield.new(sf_tag, sf_data)
self.append(sf)
end
self
Expand Down Expand Up @@ -52,6 +55,20 @@ def field_content
f
end

def xml_string(strip_datafields: true)
return if subfields.empty?
xml = ''
xml << " <datafield tag='#{tag}' ind1='#{indicator1}' " \
"ind2='#{indicator2}'>\n"
subfields.each do |sf|
data = escape_xml_reserved(sf.value)
data.strip! if strip_datafields
xml << " <subfield code='#{sf.code}'>#{data}</subfield>\n"
end
xml << ' </datafield>'
xml
end

# Filters subfields based on subfield code/value criteria.
# Returns subfields that equal / dont' equal
# strings, or match / don't match regexps
Expand Down Expand Up @@ -167,7 +184,7 @@ def meets_criteria?(tag: nil, ind1: nil, ind2: nil, value: nil,
# remove candidates according to complex subfield criteria
complex_subfields.each do |rule|
unless rule.is_a?(Array)
raise "complex_subfields should be an array of arrays."
raise 'complex_subfields should be an array of arrays.'
end
type, hsh = rule
case type
Expand Down
Loading

0 comments on commit daf4433

Please sign in to comment.