Class: Referent

Inherits:
ActiveRecord::Base
  • Object
show all
Includes:
MetadataHelper
Defined in:
app/models/referent.rb

Overview

Note: There are a few actual attributes stored as Columns in referent – these were originally used for identifying a Referent identifying the ‘same thing’ as an incoming OpenURL, to re-use it. But we don’t re-use cached referents anymore. So these attributes are NOT USED – actual values are over in ReferentValues. But the attributes are left for now (and set) merely for making it easier to eyeball the database by hand: atitle, title, issn, isbn, volume, year. (why no issue/page number? hell if i know).

Class Method Summary collapse

Instance Method Summary collapse

Methods included from MetadataHelper

#get_doi, #get_gpo_item_nums, #get_identifier, #get_isbn, #get_issn, #get_lccn, #get_oclcnum, #get_pmid, #get_search_creator, #get_search_terms, #get_search_title, #get_sudoc, #get_top_level_creator, #get_year, #normalize_lccn, #normalize_title, #raw_search_title, #title_is_serial?

Methods included from MarcHelper

#add_856_links, #edition_statement, #get_title, #get_years, #gmd_values, #service_type_for_856, #should_skip_856_link?, #strip_gmd

Class Method Details

.clean_up_context_object(co) ⇒ Object

Okay, we need to do some pre-processing on weird context objects sent by, for example, firstSearch. Remove invalid identifiers. Also will adjust context objects according to configured umlaut refernet filters (see config.app_config.referent_filters in environment.rb ) Mutator: Modifies ContextObject arg passed in.



71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
# File 'app/models/referent.rb', line 71

def self.clean_up_context_object(co)
  # First, remove any empty DOIs! or other empty identifiers?
  # LOTS of sources send awful empty identifiers. 
  # That's not a valid identifier!
  empty_ids = co.referent.identifiers.find_all { |i| i =~ Regexp.new('^[^:]+:[^/:]*(/|:)?$')}
  empty_ids.each { |e| co.referent.delete_identifier( e )}
  
  # Now look for ISSN identifiers that are on article_level. FirstSearch
  # gives us ISSN identifiers incorrectly on article level cites. 
  issn_ids = co.referent.identifiers.find_all { |i| i =~ /^urn:ISSN/}
  issn_ids.each do |issn_id|
    # Long as we're at it, add an rft.issn if one's not there.
    issn_data = issn_id.slice( (9..issn_id.length)) # actual ISSN without identifier prefix
    co.referent.(issn, issn_data) if co.referent.('issn').blank? && ! issn_data.blank?

    # And remove it as an identifier unless we know this is journal-level
    # cite.
    unless ( co.referent.('genre') == 'journal' )
      co.referent.delete_identifier( issn_id )
    end      
  end

  # Clean up OCLC numbers from old bad formats that may have snuck in to an info url incorrectly. # also delete preceding 0's
  oclcnum_ids = co.referent.identifiers.find_all { |i| i =~ /^info:oclcnum/}
  oclcnum_ids.each do |oclcnum_id|
    # FIXME Does this regex need "ocn" as well?
    if (oclcnum_id =~ /^info:oclcnum\/(ocm0*|ocn0*|on0*|\(OCoLC\)0*|ocl70*|0+)(.*)$/)
      # Delete the original, take out just the actual oclcnum, not
      # those old prefixes. or preceding 0s.
      co.referent.delete_identifier( oclcnum_id )
      co.referent.add_identifier("info:oclcnum/#{$2}")
    end
  end


  
  
end

.create_by_context_object(co, options = {}) ⇒ Object

Does call save! on referent created. :permalink => false if you already have a permalink and don’t need to create one. Caller should attach that permalink to this referent!



21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
# File 'app/models/referent.rb', line 21

def self.create_by_context_object(co, options = {})    
  options = { :permalink => UmlautController.umlaut_config.create_permalinks    
  }.merge(options)
      
  self.clean_up_context_object(co)    
  
  rft = Referent.new

  # Wrap everything in a transaction for better efficiency, at least
  # with MySQL, I think. 
  
  Referent.transaction do
    
    rft.set_values_from_context_object(co)

    unless ( options[:permalink] == false)
      permalink = Permalink.new_with_values!(rft, co.referrer.identifier)            
    end

    # Add shortcuts.
    rft.referent_values.each do | val |
      rft.atitle = val.normalized_value if val.key_name == 'atitle' and val.metadata?
      rft.title = val.normalized_value if val.key_name.match(/^[bj]?title$/) and val.metadata? 
      rft.issn = val.normalized_value if val.key_name == 'issn' and val.metadata?
      rft.isbn = val.normalized_value if val.key_name == 'isbn' and val.metadata?      
      rft.volume = val.normalized_value if val.key_name == 'volume' and val.metadata?
      rft.year = val.normalized_value if val.key_name == 'date' and val.metadata?
    end
    rft.save!

    # Apply referent filters
    rfr_id = ""
    rfr_id = co.referrer.identifier if (co.referrer && ! co.referrer.identifier.blank?)
    UmlautController.umlaut_config.lookup!("referent_filters", []).each do |filter_config|
      regexp = filter_config[:match]
      filter = filter_config[:filter]
      if (regexp =~ rfr_id)
        filter.filter(rft) if filter.respond_to?(:filter)
      end
    end
  end
  return rft          
end

Instance Method Details

#add_identifier(id) ⇒ Object



221
222
223
224
225
# File 'app/models/referent.rb', line 221

def add_identifier(id)
  unless ( identifiers.find{|i| i == id}  )
    self.referent_values.create(:key_name => 'identifier', :value => id, :normalized_value => ReferentValue.normalize(id), :metadata => false, :private_data => false).save!            
  end
end

#enhance_referent(key, value, metadata = true, private_data = false, options = {}) ⇒ Object

options => { :overwrite => false } to only enhance if not already there



389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
# File 'app/models/referent.rb', line 389

def enhance_referent(key, value, =true, private_data=false, options = {})
  ActiveRecord::Base.connection_pool.with_connection do
    return if value.nil?

    matches = self.referent_values.to_a.find_all do |rv| 
      (rv.key_name == key) && (rv. == ) && (rv.private_data == private_data) 
    end
    
    matches.each do |rv|
      unless (options[:overwrite] == false || rv.value == value)
        rv.value = value
        rv.save!
      end
    end
    
    if (matches.length == 0)
      val = self.referent_values.create(:key_name => key, :value => value, :normalized_value => ReferentValue.normalize(value), :metadata => , :private_data => private_data)
      val.save!
    end
    
    if key.match((/(^[ajb]?title$)|(^is[sb]n$)|(^volume$)|(^date$)/))
      case key
        when 'date' then self.year = ReferentValue.normalize(value)
        when 'volume' then self.volume = ReferentValue.normalize(value)
        when 'issn' then self.issn = ReferentValue.normalize(value)
        when 'isbn' then self.isbn = ReferentValue.normalize(value)
        when 'atitle' then self.atitle = ReferentValue.normalize(value)
        else self.title = ReferentValue.normalize(value)
      end
      self.save!
    end
  end
end

#ensure_value!(key_name, value) ⇒ Object

Find or create a ReferentValue object hanging off this Referent, with given key name and value. key_name can be ‘identifier’, ‘format’, or any metadata key.



114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
# File 'app/models/referent.rb', line 114

def ensure_value!(key_name, value)
   normalized_value = ReferentValue.normalize(value)
   
   rv = ReferentValue.find(:first, 
                     :conditions => { :referent_id => self.id,
                                      :key_name => key_name,
                                      :normalized_value => normalized_value })
    unless (rv)
      rv = ReferentValue.new
      rv.referent = self
      
      rv.key_name = key_name
      rv.value = value
      rv.normalized_value = normalized_value
      
      if key_name == "private_data"
        rv.private_data = true
      elsif key_name != "identifier" && key_name != "format"
        rv. = true
      end

      rv.save!
    end
    return rv
end

#formatObject



227
228
229
230
231
232
233
234
# File 'app/models/referent.rb', line 227

def format
  self.referent_values
  self.referent_values.each { | val |    
    if val.key_name == 'format'
      return val.value
    end
  }    
end

#identifiersObject



210
211
212
213
214
215
216
217
218
219
# File 'app/models/referent.rb', line 210

def identifiers
  self.referent_values
  identifiers = []
  self.referent_values.each { | val |    
    if val.key_name == 'identifier'
      identifiers << val.value
    end
  }
  return identifiers
end

#isbnObject



253
254
255
# File 'app/models/referent.rb', line 253

def isbn
  return get_isbn(self)
end

#issnObject

Gets an ISSN, makes sure it’s a valid ISSN or else returns nil. So will return a valid ISSN (NOT empty string) or nil.



249
250
251
# File 'app/models/referent.rb', line 249

def issn
  return get_issn(self)
end

#lccnObject

finds and normalizes an LCCN. If multiple LCCNs are in the record, returns the first one. Returns a NORMALIZED lccn, but does NOT do validation. see: info-uri.info/registry/OAIHandler?verb=GetRecord&metadataPrefix=reg&identifier=info:lccn/



243
244
245
# File 'app/models/referent.rb', line 243

def lccn
  return get_lccn(self)
end

#metadataObject

Creates a hash of values from referrent_values, to assemble what was spread accross differnet db rows into one easy-lookup hash, for easy access. See also #to_citation for a different hash, specifically for use in View to print citation. And #to_context_object.



193
194
195
196
197
198
199
# File 'app/models/referent.rb', line 193

def 
   = {}
  self.referent_values.each { | val |
    [val.key_name] = val.value if val.metadata? and not val.private_data?
  }
  return 
end

#metadata_intersects?(arg) ⇒ Boolean

pass in a Referent, or a ropenurl ContextObjectEntity that has a metadata method. Or really anything with a #metadata method returning openurl-style keys and values. Method returns true iff the keys in common to both metadata packages have equal (==) values.

Returns:

  • (Boolean)


173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
# File 'app/models/referent.rb', line 173

def (arg)
  
  # if it's empty, good enough. 
  return true unless arg
  
  intersect_keys = self..keys & arg..keys
  # Take out keys who's values are blank. If one is blank but not
  # both, we can still consider that a match. 
  intersect_keys.delete_if{ |k| self.[k].blank? || arg.[k].blank? }
  
  self_subset = self..reject{ |k, v| ! intersect_keys.include?(k) }
  arg_subset = arg..reject{ |k, v| ! intersect_keys.include?(k) }

  return self_subset == arg_subset    
end

#oclcnumObject



257
258
259
# File 'app/models/referent.rb', line 257

def oclcnum
  return get_oclcnum(self)
end

#private_dataObject



201
202
203
204
205
206
207
208
# File 'app/models/referent.rb', line 201

def private_data
  self.referent_values
  priv_data = {}
  self.referent_values.each { | val |
    priv_data[val.key_name] = val.value if val.private_data?
  }
  return priv_data    
end

#remove_value(key) ⇒ Object



382
383
384
385
386
# File 'app/models/referent.rb', line 382

def remove_value(key)
  referent_values.find(:all, :conditions=> ['key_name =?', key]).each do |rv|
    referent_values.delete(rv)
  end    
end

#set_values_from_context_object(co) ⇒ Object

Populate the referent_values table with a ropenurl contextobject object



141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
# File 'app/models/referent.rb', line 141

def set_values_from_context_object(co)
  
  rft = co.referent


  # Multiple identifiers are possible! 
  rft.identifiers.each do |id_string|
    ensure_value!('identifier', id_string)            
  end
  if rft.format
    ensure_value!('format', rft.format)
  end
  if rft.private_data
    # this comes in as "pid" or "rft_dat", we store it in
    # our database as "private_data", sorry, easiest way to
    # fit this in at the moment. 
    ensure_value!("private_data", rft.private_data)
  end
  
  rft..each { | key, value |
    next unless value
    ensure_value!( key, value)      
  }

  
end

#to_citationObject

Creates a hash for use in View code to display a citation



284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
# File 'app/models/referent.rb', line 284

def to_citation
  citation = {}
  # call self.metadata once and use the array for efficiency, don't
  # keep calling it. profiling shows it DOES make a difference. 
   = self.

  if ['atitle'] && ! ['atitle'].blank?
    citation[:title] = ['atitle']
    citation[:title_label], citation[:subtitle_label] = 
      case ['genre']
        when /article|journal|issue/ then ['Article Title', 'Journal Title']
        when /bookitem|book/ then ['Chapter/Part Title', 'Book Title']
        when /proceeding|conference/ then ['Proceeding Title', 'Conference Name']
        when 'report' then ['Report Title','Report']    
        else
        if self.format == 'book'
          ['Chapter/Part Title', 'Title']
        elsif self.format == 'journal'
          ['Article Title', 'Journal Title']
        else # default fall through, use much what SFX uses. 
          ['Title', 'Source']
        end
      end
    ['title','btitle','jtitle'].each do | t_type |
      if ! [t_type].blank?
        citation[:subtitle] = [t_type]
        citation[:container_title] = [t_type]
        break
      end
    end
  else      
    citation[:title_label] = case ["genre"]
      when /article|journal|issue/ then 'Journal Title'
      when /bookitem|book/ then 'Book Title'
      when /proceeding|conference/ then 'Conference Name'
      when 'report' then 'Report Title'
      else'Title'
    end
    ['title','btitle','jtitle'].each do | t_type |
      if ! [t_type].blank?
        citation[:title] = [t_type]
        break
      end
    end      
  end
  # add publisher for books
  if (['genre'] == 'book')
    citation[:pub] = ['pub'] unless ['pub'].blank?
  end

  citation[:issn] = issn if issn
  citation[:isbn] = isbn if isbn
  
  ['volume','issue','date'].each do | key |
    citation[key.to_sym] = [key]
  end
  if ! ["au"].blank?
    citation[:author] = ["au"]
  elsif ["aulast"]
    citation[:author] = ["aulast"]
    if ! ["aufirst"].blank?
      citation[:author] += ',	'+["aufirst"]
    else
      if ! ["auinit"].blank?
        citation[:author] += ',	'+["auinit"]
      else
        if ! ["auinit1"].blank?
          citation[:author] += ',	'+["auinit1"]
        end
        if ! ["auinitm"].blank?
          citation[:author] += ["auinitm"]
        end
      end
    end
  elsif ["aucorp"]
    citation[:author] = ["aucorp"]
  end 
 	if ['spage']
 	  citation[:page] = ['spage']
 	  citation[:page] += ' - ' + ['epage'] if ! ['epage'].blank?
 	end
 	citation[:identifiers] = []
 	self.identifiers.each do | id |
 	  citation[:identifiers] << id unless (id.blank? || id.match(/^tag:/))
 	end
 	return citation
end

#to_context_objectObject

Creates an OpenURL::ContextObject assembling all the data in this referrent.



263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
# File 'app/models/referent.rb', line 263

def to_context_object
  co = OpenURL::ContextObject.new

  # Got to initialize the referent entity properly for our format.
  # OpenURL sucks, this is confusing, yes. 
  fmt_uri = 'info:ofi/fmt:xml:xsd:' + self.format
  co.referent = OpenURL::ContextObjectEntity.new_from_format( fmt_uri )
  rft = co.referent
  
  # Now set all the values.
  self.referent_values.each do | val |
    if val.metadata?
      rft.(val.key_name, val.value)
      next
    end
    rft.send('set_'+val.key_name, val.value) if rft.respond_to?('set_'+val.key_name)        
  end
  return co
end

#type_of_thingObject



372
373
374
375
376
377
378
379
380
# File 'app/models/referent.rb', line 372

def type_of_thing
  genre = self.["genre"]
  genre = nil if genre =~ /^unknown$/i
  genre ||= "resource"

  genre = "book section" if genre =~ /^bookitem$/i

  return genre
end