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.



69
70
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
# File 'app/models/referent.rb', line 69

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
# 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 |regexp, 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



219
220
221
222
223
# File 'app/models/referent.rb', line 219

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



387
388
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
# File 'app/models/referent.rb', line 387

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.



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

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



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

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

#identifiersObject



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

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

#isbnObject



251
252
253
# File 'app/models/referent.rb', line 251

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.



247
248
249
# File 'app/models/referent.rb', line 247

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/



241
242
243
# File 'app/models/referent.rb', line 241

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.



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

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)


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

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



255
256
257
# File 'app/models/referent.rb', line 255

def oclcnum
  return get_oclcnum(self)
end

#private_dataObject



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

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



380
381
382
383
384
# File 'app/models/referent.rb', line 380

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



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

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



282
283
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
# File 'app/models/referent.rb', line 282

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.



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

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



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

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