Class: Source
- Inherits:
-
ApplicationRecord
- Object
- ActiveRecord::Base
- ApplicationRecord
- Source
- Includes:
- Housekeeping::Timestamps, Housekeeping::Users, Shared::AlternateValues, Shared::DataAttributes, Shared::Documentation, Shared::HasPapertrail, Shared::Identifiers, Shared::IsData, Shared::Notes, Shared::SharedAcrossProjects, Shared::Tags, SoftValidation
- Defined in:
- app/models/source.rb
Overview
A Source is the metadata that identifies the origin of some information/data.
The primary purpose of Source metadata is to allow the user to find the source, that’s all.
See en.wikipedia.org/wiki/BibTeX for a definition of attributes, in nearly all cases they are 1:1 with the TW model. We use this github.com/inukshuk/bibtex-ruby awesomeness. See github.com/inukshuk/bibtex-ruby/tree/master/lib/bibtex/entry, in particular rdf_converter.rb for the types of field managed.
Defined Under Namespace
Classes: Bibtex, Human, Verbatim
Constant Summary collapse
- ALTERNATE_VALUES_FOR =
[ :address, :annote, :booktitle, :edition, :editor, :institution, :journal, :note, :organization, :publisher, :school, :title, :doi, :abstract, :language, :translator, :author, :url].freeze
Constants included from SoftValidation
SoftValidation::ANCESTORS_WITH_SOFT_VALIDATIONS
Instance Attribute Summary collapse
- #abstract ⇒ String
- #address ⇒ String
- #annote ⇒ String
- #author ⇒ String
-
#bibtex_type ⇒ String
Alias for “type” in the bibtex framework see en.wikipedia.org/wiki/BibTeX#Field_types.
- #booktitle ⇒ String
-
#cached ⇒ String
Calculated full citation, searched again in “full text”.
-
#cached_author_string ⇒ String
Calculated author string.
-
#cached_nomenclature_date ⇒ DateTime
Date sensu nomenclature algorithm in TaxonWorks (see Utilities::Dates).
- #chapter ⇒ String
- #copyright ⇒ String
- #crossref ⇒ String
-
#day ⇒ Integer
The calendar day (1-31).
-
#doi ⇒ String
When provided also cloned to an Identifier::Global.
- #edition ⇒ String
- #editor ⇒ String
- #howpublished ⇒ String
- #institution ⇒ String
- #isbn ⇒ String
- #issn ⇒ String
- #journal ⇒ String
- #key ⇒ String
- #language ⇒ String
-
#language_id ⇒ Integer
The TaxonWorks normalization of language to Language.
-
#month ⇒ String
See en.wikipedia.org/wiki/BibTeX#Field_types stored as a three letter value, see ::VALID_BIBTEX_MONTHS.
-
#no_year_suffix_validation ⇒ Boolean?
When true, cached values are not built.
- #note ⇒ String
- #number ⇒ String
- #organization ⇒ String
- #pages ⇒ String
- #publisher ⇒ String
- #school ⇒ String
-
#serial_id ⇒ Integer
The TaxonWorks Serial.
- #series ⇒ String
-
#stated_year ⇒ String
See source/bibtex.rb TODO: Why is this character but year is int?.
- #title ⇒ String
- #translator ⇒ String
-
#type ⇒ String
An exception to the 1:1 modelling.
- #url ⇒ String
-
#verbatim ⇒ String
The full citation, used only for type = SourceVerbatim.
- #verbatim_contents ⇒ String
- #verbatim_keywords ⇒ String
- #volume ⇒ String
- #year ⇒ Integer
-
#year_suffix ⇒ String
Arbitrary user-provided suffix to the year.
Attributes included from Housekeeping::Users
Class Method Summary collapse
- .batch_create(file) ⇒ Array, Boolean
-
.batch_preview(file) ⇒ [Array, message]
TODO: return a more informative response?.
-
.select_optimized(user_id, project_id, target = 'TaxonName') ⇒ Hash
Sources optimized for user selection.
-
.used_recently(user_id, project_id, used_on = 'TaxonName') ⇒ Scope
The max 10 most recently used (1 week, could parameterize) TaxonName, as used.
Instance Method Summary collapse
-
#author_year ⇒ String
A string that represents the authors last_names and year (no suffix).
-
#cited_objects ⇒ Array
Objects this source is linked to through citations.
- #clone ⇒ Source
-
#get_cached ⇒ Object
protected
Defined in subclasses.
- #is_bibtex? ⇒ Boolean
- #is_in_project?(project_id) ⇒ Boolean
-
#nomenclature_date ⇒ Date
Month handling allows values from bibtex like ‘may’ to be handled.
- #reject_project_sources(attributed) ⇒ Boolean protected
-
#set_cached ⇒ Nil
protected
Defined in subclasses.
- #sv_cached_names ⇒ Object protected
- #sv_fix_cached_names ⇒ Object protected
- #sv_fix_stated_year ⇒ Object protected
- #sv_html_tags ⇒ Object protected
- #sv_stated_year ⇒ Object protected
Methods included from Shared::IsData
#errors_excepting, #full_error_messages_excepting, #identical, #is_community?, #is_destroyable?, #is_editable?, #is_in_use?, #is_in_users_projects?, #metamorphosize, #similar
Methods included from SoftValidation
#clear_soft_validations, #fix_for, #fix_soft_validations, #soft_fixed?, #soft_valid?, #soft_validate, #soft_validated?, #soft_validations, #soft_validators
Methods included from Shared::HasPapertrail
#attribute_updated, #attribute_updater
Methods included from Shared::Tags
#reject_tags, #tag_with, #tagged?, #tagged_with?
Methods included from Shared::Notes
#concatenated_notes_string, #reject_notes
Methods included from Shared::Identifiers
#dwc_occurrence_id, #identified?, #next_by_identifier, #previous_by_identifier, #reject_identifiers, #uri, #uuid
Methods included from Shared::Documentation
#document_array=, #documented?, #reject_documentation, #reject_documents
Methods included from Shared::DataAttributes
#import_attributes, #internal_attributes, #keyword_value_hash, #reject_data_attributes
Methods included from Shared::AlternateValues
#all_values_for, #alternate_valued?
Methods included from Housekeeping::Users
#set_created_by_id, #set_updated_by_id
Methods inherited from ApplicationRecord
Instance Attribute Details
#abstract ⇒ String
Returns see en.wikipedia.org/wiki/BibTeX#Field_types.
188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 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 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 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 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 |
# File 'app/models/source.rb', line 188 class Source < ApplicationRecord include Housekeeping::Users include Housekeeping::Timestamps include Shared::AlternateValues include Shared::DataAttributes include Shared::Documentation include Shared::Identifiers include Shared::Notes include Shared::SharedAcrossProjects include Shared::Tags include Shared::HasPapertrail include SoftValidation include Shared::IsData # !! Must not have Shared::Depictions ignore_whitespace_on(:verbatim_contents) ALTERNATE_VALUES_FOR = [ :address, :annote, :booktitle, :edition, :editor, :institution, :journal, :note, :organization, :publisher, :school, :title, :doi, :abstract, :language, :translator, :author, :url].freeze # @return [Boolean, nil] # When true, cached values are not built attr_accessor :no_year_suffix_validation # Keep this order for citations/topics has_many :citations, inverse_of: :source, dependent: :restrict_with_error has_many :origin_citations, -> {where(citations: {is_original: true})}, class_name: 'Citation', dependent: :restrict_with_error, inverse_of: :source has_many :citation_topics, through: :citations, inverse_of: :source has_many :topics, through: :citation_topics, inverse_of: :sources has_many :project_sources, inverse_of: :source, dependent: :destroy has_many :projects, inverse_of: :sources, through: :project_sources after_save :set_cached validates_presence_of :type validates :type, inclusion: {in: ['Source::Bibtex', 'Source::Human', 'Source::Verbatim']} # TODO: not needed accepts_nested_attributes_for :project_sources, reject_if: :reject_project_sources soft_validate( :sv_cached_names, set: :cached_names, fix: :sv_fix_cached_names, name: 'Cached names', description: 'Check if cached values need to be updated' ) soft_validate( :sv_stated_year, set: :stated_year, fix: :sv_fix_stated_year, name: 'Stated year', description: "'Stated year' is not needed if identical to 'year'" ) soft_validate( :sv_html_tags, set: :html_tags, name: 'html tags', description: 'Check if html has both open and close tags' ) # Redirect type here # @param [String] file # @return [[Array, message]] # TODO: return a more informative response? def self.batch_preview(file) begin bibliography = BibTeX::Bibliography.parse(file.read.force_encoding('UTF-8'), filter: :latex) sources = [] bibliography.each do |record| a = Source::Bibtex.new_from_bibtex(record) sources.push(a) end return sources, nil rescue BibTeX::ParseError => e return [], e. rescue raise end end # @return [String] # A string that represents the authors last_names and year (no suffix) def return 'not yet calculated' if new_record? [, year].compact.join(', ') end # @param [String] file # @return [Array, Boolean] def self.batch_create(file) sources = [] valid = 0 begin # error_msg = [] Source.transaction do bibliography = BibTeX::Bibliography.parse(file.read.force_encoding('UTF-8'), filter: :latex) bibliography.each do |record| a = Source::Bibtex.new_from_bibtex(record) if a.valid? if a.save valid += 1 end else # error_msg = a.errors.messages.to_s end sources.push(a) end end rescue return false end return {records: sources, count: valid} end # @param used_on [String] a model name # @return [Scope] # the max 10 most recently used (1 week, could parameterize) TaxonName, as used def self.used_recently(user_id, project_id, used_on = 'TaxonName') Source.select('sources.id'). joins(:citations) .where(citations: {updated_by_id: user_id, project_id:, citation_object_type: used_on, updated_at: 1.week.ago..}) .order('citations.updated_at DESC') .pluck(:id).uniq end # @params target [String] a citable model name # @return [Hash] sources optimized for user selection def self.select_optimized(user_id, project_id, target = 'TaxonName') r = used_recently(user_id, project_id, target) h = { quick: [], pinboard: Source.pinned_by(user_id).where(pinboard_items: {project_id:}).to_a, recent: [] } if r.empty? h[:recent] = Source.where(created_by_id: user_id, updated_at: 2.hours.ago..Time.now ) .order('created_at DESC') .limit(5).order(:cached).to_a h[:quick] = Source.pinned_by(user_id).pinboard_inserted.where(pinboard_items: {project_id:}).to_a else h[:recent] = (Source.where(created_by_id: user_id, updated_at: 2.hours.ago..Time.now ) .order('created_at DESC') .limit(5).order(:cached).to_a + Source.where('"sources"."id" IN (?)', r.first(6) ).to_a).uniq h[:quick] = ( Source.pinned_by(user_id).pinboard_inserted.where(pinboard_items: {project_id:}).to_a + Source.where('"sources"."id" IN (?)', r.first(4) ).to_a).uniq end h end # @return [Array] # objects this source is linked to through citations def cited_objects self.citations.collect { |t| t.citation_object } end # @return [Boolean] def is_bibtex? type == 'Source::Bibtex' end # @return [Boolean] def is_in_project?(project_id) projects.where(id: project_id).any? end # Month handling allows values from bibtex like 'may' to be handled # @return [Date] def nomenclature_date Utilities::Dates.nomenclature_date(day, Utilities::Dates.month_index(month), year)&.to_date end # @return [Source] def clone s = dup m = "[CLONE of #{id}] " case type when 'Source::Verbatim' s.verbatim = m + verbatim.to_s when 'Source::Bibtex' s.title = m + title.to_s end roles.reload.each do |r| s.roles << Role.new(person: r.person, type: r.type, position: r.position ) end s.year_suffix = nil s.save s end protected # Defined in subclasses # @return [Nil] def set_cached end # Defined in subclasses def get_cached end # @param [Hash] attributed # @return [Boolean] def reject_project_sources(attributed) return true if attributed['project_id'].blank? return true if ProjectSource.where(project_id: attributed['project_id'], source_id: id).any? end def sv_cached_names true # see validation in subclasses end def sv_fix_cached_names begin Source.transaction do self.set_cached end true rescue false end end def sv_stated_year soft_validations.add( :base, "'Stated year' is not needed if identical to 'year'; applying the Fix will delete it", success_message: "'Stated year' was deleted", failure_message: "Failed to delete 'Stated year'") if year.to_s == stated_year.to_s end def sv_fix_stated_year begin Source.transaction do self.stated_year = nil self.save end true rescue false end end def if title.present? str = title.squish.gsub(/\<i>[^<>]*?<\/i>/, '') soft_validations.add(:title, 'The title contains unmatched html tags') if str.include?('<i>') || str.include?('</i>') end end end |
#address ⇒ String
Returns see en.wikipedia.org/wiki/BibTeX#Field_types.
188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 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 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 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 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 |
# File 'app/models/source.rb', line 188 class Source < ApplicationRecord include Housekeeping::Users include Housekeeping::Timestamps include Shared::AlternateValues include Shared::DataAttributes include Shared::Documentation include Shared::Identifiers include Shared::Notes include Shared::SharedAcrossProjects include Shared::Tags include Shared::HasPapertrail include SoftValidation include Shared::IsData # !! Must not have Shared::Depictions ignore_whitespace_on(:verbatim_contents) ALTERNATE_VALUES_FOR = [ :address, :annote, :booktitle, :edition, :editor, :institution, :journal, :note, :organization, :publisher, :school, :title, :doi, :abstract, :language, :translator, :author, :url].freeze # @return [Boolean, nil] # When true, cached values are not built attr_accessor :no_year_suffix_validation # Keep this order for citations/topics has_many :citations, inverse_of: :source, dependent: :restrict_with_error has_many :origin_citations, -> {where(citations: {is_original: true})}, class_name: 'Citation', dependent: :restrict_with_error, inverse_of: :source has_many :citation_topics, through: :citations, inverse_of: :source has_many :topics, through: :citation_topics, inverse_of: :sources has_many :project_sources, inverse_of: :source, dependent: :destroy has_many :projects, inverse_of: :sources, through: :project_sources after_save :set_cached validates_presence_of :type validates :type, inclusion: {in: ['Source::Bibtex', 'Source::Human', 'Source::Verbatim']} # TODO: not needed accepts_nested_attributes_for :project_sources, reject_if: :reject_project_sources soft_validate( :sv_cached_names, set: :cached_names, fix: :sv_fix_cached_names, name: 'Cached names', description: 'Check if cached values need to be updated' ) soft_validate( :sv_stated_year, set: :stated_year, fix: :sv_fix_stated_year, name: 'Stated year', description: "'Stated year' is not needed if identical to 'year'" ) soft_validate( :sv_html_tags, set: :html_tags, name: 'html tags', description: 'Check if html has both open and close tags' ) # Redirect type here # @param [String] file # @return [[Array, message]] # TODO: return a more informative response? def self.batch_preview(file) begin bibliography = BibTeX::Bibliography.parse(file.read.force_encoding('UTF-8'), filter: :latex) sources = [] bibliography.each do |record| a = Source::Bibtex.new_from_bibtex(record) sources.push(a) end return sources, nil rescue BibTeX::ParseError => e return [], e. rescue raise end end # @return [String] # A string that represents the authors last_names and year (no suffix) def return 'not yet calculated' if new_record? [, year].compact.join(', ') end # @param [String] file # @return [Array, Boolean] def self.batch_create(file) sources = [] valid = 0 begin # error_msg = [] Source.transaction do bibliography = BibTeX::Bibliography.parse(file.read.force_encoding('UTF-8'), filter: :latex) bibliography.each do |record| a = Source::Bibtex.new_from_bibtex(record) if a.valid? if a.save valid += 1 end else # error_msg = a.errors.messages.to_s end sources.push(a) end end rescue return false end return {records: sources, count: valid} end # @param used_on [String] a model name # @return [Scope] # the max 10 most recently used (1 week, could parameterize) TaxonName, as used def self.used_recently(user_id, project_id, used_on = 'TaxonName') Source.select('sources.id'). joins(:citations) .where(citations: {updated_by_id: user_id, project_id:, citation_object_type: used_on, updated_at: 1.week.ago..}) .order('citations.updated_at DESC') .pluck(:id).uniq end # @params target [String] a citable model name # @return [Hash] sources optimized for user selection def self.select_optimized(user_id, project_id, target = 'TaxonName') r = used_recently(user_id, project_id, target) h = { quick: [], pinboard: Source.pinned_by(user_id).where(pinboard_items: {project_id:}).to_a, recent: [] } if r.empty? h[:recent] = Source.where(created_by_id: user_id, updated_at: 2.hours.ago..Time.now ) .order('created_at DESC') .limit(5).order(:cached).to_a h[:quick] = Source.pinned_by(user_id).pinboard_inserted.where(pinboard_items: {project_id:}).to_a else h[:recent] = (Source.where(created_by_id: user_id, updated_at: 2.hours.ago..Time.now ) .order('created_at DESC') .limit(5).order(:cached).to_a + Source.where('"sources"."id" IN (?)', r.first(6) ).to_a).uniq h[:quick] = ( Source.pinned_by(user_id).pinboard_inserted.where(pinboard_items: {project_id:}).to_a + Source.where('"sources"."id" IN (?)', r.first(4) ).to_a).uniq end h end # @return [Array] # objects this source is linked to through citations def cited_objects self.citations.collect { |t| t.citation_object } end # @return [Boolean] def is_bibtex? type == 'Source::Bibtex' end # @return [Boolean] def is_in_project?(project_id) projects.where(id: project_id).any? end # Month handling allows values from bibtex like 'may' to be handled # @return [Date] def nomenclature_date Utilities::Dates.nomenclature_date(day, Utilities::Dates.month_index(month), year)&.to_date end # @return [Source] def clone s = dup m = "[CLONE of #{id}] " case type when 'Source::Verbatim' s.verbatim = m + verbatim.to_s when 'Source::Bibtex' s.title = m + title.to_s end roles.reload.each do |r| s.roles << Role.new(person: r.person, type: r.type, position: r.position ) end s.year_suffix = nil s.save s end protected # Defined in subclasses # @return [Nil] def set_cached end # Defined in subclasses def get_cached end # @param [Hash] attributed # @return [Boolean] def reject_project_sources(attributed) return true if attributed['project_id'].blank? return true if ProjectSource.where(project_id: attributed['project_id'], source_id: id).any? end def sv_cached_names true # see validation in subclasses end def sv_fix_cached_names begin Source.transaction do self.set_cached end true rescue false end end def sv_stated_year soft_validations.add( :base, "'Stated year' is not needed if identical to 'year'; applying the Fix will delete it", success_message: "'Stated year' was deleted", failure_message: "Failed to delete 'Stated year'") if year.to_s == stated_year.to_s end def sv_fix_stated_year begin Source.transaction do self.stated_year = nil self.save end true rescue false end end def if title.present? str = title.squish.gsub(/\<i>[^<>]*?<\/i>/, '') soft_validations.add(:title, 'The title contains unmatched html tags') if str.include?('<i>') || str.include?('</i>') end end end |
#annote ⇒ String
Returns see en.wikipedia.org/wiki/BibTeX#Field_types.
188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 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 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 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 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 |
# File 'app/models/source.rb', line 188 class Source < ApplicationRecord include Housekeeping::Users include Housekeeping::Timestamps include Shared::AlternateValues include Shared::DataAttributes include Shared::Documentation include Shared::Identifiers include Shared::Notes include Shared::SharedAcrossProjects include Shared::Tags include Shared::HasPapertrail include SoftValidation include Shared::IsData # !! Must not have Shared::Depictions ignore_whitespace_on(:verbatim_contents) ALTERNATE_VALUES_FOR = [ :address, :annote, :booktitle, :edition, :editor, :institution, :journal, :note, :organization, :publisher, :school, :title, :doi, :abstract, :language, :translator, :author, :url].freeze # @return [Boolean, nil] # When true, cached values are not built attr_accessor :no_year_suffix_validation # Keep this order for citations/topics has_many :citations, inverse_of: :source, dependent: :restrict_with_error has_many :origin_citations, -> {where(citations: {is_original: true})}, class_name: 'Citation', dependent: :restrict_with_error, inverse_of: :source has_many :citation_topics, through: :citations, inverse_of: :source has_many :topics, through: :citation_topics, inverse_of: :sources has_many :project_sources, inverse_of: :source, dependent: :destroy has_many :projects, inverse_of: :sources, through: :project_sources after_save :set_cached validates_presence_of :type validates :type, inclusion: {in: ['Source::Bibtex', 'Source::Human', 'Source::Verbatim']} # TODO: not needed accepts_nested_attributes_for :project_sources, reject_if: :reject_project_sources soft_validate( :sv_cached_names, set: :cached_names, fix: :sv_fix_cached_names, name: 'Cached names', description: 'Check if cached values need to be updated' ) soft_validate( :sv_stated_year, set: :stated_year, fix: :sv_fix_stated_year, name: 'Stated year', description: "'Stated year' is not needed if identical to 'year'" ) soft_validate( :sv_html_tags, set: :html_tags, name: 'html tags', description: 'Check if html has both open and close tags' ) # Redirect type here # @param [String] file # @return [[Array, message]] # TODO: return a more informative response? def self.batch_preview(file) begin bibliography = BibTeX::Bibliography.parse(file.read.force_encoding('UTF-8'), filter: :latex) sources = [] bibliography.each do |record| a = Source::Bibtex.new_from_bibtex(record) sources.push(a) end return sources, nil rescue BibTeX::ParseError => e return [], e. rescue raise end end # @return [String] # A string that represents the authors last_names and year (no suffix) def return 'not yet calculated' if new_record? [, year].compact.join(', ') end # @param [String] file # @return [Array, Boolean] def self.batch_create(file) sources = [] valid = 0 begin # error_msg = [] Source.transaction do bibliography = BibTeX::Bibliography.parse(file.read.force_encoding('UTF-8'), filter: :latex) bibliography.each do |record| a = Source::Bibtex.new_from_bibtex(record) if a.valid? if a.save valid += 1 end else # error_msg = a.errors.messages.to_s end sources.push(a) end end rescue return false end return {records: sources, count: valid} end # @param used_on [String] a model name # @return [Scope] # the max 10 most recently used (1 week, could parameterize) TaxonName, as used def self.used_recently(user_id, project_id, used_on = 'TaxonName') Source.select('sources.id'). joins(:citations) .where(citations: {updated_by_id: user_id, project_id:, citation_object_type: used_on, updated_at: 1.week.ago..}) .order('citations.updated_at DESC') .pluck(:id).uniq end # @params target [String] a citable model name # @return [Hash] sources optimized for user selection def self.select_optimized(user_id, project_id, target = 'TaxonName') r = used_recently(user_id, project_id, target) h = { quick: [], pinboard: Source.pinned_by(user_id).where(pinboard_items: {project_id:}).to_a, recent: [] } if r.empty? h[:recent] = Source.where(created_by_id: user_id, updated_at: 2.hours.ago..Time.now ) .order('created_at DESC') .limit(5).order(:cached).to_a h[:quick] = Source.pinned_by(user_id).pinboard_inserted.where(pinboard_items: {project_id:}).to_a else h[:recent] = (Source.where(created_by_id: user_id, updated_at: 2.hours.ago..Time.now ) .order('created_at DESC') .limit(5).order(:cached).to_a + Source.where('"sources"."id" IN (?)', r.first(6) ).to_a).uniq h[:quick] = ( Source.pinned_by(user_id).pinboard_inserted.where(pinboard_items: {project_id:}).to_a + Source.where('"sources"."id" IN (?)', r.first(4) ).to_a).uniq end h end # @return [Array] # objects this source is linked to through citations def cited_objects self.citations.collect { |t| t.citation_object } end # @return [Boolean] def is_bibtex? type == 'Source::Bibtex' end # @return [Boolean] def is_in_project?(project_id) projects.where(id: project_id).any? end # Month handling allows values from bibtex like 'may' to be handled # @return [Date] def nomenclature_date Utilities::Dates.nomenclature_date(day, Utilities::Dates.month_index(month), year)&.to_date end # @return [Source] def clone s = dup m = "[CLONE of #{id}] " case type when 'Source::Verbatim' s.verbatim = m + verbatim.to_s when 'Source::Bibtex' s.title = m + title.to_s end roles.reload.each do |r| s.roles << Role.new(person: r.person, type: r.type, position: r.position ) end s.year_suffix = nil s.save s end protected # Defined in subclasses # @return [Nil] def set_cached end # Defined in subclasses def get_cached end # @param [Hash] attributed # @return [Boolean] def reject_project_sources(attributed) return true if attributed['project_id'].blank? return true if ProjectSource.where(project_id: attributed['project_id'], source_id: id).any? end def sv_cached_names true # see validation in subclasses end def sv_fix_cached_names begin Source.transaction do self.set_cached end true rescue false end end def sv_stated_year soft_validations.add( :base, "'Stated year' is not needed if identical to 'year'; applying the Fix will delete it", success_message: "'Stated year' was deleted", failure_message: "Failed to delete 'Stated year'") if year.to_s == stated_year.to_s end def sv_fix_stated_year begin Source.transaction do self.stated_year = nil self.save end true rescue false end end def if title.present? str = title.squish.gsub(/\<i>[^<>]*?<\/i>/, '') soft_validations.add(:title, 'The title contains unmatched html tags') if str.include?('<i>') || str.include?('</i>') end end end |
#author ⇒ String
Returns see en.wikipedia.org/wiki/BibTeX#Field_types.
188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 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 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 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 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 |
# File 'app/models/source.rb', line 188 class Source < ApplicationRecord include Housekeeping::Users include Housekeeping::Timestamps include Shared::AlternateValues include Shared::DataAttributes include Shared::Documentation include Shared::Identifiers include Shared::Notes include Shared::SharedAcrossProjects include Shared::Tags include Shared::HasPapertrail include SoftValidation include Shared::IsData # !! Must not have Shared::Depictions ignore_whitespace_on(:verbatim_contents) ALTERNATE_VALUES_FOR = [ :address, :annote, :booktitle, :edition, :editor, :institution, :journal, :note, :organization, :publisher, :school, :title, :doi, :abstract, :language, :translator, :author, :url].freeze # @return [Boolean, nil] # When true, cached values are not built attr_accessor :no_year_suffix_validation # Keep this order for citations/topics has_many :citations, inverse_of: :source, dependent: :restrict_with_error has_many :origin_citations, -> {where(citations: {is_original: true})}, class_name: 'Citation', dependent: :restrict_with_error, inverse_of: :source has_many :citation_topics, through: :citations, inverse_of: :source has_many :topics, through: :citation_topics, inverse_of: :sources has_many :project_sources, inverse_of: :source, dependent: :destroy has_many :projects, inverse_of: :sources, through: :project_sources after_save :set_cached validates_presence_of :type validates :type, inclusion: {in: ['Source::Bibtex', 'Source::Human', 'Source::Verbatim']} # TODO: not needed accepts_nested_attributes_for :project_sources, reject_if: :reject_project_sources soft_validate( :sv_cached_names, set: :cached_names, fix: :sv_fix_cached_names, name: 'Cached names', description: 'Check if cached values need to be updated' ) soft_validate( :sv_stated_year, set: :stated_year, fix: :sv_fix_stated_year, name: 'Stated year', description: "'Stated year' is not needed if identical to 'year'" ) soft_validate( :sv_html_tags, set: :html_tags, name: 'html tags', description: 'Check if html has both open and close tags' ) # Redirect type here # @param [String] file # @return [[Array, message]] # TODO: return a more informative response? def self.batch_preview(file) begin bibliography = BibTeX::Bibliography.parse(file.read.force_encoding('UTF-8'), filter: :latex) sources = [] bibliography.each do |record| a = Source::Bibtex.new_from_bibtex(record) sources.push(a) end return sources, nil rescue BibTeX::ParseError => e return [], e. rescue raise end end # @return [String] # A string that represents the authors last_names and year (no suffix) def return 'not yet calculated' if new_record? [, year].compact.join(', ') end # @param [String] file # @return [Array, Boolean] def self.batch_create(file) sources = [] valid = 0 begin # error_msg = [] Source.transaction do bibliography = BibTeX::Bibliography.parse(file.read.force_encoding('UTF-8'), filter: :latex) bibliography.each do |record| a = Source::Bibtex.new_from_bibtex(record) if a.valid? if a.save valid += 1 end else # error_msg = a.errors.messages.to_s end sources.push(a) end end rescue return false end return {records: sources, count: valid} end # @param used_on [String] a model name # @return [Scope] # the max 10 most recently used (1 week, could parameterize) TaxonName, as used def self.used_recently(user_id, project_id, used_on = 'TaxonName') Source.select('sources.id'). joins(:citations) .where(citations: {updated_by_id: user_id, project_id:, citation_object_type: used_on, updated_at: 1.week.ago..}) .order('citations.updated_at DESC') .pluck(:id).uniq end # @params target [String] a citable model name # @return [Hash] sources optimized for user selection def self.select_optimized(user_id, project_id, target = 'TaxonName') r = used_recently(user_id, project_id, target) h = { quick: [], pinboard: Source.pinned_by(user_id).where(pinboard_items: {project_id:}).to_a, recent: [] } if r.empty? h[:recent] = Source.where(created_by_id: user_id, updated_at: 2.hours.ago..Time.now ) .order('created_at DESC') .limit(5).order(:cached).to_a h[:quick] = Source.pinned_by(user_id).pinboard_inserted.where(pinboard_items: {project_id:}).to_a else h[:recent] = (Source.where(created_by_id: user_id, updated_at: 2.hours.ago..Time.now ) .order('created_at DESC') .limit(5).order(:cached).to_a + Source.where('"sources"."id" IN (?)', r.first(6) ).to_a).uniq h[:quick] = ( Source.pinned_by(user_id).pinboard_inserted.where(pinboard_items: {project_id:}).to_a + Source.where('"sources"."id" IN (?)', r.first(4) ).to_a).uniq end h end # @return [Array] # objects this source is linked to through citations def cited_objects self.citations.collect { |t| t.citation_object } end # @return [Boolean] def is_bibtex? type == 'Source::Bibtex' end # @return [Boolean] def is_in_project?(project_id) projects.where(id: project_id).any? end # Month handling allows values from bibtex like 'may' to be handled # @return [Date] def nomenclature_date Utilities::Dates.nomenclature_date(day, Utilities::Dates.month_index(month), year)&.to_date end # @return [Source] def clone s = dup m = "[CLONE of #{id}] " case type when 'Source::Verbatim' s.verbatim = m + verbatim.to_s when 'Source::Bibtex' s.title = m + title.to_s end roles.reload.each do |r| s.roles << Role.new(person: r.person, type: r.type, position: r.position ) end s.year_suffix = nil s.save s end protected # Defined in subclasses # @return [Nil] def set_cached end # Defined in subclasses def get_cached end # @param [Hash] attributed # @return [Boolean] def reject_project_sources(attributed) return true if attributed['project_id'].blank? return true if ProjectSource.where(project_id: attributed['project_id'], source_id: id).any? end def sv_cached_names true # see validation in subclasses end def sv_fix_cached_names begin Source.transaction do self.set_cached end true rescue false end end def sv_stated_year soft_validations.add( :base, "'Stated year' is not needed if identical to 'year'; applying the Fix will delete it", success_message: "'Stated year' was deleted", failure_message: "Failed to delete 'Stated year'") if year.to_s == stated_year.to_s end def sv_fix_stated_year begin Source.transaction do self.stated_year = nil self.save end true rescue false end end def if title.present? str = title.squish.gsub(/\<i>[^<>]*?<\/i>/, '') soft_validations.add(:title, 'The title contains unmatched html tags') if str.include?('<i>') || str.include?('</i>') end end end |
#bibtex_type ⇒ String
Returns alias for “type” in the bibtex framework see en.wikipedia.org/wiki/BibTeX#Field_types.
188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 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 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 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 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 |
# File 'app/models/source.rb', line 188 class Source < ApplicationRecord include Housekeeping::Users include Housekeeping::Timestamps include Shared::AlternateValues include Shared::DataAttributes include Shared::Documentation include Shared::Identifiers include Shared::Notes include Shared::SharedAcrossProjects include Shared::Tags include Shared::HasPapertrail include SoftValidation include Shared::IsData # !! Must not have Shared::Depictions ignore_whitespace_on(:verbatim_contents) ALTERNATE_VALUES_FOR = [ :address, :annote, :booktitle, :edition, :editor, :institution, :journal, :note, :organization, :publisher, :school, :title, :doi, :abstract, :language, :translator, :author, :url].freeze # @return [Boolean, nil] # When true, cached values are not built attr_accessor :no_year_suffix_validation # Keep this order for citations/topics has_many :citations, inverse_of: :source, dependent: :restrict_with_error has_many :origin_citations, -> {where(citations: {is_original: true})}, class_name: 'Citation', dependent: :restrict_with_error, inverse_of: :source has_many :citation_topics, through: :citations, inverse_of: :source has_many :topics, through: :citation_topics, inverse_of: :sources has_many :project_sources, inverse_of: :source, dependent: :destroy has_many :projects, inverse_of: :sources, through: :project_sources after_save :set_cached validates_presence_of :type validates :type, inclusion: {in: ['Source::Bibtex', 'Source::Human', 'Source::Verbatim']} # TODO: not needed accepts_nested_attributes_for :project_sources, reject_if: :reject_project_sources soft_validate( :sv_cached_names, set: :cached_names, fix: :sv_fix_cached_names, name: 'Cached names', description: 'Check if cached values need to be updated' ) soft_validate( :sv_stated_year, set: :stated_year, fix: :sv_fix_stated_year, name: 'Stated year', description: "'Stated year' is not needed if identical to 'year'" ) soft_validate( :sv_html_tags, set: :html_tags, name: 'html tags', description: 'Check if html has both open and close tags' ) # Redirect type here # @param [String] file # @return [[Array, message]] # TODO: return a more informative response? def self.batch_preview(file) begin bibliography = BibTeX::Bibliography.parse(file.read.force_encoding('UTF-8'), filter: :latex) sources = [] bibliography.each do |record| a = Source::Bibtex.new_from_bibtex(record) sources.push(a) end return sources, nil rescue BibTeX::ParseError => e return [], e. rescue raise end end # @return [String] # A string that represents the authors last_names and year (no suffix) def return 'not yet calculated' if new_record? [, year].compact.join(', ') end # @param [String] file # @return [Array, Boolean] def self.batch_create(file) sources = [] valid = 0 begin # error_msg = [] Source.transaction do bibliography = BibTeX::Bibliography.parse(file.read.force_encoding('UTF-8'), filter: :latex) bibliography.each do |record| a = Source::Bibtex.new_from_bibtex(record) if a.valid? if a.save valid += 1 end else # error_msg = a.errors.messages.to_s end sources.push(a) end end rescue return false end return {records: sources, count: valid} end # @param used_on [String] a model name # @return [Scope] # the max 10 most recently used (1 week, could parameterize) TaxonName, as used def self.used_recently(user_id, project_id, used_on = 'TaxonName') Source.select('sources.id'). joins(:citations) .where(citations: {updated_by_id: user_id, project_id:, citation_object_type: used_on, updated_at: 1.week.ago..}) .order('citations.updated_at DESC') .pluck(:id).uniq end # @params target [String] a citable model name # @return [Hash] sources optimized for user selection def self.select_optimized(user_id, project_id, target = 'TaxonName') r = used_recently(user_id, project_id, target) h = { quick: [], pinboard: Source.pinned_by(user_id).where(pinboard_items: {project_id:}).to_a, recent: [] } if r.empty? h[:recent] = Source.where(created_by_id: user_id, updated_at: 2.hours.ago..Time.now ) .order('created_at DESC') .limit(5).order(:cached).to_a h[:quick] = Source.pinned_by(user_id).pinboard_inserted.where(pinboard_items: {project_id:}).to_a else h[:recent] = (Source.where(created_by_id: user_id, updated_at: 2.hours.ago..Time.now ) .order('created_at DESC') .limit(5).order(:cached).to_a + Source.where('"sources"."id" IN (?)', r.first(6) ).to_a).uniq h[:quick] = ( Source.pinned_by(user_id).pinboard_inserted.where(pinboard_items: {project_id:}).to_a + Source.where('"sources"."id" IN (?)', r.first(4) ).to_a).uniq end h end # @return [Array] # objects this source is linked to through citations def cited_objects self.citations.collect { |t| t.citation_object } end # @return [Boolean] def is_bibtex? type == 'Source::Bibtex' end # @return [Boolean] def is_in_project?(project_id) projects.where(id: project_id).any? end # Month handling allows values from bibtex like 'may' to be handled # @return [Date] def nomenclature_date Utilities::Dates.nomenclature_date(day, Utilities::Dates.month_index(month), year)&.to_date end # @return [Source] def clone s = dup m = "[CLONE of #{id}] " case type when 'Source::Verbatim' s.verbatim = m + verbatim.to_s when 'Source::Bibtex' s.title = m + title.to_s end roles.reload.each do |r| s.roles << Role.new(person: r.person, type: r.type, position: r.position ) end s.year_suffix = nil s.save s end protected # Defined in subclasses # @return [Nil] def set_cached end # Defined in subclasses def get_cached end # @param [Hash] attributed # @return [Boolean] def reject_project_sources(attributed) return true if attributed['project_id'].blank? return true if ProjectSource.where(project_id: attributed['project_id'], source_id: id).any? end def sv_cached_names true # see validation in subclasses end def sv_fix_cached_names begin Source.transaction do self.set_cached end true rescue false end end def sv_stated_year soft_validations.add( :base, "'Stated year' is not needed if identical to 'year'; applying the Fix will delete it", success_message: "'Stated year' was deleted", failure_message: "Failed to delete 'Stated year'") if year.to_s == stated_year.to_s end def sv_fix_stated_year begin Source.transaction do self.stated_year = nil self.save end true rescue false end end def if title.present? str = title.squish.gsub(/\<i>[^<>]*?<\/i>/, '') soft_validations.add(:title, 'The title contains unmatched html tags') if str.include?('<i>') || str.include?('</i>') end end end |
#booktitle ⇒ String
Returns see en.wikipedia.org/wiki/BibTeX#Field_types.
188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 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 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 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 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 |
# File 'app/models/source.rb', line 188 class Source < ApplicationRecord include Housekeeping::Users include Housekeeping::Timestamps include Shared::AlternateValues include Shared::DataAttributes include Shared::Documentation include Shared::Identifiers include Shared::Notes include Shared::SharedAcrossProjects include Shared::Tags include Shared::HasPapertrail include SoftValidation include Shared::IsData # !! Must not have Shared::Depictions ignore_whitespace_on(:verbatim_contents) ALTERNATE_VALUES_FOR = [ :address, :annote, :booktitle, :edition, :editor, :institution, :journal, :note, :organization, :publisher, :school, :title, :doi, :abstract, :language, :translator, :author, :url].freeze # @return [Boolean, nil] # When true, cached values are not built attr_accessor :no_year_suffix_validation # Keep this order for citations/topics has_many :citations, inverse_of: :source, dependent: :restrict_with_error has_many :origin_citations, -> {where(citations: {is_original: true})}, class_name: 'Citation', dependent: :restrict_with_error, inverse_of: :source has_many :citation_topics, through: :citations, inverse_of: :source has_many :topics, through: :citation_topics, inverse_of: :sources has_many :project_sources, inverse_of: :source, dependent: :destroy has_many :projects, inverse_of: :sources, through: :project_sources after_save :set_cached validates_presence_of :type validates :type, inclusion: {in: ['Source::Bibtex', 'Source::Human', 'Source::Verbatim']} # TODO: not needed accepts_nested_attributes_for :project_sources, reject_if: :reject_project_sources soft_validate( :sv_cached_names, set: :cached_names, fix: :sv_fix_cached_names, name: 'Cached names', description: 'Check if cached values need to be updated' ) soft_validate( :sv_stated_year, set: :stated_year, fix: :sv_fix_stated_year, name: 'Stated year', description: "'Stated year' is not needed if identical to 'year'" ) soft_validate( :sv_html_tags, set: :html_tags, name: 'html tags', description: 'Check if html has both open and close tags' ) # Redirect type here # @param [String] file # @return [[Array, message]] # TODO: return a more informative response? def self.batch_preview(file) begin bibliography = BibTeX::Bibliography.parse(file.read.force_encoding('UTF-8'), filter: :latex) sources = [] bibliography.each do |record| a = Source::Bibtex.new_from_bibtex(record) sources.push(a) end return sources, nil rescue BibTeX::ParseError => e return [], e. rescue raise end end # @return [String] # A string that represents the authors last_names and year (no suffix) def return 'not yet calculated' if new_record? [, year].compact.join(', ') end # @param [String] file # @return [Array, Boolean] def self.batch_create(file) sources = [] valid = 0 begin # error_msg = [] Source.transaction do bibliography = BibTeX::Bibliography.parse(file.read.force_encoding('UTF-8'), filter: :latex) bibliography.each do |record| a = Source::Bibtex.new_from_bibtex(record) if a.valid? if a.save valid += 1 end else # error_msg = a.errors.messages.to_s end sources.push(a) end end rescue return false end return {records: sources, count: valid} end # @param used_on [String] a model name # @return [Scope] # the max 10 most recently used (1 week, could parameterize) TaxonName, as used def self.used_recently(user_id, project_id, used_on = 'TaxonName') Source.select('sources.id'). joins(:citations) .where(citations: {updated_by_id: user_id, project_id:, citation_object_type: used_on, updated_at: 1.week.ago..}) .order('citations.updated_at DESC') .pluck(:id).uniq end # @params target [String] a citable model name # @return [Hash] sources optimized for user selection def self.select_optimized(user_id, project_id, target = 'TaxonName') r = used_recently(user_id, project_id, target) h = { quick: [], pinboard: Source.pinned_by(user_id).where(pinboard_items: {project_id:}).to_a, recent: [] } if r.empty? h[:recent] = Source.where(created_by_id: user_id, updated_at: 2.hours.ago..Time.now ) .order('created_at DESC') .limit(5).order(:cached).to_a h[:quick] = Source.pinned_by(user_id).pinboard_inserted.where(pinboard_items: {project_id:}).to_a else h[:recent] = (Source.where(created_by_id: user_id, updated_at: 2.hours.ago..Time.now ) .order('created_at DESC') .limit(5).order(:cached).to_a + Source.where('"sources"."id" IN (?)', r.first(6) ).to_a).uniq h[:quick] = ( Source.pinned_by(user_id).pinboard_inserted.where(pinboard_items: {project_id:}).to_a + Source.where('"sources"."id" IN (?)', r.first(4) ).to_a).uniq end h end # @return [Array] # objects this source is linked to through citations def cited_objects self.citations.collect { |t| t.citation_object } end # @return [Boolean] def is_bibtex? type == 'Source::Bibtex' end # @return [Boolean] def is_in_project?(project_id) projects.where(id: project_id).any? end # Month handling allows values from bibtex like 'may' to be handled # @return [Date] def nomenclature_date Utilities::Dates.nomenclature_date(day, Utilities::Dates.month_index(month), year)&.to_date end # @return [Source] def clone s = dup m = "[CLONE of #{id}] " case type when 'Source::Verbatim' s.verbatim = m + verbatim.to_s when 'Source::Bibtex' s.title = m + title.to_s end roles.reload.each do |r| s.roles << Role.new(person: r.person, type: r.type, position: r.position ) end s.year_suffix = nil s.save s end protected # Defined in subclasses # @return [Nil] def set_cached end # Defined in subclasses def get_cached end # @param [Hash] attributed # @return [Boolean] def reject_project_sources(attributed) return true if attributed['project_id'].blank? return true if ProjectSource.where(project_id: attributed['project_id'], source_id: id).any? end def sv_cached_names true # see validation in subclasses end def sv_fix_cached_names begin Source.transaction do self.set_cached end true rescue false end end def sv_stated_year soft_validations.add( :base, "'Stated year' is not needed if identical to 'year'; applying the Fix will delete it", success_message: "'Stated year' was deleted", failure_message: "Failed to delete 'Stated year'") if year.to_s == stated_year.to_s end def sv_fix_stated_year begin Source.transaction do self.stated_year = nil self.save end true rescue false end end def if title.present? str = title.squish.gsub(/\<i>[^<>]*?<\/i>/, '') soft_validations.add(:title, 'The title contains unmatched html tags') if str.include?('<i>') || str.include?('</i>') end end end |
#cached ⇒ String
Returns calculated full citation, searched again in “full text”.
188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 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 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 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 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 |
# File 'app/models/source.rb', line 188 class Source < ApplicationRecord include Housekeeping::Users include Housekeeping::Timestamps include Shared::AlternateValues include Shared::DataAttributes include Shared::Documentation include Shared::Identifiers include Shared::Notes include Shared::SharedAcrossProjects include Shared::Tags include Shared::HasPapertrail include SoftValidation include Shared::IsData # !! Must not have Shared::Depictions ignore_whitespace_on(:verbatim_contents) ALTERNATE_VALUES_FOR = [ :address, :annote, :booktitle, :edition, :editor, :institution, :journal, :note, :organization, :publisher, :school, :title, :doi, :abstract, :language, :translator, :author, :url].freeze # @return [Boolean, nil] # When true, cached values are not built attr_accessor :no_year_suffix_validation # Keep this order for citations/topics has_many :citations, inverse_of: :source, dependent: :restrict_with_error has_many :origin_citations, -> {where(citations: {is_original: true})}, class_name: 'Citation', dependent: :restrict_with_error, inverse_of: :source has_many :citation_topics, through: :citations, inverse_of: :source has_many :topics, through: :citation_topics, inverse_of: :sources has_many :project_sources, inverse_of: :source, dependent: :destroy has_many :projects, inverse_of: :sources, through: :project_sources after_save :set_cached validates_presence_of :type validates :type, inclusion: {in: ['Source::Bibtex', 'Source::Human', 'Source::Verbatim']} # TODO: not needed accepts_nested_attributes_for :project_sources, reject_if: :reject_project_sources soft_validate( :sv_cached_names, set: :cached_names, fix: :sv_fix_cached_names, name: 'Cached names', description: 'Check if cached values need to be updated' ) soft_validate( :sv_stated_year, set: :stated_year, fix: :sv_fix_stated_year, name: 'Stated year', description: "'Stated year' is not needed if identical to 'year'" ) soft_validate( :sv_html_tags, set: :html_tags, name: 'html tags', description: 'Check if html has both open and close tags' ) # Redirect type here # @param [String] file # @return [[Array, message]] # TODO: return a more informative response? def self.batch_preview(file) begin bibliography = BibTeX::Bibliography.parse(file.read.force_encoding('UTF-8'), filter: :latex) sources = [] bibliography.each do |record| a = Source::Bibtex.new_from_bibtex(record) sources.push(a) end return sources, nil rescue BibTeX::ParseError => e return [], e. rescue raise end end # @return [String] # A string that represents the authors last_names and year (no suffix) def return 'not yet calculated' if new_record? [, year].compact.join(', ') end # @param [String] file # @return [Array, Boolean] def self.batch_create(file) sources = [] valid = 0 begin # error_msg = [] Source.transaction do bibliography = BibTeX::Bibliography.parse(file.read.force_encoding('UTF-8'), filter: :latex) bibliography.each do |record| a = Source::Bibtex.new_from_bibtex(record) if a.valid? if a.save valid += 1 end else # error_msg = a.errors.messages.to_s end sources.push(a) end end rescue return false end return {records: sources, count: valid} end # @param used_on [String] a model name # @return [Scope] # the max 10 most recently used (1 week, could parameterize) TaxonName, as used def self.used_recently(user_id, project_id, used_on = 'TaxonName') Source.select('sources.id'). joins(:citations) .where(citations: {updated_by_id: user_id, project_id:, citation_object_type: used_on, updated_at: 1.week.ago..}) .order('citations.updated_at DESC') .pluck(:id).uniq end # @params target [String] a citable model name # @return [Hash] sources optimized for user selection def self.select_optimized(user_id, project_id, target = 'TaxonName') r = used_recently(user_id, project_id, target) h = { quick: [], pinboard: Source.pinned_by(user_id).where(pinboard_items: {project_id:}).to_a, recent: [] } if r.empty? h[:recent] = Source.where(created_by_id: user_id, updated_at: 2.hours.ago..Time.now ) .order('created_at DESC') .limit(5).order(:cached).to_a h[:quick] = Source.pinned_by(user_id).pinboard_inserted.where(pinboard_items: {project_id:}).to_a else h[:recent] = (Source.where(created_by_id: user_id, updated_at: 2.hours.ago..Time.now ) .order('created_at DESC') .limit(5).order(:cached).to_a + Source.where('"sources"."id" IN (?)', r.first(6) ).to_a).uniq h[:quick] = ( Source.pinned_by(user_id).pinboard_inserted.where(pinboard_items: {project_id:}).to_a + Source.where('"sources"."id" IN (?)', r.first(4) ).to_a).uniq end h end # @return [Array] # objects this source is linked to through citations def cited_objects self.citations.collect { |t| t.citation_object } end # @return [Boolean] def is_bibtex? type == 'Source::Bibtex' end # @return [Boolean] def is_in_project?(project_id) projects.where(id: project_id).any? end # Month handling allows values from bibtex like 'may' to be handled # @return [Date] def nomenclature_date Utilities::Dates.nomenclature_date(day, Utilities::Dates.month_index(month), year)&.to_date end # @return [Source] def clone s = dup m = "[CLONE of #{id}] " case type when 'Source::Verbatim' s.verbatim = m + verbatim.to_s when 'Source::Bibtex' s.title = m + title.to_s end roles.reload.each do |r| s.roles << Role.new(person: r.person, type: r.type, position: r.position ) end s.year_suffix = nil s.save s end protected # Defined in subclasses # @return [Nil] def set_cached end # Defined in subclasses def get_cached end # @param [Hash] attributed # @return [Boolean] def reject_project_sources(attributed) return true if attributed['project_id'].blank? return true if ProjectSource.where(project_id: attributed['project_id'], source_id: id).any? end def sv_cached_names true # see validation in subclasses end def sv_fix_cached_names begin Source.transaction do self.set_cached end true rescue false end end def sv_stated_year soft_validations.add( :base, "'Stated year' is not needed if identical to 'year'; applying the Fix will delete it", success_message: "'Stated year' was deleted", failure_message: "Failed to delete 'Stated year'") if year.to_s == stated_year.to_s end def sv_fix_stated_year begin Source.transaction do self.stated_year = nil self.save end true rescue false end end def if title.present? str = title.squish.gsub(/\<i>[^<>]*?<\/i>/, '') soft_validations.add(:title, 'The title contains unmatched html tags') if str.include?('<i>') || str.include?('</i>') end end end |
#cached_author_string ⇒ String
Returns calculated author string.
188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 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 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 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 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 |
# File 'app/models/source.rb', line 188 class Source < ApplicationRecord include Housekeeping::Users include Housekeeping::Timestamps include Shared::AlternateValues include Shared::DataAttributes include Shared::Documentation include Shared::Identifiers include Shared::Notes include Shared::SharedAcrossProjects include Shared::Tags include Shared::HasPapertrail include SoftValidation include Shared::IsData # !! Must not have Shared::Depictions ignore_whitespace_on(:verbatim_contents) ALTERNATE_VALUES_FOR = [ :address, :annote, :booktitle, :edition, :editor, :institution, :journal, :note, :organization, :publisher, :school, :title, :doi, :abstract, :language, :translator, :author, :url].freeze # @return [Boolean, nil] # When true, cached values are not built attr_accessor :no_year_suffix_validation # Keep this order for citations/topics has_many :citations, inverse_of: :source, dependent: :restrict_with_error has_many :origin_citations, -> {where(citations: {is_original: true})}, class_name: 'Citation', dependent: :restrict_with_error, inverse_of: :source has_many :citation_topics, through: :citations, inverse_of: :source has_many :topics, through: :citation_topics, inverse_of: :sources has_many :project_sources, inverse_of: :source, dependent: :destroy has_many :projects, inverse_of: :sources, through: :project_sources after_save :set_cached validates_presence_of :type validates :type, inclusion: {in: ['Source::Bibtex', 'Source::Human', 'Source::Verbatim']} # TODO: not needed accepts_nested_attributes_for :project_sources, reject_if: :reject_project_sources soft_validate( :sv_cached_names, set: :cached_names, fix: :sv_fix_cached_names, name: 'Cached names', description: 'Check if cached values need to be updated' ) soft_validate( :sv_stated_year, set: :stated_year, fix: :sv_fix_stated_year, name: 'Stated year', description: "'Stated year' is not needed if identical to 'year'" ) soft_validate( :sv_html_tags, set: :html_tags, name: 'html tags', description: 'Check if html has both open and close tags' ) # Redirect type here # @param [String] file # @return [[Array, message]] # TODO: return a more informative response? def self.batch_preview(file) begin bibliography = BibTeX::Bibliography.parse(file.read.force_encoding('UTF-8'), filter: :latex) sources = [] bibliography.each do |record| a = Source::Bibtex.new_from_bibtex(record) sources.push(a) end return sources, nil rescue BibTeX::ParseError => e return [], e. rescue raise end end # @return [String] # A string that represents the authors last_names and year (no suffix) def return 'not yet calculated' if new_record? [, year].compact.join(', ') end # @param [String] file # @return [Array, Boolean] def self.batch_create(file) sources = [] valid = 0 begin # error_msg = [] Source.transaction do bibliography = BibTeX::Bibliography.parse(file.read.force_encoding('UTF-8'), filter: :latex) bibliography.each do |record| a = Source::Bibtex.new_from_bibtex(record) if a.valid? if a.save valid += 1 end else # error_msg = a.errors.messages.to_s end sources.push(a) end end rescue return false end return {records: sources, count: valid} end # @param used_on [String] a model name # @return [Scope] # the max 10 most recently used (1 week, could parameterize) TaxonName, as used def self.used_recently(user_id, project_id, used_on = 'TaxonName') Source.select('sources.id'). joins(:citations) .where(citations: {updated_by_id: user_id, project_id:, citation_object_type: used_on, updated_at: 1.week.ago..}) .order('citations.updated_at DESC') .pluck(:id).uniq end # @params target [String] a citable model name # @return [Hash] sources optimized for user selection def self.select_optimized(user_id, project_id, target = 'TaxonName') r = used_recently(user_id, project_id, target) h = { quick: [], pinboard: Source.pinned_by(user_id).where(pinboard_items: {project_id:}).to_a, recent: [] } if r.empty? h[:recent] = Source.where(created_by_id: user_id, updated_at: 2.hours.ago..Time.now ) .order('created_at DESC') .limit(5).order(:cached).to_a h[:quick] = Source.pinned_by(user_id).pinboard_inserted.where(pinboard_items: {project_id:}).to_a else h[:recent] = (Source.where(created_by_id: user_id, updated_at: 2.hours.ago..Time.now ) .order('created_at DESC') .limit(5).order(:cached).to_a + Source.where('"sources"."id" IN (?)', r.first(6) ).to_a).uniq h[:quick] = ( Source.pinned_by(user_id).pinboard_inserted.where(pinboard_items: {project_id:}).to_a + Source.where('"sources"."id" IN (?)', r.first(4) ).to_a).uniq end h end # @return [Array] # objects this source is linked to through citations def cited_objects self.citations.collect { |t| t.citation_object } end # @return [Boolean] def is_bibtex? type == 'Source::Bibtex' end # @return [Boolean] def is_in_project?(project_id) projects.where(id: project_id).any? end # Month handling allows values from bibtex like 'may' to be handled # @return [Date] def nomenclature_date Utilities::Dates.nomenclature_date(day, Utilities::Dates.month_index(month), year)&.to_date end # @return [Source] def clone s = dup m = "[CLONE of #{id}] " case type when 'Source::Verbatim' s.verbatim = m + verbatim.to_s when 'Source::Bibtex' s.title = m + title.to_s end roles.reload.each do |r| s.roles << Role.new(person: r.person, type: r.type, position: r.position ) end s.year_suffix = nil s.save s end protected # Defined in subclasses # @return [Nil] def set_cached end # Defined in subclasses def get_cached end # @param [Hash] attributed # @return [Boolean] def reject_project_sources(attributed) return true if attributed['project_id'].blank? return true if ProjectSource.where(project_id: attributed['project_id'], source_id: id).any? end def sv_cached_names true # see validation in subclasses end def sv_fix_cached_names begin Source.transaction do self.set_cached end true rescue false end end def sv_stated_year soft_validations.add( :base, "'Stated year' is not needed if identical to 'year'; applying the Fix will delete it", success_message: "'Stated year' was deleted", failure_message: "Failed to delete 'Stated year'") if year.to_s == stated_year.to_s end def sv_fix_stated_year begin Source.transaction do self.stated_year = nil self.save end true rescue false end end def if title.present? str = title.squish.gsub(/\<i>[^<>]*?<\/i>/, '') soft_validations.add(:title, 'The title contains unmatched html tags') if str.include?('<i>') || str.include?('</i>') end end end |
#cached_nomenclature_date ⇒ DateTime
Returns Date sensu nomenclature algorithm in TaxonWorks (see Utilities::Dates).
188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 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 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 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 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 |
# File 'app/models/source.rb', line 188 class Source < ApplicationRecord include Housekeeping::Users include Housekeeping::Timestamps include Shared::AlternateValues include Shared::DataAttributes include Shared::Documentation include Shared::Identifiers include Shared::Notes include Shared::SharedAcrossProjects include Shared::Tags include Shared::HasPapertrail include SoftValidation include Shared::IsData # !! Must not have Shared::Depictions ignore_whitespace_on(:verbatim_contents) ALTERNATE_VALUES_FOR = [ :address, :annote, :booktitle, :edition, :editor, :institution, :journal, :note, :organization, :publisher, :school, :title, :doi, :abstract, :language, :translator, :author, :url].freeze # @return [Boolean, nil] # When true, cached values are not built attr_accessor :no_year_suffix_validation # Keep this order for citations/topics has_many :citations, inverse_of: :source, dependent: :restrict_with_error has_many :origin_citations, -> {where(citations: {is_original: true})}, class_name: 'Citation', dependent: :restrict_with_error, inverse_of: :source has_many :citation_topics, through: :citations, inverse_of: :source has_many :topics, through: :citation_topics, inverse_of: :sources has_many :project_sources, inverse_of: :source, dependent: :destroy has_many :projects, inverse_of: :sources, through: :project_sources after_save :set_cached validates_presence_of :type validates :type, inclusion: {in: ['Source::Bibtex', 'Source::Human', 'Source::Verbatim']} # TODO: not needed accepts_nested_attributes_for :project_sources, reject_if: :reject_project_sources soft_validate( :sv_cached_names, set: :cached_names, fix: :sv_fix_cached_names, name: 'Cached names', description: 'Check if cached values need to be updated' ) soft_validate( :sv_stated_year, set: :stated_year, fix: :sv_fix_stated_year, name: 'Stated year', description: "'Stated year' is not needed if identical to 'year'" ) soft_validate( :sv_html_tags, set: :html_tags, name: 'html tags', description: 'Check if html has both open and close tags' ) # Redirect type here # @param [String] file # @return [[Array, message]] # TODO: return a more informative response? def self.batch_preview(file) begin bibliography = BibTeX::Bibliography.parse(file.read.force_encoding('UTF-8'), filter: :latex) sources = [] bibliography.each do |record| a = Source::Bibtex.new_from_bibtex(record) sources.push(a) end return sources, nil rescue BibTeX::ParseError => e return [], e. rescue raise end end # @return [String] # A string that represents the authors last_names and year (no suffix) def return 'not yet calculated' if new_record? [, year].compact.join(', ') end # @param [String] file # @return [Array, Boolean] def self.batch_create(file) sources = [] valid = 0 begin # error_msg = [] Source.transaction do bibliography = BibTeX::Bibliography.parse(file.read.force_encoding('UTF-8'), filter: :latex) bibliography.each do |record| a = Source::Bibtex.new_from_bibtex(record) if a.valid? if a.save valid += 1 end else # error_msg = a.errors.messages.to_s end sources.push(a) end end rescue return false end return {records: sources, count: valid} end # @param used_on [String] a model name # @return [Scope] # the max 10 most recently used (1 week, could parameterize) TaxonName, as used def self.used_recently(user_id, project_id, used_on = 'TaxonName') Source.select('sources.id'). joins(:citations) .where(citations: {updated_by_id: user_id, project_id:, citation_object_type: used_on, updated_at: 1.week.ago..}) .order('citations.updated_at DESC') .pluck(:id).uniq end # @params target [String] a citable model name # @return [Hash] sources optimized for user selection def self.select_optimized(user_id, project_id, target = 'TaxonName') r = used_recently(user_id, project_id, target) h = { quick: [], pinboard: Source.pinned_by(user_id).where(pinboard_items: {project_id:}).to_a, recent: [] } if r.empty? h[:recent] = Source.where(created_by_id: user_id, updated_at: 2.hours.ago..Time.now ) .order('created_at DESC') .limit(5).order(:cached).to_a h[:quick] = Source.pinned_by(user_id).pinboard_inserted.where(pinboard_items: {project_id:}).to_a else h[:recent] = (Source.where(created_by_id: user_id, updated_at: 2.hours.ago..Time.now ) .order('created_at DESC') .limit(5).order(:cached).to_a + Source.where('"sources"."id" IN (?)', r.first(6) ).to_a).uniq h[:quick] = ( Source.pinned_by(user_id).pinboard_inserted.where(pinboard_items: {project_id:}).to_a + Source.where('"sources"."id" IN (?)', r.first(4) ).to_a).uniq end h end # @return [Array] # objects this source is linked to through citations def cited_objects self.citations.collect { |t| t.citation_object } end # @return [Boolean] def is_bibtex? type == 'Source::Bibtex' end # @return [Boolean] def is_in_project?(project_id) projects.where(id: project_id).any? end # Month handling allows values from bibtex like 'may' to be handled # @return [Date] def nomenclature_date Utilities::Dates.nomenclature_date(day, Utilities::Dates.month_index(month), year)&.to_date end # @return [Source] def clone s = dup m = "[CLONE of #{id}] " case type when 'Source::Verbatim' s.verbatim = m + verbatim.to_s when 'Source::Bibtex' s.title = m + title.to_s end roles.reload.each do |r| s.roles << Role.new(person: r.person, type: r.type, position: r.position ) end s.year_suffix = nil s.save s end protected # Defined in subclasses # @return [Nil] def set_cached end # Defined in subclasses def get_cached end # @param [Hash] attributed # @return [Boolean] def reject_project_sources(attributed) return true if attributed['project_id'].blank? return true if ProjectSource.where(project_id: attributed['project_id'], source_id: id).any? end def sv_cached_names true # see validation in subclasses end def sv_fix_cached_names begin Source.transaction do self.set_cached end true rescue false end end def sv_stated_year soft_validations.add( :base, "'Stated year' is not needed if identical to 'year'; applying the Fix will delete it", success_message: "'Stated year' was deleted", failure_message: "Failed to delete 'Stated year'") if year.to_s == stated_year.to_s end def sv_fix_stated_year begin Source.transaction do self.stated_year = nil self.save end true rescue false end end def if title.present? str = title.squish.gsub(/\<i>[^<>]*?<\/i>/, '') soft_validations.add(:title, 'The title contains unmatched html tags') if str.include?('<i>') || str.include?('</i>') end end end |
#chapter ⇒ String
Returns see en.wikipedia.org/wiki/BibTeX#Field_types.
188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 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 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 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 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 |
# File 'app/models/source.rb', line 188 class Source < ApplicationRecord include Housekeeping::Users include Housekeeping::Timestamps include Shared::AlternateValues include Shared::DataAttributes include Shared::Documentation include Shared::Identifiers include Shared::Notes include Shared::SharedAcrossProjects include Shared::Tags include Shared::HasPapertrail include SoftValidation include Shared::IsData # !! Must not have Shared::Depictions ignore_whitespace_on(:verbatim_contents) ALTERNATE_VALUES_FOR = [ :address, :annote, :booktitle, :edition, :editor, :institution, :journal, :note, :organization, :publisher, :school, :title, :doi, :abstract, :language, :translator, :author, :url].freeze # @return [Boolean, nil] # When true, cached values are not built attr_accessor :no_year_suffix_validation # Keep this order for citations/topics has_many :citations, inverse_of: :source, dependent: :restrict_with_error has_many :origin_citations, -> {where(citations: {is_original: true})}, class_name: 'Citation', dependent: :restrict_with_error, inverse_of: :source has_many :citation_topics, through: :citations, inverse_of: :source has_many :topics, through: :citation_topics, inverse_of: :sources has_many :project_sources, inverse_of: :source, dependent: :destroy has_many :projects, inverse_of: :sources, through: :project_sources after_save :set_cached validates_presence_of :type validates :type, inclusion: {in: ['Source::Bibtex', 'Source::Human', 'Source::Verbatim']} # TODO: not needed accepts_nested_attributes_for :project_sources, reject_if: :reject_project_sources soft_validate( :sv_cached_names, set: :cached_names, fix: :sv_fix_cached_names, name: 'Cached names', description: 'Check if cached values need to be updated' ) soft_validate( :sv_stated_year, set: :stated_year, fix: :sv_fix_stated_year, name: 'Stated year', description: "'Stated year' is not needed if identical to 'year'" ) soft_validate( :sv_html_tags, set: :html_tags, name: 'html tags', description: 'Check if html has both open and close tags' ) # Redirect type here # @param [String] file # @return [[Array, message]] # TODO: return a more informative response? def self.batch_preview(file) begin bibliography = BibTeX::Bibliography.parse(file.read.force_encoding('UTF-8'), filter: :latex) sources = [] bibliography.each do |record| a = Source::Bibtex.new_from_bibtex(record) sources.push(a) end return sources, nil rescue BibTeX::ParseError => e return [], e. rescue raise end end # @return [String] # A string that represents the authors last_names and year (no suffix) def return 'not yet calculated' if new_record? [, year].compact.join(', ') end # @param [String] file # @return [Array, Boolean] def self.batch_create(file) sources = [] valid = 0 begin # error_msg = [] Source.transaction do bibliography = BibTeX::Bibliography.parse(file.read.force_encoding('UTF-8'), filter: :latex) bibliography.each do |record| a = Source::Bibtex.new_from_bibtex(record) if a.valid? if a.save valid += 1 end else # error_msg = a.errors.messages.to_s end sources.push(a) end end rescue return false end return {records: sources, count: valid} end # @param used_on [String] a model name # @return [Scope] # the max 10 most recently used (1 week, could parameterize) TaxonName, as used def self.used_recently(user_id, project_id, used_on = 'TaxonName') Source.select('sources.id'). joins(:citations) .where(citations: {updated_by_id: user_id, project_id:, citation_object_type: used_on, updated_at: 1.week.ago..}) .order('citations.updated_at DESC') .pluck(:id).uniq end # @params target [String] a citable model name # @return [Hash] sources optimized for user selection def self.select_optimized(user_id, project_id, target = 'TaxonName') r = used_recently(user_id, project_id, target) h = { quick: [], pinboard: Source.pinned_by(user_id).where(pinboard_items: {project_id:}).to_a, recent: [] } if r.empty? h[:recent] = Source.where(created_by_id: user_id, updated_at: 2.hours.ago..Time.now ) .order('created_at DESC') .limit(5).order(:cached).to_a h[:quick] = Source.pinned_by(user_id).pinboard_inserted.where(pinboard_items: {project_id:}).to_a else h[:recent] = (Source.where(created_by_id: user_id, updated_at: 2.hours.ago..Time.now ) .order('created_at DESC') .limit(5).order(:cached).to_a + Source.where('"sources"."id" IN (?)', r.first(6) ).to_a).uniq h[:quick] = ( Source.pinned_by(user_id).pinboard_inserted.where(pinboard_items: {project_id:}).to_a + Source.where('"sources"."id" IN (?)', r.first(4) ).to_a).uniq end h end # @return [Array] # objects this source is linked to through citations def cited_objects self.citations.collect { |t| t.citation_object } end # @return [Boolean] def is_bibtex? type == 'Source::Bibtex' end # @return [Boolean] def is_in_project?(project_id) projects.where(id: project_id).any? end # Month handling allows values from bibtex like 'may' to be handled # @return [Date] def nomenclature_date Utilities::Dates.nomenclature_date(day, Utilities::Dates.month_index(month), year)&.to_date end # @return [Source] def clone s = dup m = "[CLONE of #{id}] " case type when 'Source::Verbatim' s.verbatim = m + verbatim.to_s when 'Source::Bibtex' s.title = m + title.to_s end roles.reload.each do |r| s.roles << Role.new(person: r.person, type: r.type, position: r.position ) end s.year_suffix = nil s.save s end protected # Defined in subclasses # @return [Nil] def set_cached end # Defined in subclasses def get_cached end # @param [Hash] attributed # @return [Boolean] def reject_project_sources(attributed) return true if attributed['project_id'].blank? return true if ProjectSource.where(project_id: attributed['project_id'], source_id: id).any? end def sv_cached_names true # see validation in subclasses end def sv_fix_cached_names begin Source.transaction do self.set_cached end true rescue false end end def sv_stated_year soft_validations.add( :base, "'Stated year' is not needed if identical to 'year'; applying the Fix will delete it", success_message: "'Stated year' was deleted", failure_message: "Failed to delete 'Stated year'") if year.to_s == stated_year.to_s end def sv_fix_stated_year begin Source.transaction do self.stated_year = nil self.save end true rescue false end end def if title.present? str = title.squish.gsub(/\<i>[^<>]*?<\/i>/, '') soft_validations.add(:title, 'The title contains unmatched html tags') if str.include?('<i>') || str.include?('</i>') end end end |
#copyright ⇒ String
Returns see en.wikipedia.org/wiki/BibTeX#Field_types.
188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 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 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 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 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 |
# File 'app/models/source.rb', line 188 class Source < ApplicationRecord include Housekeeping::Users include Housekeeping::Timestamps include Shared::AlternateValues include Shared::DataAttributes include Shared::Documentation include Shared::Identifiers include Shared::Notes include Shared::SharedAcrossProjects include Shared::Tags include Shared::HasPapertrail include SoftValidation include Shared::IsData # !! Must not have Shared::Depictions ignore_whitespace_on(:verbatim_contents) ALTERNATE_VALUES_FOR = [ :address, :annote, :booktitle, :edition, :editor, :institution, :journal, :note, :organization, :publisher, :school, :title, :doi, :abstract, :language, :translator, :author, :url].freeze # @return [Boolean, nil] # When true, cached values are not built attr_accessor :no_year_suffix_validation # Keep this order for citations/topics has_many :citations, inverse_of: :source, dependent: :restrict_with_error has_many :origin_citations, -> {where(citations: {is_original: true})}, class_name: 'Citation', dependent: :restrict_with_error, inverse_of: :source has_many :citation_topics, through: :citations, inverse_of: :source has_many :topics, through: :citation_topics, inverse_of: :sources has_many :project_sources, inverse_of: :source, dependent: :destroy has_many :projects, inverse_of: :sources, through: :project_sources after_save :set_cached validates_presence_of :type validates :type, inclusion: {in: ['Source::Bibtex', 'Source::Human', 'Source::Verbatim']} # TODO: not needed accepts_nested_attributes_for :project_sources, reject_if: :reject_project_sources soft_validate( :sv_cached_names, set: :cached_names, fix: :sv_fix_cached_names, name: 'Cached names', description: 'Check if cached values need to be updated' ) soft_validate( :sv_stated_year, set: :stated_year, fix: :sv_fix_stated_year, name: 'Stated year', description: "'Stated year' is not needed if identical to 'year'" ) soft_validate( :sv_html_tags, set: :html_tags, name: 'html tags', description: 'Check if html has both open and close tags' ) # Redirect type here # @param [String] file # @return [[Array, message]] # TODO: return a more informative response? def self.batch_preview(file) begin bibliography = BibTeX::Bibliography.parse(file.read.force_encoding('UTF-8'), filter: :latex) sources = [] bibliography.each do |record| a = Source::Bibtex.new_from_bibtex(record) sources.push(a) end return sources, nil rescue BibTeX::ParseError => e return [], e. rescue raise end end # @return [String] # A string that represents the authors last_names and year (no suffix) def return 'not yet calculated' if new_record? [, year].compact.join(', ') end # @param [String] file # @return [Array, Boolean] def self.batch_create(file) sources = [] valid = 0 begin # error_msg = [] Source.transaction do bibliography = BibTeX::Bibliography.parse(file.read.force_encoding('UTF-8'), filter: :latex) bibliography.each do |record| a = Source::Bibtex.new_from_bibtex(record) if a.valid? if a.save valid += 1 end else # error_msg = a.errors.messages.to_s end sources.push(a) end end rescue return false end return {records: sources, count: valid} end # @param used_on [String] a model name # @return [Scope] # the max 10 most recently used (1 week, could parameterize) TaxonName, as used def self.used_recently(user_id, project_id, used_on = 'TaxonName') Source.select('sources.id'). joins(:citations) .where(citations: {updated_by_id: user_id, project_id:, citation_object_type: used_on, updated_at: 1.week.ago..}) .order('citations.updated_at DESC') .pluck(:id).uniq end # @params target [String] a citable model name # @return [Hash] sources optimized for user selection def self.select_optimized(user_id, project_id, target = 'TaxonName') r = used_recently(user_id, project_id, target) h = { quick: [], pinboard: Source.pinned_by(user_id).where(pinboard_items: {project_id:}).to_a, recent: [] } if r.empty? h[:recent] = Source.where(created_by_id: user_id, updated_at: 2.hours.ago..Time.now ) .order('created_at DESC') .limit(5).order(:cached).to_a h[:quick] = Source.pinned_by(user_id).pinboard_inserted.where(pinboard_items: {project_id:}).to_a else h[:recent] = (Source.where(created_by_id: user_id, updated_at: 2.hours.ago..Time.now ) .order('created_at DESC') .limit(5).order(:cached).to_a + Source.where('"sources"."id" IN (?)', r.first(6) ).to_a).uniq h[:quick] = ( Source.pinned_by(user_id).pinboard_inserted.where(pinboard_items: {project_id:}).to_a + Source.where('"sources"."id" IN (?)', r.first(4) ).to_a).uniq end h end # @return [Array] # objects this source is linked to through citations def cited_objects self.citations.collect { |t| t.citation_object } end # @return [Boolean] def is_bibtex? type == 'Source::Bibtex' end # @return [Boolean] def is_in_project?(project_id) projects.where(id: project_id).any? end # Month handling allows values from bibtex like 'may' to be handled # @return [Date] def nomenclature_date Utilities::Dates.nomenclature_date(day, Utilities::Dates.month_index(month), year)&.to_date end # @return [Source] def clone s = dup m = "[CLONE of #{id}] " case type when 'Source::Verbatim' s.verbatim = m + verbatim.to_s when 'Source::Bibtex' s.title = m + title.to_s end roles.reload.each do |r| s.roles << Role.new(person: r.person, type: r.type, position: r.position ) end s.year_suffix = nil s.save s end protected # Defined in subclasses # @return [Nil] def set_cached end # Defined in subclasses def get_cached end # @param [Hash] attributed # @return [Boolean] def reject_project_sources(attributed) return true if attributed['project_id'].blank? return true if ProjectSource.where(project_id: attributed['project_id'], source_id: id).any? end def sv_cached_names true # see validation in subclasses end def sv_fix_cached_names begin Source.transaction do self.set_cached end true rescue false end end def sv_stated_year soft_validations.add( :base, "'Stated year' is not needed if identical to 'year'; applying the Fix will delete it", success_message: "'Stated year' was deleted", failure_message: "Failed to delete 'Stated year'") if year.to_s == stated_year.to_s end def sv_fix_stated_year begin Source.transaction do self.stated_year = nil self.save end true rescue false end end def if title.present? str = title.squish.gsub(/\<i>[^<>]*?<\/i>/, '') soft_validations.add(:title, 'The title contains unmatched html tags') if str.include?('<i>') || str.include?('</i>') end end end |
#crossref ⇒ String
Returns see en.wikipedia.org/wiki/BibTeX#Field_types.
188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 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 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 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 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 |
# File 'app/models/source.rb', line 188 class Source < ApplicationRecord include Housekeeping::Users include Housekeeping::Timestamps include Shared::AlternateValues include Shared::DataAttributes include Shared::Documentation include Shared::Identifiers include Shared::Notes include Shared::SharedAcrossProjects include Shared::Tags include Shared::HasPapertrail include SoftValidation include Shared::IsData # !! Must not have Shared::Depictions ignore_whitespace_on(:verbatim_contents) ALTERNATE_VALUES_FOR = [ :address, :annote, :booktitle, :edition, :editor, :institution, :journal, :note, :organization, :publisher, :school, :title, :doi, :abstract, :language, :translator, :author, :url].freeze # @return [Boolean, nil] # When true, cached values are not built attr_accessor :no_year_suffix_validation # Keep this order for citations/topics has_many :citations, inverse_of: :source, dependent: :restrict_with_error has_many :origin_citations, -> {where(citations: {is_original: true})}, class_name: 'Citation', dependent: :restrict_with_error, inverse_of: :source has_many :citation_topics, through: :citations, inverse_of: :source has_many :topics, through: :citation_topics, inverse_of: :sources has_many :project_sources, inverse_of: :source, dependent: :destroy has_many :projects, inverse_of: :sources, through: :project_sources after_save :set_cached validates_presence_of :type validates :type, inclusion: {in: ['Source::Bibtex', 'Source::Human', 'Source::Verbatim']} # TODO: not needed accepts_nested_attributes_for :project_sources, reject_if: :reject_project_sources soft_validate( :sv_cached_names, set: :cached_names, fix: :sv_fix_cached_names, name: 'Cached names', description: 'Check if cached values need to be updated' ) soft_validate( :sv_stated_year, set: :stated_year, fix: :sv_fix_stated_year, name: 'Stated year', description: "'Stated year' is not needed if identical to 'year'" ) soft_validate( :sv_html_tags, set: :html_tags, name: 'html tags', description: 'Check if html has both open and close tags' ) # Redirect type here # @param [String] file # @return [[Array, message]] # TODO: return a more informative response? def self.batch_preview(file) begin bibliography = BibTeX::Bibliography.parse(file.read.force_encoding('UTF-8'), filter: :latex) sources = [] bibliography.each do |record| a = Source::Bibtex.new_from_bibtex(record) sources.push(a) end return sources, nil rescue BibTeX::ParseError => e return [], e. rescue raise end end # @return [String] # A string that represents the authors last_names and year (no suffix) def return 'not yet calculated' if new_record? [, year].compact.join(', ') end # @param [String] file # @return [Array, Boolean] def self.batch_create(file) sources = [] valid = 0 begin # error_msg = [] Source.transaction do bibliography = BibTeX::Bibliography.parse(file.read.force_encoding('UTF-8'), filter: :latex) bibliography.each do |record| a = Source::Bibtex.new_from_bibtex(record) if a.valid? if a.save valid += 1 end else # error_msg = a.errors.messages.to_s end sources.push(a) end end rescue return false end return {records: sources, count: valid} end # @param used_on [String] a model name # @return [Scope] # the max 10 most recently used (1 week, could parameterize) TaxonName, as used def self.used_recently(user_id, project_id, used_on = 'TaxonName') Source.select('sources.id'). joins(:citations) .where(citations: {updated_by_id: user_id, project_id:, citation_object_type: used_on, updated_at: 1.week.ago..}) .order('citations.updated_at DESC') .pluck(:id).uniq end # @params target [String] a citable model name # @return [Hash] sources optimized for user selection def self.select_optimized(user_id, project_id, target = 'TaxonName') r = used_recently(user_id, project_id, target) h = { quick: [], pinboard: Source.pinned_by(user_id).where(pinboard_items: {project_id:}).to_a, recent: [] } if r.empty? h[:recent] = Source.where(created_by_id: user_id, updated_at: 2.hours.ago..Time.now ) .order('created_at DESC') .limit(5).order(:cached).to_a h[:quick] = Source.pinned_by(user_id).pinboard_inserted.where(pinboard_items: {project_id:}).to_a else h[:recent] = (Source.where(created_by_id: user_id, updated_at: 2.hours.ago..Time.now ) .order('created_at DESC') .limit(5).order(:cached).to_a + Source.where('"sources"."id" IN (?)', r.first(6) ).to_a).uniq h[:quick] = ( Source.pinned_by(user_id).pinboard_inserted.where(pinboard_items: {project_id:}).to_a + Source.where('"sources"."id" IN (?)', r.first(4) ).to_a).uniq end h end # @return [Array] # objects this source is linked to through citations def cited_objects self.citations.collect { |t| t.citation_object } end # @return [Boolean] def is_bibtex? type == 'Source::Bibtex' end # @return [Boolean] def is_in_project?(project_id) projects.where(id: project_id).any? end # Month handling allows values from bibtex like 'may' to be handled # @return [Date] def nomenclature_date Utilities::Dates.nomenclature_date(day, Utilities::Dates.month_index(month), year)&.to_date end # @return [Source] def clone s = dup m = "[CLONE of #{id}] " case type when 'Source::Verbatim' s.verbatim = m + verbatim.to_s when 'Source::Bibtex' s.title = m + title.to_s end roles.reload.each do |r| s.roles << Role.new(person: r.person, type: r.type, position: r.position ) end s.year_suffix = nil s.save s end protected # Defined in subclasses # @return [Nil] def set_cached end # Defined in subclasses def get_cached end # @param [Hash] attributed # @return [Boolean] def reject_project_sources(attributed) return true if attributed['project_id'].blank? return true if ProjectSource.where(project_id: attributed['project_id'], source_id: id).any? end def sv_cached_names true # see validation in subclasses end def sv_fix_cached_names begin Source.transaction do self.set_cached end true rescue false end end def sv_stated_year soft_validations.add( :base, "'Stated year' is not needed if identical to 'year'; applying the Fix will delete it", success_message: "'Stated year' was deleted", failure_message: "Failed to delete 'Stated year'") if year.to_s == stated_year.to_s end def sv_fix_stated_year begin Source.transaction do self.stated_year = nil self.save end true rescue false end end def if title.present? str = title.squish.gsub(/\<i>[^<>]*?<\/i>/, '') soft_validations.add(:title, 'The title contains unmatched html tags') if str.include?('<i>') || str.include?('</i>') end end end |
#day ⇒ Integer
Returns the calendar day (1-31).
188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 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 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 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 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 |
# File 'app/models/source.rb', line 188 class Source < ApplicationRecord include Housekeeping::Users include Housekeeping::Timestamps include Shared::AlternateValues include Shared::DataAttributes include Shared::Documentation include Shared::Identifiers include Shared::Notes include Shared::SharedAcrossProjects include Shared::Tags include Shared::HasPapertrail include SoftValidation include Shared::IsData # !! Must not have Shared::Depictions ignore_whitespace_on(:verbatim_contents) ALTERNATE_VALUES_FOR = [ :address, :annote, :booktitle, :edition, :editor, :institution, :journal, :note, :organization, :publisher, :school, :title, :doi, :abstract, :language, :translator, :author, :url].freeze # @return [Boolean, nil] # When true, cached values are not built attr_accessor :no_year_suffix_validation # Keep this order for citations/topics has_many :citations, inverse_of: :source, dependent: :restrict_with_error has_many :origin_citations, -> {where(citations: {is_original: true})}, class_name: 'Citation', dependent: :restrict_with_error, inverse_of: :source has_many :citation_topics, through: :citations, inverse_of: :source has_many :topics, through: :citation_topics, inverse_of: :sources has_many :project_sources, inverse_of: :source, dependent: :destroy has_many :projects, inverse_of: :sources, through: :project_sources after_save :set_cached validates_presence_of :type validates :type, inclusion: {in: ['Source::Bibtex', 'Source::Human', 'Source::Verbatim']} # TODO: not needed accepts_nested_attributes_for :project_sources, reject_if: :reject_project_sources soft_validate( :sv_cached_names, set: :cached_names, fix: :sv_fix_cached_names, name: 'Cached names', description: 'Check if cached values need to be updated' ) soft_validate( :sv_stated_year, set: :stated_year, fix: :sv_fix_stated_year, name: 'Stated year', description: "'Stated year' is not needed if identical to 'year'" ) soft_validate( :sv_html_tags, set: :html_tags, name: 'html tags', description: 'Check if html has both open and close tags' ) # Redirect type here # @param [String] file # @return [[Array, message]] # TODO: return a more informative response? def self.batch_preview(file) begin bibliography = BibTeX::Bibliography.parse(file.read.force_encoding('UTF-8'), filter: :latex) sources = [] bibliography.each do |record| a = Source::Bibtex.new_from_bibtex(record) sources.push(a) end return sources, nil rescue BibTeX::ParseError => e return [], e. rescue raise end end # @return [String] # A string that represents the authors last_names and year (no suffix) def return 'not yet calculated' if new_record? [, year].compact.join(', ') end # @param [String] file # @return [Array, Boolean] def self.batch_create(file) sources = [] valid = 0 begin # error_msg = [] Source.transaction do bibliography = BibTeX::Bibliography.parse(file.read.force_encoding('UTF-8'), filter: :latex) bibliography.each do |record| a = Source::Bibtex.new_from_bibtex(record) if a.valid? if a.save valid += 1 end else # error_msg = a.errors.messages.to_s end sources.push(a) end end rescue return false end return {records: sources, count: valid} end # @param used_on [String] a model name # @return [Scope] # the max 10 most recently used (1 week, could parameterize) TaxonName, as used def self.used_recently(user_id, project_id, used_on = 'TaxonName') Source.select('sources.id'). joins(:citations) .where(citations: {updated_by_id: user_id, project_id:, citation_object_type: used_on, updated_at: 1.week.ago..}) .order('citations.updated_at DESC') .pluck(:id).uniq end # @params target [String] a citable model name # @return [Hash] sources optimized for user selection def self.select_optimized(user_id, project_id, target = 'TaxonName') r = used_recently(user_id, project_id, target) h = { quick: [], pinboard: Source.pinned_by(user_id).where(pinboard_items: {project_id:}).to_a, recent: [] } if r.empty? h[:recent] = Source.where(created_by_id: user_id, updated_at: 2.hours.ago..Time.now ) .order('created_at DESC') .limit(5).order(:cached).to_a h[:quick] = Source.pinned_by(user_id).pinboard_inserted.where(pinboard_items: {project_id:}).to_a else h[:recent] = (Source.where(created_by_id: user_id, updated_at: 2.hours.ago..Time.now ) .order('created_at DESC') .limit(5).order(:cached).to_a + Source.where('"sources"."id" IN (?)', r.first(6) ).to_a).uniq h[:quick] = ( Source.pinned_by(user_id).pinboard_inserted.where(pinboard_items: {project_id:}).to_a + Source.where('"sources"."id" IN (?)', r.first(4) ).to_a).uniq end h end # @return [Array] # objects this source is linked to through citations def cited_objects self.citations.collect { |t| t.citation_object } end # @return [Boolean] def is_bibtex? type == 'Source::Bibtex' end # @return [Boolean] def is_in_project?(project_id) projects.where(id: project_id).any? end # Month handling allows values from bibtex like 'may' to be handled # @return [Date] def nomenclature_date Utilities::Dates.nomenclature_date(day, Utilities::Dates.month_index(month), year)&.to_date end # @return [Source] def clone s = dup m = "[CLONE of #{id}] " case type when 'Source::Verbatim' s.verbatim = m + verbatim.to_s when 'Source::Bibtex' s.title = m + title.to_s end roles.reload.each do |r| s.roles << Role.new(person: r.person, type: r.type, position: r.position ) end s.year_suffix = nil s.save s end protected # Defined in subclasses # @return [Nil] def set_cached end # Defined in subclasses def get_cached end # @param [Hash] attributed # @return [Boolean] def reject_project_sources(attributed) return true if attributed['project_id'].blank? return true if ProjectSource.where(project_id: attributed['project_id'], source_id: id).any? end def sv_cached_names true # see validation in subclasses end def sv_fix_cached_names begin Source.transaction do self.set_cached end true rescue false end end def sv_stated_year soft_validations.add( :base, "'Stated year' is not needed if identical to 'year'; applying the Fix will delete it", success_message: "'Stated year' was deleted", failure_message: "Failed to delete 'Stated year'") if year.to_s == stated_year.to_s end def sv_fix_stated_year begin Source.transaction do self.stated_year = nil self.save end true rescue false end end def if title.present? str = title.squish.gsub(/\<i>[^<>]*?<\/i>/, '') soft_validations.add(:title, 'The title contains unmatched html tags') if str.include?('<i>') || str.include?('</i>') end end end |
#doi ⇒ String
Returns When provided also cloned to an Identifier::Global. See en.wikipedia.org/wiki/BibTeX#Field_types.
188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 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 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 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 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 |
# File 'app/models/source.rb', line 188 class Source < ApplicationRecord include Housekeeping::Users include Housekeeping::Timestamps include Shared::AlternateValues include Shared::DataAttributes include Shared::Documentation include Shared::Identifiers include Shared::Notes include Shared::SharedAcrossProjects include Shared::Tags include Shared::HasPapertrail include SoftValidation include Shared::IsData # !! Must not have Shared::Depictions ignore_whitespace_on(:verbatim_contents) ALTERNATE_VALUES_FOR = [ :address, :annote, :booktitle, :edition, :editor, :institution, :journal, :note, :organization, :publisher, :school, :title, :doi, :abstract, :language, :translator, :author, :url].freeze # @return [Boolean, nil] # When true, cached values are not built attr_accessor :no_year_suffix_validation # Keep this order for citations/topics has_many :citations, inverse_of: :source, dependent: :restrict_with_error has_many :origin_citations, -> {where(citations: {is_original: true})}, class_name: 'Citation', dependent: :restrict_with_error, inverse_of: :source has_many :citation_topics, through: :citations, inverse_of: :source has_many :topics, through: :citation_topics, inverse_of: :sources has_many :project_sources, inverse_of: :source, dependent: :destroy has_many :projects, inverse_of: :sources, through: :project_sources after_save :set_cached validates_presence_of :type validates :type, inclusion: {in: ['Source::Bibtex', 'Source::Human', 'Source::Verbatim']} # TODO: not needed accepts_nested_attributes_for :project_sources, reject_if: :reject_project_sources soft_validate( :sv_cached_names, set: :cached_names, fix: :sv_fix_cached_names, name: 'Cached names', description: 'Check if cached values need to be updated' ) soft_validate( :sv_stated_year, set: :stated_year, fix: :sv_fix_stated_year, name: 'Stated year', description: "'Stated year' is not needed if identical to 'year'" ) soft_validate( :sv_html_tags, set: :html_tags, name: 'html tags', description: 'Check if html has both open and close tags' ) # Redirect type here # @param [String] file # @return [[Array, message]] # TODO: return a more informative response? def self.batch_preview(file) begin bibliography = BibTeX::Bibliography.parse(file.read.force_encoding('UTF-8'), filter: :latex) sources = [] bibliography.each do |record| a = Source::Bibtex.new_from_bibtex(record) sources.push(a) end return sources, nil rescue BibTeX::ParseError => e return [], e. rescue raise end end # @return [String] # A string that represents the authors last_names and year (no suffix) def return 'not yet calculated' if new_record? [, year].compact.join(', ') end # @param [String] file # @return [Array, Boolean] def self.batch_create(file) sources = [] valid = 0 begin # error_msg = [] Source.transaction do bibliography = BibTeX::Bibliography.parse(file.read.force_encoding('UTF-8'), filter: :latex) bibliography.each do |record| a = Source::Bibtex.new_from_bibtex(record) if a.valid? if a.save valid += 1 end else # error_msg = a.errors.messages.to_s end sources.push(a) end end rescue return false end return {records: sources, count: valid} end # @param used_on [String] a model name # @return [Scope] # the max 10 most recently used (1 week, could parameterize) TaxonName, as used def self.used_recently(user_id, project_id, used_on = 'TaxonName') Source.select('sources.id'). joins(:citations) .where(citations: {updated_by_id: user_id, project_id:, citation_object_type: used_on, updated_at: 1.week.ago..}) .order('citations.updated_at DESC') .pluck(:id).uniq end # @params target [String] a citable model name # @return [Hash] sources optimized for user selection def self.select_optimized(user_id, project_id, target = 'TaxonName') r = used_recently(user_id, project_id, target) h = { quick: [], pinboard: Source.pinned_by(user_id).where(pinboard_items: {project_id:}).to_a, recent: [] } if r.empty? h[:recent] = Source.where(created_by_id: user_id, updated_at: 2.hours.ago..Time.now ) .order('created_at DESC') .limit(5).order(:cached).to_a h[:quick] = Source.pinned_by(user_id).pinboard_inserted.where(pinboard_items: {project_id:}).to_a else h[:recent] = (Source.where(created_by_id: user_id, updated_at: 2.hours.ago..Time.now ) .order('created_at DESC') .limit(5).order(:cached).to_a + Source.where('"sources"."id" IN (?)', r.first(6) ).to_a).uniq h[:quick] = ( Source.pinned_by(user_id).pinboard_inserted.where(pinboard_items: {project_id:}).to_a + Source.where('"sources"."id" IN (?)', r.first(4) ).to_a).uniq end h end # @return [Array] # objects this source is linked to through citations def cited_objects self.citations.collect { |t| t.citation_object } end # @return [Boolean] def is_bibtex? type == 'Source::Bibtex' end # @return [Boolean] def is_in_project?(project_id) projects.where(id: project_id).any? end # Month handling allows values from bibtex like 'may' to be handled # @return [Date] def nomenclature_date Utilities::Dates.nomenclature_date(day, Utilities::Dates.month_index(month), year)&.to_date end # @return [Source] def clone s = dup m = "[CLONE of #{id}] " case type when 'Source::Verbatim' s.verbatim = m + verbatim.to_s when 'Source::Bibtex' s.title = m + title.to_s end roles.reload.each do |r| s.roles << Role.new(person: r.person, type: r.type, position: r.position ) end s.year_suffix = nil s.save s end protected # Defined in subclasses # @return [Nil] def set_cached end # Defined in subclasses def get_cached end # @param [Hash] attributed # @return [Boolean] def reject_project_sources(attributed) return true if attributed['project_id'].blank? return true if ProjectSource.where(project_id: attributed['project_id'], source_id: id).any? end def sv_cached_names true # see validation in subclasses end def sv_fix_cached_names begin Source.transaction do self.set_cached end true rescue false end end def sv_stated_year soft_validations.add( :base, "'Stated year' is not needed if identical to 'year'; applying the Fix will delete it", success_message: "'Stated year' was deleted", failure_message: "Failed to delete 'Stated year'") if year.to_s == stated_year.to_s end def sv_fix_stated_year begin Source.transaction do self.stated_year = nil self.save end true rescue false end end def if title.present? str = title.squish.gsub(/\<i>[^<>]*?<\/i>/, '') soft_validations.add(:title, 'The title contains unmatched html tags') if str.include?('<i>') || str.include?('</i>') end end end |
#edition ⇒ String
Returns see en.wikipedia.org/wiki/BibTeX#Field_types.
188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 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 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 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 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 |
# File 'app/models/source.rb', line 188 class Source < ApplicationRecord include Housekeeping::Users include Housekeeping::Timestamps include Shared::AlternateValues include Shared::DataAttributes include Shared::Documentation include Shared::Identifiers include Shared::Notes include Shared::SharedAcrossProjects include Shared::Tags include Shared::HasPapertrail include SoftValidation include Shared::IsData # !! Must not have Shared::Depictions ignore_whitespace_on(:verbatim_contents) ALTERNATE_VALUES_FOR = [ :address, :annote, :booktitle, :edition, :editor, :institution, :journal, :note, :organization, :publisher, :school, :title, :doi, :abstract, :language, :translator, :author, :url].freeze # @return [Boolean, nil] # When true, cached values are not built attr_accessor :no_year_suffix_validation # Keep this order for citations/topics has_many :citations, inverse_of: :source, dependent: :restrict_with_error has_many :origin_citations, -> {where(citations: {is_original: true})}, class_name: 'Citation', dependent: :restrict_with_error, inverse_of: :source has_many :citation_topics, through: :citations, inverse_of: :source has_many :topics, through: :citation_topics, inverse_of: :sources has_many :project_sources, inverse_of: :source, dependent: :destroy has_many :projects, inverse_of: :sources, through: :project_sources after_save :set_cached validates_presence_of :type validates :type, inclusion: {in: ['Source::Bibtex', 'Source::Human', 'Source::Verbatim']} # TODO: not needed accepts_nested_attributes_for :project_sources, reject_if: :reject_project_sources soft_validate( :sv_cached_names, set: :cached_names, fix: :sv_fix_cached_names, name: 'Cached names', description: 'Check if cached values need to be updated' ) soft_validate( :sv_stated_year, set: :stated_year, fix: :sv_fix_stated_year, name: 'Stated year', description: "'Stated year' is not needed if identical to 'year'" ) soft_validate( :sv_html_tags, set: :html_tags, name: 'html tags', description: 'Check if html has both open and close tags' ) # Redirect type here # @param [String] file # @return [[Array, message]] # TODO: return a more informative response? def self.batch_preview(file) begin bibliography = BibTeX::Bibliography.parse(file.read.force_encoding('UTF-8'), filter: :latex) sources = [] bibliography.each do |record| a = Source::Bibtex.new_from_bibtex(record) sources.push(a) end return sources, nil rescue BibTeX::ParseError => e return [], e. rescue raise end end # @return [String] # A string that represents the authors last_names and year (no suffix) def return 'not yet calculated' if new_record? [, year].compact.join(', ') end # @param [String] file # @return [Array, Boolean] def self.batch_create(file) sources = [] valid = 0 begin # error_msg = [] Source.transaction do bibliography = BibTeX::Bibliography.parse(file.read.force_encoding('UTF-8'), filter: :latex) bibliography.each do |record| a = Source::Bibtex.new_from_bibtex(record) if a.valid? if a.save valid += 1 end else # error_msg = a.errors.messages.to_s end sources.push(a) end end rescue return false end return {records: sources, count: valid} end # @param used_on [String] a model name # @return [Scope] # the max 10 most recently used (1 week, could parameterize) TaxonName, as used def self.used_recently(user_id, project_id, used_on = 'TaxonName') Source.select('sources.id'). joins(:citations) .where(citations: {updated_by_id: user_id, project_id:, citation_object_type: used_on, updated_at: 1.week.ago..}) .order('citations.updated_at DESC') .pluck(:id).uniq end # @params target [String] a citable model name # @return [Hash] sources optimized for user selection def self.select_optimized(user_id, project_id, target = 'TaxonName') r = used_recently(user_id, project_id, target) h = { quick: [], pinboard: Source.pinned_by(user_id).where(pinboard_items: {project_id:}).to_a, recent: [] } if r.empty? h[:recent] = Source.where(created_by_id: user_id, updated_at: 2.hours.ago..Time.now ) .order('created_at DESC') .limit(5).order(:cached).to_a h[:quick] = Source.pinned_by(user_id).pinboard_inserted.where(pinboard_items: {project_id:}).to_a else h[:recent] = (Source.where(created_by_id: user_id, updated_at: 2.hours.ago..Time.now ) .order('created_at DESC') .limit(5).order(:cached).to_a + Source.where('"sources"."id" IN (?)', r.first(6) ).to_a).uniq h[:quick] = ( Source.pinned_by(user_id).pinboard_inserted.where(pinboard_items: {project_id:}).to_a + Source.where('"sources"."id" IN (?)', r.first(4) ).to_a).uniq end h end # @return [Array] # objects this source is linked to through citations def cited_objects self.citations.collect { |t| t.citation_object } end # @return [Boolean] def is_bibtex? type == 'Source::Bibtex' end # @return [Boolean] def is_in_project?(project_id) projects.where(id: project_id).any? end # Month handling allows values from bibtex like 'may' to be handled # @return [Date] def nomenclature_date Utilities::Dates.nomenclature_date(day, Utilities::Dates.month_index(month), year)&.to_date end # @return [Source] def clone s = dup m = "[CLONE of #{id}] " case type when 'Source::Verbatim' s.verbatim = m + verbatim.to_s when 'Source::Bibtex' s.title = m + title.to_s end roles.reload.each do |r| s.roles << Role.new(person: r.person, type: r.type, position: r.position ) end s.year_suffix = nil s.save s end protected # Defined in subclasses # @return [Nil] def set_cached end # Defined in subclasses def get_cached end # @param [Hash] attributed # @return [Boolean] def reject_project_sources(attributed) return true if attributed['project_id'].blank? return true if ProjectSource.where(project_id: attributed['project_id'], source_id: id).any? end def sv_cached_names true # see validation in subclasses end def sv_fix_cached_names begin Source.transaction do self.set_cached end true rescue false end end def sv_stated_year soft_validations.add( :base, "'Stated year' is not needed if identical to 'year'; applying the Fix will delete it", success_message: "'Stated year' was deleted", failure_message: "Failed to delete 'Stated year'") if year.to_s == stated_year.to_s end def sv_fix_stated_year begin Source.transaction do self.stated_year = nil self.save end true rescue false end end def if title.present? str = title.squish.gsub(/\<i>[^<>]*?<\/i>/, '') soft_validations.add(:title, 'The title contains unmatched html tags') if str.include?('<i>') || str.include?('</i>') end end end |
#editor ⇒ String
Returns see en.wikipedia.org/wiki/BibTeX#Field_types.
188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 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 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 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 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 |
# File 'app/models/source.rb', line 188 class Source < ApplicationRecord include Housekeeping::Users include Housekeeping::Timestamps include Shared::AlternateValues include Shared::DataAttributes include Shared::Documentation include Shared::Identifiers include Shared::Notes include Shared::SharedAcrossProjects include Shared::Tags include Shared::HasPapertrail include SoftValidation include Shared::IsData # !! Must not have Shared::Depictions ignore_whitespace_on(:verbatim_contents) ALTERNATE_VALUES_FOR = [ :address, :annote, :booktitle, :edition, :editor, :institution, :journal, :note, :organization, :publisher, :school, :title, :doi, :abstract, :language, :translator, :author, :url].freeze # @return [Boolean, nil] # When true, cached values are not built attr_accessor :no_year_suffix_validation # Keep this order for citations/topics has_many :citations, inverse_of: :source, dependent: :restrict_with_error has_many :origin_citations, -> {where(citations: {is_original: true})}, class_name: 'Citation', dependent: :restrict_with_error, inverse_of: :source has_many :citation_topics, through: :citations, inverse_of: :source has_many :topics, through: :citation_topics, inverse_of: :sources has_many :project_sources, inverse_of: :source, dependent: :destroy has_many :projects, inverse_of: :sources, through: :project_sources after_save :set_cached validates_presence_of :type validates :type, inclusion: {in: ['Source::Bibtex', 'Source::Human', 'Source::Verbatim']} # TODO: not needed accepts_nested_attributes_for :project_sources, reject_if: :reject_project_sources soft_validate( :sv_cached_names, set: :cached_names, fix: :sv_fix_cached_names, name: 'Cached names', description: 'Check if cached values need to be updated' ) soft_validate( :sv_stated_year, set: :stated_year, fix: :sv_fix_stated_year, name: 'Stated year', description: "'Stated year' is not needed if identical to 'year'" ) soft_validate( :sv_html_tags, set: :html_tags, name: 'html tags', description: 'Check if html has both open and close tags' ) # Redirect type here # @param [String] file # @return [[Array, message]] # TODO: return a more informative response? def self.batch_preview(file) begin bibliography = BibTeX::Bibliography.parse(file.read.force_encoding('UTF-8'), filter: :latex) sources = [] bibliography.each do |record| a = Source::Bibtex.new_from_bibtex(record) sources.push(a) end return sources, nil rescue BibTeX::ParseError => e return [], e. rescue raise end end # @return [String] # A string that represents the authors last_names and year (no suffix) def return 'not yet calculated' if new_record? [, year].compact.join(', ') end # @param [String] file # @return [Array, Boolean] def self.batch_create(file) sources = [] valid = 0 begin # error_msg = [] Source.transaction do bibliography = BibTeX::Bibliography.parse(file.read.force_encoding('UTF-8'), filter: :latex) bibliography.each do |record| a = Source::Bibtex.new_from_bibtex(record) if a.valid? if a.save valid += 1 end else # error_msg = a.errors.messages.to_s end sources.push(a) end end rescue return false end return {records: sources, count: valid} end # @param used_on [String] a model name # @return [Scope] # the max 10 most recently used (1 week, could parameterize) TaxonName, as used def self.used_recently(user_id, project_id, used_on = 'TaxonName') Source.select('sources.id'). joins(:citations) .where(citations: {updated_by_id: user_id, project_id:, citation_object_type: used_on, updated_at: 1.week.ago..}) .order('citations.updated_at DESC') .pluck(:id).uniq end # @params target [String] a citable model name # @return [Hash] sources optimized for user selection def self.select_optimized(user_id, project_id, target = 'TaxonName') r = used_recently(user_id, project_id, target) h = { quick: [], pinboard: Source.pinned_by(user_id).where(pinboard_items: {project_id:}).to_a, recent: [] } if r.empty? h[:recent] = Source.where(created_by_id: user_id, updated_at: 2.hours.ago..Time.now ) .order('created_at DESC') .limit(5).order(:cached).to_a h[:quick] = Source.pinned_by(user_id).pinboard_inserted.where(pinboard_items: {project_id:}).to_a else h[:recent] = (Source.where(created_by_id: user_id, updated_at: 2.hours.ago..Time.now ) .order('created_at DESC') .limit(5).order(:cached).to_a + Source.where('"sources"."id" IN (?)', r.first(6) ).to_a).uniq h[:quick] = ( Source.pinned_by(user_id).pinboard_inserted.where(pinboard_items: {project_id:}).to_a + Source.where('"sources"."id" IN (?)', r.first(4) ).to_a).uniq end h end # @return [Array] # objects this source is linked to through citations def cited_objects self.citations.collect { |t| t.citation_object } end # @return [Boolean] def is_bibtex? type == 'Source::Bibtex' end # @return [Boolean] def is_in_project?(project_id) projects.where(id: project_id).any? end # Month handling allows values from bibtex like 'may' to be handled # @return [Date] def nomenclature_date Utilities::Dates.nomenclature_date(day, Utilities::Dates.month_index(month), year)&.to_date end # @return [Source] def clone s = dup m = "[CLONE of #{id}] " case type when 'Source::Verbatim' s.verbatim = m + verbatim.to_s when 'Source::Bibtex' s.title = m + title.to_s end roles.reload.each do |r| s.roles << Role.new(person: r.person, type: r.type, position: r.position ) end s.year_suffix = nil s.save s end protected # Defined in subclasses # @return [Nil] def set_cached end # Defined in subclasses def get_cached end # @param [Hash] attributed # @return [Boolean] def reject_project_sources(attributed) return true if attributed['project_id'].blank? return true if ProjectSource.where(project_id: attributed['project_id'], source_id: id).any? end def sv_cached_names true # see validation in subclasses end def sv_fix_cached_names begin Source.transaction do self.set_cached end true rescue false end end def sv_stated_year soft_validations.add( :base, "'Stated year' is not needed if identical to 'year'; applying the Fix will delete it", success_message: "'Stated year' was deleted", failure_message: "Failed to delete 'Stated year'") if year.to_s == stated_year.to_s end def sv_fix_stated_year begin Source.transaction do self.stated_year = nil self.save end true rescue false end end def if title.present? str = title.squish.gsub(/\<i>[^<>]*?<\/i>/, '') soft_validations.add(:title, 'The title contains unmatched html tags') if str.include?('<i>') || str.include?('</i>') end end end |
#howpublished ⇒ String
Returns see en.wikipedia.org/wiki/BibTeX#Field_types.
188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 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 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 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 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 |
# File 'app/models/source.rb', line 188 class Source < ApplicationRecord include Housekeeping::Users include Housekeeping::Timestamps include Shared::AlternateValues include Shared::DataAttributes include Shared::Documentation include Shared::Identifiers include Shared::Notes include Shared::SharedAcrossProjects include Shared::Tags include Shared::HasPapertrail include SoftValidation include Shared::IsData # !! Must not have Shared::Depictions ignore_whitespace_on(:verbatim_contents) ALTERNATE_VALUES_FOR = [ :address, :annote, :booktitle, :edition, :editor, :institution, :journal, :note, :organization, :publisher, :school, :title, :doi, :abstract, :language, :translator, :author, :url].freeze # @return [Boolean, nil] # When true, cached values are not built attr_accessor :no_year_suffix_validation # Keep this order for citations/topics has_many :citations, inverse_of: :source, dependent: :restrict_with_error has_many :origin_citations, -> {where(citations: {is_original: true})}, class_name: 'Citation', dependent: :restrict_with_error, inverse_of: :source has_many :citation_topics, through: :citations, inverse_of: :source has_many :topics, through: :citation_topics, inverse_of: :sources has_many :project_sources, inverse_of: :source, dependent: :destroy has_many :projects, inverse_of: :sources, through: :project_sources after_save :set_cached validates_presence_of :type validates :type, inclusion: {in: ['Source::Bibtex', 'Source::Human', 'Source::Verbatim']} # TODO: not needed accepts_nested_attributes_for :project_sources, reject_if: :reject_project_sources soft_validate( :sv_cached_names, set: :cached_names, fix: :sv_fix_cached_names, name: 'Cached names', description: 'Check if cached values need to be updated' ) soft_validate( :sv_stated_year, set: :stated_year, fix: :sv_fix_stated_year, name: 'Stated year', description: "'Stated year' is not needed if identical to 'year'" ) soft_validate( :sv_html_tags, set: :html_tags, name: 'html tags', description: 'Check if html has both open and close tags' ) # Redirect type here # @param [String] file # @return [[Array, message]] # TODO: return a more informative response? def self.batch_preview(file) begin bibliography = BibTeX::Bibliography.parse(file.read.force_encoding('UTF-8'), filter: :latex) sources = [] bibliography.each do |record| a = Source::Bibtex.new_from_bibtex(record) sources.push(a) end return sources, nil rescue BibTeX::ParseError => e return [], e. rescue raise end end # @return [String] # A string that represents the authors last_names and year (no suffix) def return 'not yet calculated' if new_record? [, year].compact.join(', ') end # @param [String] file # @return [Array, Boolean] def self.batch_create(file) sources = [] valid = 0 begin # error_msg = [] Source.transaction do bibliography = BibTeX::Bibliography.parse(file.read.force_encoding('UTF-8'), filter: :latex) bibliography.each do |record| a = Source::Bibtex.new_from_bibtex(record) if a.valid? if a.save valid += 1 end else # error_msg = a.errors.messages.to_s end sources.push(a) end end rescue return false end return {records: sources, count: valid} end # @param used_on [String] a model name # @return [Scope] # the max 10 most recently used (1 week, could parameterize) TaxonName, as used def self.used_recently(user_id, project_id, used_on = 'TaxonName') Source.select('sources.id'). joins(:citations) .where(citations: {updated_by_id: user_id, project_id:, citation_object_type: used_on, updated_at: 1.week.ago..}) .order('citations.updated_at DESC') .pluck(:id).uniq end # @params target [String] a citable model name # @return [Hash] sources optimized for user selection def self.select_optimized(user_id, project_id, target = 'TaxonName') r = used_recently(user_id, project_id, target) h = { quick: [], pinboard: Source.pinned_by(user_id).where(pinboard_items: {project_id:}).to_a, recent: [] } if r.empty? h[:recent] = Source.where(created_by_id: user_id, updated_at: 2.hours.ago..Time.now ) .order('created_at DESC') .limit(5).order(:cached).to_a h[:quick] = Source.pinned_by(user_id).pinboard_inserted.where(pinboard_items: {project_id:}).to_a else h[:recent] = (Source.where(created_by_id: user_id, updated_at: 2.hours.ago..Time.now ) .order('created_at DESC') .limit(5).order(:cached).to_a + Source.where('"sources"."id" IN (?)', r.first(6) ).to_a).uniq h[:quick] = ( Source.pinned_by(user_id).pinboard_inserted.where(pinboard_items: {project_id:}).to_a + Source.where('"sources"."id" IN (?)', r.first(4) ).to_a).uniq end h end # @return [Array] # objects this source is linked to through citations def cited_objects self.citations.collect { |t| t.citation_object } end # @return [Boolean] def is_bibtex? type == 'Source::Bibtex' end # @return [Boolean] def is_in_project?(project_id) projects.where(id: project_id).any? end # Month handling allows values from bibtex like 'may' to be handled # @return [Date] def nomenclature_date Utilities::Dates.nomenclature_date(day, Utilities::Dates.month_index(month), year)&.to_date end # @return [Source] def clone s = dup m = "[CLONE of #{id}] " case type when 'Source::Verbatim' s.verbatim = m + verbatim.to_s when 'Source::Bibtex' s.title = m + title.to_s end roles.reload.each do |r| s.roles << Role.new(person: r.person, type: r.type, position: r.position ) end s.year_suffix = nil s.save s end protected # Defined in subclasses # @return [Nil] def set_cached end # Defined in subclasses def get_cached end # @param [Hash] attributed # @return [Boolean] def reject_project_sources(attributed) return true if attributed['project_id'].blank? return true if ProjectSource.where(project_id: attributed['project_id'], source_id: id).any? end def sv_cached_names true # see validation in subclasses end def sv_fix_cached_names begin Source.transaction do self.set_cached end true rescue false end end def sv_stated_year soft_validations.add( :base, "'Stated year' is not needed if identical to 'year'; applying the Fix will delete it", success_message: "'Stated year' was deleted", failure_message: "Failed to delete 'Stated year'") if year.to_s == stated_year.to_s end def sv_fix_stated_year begin Source.transaction do self.stated_year = nil self.save end true rescue false end end def if title.present? str = title.squish.gsub(/\<i>[^<>]*?<\/i>/, '') soft_validations.add(:title, 'The title contains unmatched html tags') if str.include?('<i>') || str.include?('</i>') end end end |
#institution ⇒ String
Returns see en.wikipedia.org/wiki/BibTeX#Field_types.
188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 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 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 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 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 |
# File 'app/models/source.rb', line 188 class Source < ApplicationRecord include Housekeeping::Users include Housekeeping::Timestamps include Shared::AlternateValues include Shared::DataAttributes include Shared::Documentation include Shared::Identifiers include Shared::Notes include Shared::SharedAcrossProjects include Shared::Tags include Shared::HasPapertrail include SoftValidation include Shared::IsData # !! Must not have Shared::Depictions ignore_whitespace_on(:verbatim_contents) ALTERNATE_VALUES_FOR = [ :address, :annote, :booktitle, :edition, :editor, :institution, :journal, :note, :organization, :publisher, :school, :title, :doi, :abstract, :language, :translator, :author, :url].freeze # @return [Boolean, nil] # When true, cached values are not built attr_accessor :no_year_suffix_validation # Keep this order for citations/topics has_many :citations, inverse_of: :source, dependent: :restrict_with_error has_many :origin_citations, -> {where(citations: {is_original: true})}, class_name: 'Citation', dependent: :restrict_with_error, inverse_of: :source has_many :citation_topics, through: :citations, inverse_of: :source has_many :topics, through: :citation_topics, inverse_of: :sources has_many :project_sources, inverse_of: :source, dependent: :destroy has_many :projects, inverse_of: :sources, through: :project_sources after_save :set_cached validates_presence_of :type validates :type, inclusion: {in: ['Source::Bibtex', 'Source::Human', 'Source::Verbatim']} # TODO: not needed accepts_nested_attributes_for :project_sources, reject_if: :reject_project_sources soft_validate( :sv_cached_names, set: :cached_names, fix: :sv_fix_cached_names, name: 'Cached names', description: 'Check if cached values need to be updated' ) soft_validate( :sv_stated_year, set: :stated_year, fix: :sv_fix_stated_year, name: 'Stated year', description: "'Stated year' is not needed if identical to 'year'" ) soft_validate( :sv_html_tags, set: :html_tags, name: 'html tags', description: 'Check if html has both open and close tags' ) # Redirect type here # @param [String] file # @return [[Array, message]] # TODO: return a more informative response? def self.batch_preview(file) begin bibliography = BibTeX::Bibliography.parse(file.read.force_encoding('UTF-8'), filter: :latex) sources = [] bibliography.each do |record| a = Source::Bibtex.new_from_bibtex(record) sources.push(a) end return sources, nil rescue BibTeX::ParseError => e return [], e. rescue raise end end # @return [String] # A string that represents the authors last_names and year (no suffix) def return 'not yet calculated' if new_record? [, year].compact.join(', ') end # @param [String] file # @return [Array, Boolean] def self.batch_create(file) sources = [] valid = 0 begin # error_msg = [] Source.transaction do bibliography = BibTeX::Bibliography.parse(file.read.force_encoding('UTF-8'), filter: :latex) bibliography.each do |record| a = Source::Bibtex.new_from_bibtex(record) if a.valid? if a.save valid += 1 end else # error_msg = a.errors.messages.to_s end sources.push(a) end end rescue return false end return {records: sources, count: valid} end # @param used_on [String] a model name # @return [Scope] # the max 10 most recently used (1 week, could parameterize) TaxonName, as used def self.used_recently(user_id, project_id, used_on = 'TaxonName') Source.select('sources.id'). joins(:citations) .where(citations: {updated_by_id: user_id, project_id:, citation_object_type: used_on, updated_at: 1.week.ago..}) .order('citations.updated_at DESC') .pluck(:id).uniq end # @params target [String] a citable model name # @return [Hash] sources optimized for user selection def self.select_optimized(user_id, project_id, target = 'TaxonName') r = used_recently(user_id, project_id, target) h = { quick: [], pinboard: Source.pinned_by(user_id).where(pinboard_items: {project_id:}).to_a, recent: [] } if r.empty? h[:recent] = Source.where(created_by_id: user_id, updated_at: 2.hours.ago..Time.now ) .order('created_at DESC') .limit(5).order(:cached).to_a h[:quick] = Source.pinned_by(user_id).pinboard_inserted.where(pinboard_items: {project_id:}).to_a else h[:recent] = (Source.where(created_by_id: user_id, updated_at: 2.hours.ago..Time.now ) .order('created_at DESC') .limit(5).order(:cached).to_a + Source.where('"sources"."id" IN (?)', r.first(6) ).to_a).uniq h[:quick] = ( Source.pinned_by(user_id).pinboard_inserted.where(pinboard_items: {project_id:}).to_a + Source.where('"sources"."id" IN (?)', r.first(4) ).to_a).uniq end h end # @return [Array] # objects this source is linked to through citations def cited_objects self.citations.collect { |t| t.citation_object } end # @return [Boolean] def is_bibtex? type == 'Source::Bibtex' end # @return [Boolean] def is_in_project?(project_id) projects.where(id: project_id).any? end # Month handling allows values from bibtex like 'may' to be handled # @return [Date] def nomenclature_date Utilities::Dates.nomenclature_date(day, Utilities::Dates.month_index(month), year)&.to_date end # @return [Source] def clone s = dup m = "[CLONE of #{id}] " case type when 'Source::Verbatim' s.verbatim = m + verbatim.to_s when 'Source::Bibtex' s.title = m + title.to_s end roles.reload.each do |r| s.roles << Role.new(person: r.person, type: r.type, position: r.position ) end s.year_suffix = nil s.save s end protected # Defined in subclasses # @return [Nil] def set_cached end # Defined in subclasses def get_cached end # @param [Hash] attributed # @return [Boolean] def reject_project_sources(attributed) return true if attributed['project_id'].blank? return true if ProjectSource.where(project_id: attributed['project_id'], source_id: id).any? end def sv_cached_names true # see validation in subclasses end def sv_fix_cached_names begin Source.transaction do self.set_cached end true rescue false end end def sv_stated_year soft_validations.add( :base, "'Stated year' is not needed if identical to 'year'; applying the Fix will delete it", success_message: "'Stated year' was deleted", failure_message: "Failed to delete 'Stated year'") if year.to_s == stated_year.to_s end def sv_fix_stated_year begin Source.transaction do self.stated_year = nil self.save end true rescue false end end def if title.present? str = title.squish.gsub(/\<i>[^<>]*?<\/i>/, '') soft_validations.add(:title, 'The title contains unmatched html tags') if str.include?('<i>') || str.include?('</i>') end end end |
#isbn ⇒ String
188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 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 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 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 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 |
# File 'app/models/source.rb', line 188 class Source < ApplicationRecord include Housekeeping::Users include Housekeeping::Timestamps include Shared::AlternateValues include Shared::DataAttributes include Shared::Documentation include Shared::Identifiers include Shared::Notes include Shared::SharedAcrossProjects include Shared::Tags include Shared::HasPapertrail include SoftValidation include Shared::IsData # !! Must not have Shared::Depictions ignore_whitespace_on(:verbatim_contents) ALTERNATE_VALUES_FOR = [ :address, :annote, :booktitle, :edition, :editor, :institution, :journal, :note, :organization, :publisher, :school, :title, :doi, :abstract, :language, :translator, :author, :url].freeze # @return [Boolean, nil] # When true, cached values are not built attr_accessor :no_year_suffix_validation # Keep this order for citations/topics has_many :citations, inverse_of: :source, dependent: :restrict_with_error has_many :origin_citations, -> {where(citations: {is_original: true})}, class_name: 'Citation', dependent: :restrict_with_error, inverse_of: :source has_many :citation_topics, through: :citations, inverse_of: :source has_many :topics, through: :citation_topics, inverse_of: :sources has_many :project_sources, inverse_of: :source, dependent: :destroy has_many :projects, inverse_of: :sources, through: :project_sources after_save :set_cached validates_presence_of :type validates :type, inclusion: {in: ['Source::Bibtex', 'Source::Human', 'Source::Verbatim']} # TODO: not needed accepts_nested_attributes_for :project_sources, reject_if: :reject_project_sources soft_validate( :sv_cached_names, set: :cached_names, fix: :sv_fix_cached_names, name: 'Cached names', description: 'Check if cached values need to be updated' ) soft_validate( :sv_stated_year, set: :stated_year, fix: :sv_fix_stated_year, name: 'Stated year', description: "'Stated year' is not needed if identical to 'year'" ) soft_validate( :sv_html_tags, set: :html_tags, name: 'html tags', description: 'Check if html has both open and close tags' ) # Redirect type here # @param [String] file # @return [[Array, message]] # TODO: return a more informative response? def self.batch_preview(file) begin bibliography = BibTeX::Bibliography.parse(file.read.force_encoding('UTF-8'), filter: :latex) sources = [] bibliography.each do |record| a = Source::Bibtex.new_from_bibtex(record) sources.push(a) end return sources, nil rescue BibTeX::ParseError => e return [], e. rescue raise end end # @return [String] # A string that represents the authors last_names and year (no suffix) def return 'not yet calculated' if new_record? [, year].compact.join(', ') end # @param [String] file # @return [Array, Boolean] def self.batch_create(file) sources = [] valid = 0 begin # error_msg = [] Source.transaction do bibliography = BibTeX::Bibliography.parse(file.read.force_encoding('UTF-8'), filter: :latex) bibliography.each do |record| a = Source::Bibtex.new_from_bibtex(record) if a.valid? if a.save valid += 1 end else # error_msg = a.errors.messages.to_s end sources.push(a) end end rescue return false end return {records: sources, count: valid} end # @param used_on [String] a model name # @return [Scope] # the max 10 most recently used (1 week, could parameterize) TaxonName, as used def self.used_recently(user_id, project_id, used_on = 'TaxonName') Source.select('sources.id'). joins(:citations) .where(citations: {updated_by_id: user_id, project_id:, citation_object_type: used_on, updated_at: 1.week.ago..}) .order('citations.updated_at DESC') .pluck(:id).uniq end # @params target [String] a citable model name # @return [Hash] sources optimized for user selection def self.select_optimized(user_id, project_id, target = 'TaxonName') r = used_recently(user_id, project_id, target) h = { quick: [], pinboard: Source.pinned_by(user_id).where(pinboard_items: {project_id:}).to_a, recent: [] } if r.empty? h[:recent] = Source.where(created_by_id: user_id, updated_at: 2.hours.ago..Time.now ) .order('created_at DESC') .limit(5).order(:cached).to_a h[:quick] = Source.pinned_by(user_id).pinboard_inserted.where(pinboard_items: {project_id:}).to_a else h[:recent] = (Source.where(created_by_id: user_id, updated_at: 2.hours.ago..Time.now ) .order('created_at DESC') .limit(5).order(:cached).to_a + Source.where('"sources"."id" IN (?)', r.first(6) ).to_a).uniq h[:quick] = ( Source.pinned_by(user_id).pinboard_inserted.where(pinboard_items: {project_id:}).to_a + Source.where('"sources"."id" IN (?)', r.first(4) ).to_a).uniq end h end # @return [Array] # objects this source is linked to through citations def cited_objects self.citations.collect { |t| t.citation_object } end # @return [Boolean] def is_bibtex? type == 'Source::Bibtex' end # @return [Boolean] def is_in_project?(project_id) projects.where(id: project_id).any? end # Month handling allows values from bibtex like 'may' to be handled # @return [Date] def nomenclature_date Utilities::Dates.nomenclature_date(day, Utilities::Dates.month_index(month), year)&.to_date end # @return [Source] def clone s = dup m = "[CLONE of #{id}] " case type when 'Source::Verbatim' s.verbatim = m + verbatim.to_s when 'Source::Bibtex' s.title = m + title.to_s end roles.reload.each do |r| s.roles << Role.new(person: r.person, type: r.type, position: r.position ) end s.year_suffix = nil s.save s end protected # Defined in subclasses # @return [Nil] def set_cached end # Defined in subclasses def get_cached end # @param [Hash] attributed # @return [Boolean] def reject_project_sources(attributed) return true if attributed['project_id'].blank? return true if ProjectSource.where(project_id: attributed['project_id'], source_id: id).any? end def sv_cached_names true # see validation in subclasses end def sv_fix_cached_names begin Source.transaction do self.set_cached end true rescue false end end def sv_stated_year soft_validations.add( :base, "'Stated year' is not needed if identical to 'year'; applying the Fix will delete it", success_message: "'Stated year' was deleted", failure_message: "Failed to delete 'Stated year'") if year.to_s == stated_year.to_s end def sv_fix_stated_year begin Source.transaction do self.stated_year = nil self.save end true rescue false end end def if title.present? str = title.squish.gsub(/\<i>[^<>]*?<\/i>/, '') soft_validations.add(:title, 'The title contains unmatched html tags') if str.include?('<i>') || str.include?('</i>') end end end |
#issn ⇒ String
188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 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 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 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 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 |
# File 'app/models/source.rb', line 188 class Source < ApplicationRecord include Housekeeping::Users include Housekeeping::Timestamps include Shared::AlternateValues include Shared::DataAttributes include Shared::Documentation include Shared::Identifiers include Shared::Notes include Shared::SharedAcrossProjects include Shared::Tags include Shared::HasPapertrail include SoftValidation include Shared::IsData # !! Must not have Shared::Depictions ignore_whitespace_on(:verbatim_contents) ALTERNATE_VALUES_FOR = [ :address, :annote, :booktitle, :edition, :editor, :institution, :journal, :note, :organization, :publisher, :school, :title, :doi, :abstract, :language, :translator, :author, :url].freeze # @return [Boolean, nil] # When true, cached values are not built attr_accessor :no_year_suffix_validation # Keep this order for citations/topics has_many :citations, inverse_of: :source, dependent: :restrict_with_error has_many :origin_citations, -> {where(citations: {is_original: true})}, class_name: 'Citation', dependent: :restrict_with_error, inverse_of: :source has_many :citation_topics, through: :citations, inverse_of: :source has_many :topics, through: :citation_topics, inverse_of: :sources has_many :project_sources, inverse_of: :source, dependent: :destroy has_many :projects, inverse_of: :sources, through: :project_sources after_save :set_cached validates_presence_of :type validates :type, inclusion: {in: ['Source::Bibtex', 'Source::Human', 'Source::Verbatim']} # TODO: not needed accepts_nested_attributes_for :project_sources, reject_if: :reject_project_sources soft_validate( :sv_cached_names, set: :cached_names, fix: :sv_fix_cached_names, name: 'Cached names', description: 'Check if cached values need to be updated' ) soft_validate( :sv_stated_year, set: :stated_year, fix: :sv_fix_stated_year, name: 'Stated year', description: "'Stated year' is not needed if identical to 'year'" ) soft_validate( :sv_html_tags, set: :html_tags, name: 'html tags', description: 'Check if html has both open and close tags' ) # Redirect type here # @param [String] file # @return [[Array, message]] # TODO: return a more informative response? def self.batch_preview(file) begin bibliography = BibTeX::Bibliography.parse(file.read.force_encoding('UTF-8'), filter: :latex) sources = [] bibliography.each do |record| a = Source::Bibtex.new_from_bibtex(record) sources.push(a) end return sources, nil rescue BibTeX::ParseError => e return [], e. rescue raise end end # @return [String] # A string that represents the authors last_names and year (no suffix) def return 'not yet calculated' if new_record? [, year].compact.join(', ') end # @param [String] file # @return [Array, Boolean] def self.batch_create(file) sources = [] valid = 0 begin # error_msg = [] Source.transaction do bibliography = BibTeX::Bibliography.parse(file.read.force_encoding('UTF-8'), filter: :latex) bibliography.each do |record| a = Source::Bibtex.new_from_bibtex(record) if a.valid? if a.save valid += 1 end else # error_msg = a.errors.messages.to_s end sources.push(a) end end rescue return false end return {records: sources, count: valid} end # @param used_on [String] a model name # @return [Scope] # the max 10 most recently used (1 week, could parameterize) TaxonName, as used def self.used_recently(user_id, project_id, used_on = 'TaxonName') Source.select('sources.id'). joins(:citations) .where(citations: {updated_by_id: user_id, project_id:, citation_object_type: used_on, updated_at: 1.week.ago..}) .order('citations.updated_at DESC') .pluck(:id).uniq end # @params target [String] a citable model name # @return [Hash] sources optimized for user selection def self.select_optimized(user_id, project_id, target = 'TaxonName') r = used_recently(user_id, project_id, target) h = { quick: [], pinboard: Source.pinned_by(user_id).where(pinboard_items: {project_id:}).to_a, recent: [] } if r.empty? h[:recent] = Source.where(created_by_id: user_id, updated_at: 2.hours.ago..Time.now ) .order('created_at DESC') .limit(5).order(:cached).to_a h[:quick] = Source.pinned_by(user_id).pinboard_inserted.where(pinboard_items: {project_id:}).to_a else h[:recent] = (Source.where(created_by_id: user_id, updated_at: 2.hours.ago..Time.now ) .order('created_at DESC') .limit(5).order(:cached).to_a + Source.where('"sources"."id" IN (?)', r.first(6) ).to_a).uniq h[:quick] = ( Source.pinned_by(user_id).pinboard_inserted.where(pinboard_items: {project_id:}).to_a + Source.where('"sources"."id" IN (?)', r.first(4) ).to_a).uniq end h end # @return [Array] # objects this source is linked to through citations def cited_objects self.citations.collect { |t| t.citation_object } end # @return [Boolean] def is_bibtex? type == 'Source::Bibtex' end # @return [Boolean] def is_in_project?(project_id) projects.where(id: project_id).any? end # Month handling allows values from bibtex like 'may' to be handled # @return [Date] def nomenclature_date Utilities::Dates.nomenclature_date(day, Utilities::Dates.month_index(month), year)&.to_date end # @return [Source] def clone s = dup m = "[CLONE of #{id}] " case type when 'Source::Verbatim' s.verbatim = m + verbatim.to_s when 'Source::Bibtex' s.title = m + title.to_s end roles.reload.each do |r| s.roles << Role.new(person: r.person, type: r.type, position: r.position ) end s.year_suffix = nil s.save s end protected # Defined in subclasses # @return [Nil] def set_cached end # Defined in subclasses def get_cached end # @param [Hash] attributed # @return [Boolean] def reject_project_sources(attributed) return true if attributed['project_id'].blank? return true if ProjectSource.where(project_id: attributed['project_id'], source_id: id).any? end def sv_cached_names true # see validation in subclasses end def sv_fix_cached_names begin Source.transaction do self.set_cached end true rescue false end end def sv_stated_year soft_validations.add( :base, "'Stated year' is not needed if identical to 'year'; applying the Fix will delete it", success_message: "'Stated year' was deleted", failure_message: "Failed to delete 'Stated year'") if year.to_s == stated_year.to_s end def sv_fix_stated_year begin Source.transaction do self.stated_year = nil self.save end true rescue false end end def if title.present? str = title.squish.gsub(/\<i>[^<>]*?<\/i>/, '') soft_validations.add(:title, 'The title contains unmatched html tags') if str.include?('<i>') || str.include?('</i>') end end end |
#journal ⇒ String
Returns see en.wikipedia.org/wiki/BibTeX#Field_types.
188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 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 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 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 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 |
# File 'app/models/source.rb', line 188 class Source < ApplicationRecord include Housekeeping::Users include Housekeeping::Timestamps include Shared::AlternateValues include Shared::DataAttributes include Shared::Documentation include Shared::Identifiers include Shared::Notes include Shared::SharedAcrossProjects include Shared::Tags include Shared::HasPapertrail include SoftValidation include Shared::IsData # !! Must not have Shared::Depictions ignore_whitespace_on(:verbatim_contents) ALTERNATE_VALUES_FOR = [ :address, :annote, :booktitle, :edition, :editor, :institution, :journal, :note, :organization, :publisher, :school, :title, :doi, :abstract, :language, :translator, :author, :url].freeze # @return [Boolean, nil] # When true, cached values are not built attr_accessor :no_year_suffix_validation # Keep this order for citations/topics has_many :citations, inverse_of: :source, dependent: :restrict_with_error has_many :origin_citations, -> {where(citations: {is_original: true})}, class_name: 'Citation', dependent: :restrict_with_error, inverse_of: :source has_many :citation_topics, through: :citations, inverse_of: :source has_many :topics, through: :citation_topics, inverse_of: :sources has_many :project_sources, inverse_of: :source, dependent: :destroy has_many :projects, inverse_of: :sources, through: :project_sources after_save :set_cached validates_presence_of :type validates :type, inclusion: {in: ['Source::Bibtex', 'Source::Human', 'Source::Verbatim']} # TODO: not needed accepts_nested_attributes_for :project_sources, reject_if: :reject_project_sources soft_validate( :sv_cached_names, set: :cached_names, fix: :sv_fix_cached_names, name: 'Cached names', description: 'Check if cached values need to be updated' ) soft_validate( :sv_stated_year, set: :stated_year, fix: :sv_fix_stated_year, name: 'Stated year', description: "'Stated year' is not needed if identical to 'year'" ) soft_validate( :sv_html_tags, set: :html_tags, name: 'html tags', description: 'Check if html has both open and close tags' ) # Redirect type here # @param [String] file # @return [[Array, message]] # TODO: return a more informative response? def self.batch_preview(file) begin bibliography = BibTeX::Bibliography.parse(file.read.force_encoding('UTF-8'), filter: :latex) sources = [] bibliography.each do |record| a = Source::Bibtex.new_from_bibtex(record) sources.push(a) end return sources, nil rescue BibTeX::ParseError => e return [], e. rescue raise end end # @return [String] # A string that represents the authors last_names and year (no suffix) def return 'not yet calculated' if new_record? [, year].compact.join(', ') end # @param [String] file # @return [Array, Boolean] def self.batch_create(file) sources = [] valid = 0 begin # error_msg = [] Source.transaction do bibliography = BibTeX::Bibliography.parse(file.read.force_encoding('UTF-8'), filter: :latex) bibliography.each do |record| a = Source::Bibtex.new_from_bibtex(record) if a.valid? if a.save valid += 1 end else # error_msg = a.errors.messages.to_s end sources.push(a) end end rescue return false end return {records: sources, count: valid} end # @param used_on [String] a model name # @return [Scope] # the max 10 most recently used (1 week, could parameterize) TaxonName, as used def self.used_recently(user_id, project_id, used_on = 'TaxonName') Source.select('sources.id'). joins(:citations) .where(citations: {updated_by_id: user_id, project_id:, citation_object_type: used_on, updated_at: 1.week.ago..}) .order('citations.updated_at DESC') .pluck(:id).uniq end # @params target [String] a citable model name # @return [Hash] sources optimized for user selection def self.select_optimized(user_id, project_id, target = 'TaxonName') r = used_recently(user_id, project_id, target) h = { quick: [], pinboard: Source.pinned_by(user_id).where(pinboard_items: {project_id:}).to_a, recent: [] } if r.empty? h[:recent] = Source.where(created_by_id: user_id, updated_at: 2.hours.ago..Time.now ) .order('created_at DESC') .limit(5).order(:cached).to_a h[:quick] = Source.pinned_by(user_id).pinboard_inserted.where(pinboard_items: {project_id:}).to_a else h[:recent] = (Source.where(created_by_id: user_id, updated_at: 2.hours.ago..Time.now ) .order('created_at DESC') .limit(5).order(:cached).to_a + Source.where('"sources"."id" IN (?)', r.first(6) ).to_a).uniq h[:quick] = ( Source.pinned_by(user_id).pinboard_inserted.where(pinboard_items: {project_id:}).to_a + Source.where('"sources"."id" IN (?)', r.first(4) ).to_a).uniq end h end # @return [Array] # objects this source is linked to through citations def cited_objects self.citations.collect { |t| t.citation_object } end # @return [Boolean] def is_bibtex? type == 'Source::Bibtex' end # @return [Boolean] def is_in_project?(project_id) projects.where(id: project_id).any? end # Month handling allows values from bibtex like 'may' to be handled # @return [Date] def nomenclature_date Utilities::Dates.nomenclature_date(day, Utilities::Dates.month_index(month), year)&.to_date end # @return [Source] def clone s = dup m = "[CLONE of #{id}] " case type when 'Source::Verbatim' s.verbatim = m + verbatim.to_s when 'Source::Bibtex' s.title = m + title.to_s end roles.reload.each do |r| s.roles << Role.new(person: r.person, type: r.type, position: r.position ) end s.year_suffix = nil s.save s end protected # Defined in subclasses # @return [Nil] def set_cached end # Defined in subclasses def get_cached end # @param [Hash] attributed # @return [Boolean] def reject_project_sources(attributed) return true if attributed['project_id'].blank? return true if ProjectSource.where(project_id: attributed['project_id'], source_id: id).any? end def sv_cached_names true # see validation in subclasses end def sv_fix_cached_names begin Source.transaction do self.set_cached end true rescue false end end def sv_stated_year soft_validations.add( :base, "'Stated year' is not needed if identical to 'year'; applying the Fix will delete it", success_message: "'Stated year' was deleted", failure_message: "Failed to delete 'Stated year'") if year.to_s == stated_year.to_s end def sv_fix_stated_year begin Source.transaction do self.stated_year = nil self.save end true rescue false end end def if title.present? str = title.squish.gsub(/\<i>[^<>]*?<\/i>/, '') soft_validations.add(:title, 'The title contains unmatched html tags') if str.include?('<i>') || str.include?('</i>') end end end |
#key ⇒ String
Returns see en.wikipedia.org/wiki/BibTeX#Field_types.
188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 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 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 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 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 |
# File 'app/models/source.rb', line 188 class Source < ApplicationRecord include Housekeeping::Users include Housekeeping::Timestamps include Shared::AlternateValues include Shared::DataAttributes include Shared::Documentation include Shared::Identifiers include Shared::Notes include Shared::SharedAcrossProjects include Shared::Tags include Shared::HasPapertrail include SoftValidation include Shared::IsData # !! Must not have Shared::Depictions ignore_whitespace_on(:verbatim_contents) ALTERNATE_VALUES_FOR = [ :address, :annote, :booktitle, :edition, :editor, :institution, :journal, :note, :organization, :publisher, :school, :title, :doi, :abstract, :language, :translator, :author, :url].freeze # @return [Boolean, nil] # When true, cached values are not built attr_accessor :no_year_suffix_validation # Keep this order for citations/topics has_many :citations, inverse_of: :source, dependent: :restrict_with_error has_many :origin_citations, -> {where(citations: {is_original: true})}, class_name: 'Citation', dependent: :restrict_with_error, inverse_of: :source has_many :citation_topics, through: :citations, inverse_of: :source has_many :topics, through: :citation_topics, inverse_of: :sources has_many :project_sources, inverse_of: :source, dependent: :destroy has_many :projects, inverse_of: :sources, through: :project_sources after_save :set_cached validates_presence_of :type validates :type, inclusion: {in: ['Source::Bibtex', 'Source::Human', 'Source::Verbatim']} # TODO: not needed accepts_nested_attributes_for :project_sources, reject_if: :reject_project_sources soft_validate( :sv_cached_names, set: :cached_names, fix: :sv_fix_cached_names, name: 'Cached names', description: 'Check if cached values need to be updated' ) soft_validate( :sv_stated_year, set: :stated_year, fix: :sv_fix_stated_year, name: 'Stated year', description: "'Stated year' is not needed if identical to 'year'" ) soft_validate( :sv_html_tags, set: :html_tags, name: 'html tags', description: 'Check if html has both open and close tags' ) # Redirect type here # @param [String] file # @return [[Array, message]] # TODO: return a more informative response? def self.batch_preview(file) begin bibliography = BibTeX::Bibliography.parse(file.read.force_encoding('UTF-8'), filter: :latex) sources = [] bibliography.each do |record| a = Source::Bibtex.new_from_bibtex(record) sources.push(a) end return sources, nil rescue BibTeX::ParseError => e return [], e. rescue raise end end # @return [String] # A string that represents the authors last_names and year (no suffix) def return 'not yet calculated' if new_record? [, year].compact.join(', ') end # @param [String] file # @return [Array, Boolean] def self.batch_create(file) sources = [] valid = 0 begin # error_msg = [] Source.transaction do bibliography = BibTeX::Bibliography.parse(file.read.force_encoding('UTF-8'), filter: :latex) bibliography.each do |record| a = Source::Bibtex.new_from_bibtex(record) if a.valid? if a.save valid += 1 end else # error_msg = a.errors.messages.to_s end sources.push(a) end end rescue return false end return {records: sources, count: valid} end # @param used_on [String] a model name # @return [Scope] # the max 10 most recently used (1 week, could parameterize) TaxonName, as used def self.used_recently(user_id, project_id, used_on = 'TaxonName') Source.select('sources.id'). joins(:citations) .where(citations: {updated_by_id: user_id, project_id:, citation_object_type: used_on, updated_at: 1.week.ago..}) .order('citations.updated_at DESC') .pluck(:id).uniq end # @params target [String] a citable model name # @return [Hash] sources optimized for user selection def self.select_optimized(user_id, project_id, target = 'TaxonName') r = used_recently(user_id, project_id, target) h = { quick: [], pinboard: Source.pinned_by(user_id).where(pinboard_items: {project_id:}).to_a, recent: [] } if r.empty? h[:recent] = Source.where(created_by_id: user_id, updated_at: 2.hours.ago..Time.now ) .order('created_at DESC') .limit(5).order(:cached).to_a h[:quick] = Source.pinned_by(user_id).pinboard_inserted.where(pinboard_items: {project_id:}).to_a else h[:recent] = (Source.where(created_by_id: user_id, updated_at: 2.hours.ago..Time.now ) .order('created_at DESC') .limit(5).order(:cached).to_a + Source.where('"sources"."id" IN (?)', r.first(6) ).to_a).uniq h[:quick] = ( Source.pinned_by(user_id).pinboard_inserted.where(pinboard_items: {project_id:}).to_a + Source.where('"sources"."id" IN (?)', r.first(4) ).to_a).uniq end h end # @return [Array] # objects this source is linked to through citations def cited_objects self.citations.collect { |t| t.citation_object } end # @return [Boolean] def is_bibtex? type == 'Source::Bibtex' end # @return [Boolean] def is_in_project?(project_id) projects.where(id: project_id).any? end # Month handling allows values from bibtex like 'may' to be handled # @return [Date] def nomenclature_date Utilities::Dates.nomenclature_date(day, Utilities::Dates.month_index(month), year)&.to_date end # @return [Source] def clone s = dup m = "[CLONE of #{id}] " case type when 'Source::Verbatim' s.verbatim = m + verbatim.to_s when 'Source::Bibtex' s.title = m + title.to_s end roles.reload.each do |r| s.roles << Role.new(person: r.person, type: r.type, position: r.position ) end s.year_suffix = nil s.save s end protected # Defined in subclasses # @return [Nil] def set_cached end # Defined in subclasses def get_cached end # @param [Hash] attributed # @return [Boolean] def reject_project_sources(attributed) return true if attributed['project_id'].blank? return true if ProjectSource.where(project_id: attributed['project_id'], source_id: id).any? end def sv_cached_names true # see validation in subclasses end def sv_fix_cached_names begin Source.transaction do self.set_cached end true rescue false end end def sv_stated_year soft_validations.add( :base, "'Stated year' is not needed if identical to 'year'; applying the Fix will delete it", success_message: "'Stated year' was deleted", failure_message: "Failed to delete 'Stated year'") if year.to_s == stated_year.to_s end def sv_fix_stated_year begin Source.transaction do self.stated_year = nil self.save end true rescue false end end def if title.present? str = title.squish.gsub(/\<i>[^<>]*?<\/i>/, '') soft_validations.add(:title, 'The title contains unmatched html tags') if str.include?('<i>') || str.include?('</i>') end end end |
#language ⇒ String
Returns see en.wikipedia.org/wiki/BibTeX#Field_types.
188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 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 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 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 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 |
# File 'app/models/source.rb', line 188 class Source < ApplicationRecord include Housekeeping::Users include Housekeeping::Timestamps include Shared::AlternateValues include Shared::DataAttributes include Shared::Documentation include Shared::Identifiers include Shared::Notes include Shared::SharedAcrossProjects include Shared::Tags include Shared::HasPapertrail include SoftValidation include Shared::IsData # !! Must not have Shared::Depictions ignore_whitespace_on(:verbatim_contents) ALTERNATE_VALUES_FOR = [ :address, :annote, :booktitle, :edition, :editor, :institution, :journal, :note, :organization, :publisher, :school, :title, :doi, :abstract, :language, :translator, :author, :url].freeze # @return [Boolean, nil] # When true, cached values are not built attr_accessor :no_year_suffix_validation # Keep this order for citations/topics has_many :citations, inverse_of: :source, dependent: :restrict_with_error has_many :origin_citations, -> {where(citations: {is_original: true})}, class_name: 'Citation', dependent: :restrict_with_error, inverse_of: :source has_many :citation_topics, through: :citations, inverse_of: :source has_many :topics, through: :citation_topics, inverse_of: :sources has_many :project_sources, inverse_of: :source, dependent: :destroy has_many :projects, inverse_of: :sources, through: :project_sources after_save :set_cached validates_presence_of :type validates :type, inclusion: {in: ['Source::Bibtex', 'Source::Human', 'Source::Verbatim']} # TODO: not needed accepts_nested_attributes_for :project_sources, reject_if: :reject_project_sources soft_validate( :sv_cached_names, set: :cached_names, fix: :sv_fix_cached_names, name: 'Cached names', description: 'Check if cached values need to be updated' ) soft_validate( :sv_stated_year, set: :stated_year, fix: :sv_fix_stated_year, name: 'Stated year', description: "'Stated year' is not needed if identical to 'year'" ) soft_validate( :sv_html_tags, set: :html_tags, name: 'html tags', description: 'Check if html has both open and close tags' ) # Redirect type here # @param [String] file # @return [[Array, message]] # TODO: return a more informative response? def self.batch_preview(file) begin bibliography = BibTeX::Bibliography.parse(file.read.force_encoding('UTF-8'), filter: :latex) sources = [] bibliography.each do |record| a = Source::Bibtex.new_from_bibtex(record) sources.push(a) end return sources, nil rescue BibTeX::ParseError => e return [], e. rescue raise end end # @return [String] # A string that represents the authors last_names and year (no suffix) def return 'not yet calculated' if new_record? [, year].compact.join(', ') end # @param [String] file # @return [Array, Boolean] def self.batch_create(file) sources = [] valid = 0 begin # error_msg = [] Source.transaction do bibliography = BibTeX::Bibliography.parse(file.read.force_encoding('UTF-8'), filter: :latex) bibliography.each do |record| a = Source::Bibtex.new_from_bibtex(record) if a.valid? if a.save valid += 1 end else # error_msg = a.errors.messages.to_s end sources.push(a) end end rescue return false end return {records: sources, count: valid} end # @param used_on [String] a model name # @return [Scope] # the max 10 most recently used (1 week, could parameterize) TaxonName, as used def self.used_recently(user_id, project_id, used_on = 'TaxonName') Source.select('sources.id'). joins(:citations) .where(citations: {updated_by_id: user_id, project_id:, citation_object_type: used_on, updated_at: 1.week.ago..}) .order('citations.updated_at DESC') .pluck(:id).uniq end # @params target [String] a citable model name # @return [Hash] sources optimized for user selection def self.select_optimized(user_id, project_id, target = 'TaxonName') r = used_recently(user_id, project_id, target) h = { quick: [], pinboard: Source.pinned_by(user_id).where(pinboard_items: {project_id:}).to_a, recent: [] } if r.empty? h[:recent] = Source.where(created_by_id: user_id, updated_at: 2.hours.ago..Time.now ) .order('created_at DESC') .limit(5).order(:cached).to_a h[:quick] = Source.pinned_by(user_id).pinboard_inserted.where(pinboard_items: {project_id:}).to_a else h[:recent] = (Source.where(created_by_id: user_id, updated_at: 2.hours.ago..Time.now ) .order('created_at DESC') .limit(5).order(:cached).to_a + Source.where('"sources"."id" IN (?)', r.first(6) ).to_a).uniq h[:quick] = ( Source.pinned_by(user_id).pinboard_inserted.where(pinboard_items: {project_id:}).to_a + Source.where('"sources"."id" IN (?)', r.first(4) ).to_a).uniq end h end # @return [Array] # objects this source is linked to through citations def cited_objects self.citations.collect { |t| t.citation_object } end # @return [Boolean] def is_bibtex? type == 'Source::Bibtex' end # @return [Boolean] def is_in_project?(project_id) projects.where(id: project_id).any? end # Month handling allows values from bibtex like 'may' to be handled # @return [Date] def nomenclature_date Utilities::Dates.nomenclature_date(day, Utilities::Dates.month_index(month), year)&.to_date end # @return [Source] def clone s = dup m = "[CLONE of #{id}] " case type when 'Source::Verbatim' s.verbatim = m + verbatim.to_s when 'Source::Bibtex' s.title = m + title.to_s end roles.reload.each do |r| s.roles << Role.new(person: r.person, type: r.type, position: r.position ) end s.year_suffix = nil s.save s end protected # Defined in subclasses # @return [Nil] def set_cached end # Defined in subclasses def get_cached end # @param [Hash] attributed # @return [Boolean] def reject_project_sources(attributed) return true if attributed['project_id'].blank? return true if ProjectSource.where(project_id: attributed['project_id'], source_id: id).any? end def sv_cached_names true # see validation in subclasses end def sv_fix_cached_names begin Source.transaction do self.set_cached end true rescue false end end def sv_stated_year soft_validations.add( :base, "'Stated year' is not needed if identical to 'year'; applying the Fix will delete it", success_message: "'Stated year' was deleted", failure_message: "Failed to delete 'Stated year'") if year.to_s == stated_year.to_s end def sv_fix_stated_year begin Source.transaction do self.stated_year = nil self.save end true rescue false end end def if title.present? str = title.squish.gsub(/\<i>[^<>]*?<\/i>/, '') soft_validations.add(:title, 'The title contains unmatched html tags') if str.include?('<i>') || str.include?('</i>') end end end |
#language_id ⇒ Integer
Returns The TaxonWorks normalization of language to Language.
188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 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 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 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 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 |
# File 'app/models/source.rb', line 188 class Source < ApplicationRecord include Housekeeping::Users include Housekeeping::Timestamps include Shared::AlternateValues include Shared::DataAttributes include Shared::Documentation include Shared::Identifiers include Shared::Notes include Shared::SharedAcrossProjects include Shared::Tags include Shared::HasPapertrail include SoftValidation include Shared::IsData # !! Must not have Shared::Depictions ignore_whitespace_on(:verbatim_contents) ALTERNATE_VALUES_FOR = [ :address, :annote, :booktitle, :edition, :editor, :institution, :journal, :note, :organization, :publisher, :school, :title, :doi, :abstract, :language, :translator, :author, :url].freeze # @return [Boolean, nil] # When true, cached values are not built attr_accessor :no_year_suffix_validation # Keep this order for citations/topics has_many :citations, inverse_of: :source, dependent: :restrict_with_error has_many :origin_citations, -> {where(citations: {is_original: true})}, class_name: 'Citation', dependent: :restrict_with_error, inverse_of: :source has_many :citation_topics, through: :citations, inverse_of: :source has_many :topics, through: :citation_topics, inverse_of: :sources has_many :project_sources, inverse_of: :source, dependent: :destroy has_many :projects, inverse_of: :sources, through: :project_sources after_save :set_cached validates_presence_of :type validates :type, inclusion: {in: ['Source::Bibtex', 'Source::Human', 'Source::Verbatim']} # TODO: not needed accepts_nested_attributes_for :project_sources, reject_if: :reject_project_sources soft_validate( :sv_cached_names, set: :cached_names, fix: :sv_fix_cached_names, name: 'Cached names', description: 'Check if cached values need to be updated' ) soft_validate( :sv_stated_year, set: :stated_year, fix: :sv_fix_stated_year, name: 'Stated year', description: "'Stated year' is not needed if identical to 'year'" ) soft_validate( :sv_html_tags, set: :html_tags, name: 'html tags', description: 'Check if html has both open and close tags' ) # Redirect type here # @param [String] file # @return [[Array, message]] # TODO: return a more informative response? def self.batch_preview(file) begin bibliography = BibTeX::Bibliography.parse(file.read.force_encoding('UTF-8'), filter: :latex) sources = [] bibliography.each do |record| a = Source::Bibtex.new_from_bibtex(record) sources.push(a) end return sources, nil rescue BibTeX::ParseError => e return [], e. rescue raise end end # @return [String] # A string that represents the authors last_names and year (no suffix) def return 'not yet calculated' if new_record? [, year].compact.join(', ') end # @param [String] file # @return [Array, Boolean] def self.batch_create(file) sources = [] valid = 0 begin # error_msg = [] Source.transaction do bibliography = BibTeX::Bibliography.parse(file.read.force_encoding('UTF-8'), filter: :latex) bibliography.each do |record| a = Source::Bibtex.new_from_bibtex(record) if a.valid? if a.save valid += 1 end else # error_msg = a.errors.messages.to_s end sources.push(a) end end rescue return false end return {records: sources, count: valid} end # @param used_on [String] a model name # @return [Scope] # the max 10 most recently used (1 week, could parameterize) TaxonName, as used def self.used_recently(user_id, project_id, used_on = 'TaxonName') Source.select('sources.id'). joins(:citations) .where(citations: {updated_by_id: user_id, project_id:, citation_object_type: used_on, updated_at: 1.week.ago..}) .order('citations.updated_at DESC') .pluck(:id).uniq end # @params target [String] a citable model name # @return [Hash] sources optimized for user selection def self.select_optimized(user_id, project_id, target = 'TaxonName') r = used_recently(user_id, project_id, target) h = { quick: [], pinboard: Source.pinned_by(user_id).where(pinboard_items: {project_id:}).to_a, recent: [] } if r.empty? h[:recent] = Source.where(created_by_id: user_id, updated_at: 2.hours.ago..Time.now ) .order('created_at DESC') .limit(5).order(:cached).to_a h[:quick] = Source.pinned_by(user_id).pinboard_inserted.where(pinboard_items: {project_id:}).to_a else h[:recent] = (Source.where(created_by_id: user_id, updated_at: 2.hours.ago..Time.now ) .order('created_at DESC') .limit(5).order(:cached).to_a + Source.where('"sources"."id" IN (?)', r.first(6) ).to_a).uniq h[:quick] = ( Source.pinned_by(user_id).pinboard_inserted.where(pinboard_items: {project_id:}).to_a + Source.where('"sources"."id" IN (?)', r.first(4) ).to_a).uniq end h end # @return [Array] # objects this source is linked to through citations def cited_objects self.citations.collect { |t| t.citation_object } end # @return [Boolean] def is_bibtex? type == 'Source::Bibtex' end # @return [Boolean] def is_in_project?(project_id) projects.where(id: project_id).any? end # Month handling allows values from bibtex like 'may' to be handled # @return [Date] def nomenclature_date Utilities::Dates.nomenclature_date(day, Utilities::Dates.month_index(month), year)&.to_date end # @return [Source] def clone s = dup m = "[CLONE of #{id}] " case type when 'Source::Verbatim' s.verbatim = m + verbatim.to_s when 'Source::Bibtex' s.title = m + title.to_s end roles.reload.each do |r| s.roles << Role.new(person: r.person, type: r.type, position: r.position ) end s.year_suffix = nil s.save s end protected # Defined in subclasses # @return [Nil] def set_cached end # Defined in subclasses def get_cached end # @param [Hash] attributed # @return [Boolean] def reject_project_sources(attributed) return true if attributed['project_id'].blank? return true if ProjectSource.where(project_id: attributed['project_id'], source_id: id).any? end def sv_cached_names true # see validation in subclasses end def sv_fix_cached_names begin Source.transaction do self.set_cached end true rescue false end end def sv_stated_year soft_validations.add( :base, "'Stated year' is not needed if identical to 'year'; applying the Fix will delete it", success_message: "'Stated year' was deleted", failure_message: "Failed to delete 'Stated year'") if year.to_s == stated_year.to_s end def sv_fix_stated_year begin Source.transaction do self.stated_year = nil self.save end true rescue false end end def if title.present? str = title.squish.gsub(/\<i>[^<>]*?<\/i>/, '') soft_validations.add(:title, 'The title contains unmatched html tags') if str.include?('<i>') || str.include?('</i>') end end end |
#month ⇒ String
Returns see en.wikipedia.org/wiki/BibTeX#Field_types
stored as a three letter value, see ::VALID_BIBTEX_MONTHS.
188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 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 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 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 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 |
# File 'app/models/source.rb', line 188 class Source < ApplicationRecord include Housekeeping::Users include Housekeeping::Timestamps include Shared::AlternateValues include Shared::DataAttributes include Shared::Documentation include Shared::Identifiers include Shared::Notes include Shared::SharedAcrossProjects include Shared::Tags include Shared::HasPapertrail include SoftValidation include Shared::IsData # !! Must not have Shared::Depictions ignore_whitespace_on(:verbatim_contents) ALTERNATE_VALUES_FOR = [ :address, :annote, :booktitle, :edition, :editor, :institution, :journal, :note, :organization, :publisher, :school, :title, :doi, :abstract, :language, :translator, :author, :url].freeze # @return [Boolean, nil] # When true, cached values are not built attr_accessor :no_year_suffix_validation # Keep this order for citations/topics has_many :citations, inverse_of: :source, dependent: :restrict_with_error has_many :origin_citations, -> {where(citations: {is_original: true})}, class_name: 'Citation', dependent: :restrict_with_error, inverse_of: :source has_many :citation_topics, through: :citations, inverse_of: :source has_many :topics, through: :citation_topics, inverse_of: :sources has_many :project_sources, inverse_of: :source, dependent: :destroy has_many :projects, inverse_of: :sources, through: :project_sources after_save :set_cached validates_presence_of :type validates :type, inclusion: {in: ['Source::Bibtex', 'Source::Human', 'Source::Verbatim']} # TODO: not needed accepts_nested_attributes_for :project_sources, reject_if: :reject_project_sources soft_validate( :sv_cached_names, set: :cached_names, fix: :sv_fix_cached_names, name: 'Cached names', description: 'Check if cached values need to be updated' ) soft_validate( :sv_stated_year, set: :stated_year, fix: :sv_fix_stated_year, name: 'Stated year', description: "'Stated year' is not needed if identical to 'year'" ) soft_validate( :sv_html_tags, set: :html_tags, name: 'html tags', description: 'Check if html has both open and close tags' ) # Redirect type here # @param [String] file # @return [[Array, message]] # TODO: return a more informative response? def self.batch_preview(file) begin bibliography = BibTeX::Bibliography.parse(file.read.force_encoding('UTF-8'), filter: :latex) sources = [] bibliography.each do |record| a = Source::Bibtex.new_from_bibtex(record) sources.push(a) end return sources, nil rescue BibTeX::ParseError => e return [], e. rescue raise end end # @return [String] # A string that represents the authors last_names and year (no suffix) def return 'not yet calculated' if new_record? [, year].compact.join(', ') end # @param [String] file # @return [Array, Boolean] def self.batch_create(file) sources = [] valid = 0 begin # error_msg = [] Source.transaction do bibliography = BibTeX::Bibliography.parse(file.read.force_encoding('UTF-8'), filter: :latex) bibliography.each do |record| a = Source::Bibtex.new_from_bibtex(record) if a.valid? if a.save valid += 1 end else # error_msg = a.errors.messages.to_s end sources.push(a) end end rescue return false end return {records: sources, count: valid} end # @param used_on [String] a model name # @return [Scope] # the max 10 most recently used (1 week, could parameterize) TaxonName, as used def self.used_recently(user_id, project_id, used_on = 'TaxonName') Source.select('sources.id'). joins(:citations) .where(citations: {updated_by_id: user_id, project_id:, citation_object_type: used_on, updated_at: 1.week.ago..}) .order('citations.updated_at DESC') .pluck(:id).uniq end # @params target [String] a citable model name # @return [Hash] sources optimized for user selection def self.select_optimized(user_id, project_id, target = 'TaxonName') r = used_recently(user_id, project_id, target) h = { quick: [], pinboard: Source.pinned_by(user_id).where(pinboard_items: {project_id:}).to_a, recent: [] } if r.empty? h[:recent] = Source.where(created_by_id: user_id, updated_at: 2.hours.ago..Time.now ) .order('created_at DESC') .limit(5).order(:cached).to_a h[:quick] = Source.pinned_by(user_id).pinboard_inserted.where(pinboard_items: {project_id:}).to_a else h[:recent] = (Source.where(created_by_id: user_id, updated_at: 2.hours.ago..Time.now ) .order('created_at DESC') .limit(5).order(:cached).to_a + Source.where('"sources"."id" IN (?)', r.first(6) ).to_a).uniq h[:quick] = ( Source.pinned_by(user_id).pinboard_inserted.where(pinboard_items: {project_id:}).to_a + Source.where('"sources"."id" IN (?)', r.first(4) ).to_a).uniq end h end # @return [Array] # objects this source is linked to through citations def cited_objects self.citations.collect { |t| t.citation_object } end # @return [Boolean] def is_bibtex? type == 'Source::Bibtex' end # @return [Boolean] def is_in_project?(project_id) projects.where(id: project_id).any? end # Month handling allows values from bibtex like 'may' to be handled # @return [Date] def nomenclature_date Utilities::Dates.nomenclature_date(day, Utilities::Dates.month_index(month), year)&.to_date end # @return [Source] def clone s = dup m = "[CLONE of #{id}] " case type when 'Source::Verbatim' s.verbatim = m + verbatim.to_s when 'Source::Bibtex' s.title = m + title.to_s end roles.reload.each do |r| s.roles << Role.new(person: r.person, type: r.type, position: r.position ) end s.year_suffix = nil s.save s end protected # Defined in subclasses # @return [Nil] def set_cached end # Defined in subclasses def get_cached end # @param [Hash] attributed # @return [Boolean] def reject_project_sources(attributed) return true if attributed['project_id'].blank? return true if ProjectSource.where(project_id: attributed['project_id'], source_id: id).any? end def sv_cached_names true # see validation in subclasses end def sv_fix_cached_names begin Source.transaction do self.set_cached end true rescue false end end def sv_stated_year soft_validations.add( :base, "'Stated year' is not needed if identical to 'year'; applying the Fix will delete it", success_message: "'Stated year' was deleted", failure_message: "Failed to delete 'Stated year'") if year.to_s == stated_year.to_s end def sv_fix_stated_year begin Source.transaction do self.stated_year = nil self.save end true rescue false end end def if title.present? str = title.squish.gsub(/\<i>[^<>]*?<\/i>/, '') soft_validations.add(:title, 'The title contains unmatched html tags') if str.include?('<i>') || str.include?('</i>') end end end |
#no_year_suffix_validation ⇒ Boolean?
Returns When true, cached values are not built.
211 212 213 |
# File 'app/models/source.rb', line 211 def no_year_suffix_validation @no_year_suffix_validation end |
#note ⇒ String
Returns see en.wikipedia.org/wiki/BibTeX#Field_types.
188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 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 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 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 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 |
# File 'app/models/source.rb', line 188 class Source < ApplicationRecord include Housekeeping::Users include Housekeeping::Timestamps include Shared::AlternateValues include Shared::DataAttributes include Shared::Documentation include Shared::Identifiers include Shared::Notes include Shared::SharedAcrossProjects include Shared::Tags include Shared::HasPapertrail include SoftValidation include Shared::IsData # !! Must not have Shared::Depictions ignore_whitespace_on(:verbatim_contents) ALTERNATE_VALUES_FOR = [ :address, :annote, :booktitle, :edition, :editor, :institution, :journal, :note, :organization, :publisher, :school, :title, :doi, :abstract, :language, :translator, :author, :url].freeze # @return [Boolean, nil] # When true, cached values are not built attr_accessor :no_year_suffix_validation # Keep this order for citations/topics has_many :citations, inverse_of: :source, dependent: :restrict_with_error has_many :origin_citations, -> {where(citations: {is_original: true})}, class_name: 'Citation', dependent: :restrict_with_error, inverse_of: :source has_many :citation_topics, through: :citations, inverse_of: :source has_many :topics, through: :citation_topics, inverse_of: :sources has_many :project_sources, inverse_of: :source, dependent: :destroy has_many :projects, inverse_of: :sources, through: :project_sources after_save :set_cached validates_presence_of :type validates :type, inclusion: {in: ['Source::Bibtex', 'Source::Human', 'Source::Verbatim']} # TODO: not needed accepts_nested_attributes_for :project_sources, reject_if: :reject_project_sources soft_validate( :sv_cached_names, set: :cached_names, fix: :sv_fix_cached_names, name: 'Cached names', description: 'Check if cached values need to be updated' ) soft_validate( :sv_stated_year, set: :stated_year, fix: :sv_fix_stated_year, name: 'Stated year', description: "'Stated year' is not needed if identical to 'year'" ) soft_validate( :sv_html_tags, set: :html_tags, name: 'html tags', description: 'Check if html has both open and close tags' ) # Redirect type here # @param [String] file # @return [[Array, message]] # TODO: return a more informative response? def self.batch_preview(file) begin bibliography = BibTeX::Bibliography.parse(file.read.force_encoding('UTF-8'), filter: :latex) sources = [] bibliography.each do |record| a = Source::Bibtex.new_from_bibtex(record) sources.push(a) end return sources, nil rescue BibTeX::ParseError => e return [], e. rescue raise end end # @return [String] # A string that represents the authors last_names and year (no suffix) def return 'not yet calculated' if new_record? [, year].compact.join(', ') end # @param [String] file # @return [Array, Boolean] def self.batch_create(file) sources = [] valid = 0 begin # error_msg = [] Source.transaction do bibliography = BibTeX::Bibliography.parse(file.read.force_encoding('UTF-8'), filter: :latex) bibliography.each do |record| a = Source::Bibtex.new_from_bibtex(record) if a.valid? if a.save valid += 1 end else # error_msg = a.errors.messages.to_s end sources.push(a) end end rescue return false end return {records: sources, count: valid} end # @param used_on [String] a model name # @return [Scope] # the max 10 most recently used (1 week, could parameterize) TaxonName, as used def self.used_recently(user_id, project_id, used_on = 'TaxonName') Source.select('sources.id'). joins(:citations) .where(citations: {updated_by_id: user_id, project_id:, citation_object_type: used_on, updated_at: 1.week.ago..}) .order('citations.updated_at DESC') .pluck(:id).uniq end # @params target [String] a citable model name # @return [Hash] sources optimized for user selection def self.select_optimized(user_id, project_id, target = 'TaxonName') r = used_recently(user_id, project_id, target) h = { quick: [], pinboard: Source.pinned_by(user_id).where(pinboard_items: {project_id:}).to_a, recent: [] } if r.empty? h[:recent] = Source.where(created_by_id: user_id, updated_at: 2.hours.ago..Time.now ) .order('created_at DESC') .limit(5).order(:cached).to_a h[:quick] = Source.pinned_by(user_id).pinboard_inserted.where(pinboard_items: {project_id:}).to_a else h[:recent] = (Source.where(created_by_id: user_id, updated_at: 2.hours.ago..Time.now ) .order('created_at DESC') .limit(5).order(:cached).to_a + Source.where('"sources"."id" IN (?)', r.first(6) ).to_a).uniq h[:quick] = ( Source.pinned_by(user_id).pinboard_inserted.where(pinboard_items: {project_id:}).to_a + Source.where('"sources"."id" IN (?)', r.first(4) ).to_a).uniq end h end # @return [Array] # objects this source is linked to through citations def cited_objects self.citations.collect { |t| t.citation_object } end # @return [Boolean] def is_bibtex? type == 'Source::Bibtex' end # @return [Boolean] def is_in_project?(project_id) projects.where(id: project_id).any? end # Month handling allows values from bibtex like 'may' to be handled # @return [Date] def nomenclature_date Utilities::Dates.nomenclature_date(day, Utilities::Dates.month_index(month), year)&.to_date end # @return [Source] def clone s = dup m = "[CLONE of #{id}] " case type when 'Source::Verbatim' s.verbatim = m + verbatim.to_s when 'Source::Bibtex' s.title = m + title.to_s end roles.reload.each do |r| s.roles << Role.new(person: r.person, type: r.type, position: r.position ) end s.year_suffix = nil s.save s end protected # Defined in subclasses # @return [Nil] def set_cached end # Defined in subclasses def get_cached end # @param [Hash] attributed # @return [Boolean] def reject_project_sources(attributed) return true if attributed['project_id'].blank? return true if ProjectSource.where(project_id: attributed['project_id'], source_id: id).any? end def sv_cached_names true # see validation in subclasses end def sv_fix_cached_names begin Source.transaction do self.set_cached end true rescue false end end def sv_stated_year soft_validations.add( :base, "'Stated year' is not needed if identical to 'year'; applying the Fix will delete it", success_message: "'Stated year' was deleted", failure_message: "Failed to delete 'Stated year'") if year.to_s == stated_year.to_s end def sv_fix_stated_year begin Source.transaction do self.stated_year = nil self.save end true rescue false end end def if title.present? str = title.squish.gsub(/\<i>[^<>]*?<\/i>/, '') soft_validations.add(:title, 'The title contains unmatched html tags') if str.include?('<i>') || str.include?('</i>') end end end |
#number ⇒ String
Returns see en.wikipedia.org/wiki/BibTeX#Field_types.
188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 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 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 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 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 |
# File 'app/models/source.rb', line 188 class Source < ApplicationRecord include Housekeeping::Users include Housekeeping::Timestamps include Shared::AlternateValues include Shared::DataAttributes include Shared::Documentation include Shared::Identifiers include Shared::Notes include Shared::SharedAcrossProjects include Shared::Tags include Shared::HasPapertrail include SoftValidation include Shared::IsData # !! Must not have Shared::Depictions ignore_whitespace_on(:verbatim_contents) ALTERNATE_VALUES_FOR = [ :address, :annote, :booktitle, :edition, :editor, :institution, :journal, :note, :organization, :publisher, :school, :title, :doi, :abstract, :language, :translator, :author, :url].freeze # @return [Boolean, nil] # When true, cached values are not built attr_accessor :no_year_suffix_validation # Keep this order for citations/topics has_many :citations, inverse_of: :source, dependent: :restrict_with_error has_many :origin_citations, -> {where(citations: {is_original: true})}, class_name: 'Citation', dependent: :restrict_with_error, inverse_of: :source has_many :citation_topics, through: :citations, inverse_of: :source has_many :topics, through: :citation_topics, inverse_of: :sources has_many :project_sources, inverse_of: :source, dependent: :destroy has_many :projects, inverse_of: :sources, through: :project_sources after_save :set_cached validates_presence_of :type validates :type, inclusion: {in: ['Source::Bibtex', 'Source::Human', 'Source::Verbatim']} # TODO: not needed accepts_nested_attributes_for :project_sources, reject_if: :reject_project_sources soft_validate( :sv_cached_names, set: :cached_names, fix: :sv_fix_cached_names, name: 'Cached names', description: 'Check if cached values need to be updated' ) soft_validate( :sv_stated_year, set: :stated_year, fix: :sv_fix_stated_year, name: 'Stated year', description: "'Stated year' is not needed if identical to 'year'" ) soft_validate( :sv_html_tags, set: :html_tags, name: 'html tags', description: 'Check if html has both open and close tags' ) # Redirect type here # @param [String] file # @return [[Array, message]] # TODO: return a more informative response? def self.batch_preview(file) begin bibliography = BibTeX::Bibliography.parse(file.read.force_encoding('UTF-8'), filter: :latex) sources = [] bibliography.each do |record| a = Source::Bibtex.new_from_bibtex(record) sources.push(a) end return sources, nil rescue BibTeX::ParseError => e return [], e. rescue raise end end # @return [String] # A string that represents the authors last_names and year (no suffix) def return 'not yet calculated' if new_record? [, year].compact.join(', ') end # @param [String] file # @return [Array, Boolean] def self.batch_create(file) sources = [] valid = 0 begin # error_msg = [] Source.transaction do bibliography = BibTeX::Bibliography.parse(file.read.force_encoding('UTF-8'), filter: :latex) bibliography.each do |record| a = Source::Bibtex.new_from_bibtex(record) if a.valid? if a.save valid += 1 end else # error_msg = a.errors.messages.to_s end sources.push(a) end end rescue return false end return {records: sources, count: valid} end # @param used_on [String] a model name # @return [Scope] # the max 10 most recently used (1 week, could parameterize) TaxonName, as used def self.used_recently(user_id, project_id, used_on = 'TaxonName') Source.select('sources.id'). joins(:citations) .where(citations: {updated_by_id: user_id, project_id:, citation_object_type: used_on, updated_at: 1.week.ago..}) .order('citations.updated_at DESC') .pluck(:id).uniq end # @params target [String] a citable model name # @return [Hash] sources optimized for user selection def self.select_optimized(user_id, project_id, target = 'TaxonName') r = used_recently(user_id, project_id, target) h = { quick: [], pinboard: Source.pinned_by(user_id).where(pinboard_items: {project_id:}).to_a, recent: [] } if r.empty? h[:recent] = Source.where(created_by_id: user_id, updated_at: 2.hours.ago..Time.now ) .order('created_at DESC') .limit(5).order(:cached).to_a h[:quick] = Source.pinned_by(user_id).pinboard_inserted.where(pinboard_items: {project_id:}).to_a else h[:recent] = (Source.where(created_by_id: user_id, updated_at: 2.hours.ago..Time.now ) .order('created_at DESC') .limit(5).order(:cached).to_a + Source.where('"sources"."id" IN (?)', r.first(6) ).to_a).uniq h[:quick] = ( Source.pinned_by(user_id).pinboard_inserted.where(pinboard_items: {project_id:}).to_a + Source.where('"sources"."id" IN (?)', r.first(4) ).to_a).uniq end h end # @return [Array] # objects this source is linked to through citations def cited_objects self.citations.collect { |t| t.citation_object } end # @return [Boolean] def is_bibtex? type == 'Source::Bibtex' end # @return [Boolean] def is_in_project?(project_id) projects.where(id: project_id).any? end # Month handling allows values from bibtex like 'may' to be handled # @return [Date] def nomenclature_date Utilities::Dates.nomenclature_date(day, Utilities::Dates.month_index(month), year)&.to_date end # @return [Source] def clone s = dup m = "[CLONE of #{id}] " case type when 'Source::Verbatim' s.verbatim = m + verbatim.to_s when 'Source::Bibtex' s.title = m + title.to_s end roles.reload.each do |r| s.roles << Role.new(person: r.person, type: r.type, position: r.position ) end s.year_suffix = nil s.save s end protected # Defined in subclasses # @return [Nil] def set_cached end # Defined in subclasses def get_cached end # @param [Hash] attributed # @return [Boolean] def reject_project_sources(attributed) return true if attributed['project_id'].blank? return true if ProjectSource.where(project_id: attributed['project_id'], source_id: id).any? end def sv_cached_names true # see validation in subclasses end def sv_fix_cached_names begin Source.transaction do self.set_cached end true rescue false end end def sv_stated_year soft_validations.add( :base, "'Stated year' is not needed if identical to 'year'; applying the Fix will delete it", success_message: "'Stated year' was deleted", failure_message: "Failed to delete 'Stated year'") if year.to_s == stated_year.to_s end def sv_fix_stated_year begin Source.transaction do self.stated_year = nil self.save end true rescue false end end def if title.present? str = title.squish.gsub(/\<i>[^<>]*?<\/i>/, '') soft_validations.add(:title, 'The title contains unmatched html tags') if str.include?('<i>') || str.include?('</i>') end end end |
#organization ⇒ String
Returns see en.wikipedia.org/wiki/BibTeX#Field_types.
188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 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 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 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 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 |
# File 'app/models/source.rb', line 188 class Source < ApplicationRecord include Housekeeping::Users include Housekeeping::Timestamps include Shared::AlternateValues include Shared::DataAttributes include Shared::Documentation include Shared::Identifiers include Shared::Notes include Shared::SharedAcrossProjects include Shared::Tags include Shared::HasPapertrail include SoftValidation include Shared::IsData # !! Must not have Shared::Depictions ignore_whitespace_on(:verbatim_contents) ALTERNATE_VALUES_FOR = [ :address, :annote, :booktitle, :edition, :editor, :institution, :journal, :note, :organization, :publisher, :school, :title, :doi, :abstract, :language, :translator, :author, :url].freeze # @return [Boolean, nil] # When true, cached values are not built attr_accessor :no_year_suffix_validation # Keep this order for citations/topics has_many :citations, inverse_of: :source, dependent: :restrict_with_error has_many :origin_citations, -> {where(citations: {is_original: true})}, class_name: 'Citation', dependent: :restrict_with_error, inverse_of: :source has_many :citation_topics, through: :citations, inverse_of: :source has_many :topics, through: :citation_topics, inverse_of: :sources has_many :project_sources, inverse_of: :source, dependent: :destroy has_many :projects, inverse_of: :sources, through: :project_sources after_save :set_cached validates_presence_of :type validates :type, inclusion: {in: ['Source::Bibtex', 'Source::Human', 'Source::Verbatim']} # TODO: not needed accepts_nested_attributes_for :project_sources, reject_if: :reject_project_sources soft_validate( :sv_cached_names, set: :cached_names, fix: :sv_fix_cached_names, name: 'Cached names', description: 'Check if cached values need to be updated' ) soft_validate( :sv_stated_year, set: :stated_year, fix: :sv_fix_stated_year, name: 'Stated year', description: "'Stated year' is not needed if identical to 'year'" ) soft_validate( :sv_html_tags, set: :html_tags, name: 'html tags', description: 'Check if html has both open and close tags' ) # Redirect type here # @param [String] file # @return [[Array, message]] # TODO: return a more informative response? def self.batch_preview(file) begin bibliography = BibTeX::Bibliography.parse(file.read.force_encoding('UTF-8'), filter: :latex) sources = [] bibliography.each do |record| a = Source::Bibtex.new_from_bibtex(record) sources.push(a) end return sources, nil rescue BibTeX::ParseError => e return [], e. rescue raise end end # @return [String] # A string that represents the authors last_names and year (no suffix) def return 'not yet calculated' if new_record? [, year].compact.join(', ') end # @param [String] file # @return [Array, Boolean] def self.batch_create(file) sources = [] valid = 0 begin # error_msg = [] Source.transaction do bibliography = BibTeX::Bibliography.parse(file.read.force_encoding('UTF-8'), filter: :latex) bibliography.each do |record| a = Source::Bibtex.new_from_bibtex(record) if a.valid? if a.save valid += 1 end else # error_msg = a.errors.messages.to_s end sources.push(a) end end rescue return false end return {records: sources, count: valid} end # @param used_on [String] a model name # @return [Scope] # the max 10 most recently used (1 week, could parameterize) TaxonName, as used def self.used_recently(user_id, project_id, used_on = 'TaxonName') Source.select('sources.id'). joins(:citations) .where(citations: {updated_by_id: user_id, project_id:, citation_object_type: used_on, updated_at: 1.week.ago..}) .order('citations.updated_at DESC') .pluck(:id).uniq end # @params target [String] a citable model name # @return [Hash] sources optimized for user selection def self.select_optimized(user_id, project_id, target = 'TaxonName') r = used_recently(user_id, project_id, target) h = { quick: [], pinboard: Source.pinned_by(user_id).where(pinboard_items: {project_id:}).to_a, recent: [] } if r.empty? h[:recent] = Source.where(created_by_id: user_id, updated_at: 2.hours.ago..Time.now ) .order('created_at DESC') .limit(5).order(:cached).to_a h[:quick] = Source.pinned_by(user_id).pinboard_inserted.where(pinboard_items: {project_id:}).to_a else h[:recent] = (Source.where(created_by_id: user_id, updated_at: 2.hours.ago..Time.now ) .order('created_at DESC') .limit(5).order(:cached).to_a + Source.where('"sources"."id" IN (?)', r.first(6) ).to_a).uniq h[:quick] = ( Source.pinned_by(user_id).pinboard_inserted.where(pinboard_items: {project_id:}).to_a + Source.where('"sources"."id" IN (?)', r.first(4) ).to_a).uniq end h end # @return [Array] # objects this source is linked to through citations def cited_objects self.citations.collect { |t| t.citation_object } end # @return [Boolean] def is_bibtex? type == 'Source::Bibtex' end # @return [Boolean] def is_in_project?(project_id) projects.where(id: project_id).any? end # Month handling allows values from bibtex like 'may' to be handled # @return [Date] def nomenclature_date Utilities::Dates.nomenclature_date(day, Utilities::Dates.month_index(month), year)&.to_date end # @return [Source] def clone s = dup m = "[CLONE of #{id}] " case type when 'Source::Verbatim' s.verbatim = m + verbatim.to_s when 'Source::Bibtex' s.title = m + title.to_s end roles.reload.each do |r| s.roles << Role.new(person: r.person, type: r.type, position: r.position ) end s.year_suffix = nil s.save s end protected # Defined in subclasses # @return [Nil] def set_cached end # Defined in subclasses def get_cached end # @param [Hash] attributed # @return [Boolean] def reject_project_sources(attributed) return true if attributed['project_id'].blank? return true if ProjectSource.where(project_id: attributed['project_id'], source_id: id).any? end def sv_cached_names true # see validation in subclasses end def sv_fix_cached_names begin Source.transaction do self.set_cached end true rescue false end end def sv_stated_year soft_validations.add( :base, "'Stated year' is not needed if identical to 'year'; applying the Fix will delete it", success_message: "'Stated year' was deleted", failure_message: "Failed to delete 'Stated year'") if year.to_s == stated_year.to_s end def sv_fix_stated_year begin Source.transaction do self.stated_year = nil self.save end true rescue false end end def if title.present? str = title.squish.gsub(/\<i>[^<>]*?<\/i>/, '') soft_validations.add(:title, 'The title contains unmatched html tags') if str.include?('<i>') || str.include?('</i>') end end end |
#pages ⇒ String
Returns see en.wikipedia.org/wiki/BibTeX#Field_types.
188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 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 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 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 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 |
# File 'app/models/source.rb', line 188 class Source < ApplicationRecord include Housekeeping::Users include Housekeeping::Timestamps include Shared::AlternateValues include Shared::DataAttributes include Shared::Documentation include Shared::Identifiers include Shared::Notes include Shared::SharedAcrossProjects include Shared::Tags include Shared::HasPapertrail include SoftValidation include Shared::IsData # !! Must not have Shared::Depictions ignore_whitespace_on(:verbatim_contents) ALTERNATE_VALUES_FOR = [ :address, :annote, :booktitle, :edition, :editor, :institution, :journal, :note, :organization, :publisher, :school, :title, :doi, :abstract, :language, :translator, :author, :url].freeze # @return [Boolean, nil] # When true, cached values are not built attr_accessor :no_year_suffix_validation # Keep this order for citations/topics has_many :citations, inverse_of: :source, dependent: :restrict_with_error has_many :origin_citations, -> {where(citations: {is_original: true})}, class_name: 'Citation', dependent: :restrict_with_error, inverse_of: :source has_many :citation_topics, through: :citations, inverse_of: :source has_many :topics, through: :citation_topics, inverse_of: :sources has_many :project_sources, inverse_of: :source, dependent: :destroy has_many :projects, inverse_of: :sources, through: :project_sources after_save :set_cached validates_presence_of :type validates :type, inclusion: {in: ['Source::Bibtex', 'Source::Human', 'Source::Verbatim']} # TODO: not needed accepts_nested_attributes_for :project_sources, reject_if: :reject_project_sources soft_validate( :sv_cached_names, set: :cached_names, fix: :sv_fix_cached_names, name: 'Cached names', description: 'Check if cached values need to be updated' ) soft_validate( :sv_stated_year, set: :stated_year, fix: :sv_fix_stated_year, name: 'Stated year', description: "'Stated year' is not needed if identical to 'year'" ) soft_validate( :sv_html_tags, set: :html_tags, name: 'html tags', description: 'Check if html has both open and close tags' ) # Redirect type here # @param [String] file # @return [[Array, message]] # TODO: return a more informative response? def self.batch_preview(file) begin bibliography = BibTeX::Bibliography.parse(file.read.force_encoding('UTF-8'), filter: :latex) sources = [] bibliography.each do |record| a = Source::Bibtex.new_from_bibtex(record) sources.push(a) end return sources, nil rescue BibTeX::ParseError => e return [], e. rescue raise end end # @return [String] # A string that represents the authors last_names and year (no suffix) def return 'not yet calculated' if new_record? [, year].compact.join(', ') end # @param [String] file # @return [Array, Boolean] def self.batch_create(file) sources = [] valid = 0 begin # error_msg = [] Source.transaction do bibliography = BibTeX::Bibliography.parse(file.read.force_encoding('UTF-8'), filter: :latex) bibliography.each do |record| a = Source::Bibtex.new_from_bibtex(record) if a.valid? if a.save valid += 1 end else # error_msg = a.errors.messages.to_s end sources.push(a) end end rescue return false end return {records: sources, count: valid} end # @param used_on [String] a model name # @return [Scope] # the max 10 most recently used (1 week, could parameterize) TaxonName, as used def self.used_recently(user_id, project_id, used_on = 'TaxonName') Source.select('sources.id'). joins(:citations) .where(citations: {updated_by_id: user_id, project_id:, citation_object_type: used_on, updated_at: 1.week.ago..}) .order('citations.updated_at DESC') .pluck(:id).uniq end # @params target [String] a citable model name # @return [Hash] sources optimized for user selection def self.select_optimized(user_id, project_id, target = 'TaxonName') r = used_recently(user_id, project_id, target) h = { quick: [], pinboard: Source.pinned_by(user_id).where(pinboard_items: {project_id:}).to_a, recent: [] } if r.empty? h[:recent] = Source.where(created_by_id: user_id, updated_at: 2.hours.ago..Time.now ) .order('created_at DESC') .limit(5).order(:cached).to_a h[:quick] = Source.pinned_by(user_id).pinboard_inserted.where(pinboard_items: {project_id:}).to_a else h[:recent] = (Source.where(created_by_id: user_id, updated_at: 2.hours.ago..Time.now ) .order('created_at DESC') .limit(5).order(:cached).to_a + Source.where('"sources"."id" IN (?)', r.first(6) ).to_a).uniq h[:quick] = ( Source.pinned_by(user_id).pinboard_inserted.where(pinboard_items: {project_id:}).to_a + Source.where('"sources"."id" IN (?)', r.first(4) ).to_a).uniq end h end # @return [Array] # objects this source is linked to through citations def cited_objects self.citations.collect { |t| t.citation_object } end # @return [Boolean] def is_bibtex? type == 'Source::Bibtex' end # @return [Boolean] def is_in_project?(project_id) projects.where(id: project_id).any? end # Month handling allows values from bibtex like 'may' to be handled # @return [Date] def nomenclature_date Utilities::Dates.nomenclature_date(day, Utilities::Dates.month_index(month), year)&.to_date end # @return [Source] def clone s = dup m = "[CLONE of #{id}] " case type when 'Source::Verbatim' s.verbatim = m + verbatim.to_s when 'Source::Bibtex' s.title = m + title.to_s end roles.reload.each do |r| s.roles << Role.new(person: r.person, type: r.type, position: r.position ) end s.year_suffix = nil s.save s end protected # Defined in subclasses # @return [Nil] def set_cached end # Defined in subclasses def get_cached end # @param [Hash] attributed # @return [Boolean] def reject_project_sources(attributed) return true if attributed['project_id'].blank? return true if ProjectSource.where(project_id: attributed['project_id'], source_id: id).any? end def sv_cached_names true # see validation in subclasses end def sv_fix_cached_names begin Source.transaction do self.set_cached end true rescue false end end def sv_stated_year soft_validations.add( :base, "'Stated year' is not needed if identical to 'year'; applying the Fix will delete it", success_message: "'Stated year' was deleted", failure_message: "Failed to delete 'Stated year'") if year.to_s == stated_year.to_s end def sv_fix_stated_year begin Source.transaction do self.stated_year = nil self.save end true rescue false end end def if title.present? str = title.squish.gsub(/\<i>[^<>]*?<\/i>/, '') soft_validations.add(:title, 'The title contains unmatched html tags') if str.include?('<i>') || str.include?('</i>') end end end |
#publisher ⇒ String
Returns see en.wikipedia.org/wiki/BibTeX#Field_types.
188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 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 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 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 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 |
# File 'app/models/source.rb', line 188 class Source < ApplicationRecord include Housekeeping::Users include Housekeeping::Timestamps include Shared::AlternateValues include Shared::DataAttributes include Shared::Documentation include Shared::Identifiers include Shared::Notes include Shared::SharedAcrossProjects include Shared::Tags include Shared::HasPapertrail include SoftValidation include Shared::IsData # !! Must not have Shared::Depictions ignore_whitespace_on(:verbatim_contents) ALTERNATE_VALUES_FOR = [ :address, :annote, :booktitle, :edition, :editor, :institution, :journal, :note, :organization, :publisher, :school, :title, :doi, :abstract, :language, :translator, :author, :url].freeze # @return [Boolean, nil] # When true, cached values are not built attr_accessor :no_year_suffix_validation # Keep this order for citations/topics has_many :citations, inverse_of: :source, dependent: :restrict_with_error has_many :origin_citations, -> {where(citations: {is_original: true})}, class_name: 'Citation', dependent: :restrict_with_error, inverse_of: :source has_many :citation_topics, through: :citations, inverse_of: :source has_many :topics, through: :citation_topics, inverse_of: :sources has_many :project_sources, inverse_of: :source, dependent: :destroy has_many :projects, inverse_of: :sources, through: :project_sources after_save :set_cached validates_presence_of :type validates :type, inclusion: {in: ['Source::Bibtex', 'Source::Human', 'Source::Verbatim']} # TODO: not needed accepts_nested_attributes_for :project_sources, reject_if: :reject_project_sources soft_validate( :sv_cached_names, set: :cached_names, fix: :sv_fix_cached_names, name: 'Cached names', description: 'Check if cached values need to be updated' ) soft_validate( :sv_stated_year, set: :stated_year, fix: :sv_fix_stated_year, name: 'Stated year', description: "'Stated year' is not needed if identical to 'year'" ) soft_validate( :sv_html_tags, set: :html_tags, name: 'html tags', description: 'Check if html has both open and close tags' ) # Redirect type here # @param [String] file # @return [[Array, message]] # TODO: return a more informative response? def self.batch_preview(file) begin bibliography = BibTeX::Bibliography.parse(file.read.force_encoding('UTF-8'), filter: :latex) sources = [] bibliography.each do |record| a = Source::Bibtex.new_from_bibtex(record) sources.push(a) end return sources, nil rescue BibTeX::ParseError => e return [], e. rescue raise end end # @return [String] # A string that represents the authors last_names and year (no suffix) def return 'not yet calculated' if new_record? [, year].compact.join(', ') end # @param [String] file # @return [Array, Boolean] def self.batch_create(file) sources = [] valid = 0 begin # error_msg = [] Source.transaction do bibliography = BibTeX::Bibliography.parse(file.read.force_encoding('UTF-8'), filter: :latex) bibliography.each do |record| a = Source::Bibtex.new_from_bibtex(record) if a.valid? if a.save valid += 1 end else # error_msg = a.errors.messages.to_s end sources.push(a) end end rescue return false end return {records: sources, count: valid} end # @param used_on [String] a model name # @return [Scope] # the max 10 most recently used (1 week, could parameterize) TaxonName, as used def self.used_recently(user_id, project_id, used_on = 'TaxonName') Source.select('sources.id'). joins(:citations) .where(citations: {updated_by_id: user_id, project_id:, citation_object_type: used_on, updated_at: 1.week.ago..}) .order('citations.updated_at DESC') .pluck(:id).uniq end # @params target [String] a citable model name # @return [Hash] sources optimized for user selection def self.select_optimized(user_id, project_id, target = 'TaxonName') r = used_recently(user_id, project_id, target) h = { quick: [], pinboard: Source.pinned_by(user_id).where(pinboard_items: {project_id:}).to_a, recent: [] } if r.empty? h[:recent] = Source.where(created_by_id: user_id, updated_at: 2.hours.ago..Time.now ) .order('created_at DESC') .limit(5).order(:cached).to_a h[:quick] = Source.pinned_by(user_id).pinboard_inserted.where(pinboard_items: {project_id:}).to_a else h[:recent] = (Source.where(created_by_id: user_id, updated_at: 2.hours.ago..Time.now ) .order('created_at DESC') .limit(5).order(:cached).to_a + Source.where('"sources"."id" IN (?)', r.first(6) ).to_a).uniq h[:quick] = ( Source.pinned_by(user_id).pinboard_inserted.where(pinboard_items: {project_id:}).to_a + Source.where('"sources"."id" IN (?)', r.first(4) ).to_a).uniq end h end # @return [Array] # objects this source is linked to through citations def cited_objects self.citations.collect { |t| t.citation_object } end # @return [Boolean] def is_bibtex? type == 'Source::Bibtex' end # @return [Boolean] def is_in_project?(project_id) projects.where(id: project_id).any? end # Month handling allows values from bibtex like 'may' to be handled # @return [Date] def nomenclature_date Utilities::Dates.nomenclature_date(day, Utilities::Dates.month_index(month), year)&.to_date end # @return [Source] def clone s = dup m = "[CLONE of #{id}] " case type when 'Source::Verbatim' s.verbatim = m + verbatim.to_s when 'Source::Bibtex' s.title = m + title.to_s end roles.reload.each do |r| s.roles << Role.new(person: r.person, type: r.type, position: r.position ) end s.year_suffix = nil s.save s end protected # Defined in subclasses # @return [Nil] def set_cached end # Defined in subclasses def get_cached end # @param [Hash] attributed # @return [Boolean] def reject_project_sources(attributed) return true if attributed['project_id'].blank? return true if ProjectSource.where(project_id: attributed['project_id'], source_id: id).any? end def sv_cached_names true # see validation in subclasses end def sv_fix_cached_names begin Source.transaction do self.set_cached end true rescue false end end def sv_stated_year soft_validations.add( :base, "'Stated year' is not needed if identical to 'year'; applying the Fix will delete it", success_message: "'Stated year' was deleted", failure_message: "Failed to delete 'Stated year'") if year.to_s == stated_year.to_s end def sv_fix_stated_year begin Source.transaction do self.stated_year = nil self.save end true rescue false end end def if title.present? str = title.squish.gsub(/\<i>[^<>]*?<\/i>/, '') soft_validations.add(:title, 'The title contains unmatched html tags') if str.include?('<i>') || str.include?('</i>') end end end |
#school ⇒ String
Returns see en.wikipedia.org/wiki/BibTeX#Field_types.
188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 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 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 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 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 |
# File 'app/models/source.rb', line 188 class Source < ApplicationRecord include Housekeeping::Users include Housekeeping::Timestamps include Shared::AlternateValues include Shared::DataAttributes include Shared::Documentation include Shared::Identifiers include Shared::Notes include Shared::SharedAcrossProjects include Shared::Tags include Shared::HasPapertrail include SoftValidation include Shared::IsData # !! Must not have Shared::Depictions ignore_whitespace_on(:verbatim_contents) ALTERNATE_VALUES_FOR = [ :address, :annote, :booktitle, :edition, :editor, :institution, :journal, :note, :organization, :publisher, :school, :title, :doi, :abstract, :language, :translator, :author, :url].freeze # @return [Boolean, nil] # When true, cached values are not built attr_accessor :no_year_suffix_validation # Keep this order for citations/topics has_many :citations, inverse_of: :source, dependent: :restrict_with_error has_many :origin_citations, -> {where(citations: {is_original: true})}, class_name: 'Citation', dependent: :restrict_with_error, inverse_of: :source has_many :citation_topics, through: :citations, inverse_of: :source has_many :topics, through: :citation_topics, inverse_of: :sources has_many :project_sources, inverse_of: :source, dependent: :destroy has_many :projects, inverse_of: :sources, through: :project_sources after_save :set_cached validates_presence_of :type validates :type, inclusion: {in: ['Source::Bibtex', 'Source::Human', 'Source::Verbatim']} # TODO: not needed accepts_nested_attributes_for :project_sources, reject_if: :reject_project_sources soft_validate( :sv_cached_names, set: :cached_names, fix: :sv_fix_cached_names, name: 'Cached names', description: 'Check if cached values need to be updated' ) soft_validate( :sv_stated_year, set: :stated_year, fix: :sv_fix_stated_year, name: 'Stated year', description: "'Stated year' is not needed if identical to 'year'" ) soft_validate( :sv_html_tags, set: :html_tags, name: 'html tags', description: 'Check if html has both open and close tags' ) # Redirect type here # @param [String] file # @return [[Array, message]] # TODO: return a more informative response? def self.batch_preview(file) begin bibliography = BibTeX::Bibliography.parse(file.read.force_encoding('UTF-8'), filter: :latex) sources = [] bibliography.each do |record| a = Source::Bibtex.new_from_bibtex(record) sources.push(a) end return sources, nil rescue BibTeX::ParseError => e return [], e. rescue raise end end # @return [String] # A string that represents the authors last_names and year (no suffix) def return 'not yet calculated' if new_record? [, year].compact.join(', ') end # @param [String] file # @return [Array, Boolean] def self.batch_create(file) sources = [] valid = 0 begin # error_msg = [] Source.transaction do bibliography = BibTeX::Bibliography.parse(file.read.force_encoding('UTF-8'), filter: :latex) bibliography.each do |record| a = Source::Bibtex.new_from_bibtex(record) if a.valid? if a.save valid += 1 end else # error_msg = a.errors.messages.to_s end sources.push(a) end end rescue return false end return {records: sources, count: valid} end # @param used_on [String] a model name # @return [Scope] # the max 10 most recently used (1 week, could parameterize) TaxonName, as used def self.used_recently(user_id, project_id, used_on = 'TaxonName') Source.select('sources.id'). joins(:citations) .where(citations: {updated_by_id: user_id, project_id:, citation_object_type: used_on, updated_at: 1.week.ago..}) .order('citations.updated_at DESC') .pluck(:id).uniq end # @params target [String] a citable model name # @return [Hash] sources optimized for user selection def self.select_optimized(user_id, project_id, target = 'TaxonName') r = used_recently(user_id, project_id, target) h = { quick: [], pinboard: Source.pinned_by(user_id).where(pinboard_items: {project_id:}).to_a, recent: [] } if r.empty? h[:recent] = Source.where(created_by_id: user_id, updated_at: 2.hours.ago..Time.now ) .order('created_at DESC') .limit(5).order(:cached).to_a h[:quick] = Source.pinned_by(user_id).pinboard_inserted.where(pinboard_items: {project_id:}).to_a else h[:recent] = (Source.where(created_by_id: user_id, updated_at: 2.hours.ago..Time.now ) .order('created_at DESC') .limit(5).order(:cached).to_a + Source.where('"sources"."id" IN (?)', r.first(6) ).to_a).uniq h[:quick] = ( Source.pinned_by(user_id).pinboard_inserted.where(pinboard_items: {project_id:}).to_a + Source.where('"sources"."id" IN (?)', r.first(4) ).to_a).uniq end h end # @return [Array] # objects this source is linked to through citations def cited_objects self.citations.collect { |t| t.citation_object } end # @return [Boolean] def is_bibtex? type == 'Source::Bibtex' end # @return [Boolean] def is_in_project?(project_id) projects.where(id: project_id).any? end # Month handling allows values from bibtex like 'may' to be handled # @return [Date] def nomenclature_date Utilities::Dates.nomenclature_date(day, Utilities::Dates.month_index(month), year)&.to_date end # @return [Source] def clone s = dup m = "[CLONE of #{id}] " case type when 'Source::Verbatim' s.verbatim = m + verbatim.to_s when 'Source::Bibtex' s.title = m + title.to_s end roles.reload.each do |r| s.roles << Role.new(person: r.person, type: r.type, position: r.position ) end s.year_suffix = nil s.save s end protected # Defined in subclasses # @return [Nil] def set_cached end # Defined in subclasses def get_cached end # @param [Hash] attributed # @return [Boolean] def reject_project_sources(attributed) return true if attributed['project_id'].blank? return true if ProjectSource.where(project_id: attributed['project_id'], source_id: id).any? end def sv_cached_names true # see validation in subclasses end def sv_fix_cached_names begin Source.transaction do self.set_cached end true rescue false end end def sv_stated_year soft_validations.add( :base, "'Stated year' is not needed if identical to 'year'; applying the Fix will delete it", success_message: "'Stated year' was deleted", failure_message: "Failed to delete 'Stated year'") if year.to_s == stated_year.to_s end def sv_fix_stated_year begin Source.transaction do self.stated_year = nil self.save end true rescue false end end def if title.present? str = title.squish.gsub(/\<i>[^<>]*?<\/i>/, '') soft_validations.add(:title, 'The title contains unmatched html tags') if str.include?('<i>') || str.include?('</i>') end end end |
#serial_id ⇒ Integer
Returns The TaxonWorks Serial.
188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 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 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 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 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 |
# File 'app/models/source.rb', line 188 class Source < ApplicationRecord include Housekeeping::Users include Housekeeping::Timestamps include Shared::AlternateValues include Shared::DataAttributes include Shared::Documentation include Shared::Identifiers include Shared::Notes include Shared::SharedAcrossProjects include Shared::Tags include Shared::HasPapertrail include SoftValidation include Shared::IsData # !! Must not have Shared::Depictions ignore_whitespace_on(:verbatim_contents) ALTERNATE_VALUES_FOR = [ :address, :annote, :booktitle, :edition, :editor, :institution, :journal, :note, :organization, :publisher, :school, :title, :doi, :abstract, :language, :translator, :author, :url].freeze # @return [Boolean, nil] # When true, cached values are not built attr_accessor :no_year_suffix_validation # Keep this order for citations/topics has_many :citations, inverse_of: :source, dependent: :restrict_with_error has_many :origin_citations, -> {where(citations: {is_original: true})}, class_name: 'Citation', dependent: :restrict_with_error, inverse_of: :source has_many :citation_topics, through: :citations, inverse_of: :source has_many :topics, through: :citation_topics, inverse_of: :sources has_many :project_sources, inverse_of: :source, dependent: :destroy has_many :projects, inverse_of: :sources, through: :project_sources after_save :set_cached validates_presence_of :type validates :type, inclusion: {in: ['Source::Bibtex', 'Source::Human', 'Source::Verbatim']} # TODO: not needed accepts_nested_attributes_for :project_sources, reject_if: :reject_project_sources soft_validate( :sv_cached_names, set: :cached_names, fix: :sv_fix_cached_names, name: 'Cached names', description: 'Check if cached values need to be updated' ) soft_validate( :sv_stated_year, set: :stated_year, fix: :sv_fix_stated_year, name: 'Stated year', description: "'Stated year' is not needed if identical to 'year'" ) soft_validate( :sv_html_tags, set: :html_tags, name: 'html tags', description: 'Check if html has both open and close tags' ) # Redirect type here # @param [String] file # @return [[Array, message]] # TODO: return a more informative response? def self.batch_preview(file) begin bibliography = BibTeX::Bibliography.parse(file.read.force_encoding('UTF-8'), filter: :latex) sources = [] bibliography.each do |record| a = Source::Bibtex.new_from_bibtex(record) sources.push(a) end return sources, nil rescue BibTeX::ParseError => e return [], e. rescue raise end end # @return [String] # A string that represents the authors last_names and year (no suffix) def return 'not yet calculated' if new_record? [, year].compact.join(', ') end # @param [String] file # @return [Array, Boolean] def self.batch_create(file) sources = [] valid = 0 begin # error_msg = [] Source.transaction do bibliography = BibTeX::Bibliography.parse(file.read.force_encoding('UTF-8'), filter: :latex) bibliography.each do |record| a = Source::Bibtex.new_from_bibtex(record) if a.valid? if a.save valid += 1 end else # error_msg = a.errors.messages.to_s end sources.push(a) end end rescue return false end return {records: sources, count: valid} end # @param used_on [String] a model name # @return [Scope] # the max 10 most recently used (1 week, could parameterize) TaxonName, as used def self.used_recently(user_id, project_id, used_on = 'TaxonName') Source.select('sources.id'). joins(:citations) .where(citations: {updated_by_id: user_id, project_id:, citation_object_type: used_on, updated_at: 1.week.ago..}) .order('citations.updated_at DESC') .pluck(:id).uniq end # @params target [String] a citable model name # @return [Hash] sources optimized for user selection def self.select_optimized(user_id, project_id, target = 'TaxonName') r = used_recently(user_id, project_id, target) h = { quick: [], pinboard: Source.pinned_by(user_id).where(pinboard_items: {project_id:}).to_a, recent: [] } if r.empty? h[:recent] = Source.where(created_by_id: user_id, updated_at: 2.hours.ago..Time.now ) .order('created_at DESC') .limit(5).order(:cached).to_a h[:quick] = Source.pinned_by(user_id).pinboard_inserted.where(pinboard_items: {project_id:}).to_a else h[:recent] = (Source.where(created_by_id: user_id, updated_at: 2.hours.ago..Time.now ) .order('created_at DESC') .limit(5).order(:cached).to_a + Source.where('"sources"."id" IN (?)', r.first(6) ).to_a).uniq h[:quick] = ( Source.pinned_by(user_id).pinboard_inserted.where(pinboard_items: {project_id:}).to_a + Source.where('"sources"."id" IN (?)', r.first(4) ).to_a).uniq end h end # @return [Array] # objects this source is linked to through citations def cited_objects self.citations.collect { |t| t.citation_object } end # @return [Boolean] def is_bibtex? type == 'Source::Bibtex' end # @return [Boolean] def is_in_project?(project_id) projects.where(id: project_id).any? end # Month handling allows values from bibtex like 'may' to be handled # @return [Date] def nomenclature_date Utilities::Dates.nomenclature_date(day, Utilities::Dates.month_index(month), year)&.to_date end # @return [Source] def clone s = dup m = "[CLONE of #{id}] " case type when 'Source::Verbatim' s.verbatim = m + verbatim.to_s when 'Source::Bibtex' s.title = m + title.to_s end roles.reload.each do |r| s.roles << Role.new(person: r.person, type: r.type, position: r.position ) end s.year_suffix = nil s.save s end protected # Defined in subclasses # @return [Nil] def set_cached end # Defined in subclasses def get_cached end # @param [Hash] attributed # @return [Boolean] def reject_project_sources(attributed) return true if attributed['project_id'].blank? return true if ProjectSource.where(project_id: attributed['project_id'], source_id: id).any? end def sv_cached_names true # see validation in subclasses end def sv_fix_cached_names begin Source.transaction do self.set_cached end true rescue false end end def sv_stated_year soft_validations.add( :base, "'Stated year' is not needed if identical to 'year'; applying the Fix will delete it", success_message: "'Stated year' was deleted", failure_message: "Failed to delete 'Stated year'") if year.to_s == stated_year.to_s end def sv_fix_stated_year begin Source.transaction do self.stated_year = nil self.save end true rescue false end end def if title.present? str = title.squish.gsub(/\<i>[^<>]*?<\/i>/, '') soft_validations.add(:title, 'The title contains unmatched html tags') if str.include?('<i>') || str.include?('</i>') end end end |
#series ⇒ String
Returns see en.wikipedia.org/wiki/BibTeX#Field_types.
188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 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 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 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 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 |
# File 'app/models/source.rb', line 188 class Source < ApplicationRecord include Housekeeping::Users include Housekeeping::Timestamps include Shared::AlternateValues include Shared::DataAttributes include Shared::Documentation include Shared::Identifiers include Shared::Notes include Shared::SharedAcrossProjects include Shared::Tags include Shared::HasPapertrail include SoftValidation include Shared::IsData # !! Must not have Shared::Depictions ignore_whitespace_on(:verbatim_contents) ALTERNATE_VALUES_FOR = [ :address, :annote, :booktitle, :edition, :editor, :institution, :journal, :note, :organization, :publisher, :school, :title, :doi, :abstract, :language, :translator, :author, :url].freeze # @return [Boolean, nil] # When true, cached values are not built attr_accessor :no_year_suffix_validation # Keep this order for citations/topics has_many :citations, inverse_of: :source, dependent: :restrict_with_error has_many :origin_citations, -> {where(citations: {is_original: true})}, class_name: 'Citation', dependent: :restrict_with_error, inverse_of: :source has_many :citation_topics, through: :citations, inverse_of: :source has_many :topics, through: :citation_topics, inverse_of: :sources has_many :project_sources, inverse_of: :source, dependent: :destroy has_many :projects, inverse_of: :sources, through: :project_sources after_save :set_cached validates_presence_of :type validates :type, inclusion: {in: ['Source::Bibtex', 'Source::Human', 'Source::Verbatim']} # TODO: not needed accepts_nested_attributes_for :project_sources, reject_if: :reject_project_sources soft_validate( :sv_cached_names, set: :cached_names, fix: :sv_fix_cached_names, name: 'Cached names', description: 'Check if cached values need to be updated' ) soft_validate( :sv_stated_year, set: :stated_year, fix: :sv_fix_stated_year, name: 'Stated year', description: "'Stated year' is not needed if identical to 'year'" ) soft_validate( :sv_html_tags, set: :html_tags, name: 'html tags', description: 'Check if html has both open and close tags' ) # Redirect type here # @param [String] file # @return [[Array, message]] # TODO: return a more informative response? def self.batch_preview(file) begin bibliography = BibTeX::Bibliography.parse(file.read.force_encoding('UTF-8'), filter: :latex) sources = [] bibliography.each do |record| a = Source::Bibtex.new_from_bibtex(record) sources.push(a) end return sources, nil rescue BibTeX::ParseError => e return [], e. rescue raise end end # @return [String] # A string that represents the authors last_names and year (no suffix) def return 'not yet calculated' if new_record? [, year].compact.join(', ') end # @param [String] file # @return [Array, Boolean] def self.batch_create(file) sources = [] valid = 0 begin # error_msg = [] Source.transaction do bibliography = BibTeX::Bibliography.parse(file.read.force_encoding('UTF-8'), filter: :latex) bibliography.each do |record| a = Source::Bibtex.new_from_bibtex(record) if a.valid? if a.save valid += 1 end else # error_msg = a.errors.messages.to_s end sources.push(a) end end rescue return false end return {records: sources, count: valid} end # @param used_on [String] a model name # @return [Scope] # the max 10 most recently used (1 week, could parameterize) TaxonName, as used def self.used_recently(user_id, project_id, used_on = 'TaxonName') Source.select('sources.id'). joins(:citations) .where(citations: {updated_by_id: user_id, project_id:, citation_object_type: used_on, updated_at: 1.week.ago..}) .order('citations.updated_at DESC') .pluck(:id).uniq end # @params target [String] a citable model name # @return [Hash] sources optimized for user selection def self.select_optimized(user_id, project_id, target = 'TaxonName') r = used_recently(user_id, project_id, target) h = { quick: [], pinboard: Source.pinned_by(user_id).where(pinboard_items: {project_id:}).to_a, recent: [] } if r.empty? h[:recent] = Source.where(created_by_id: user_id, updated_at: 2.hours.ago..Time.now ) .order('created_at DESC') .limit(5).order(:cached).to_a h[:quick] = Source.pinned_by(user_id).pinboard_inserted.where(pinboard_items: {project_id:}).to_a else h[:recent] = (Source.where(created_by_id: user_id, updated_at: 2.hours.ago..Time.now ) .order('created_at DESC') .limit(5).order(:cached).to_a + Source.where('"sources"."id" IN (?)', r.first(6) ).to_a).uniq h[:quick] = ( Source.pinned_by(user_id).pinboard_inserted.where(pinboard_items: {project_id:}).to_a + Source.where('"sources"."id" IN (?)', r.first(4) ).to_a).uniq end h end # @return [Array] # objects this source is linked to through citations def cited_objects self.citations.collect { |t| t.citation_object } end # @return [Boolean] def is_bibtex? type == 'Source::Bibtex' end # @return [Boolean] def is_in_project?(project_id) projects.where(id: project_id).any? end # Month handling allows values from bibtex like 'may' to be handled # @return [Date] def nomenclature_date Utilities::Dates.nomenclature_date(day, Utilities::Dates.month_index(month), year)&.to_date end # @return [Source] def clone s = dup m = "[CLONE of #{id}] " case type when 'Source::Verbatim' s.verbatim = m + verbatim.to_s when 'Source::Bibtex' s.title = m + title.to_s end roles.reload.each do |r| s.roles << Role.new(person: r.person, type: r.type, position: r.position ) end s.year_suffix = nil s.save s end protected # Defined in subclasses # @return [Nil] def set_cached end # Defined in subclasses def get_cached end # @param [Hash] attributed # @return [Boolean] def reject_project_sources(attributed) return true if attributed['project_id'].blank? return true if ProjectSource.where(project_id: attributed['project_id'], source_id: id).any? end def sv_cached_names true # see validation in subclasses end def sv_fix_cached_names begin Source.transaction do self.set_cached end true rescue false end end def sv_stated_year soft_validations.add( :base, "'Stated year' is not needed if identical to 'year'; applying the Fix will delete it", success_message: "'Stated year' was deleted", failure_message: "Failed to delete 'Stated year'") if year.to_s == stated_year.to_s end def sv_fix_stated_year begin Source.transaction do self.stated_year = nil self.save end true rescue false end end def if title.present? str = title.squish.gsub(/\<i>[^<>]*?<\/i>/, '') soft_validations.add(:title, 'The title contains unmatched html tags') if str.include?('<i>') || str.include?('</i>') end end end |
#stated_year ⇒ String
Returns See source/bibtex.rb TODO: Why is this character but year is int?.
188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 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 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 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 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 |
# File 'app/models/source.rb', line 188 class Source < ApplicationRecord include Housekeeping::Users include Housekeeping::Timestamps include Shared::AlternateValues include Shared::DataAttributes include Shared::Documentation include Shared::Identifiers include Shared::Notes include Shared::SharedAcrossProjects include Shared::Tags include Shared::HasPapertrail include SoftValidation include Shared::IsData # !! Must not have Shared::Depictions ignore_whitespace_on(:verbatim_contents) ALTERNATE_VALUES_FOR = [ :address, :annote, :booktitle, :edition, :editor, :institution, :journal, :note, :organization, :publisher, :school, :title, :doi, :abstract, :language, :translator, :author, :url].freeze # @return [Boolean, nil] # When true, cached values are not built attr_accessor :no_year_suffix_validation # Keep this order for citations/topics has_many :citations, inverse_of: :source, dependent: :restrict_with_error has_many :origin_citations, -> {where(citations: {is_original: true})}, class_name: 'Citation', dependent: :restrict_with_error, inverse_of: :source has_many :citation_topics, through: :citations, inverse_of: :source has_many :topics, through: :citation_topics, inverse_of: :sources has_many :project_sources, inverse_of: :source, dependent: :destroy has_many :projects, inverse_of: :sources, through: :project_sources after_save :set_cached validates_presence_of :type validates :type, inclusion: {in: ['Source::Bibtex', 'Source::Human', 'Source::Verbatim']} # TODO: not needed accepts_nested_attributes_for :project_sources, reject_if: :reject_project_sources soft_validate( :sv_cached_names, set: :cached_names, fix: :sv_fix_cached_names, name: 'Cached names', description: 'Check if cached values need to be updated' ) soft_validate( :sv_stated_year, set: :stated_year, fix: :sv_fix_stated_year, name: 'Stated year', description: "'Stated year' is not needed if identical to 'year'" ) soft_validate( :sv_html_tags, set: :html_tags, name: 'html tags', description: 'Check if html has both open and close tags' ) # Redirect type here # @param [String] file # @return [[Array, message]] # TODO: return a more informative response? def self.batch_preview(file) begin bibliography = BibTeX::Bibliography.parse(file.read.force_encoding('UTF-8'), filter: :latex) sources = [] bibliography.each do |record| a = Source::Bibtex.new_from_bibtex(record) sources.push(a) end return sources, nil rescue BibTeX::ParseError => e return [], e. rescue raise end end # @return [String] # A string that represents the authors last_names and year (no suffix) def return 'not yet calculated' if new_record? [, year].compact.join(', ') end # @param [String] file # @return [Array, Boolean] def self.batch_create(file) sources = [] valid = 0 begin # error_msg = [] Source.transaction do bibliography = BibTeX::Bibliography.parse(file.read.force_encoding('UTF-8'), filter: :latex) bibliography.each do |record| a = Source::Bibtex.new_from_bibtex(record) if a.valid? if a.save valid += 1 end else # error_msg = a.errors.messages.to_s end sources.push(a) end end rescue return false end return {records: sources, count: valid} end # @param used_on [String] a model name # @return [Scope] # the max 10 most recently used (1 week, could parameterize) TaxonName, as used def self.used_recently(user_id, project_id, used_on = 'TaxonName') Source.select('sources.id'). joins(:citations) .where(citations: {updated_by_id: user_id, project_id:, citation_object_type: used_on, updated_at: 1.week.ago..}) .order('citations.updated_at DESC') .pluck(:id).uniq end # @params target [String] a citable model name # @return [Hash] sources optimized for user selection def self.select_optimized(user_id, project_id, target = 'TaxonName') r = used_recently(user_id, project_id, target) h = { quick: [], pinboard: Source.pinned_by(user_id).where(pinboard_items: {project_id:}).to_a, recent: [] } if r.empty? h[:recent] = Source.where(created_by_id: user_id, updated_at: 2.hours.ago..Time.now ) .order('created_at DESC') .limit(5).order(:cached).to_a h[:quick] = Source.pinned_by(user_id).pinboard_inserted.where(pinboard_items: {project_id:}).to_a else h[:recent] = (Source.where(created_by_id: user_id, updated_at: 2.hours.ago..Time.now ) .order('created_at DESC') .limit(5).order(:cached).to_a + Source.where('"sources"."id" IN (?)', r.first(6) ).to_a).uniq h[:quick] = ( Source.pinned_by(user_id).pinboard_inserted.where(pinboard_items: {project_id:}).to_a + Source.where('"sources"."id" IN (?)', r.first(4) ).to_a).uniq end h end # @return [Array] # objects this source is linked to through citations def cited_objects self.citations.collect { |t| t.citation_object } end # @return [Boolean] def is_bibtex? type == 'Source::Bibtex' end # @return [Boolean] def is_in_project?(project_id) projects.where(id: project_id).any? end # Month handling allows values from bibtex like 'may' to be handled # @return [Date] def nomenclature_date Utilities::Dates.nomenclature_date(day, Utilities::Dates.month_index(month), year)&.to_date end # @return [Source] def clone s = dup m = "[CLONE of #{id}] " case type when 'Source::Verbatim' s.verbatim = m + verbatim.to_s when 'Source::Bibtex' s.title = m + title.to_s end roles.reload.each do |r| s.roles << Role.new(person: r.person, type: r.type, position: r.position ) end s.year_suffix = nil s.save s end protected # Defined in subclasses # @return [Nil] def set_cached end # Defined in subclasses def get_cached end # @param [Hash] attributed # @return [Boolean] def reject_project_sources(attributed) return true if attributed['project_id'].blank? return true if ProjectSource.where(project_id: attributed['project_id'], source_id: id).any? end def sv_cached_names true # see validation in subclasses end def sv_fix_cached_names begin Source.transaction do self.set_cached end true rescue false end end def sv_stated_year soft_validations.add( :base, "'Stated year' is not needed if identical to 'year'; applying the Fix will delete it", success_message: "'Stated year' was deleted", failure_message: "Failed to delete 'Stated year'") if year.to_s == stated_year.to_s end def sv_fix_stated_year begin Source.transaction do self.stated_year = nil self.save end true rescue false end end def if title.present? str = title.squish.gsub(/\<i>[^<>]*?<\/i>/, '') soft_validations.add(:title, 'The title contains unmatched html tags') if str.include?('<i>') || str.include?('</i>') end end end |
#title ⇒ String
Returns see en.wikipedia.org/wiki/BibTeX#Field_types.
188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 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 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 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 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 |
# File 'app/models/source.rb', line 188 class Source < ApplicationRecord include Housekeeping::Users include Housekeeping::Timestamps include Shared::AlternateValues include Shared::DataAttributes include Shared::Documentation include Shared::Identifiers include Shared::Notes include Shared::SharedAcrossProjects include Shared::Tags include Shared::HasPapertrail include SoftValidation include Shared::IsData # !! Must not have Shared::Depictions ignore_whitespace_on(:verbatim_contents) ALTERNATE_VALUES_FOR = [ :address, :annote, :booktitle, :edition, :editor, :institution, :journal, :note, :organization, :publisher, :school, :title, :doi, :abstract, :language, :translator, :author, :url].freeze # @return [Boolean, nil] # When true, cached values are not built attr_accessor :no_year_suffix_validation # Keep this order for citations/topics has_many :citations, inverse_of: :source, dependent: :restrict_with_error has_many :origin_citations, -> {where(citations: {is_original: true})}, class_name: 'Citation', dependent: :restrict_with_error, inverse_of: :source has_many :citation_topics, through: :citations, inverse_of: :source has_many :topics, through: :citation_topics, inverse_of: :sources has_many :project_sources, inverse_of: :source, dependent: :destroy has_many :projects, inverse_of: :sources, through: :project_sources after_save :set_cached validates_presence_of :type validates :type, inclusion: {in: ['Source::Bibtex', 'Source::Human', 'Source::Verbatim']} # TODO: not needed accepts_nested_attributes_for :project_sources, reject_if: :reject_project_sources soft_validate( :sv_cached_names, set: :cached_names, fix: :sv_fix_cached_names, name: 'Cached names', description: 'Check if cached values need to be updated' ) soft_validate( :sv_stated_year, set: :stated_year, fix: :sv_fix_stated_year, name: 'Stated year', description: "'Stated year' is not needed if identical to 'year'" ) soft_validate( :sv_html_tags, set: :html_tags, name: 'html tags', description: 'Check if html has both open and close tags' ) # Redirect type here # @param [String] file # @return [[Array, message]] # TODO: return a more informative response? def self.batch_preview(file) begin bibliography = BibTeX::Bibliography.parse(file.read.force_encoding('UTF-8'), filter: :latex) sources = [] bibliography.each do |record| a = Source::Bibtex.new_from_bibtex(record) sources.push(a) end return sources, nil rescue BibTeX::ParseError => e return [], e. rescue raise end end # @return [String] # A string that represents the authors last_names and year (no suffix) def return 'not yet calculated' if new_record? [, year].compact.join(', ') end # @param [String] file # @return [Array, Boolean] def self.batch_create(file) sources = [] valid = 0 begin # error_msg = [] Source.transaction do bibliography = BibTeX::Bibliography.parse(file.read.force_encoding('UTF-8'), filter: :latex) bibliography.each do |record| a = Source::Bibtex.new_from_bibtex(record) if a.valid? if a.save valid += 1 end else # error_msg = a.errors.messages.to_s end sources.push(a) end end rescue return false end return {records: sources, count: valid} end # @param used_on [String] a model name # @return [Scope] # the max 10 most recently used (1 week, could parameterize) TaxonName, as used def self.used_recently(user_id, project_id, used_on = 'TaxonName') Source.select('sources.id'). joins(:citations) .where(citations: {updated_by_id: user_id, project_id:, citation_object_type: used_on, updated_at: 1.week.ago..}) .order('citations.updated_at DESC') .pluck(:id).uniq end # @params target [String] a citable model name # @return [Hash] sources optimized for user selection def self.select_optimized(user_id, project_id, target = 'TaxonName') r = used_recently(user_id, project_id, target) h = { quick: [], pinboard: Source.pinned_by(user_id).where(pinboard_items: {project_id:}).to_a, recent: [] } if r.empty? h[:recent] = Source.where(created_by_id: user_id, updated_at: 2.hours.ago..Time.now ) .order('created_at DESC') .limit(5).order(:cached).to_a h[:quick] = Source.pinned_by(user_id).pinboard_inserted.where(pinboard_items: {project_id:}).to_a else h[:recent] = (Source.where(created_by_id: user_id, updated_at: 2.hours.ago..Time.now ) .order('created_at DESC') .limit(5).order(:cached).to_a + Source.where('"sources"."id" IN (?)', r.first(6) ).to_a).uniq h[:quick] = ( Source.pinned_by(user_id).pinboard_inserted.where(pinboard_items: {project_id:}).to_a + Source.where('"sources"."id" IN (?)', r.first(4) ).to_a).uniq end h end # @return [Array] # objects this source is linked to through citations def cited_objects self.citations.collect { |t| t.citation_object } end # @return [Boolean] def is_bibtex? type == 'Source::Bibtex' end # @return [Boolean] def is_in_project?(project_id) projects.where(id: project_id).any? end # Month handling allows values from bibtex like 'may' to be handled # @return [Date] def nomenclature_date Utilities::Dates.nomenclature_date(day, Utilities::Dates.month_index(month), year)&.to_date end # @return [Source] def clone s = dup m = "[CLONE of #{id}] " case type when 'Source::Verbatim' s.verbatim = m + verbatim.to_s when 'Source::Bibtex' s.title = m + title.to_s end roles.reload.each do |r| s.roles << Role.new(person: r.person, type: r.type, position: r.position ) end s.year_suffix = nil s.save s end protected # Defined in subclasses # @return [Nil] def set_cached end # Defined in subclasses def get_cached end # @param [Hash] attributed # @return [Boolean] def reject_project_sources(attributed) return true if attributed['project_id'].blank? return true if ProjectSource.where(project_id: attributed['project_id'], source_id: id).any? end def sv_cached_names true # see validation in subclasses end def sv_fix_cached_names begin Source.transaction do self.set_cached end true rescue false end end def sv_stated_year soft_validations.add( :base, "'Stated year' is not needed if identical to 'year'; applying the Fix will delete it", success_message: "'Stated year' was deleted", failure_message: "Failed to delete 'Stated year'") if year.to_s == stated_year.to_s end def sv_fix_stated_year begin Source.transaction do self.stated_year = nil self.save end true rescue false end end def if title.present? str = title.squish.gsub(/\<i>[^<>]*?<\/i>/, '') soft_validations.add(:title, 'The title contains unmatched html tags') if str.include?('<i>') || str.include?('</i>') end end end |
#translator ⇒ String
188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 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 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 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 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 |
# File 'app/models/source.rb', line 188 class Source < ApplicationRecord include Housekeeping::Users include Housekeeping::Timestamps include Shared::AlternateValues include Shared::DataAttributes include Shared::Documentation include Shared::Identifiers include Shared::Notes include Shared::SharedAcrossProjects include Shared::Tags include Shared::HasPapertrail include SoftValidation include Shared::IsData # !! Must not have Shared::Depictions ignore_whitespace_on(:verbatim_contents) ALTERNATE_VALUES_FOR = [ :address, :annote, :booktitle, :edition, :editor, :institution, :journal, :note, :organization, :publisher, :school, :title, :doi, :abstract, :language, :translator, :author, :url].freeze # @return [Boolean, nil] # When true, cached values are not built attr_accessor :no_year_suffix_validation # Keep this order for citations/topics has_many :citations, inverse_of: :source, dependent: :restrict_with_error has_many :origin_citations, -> {where(citations: {is_original: true})}, class_name: 'Citation', dependent: :restrict_with_error, inverse_of: :source has_many :citation_topics, through: :citations, inverse_of: :source has_many :topics, through: :citation_topics, inverse_of: :sources has_many :project_sources, inverse_of: :source, dependent: :destroy has_many :projects, inverse_of: :sources, through: :project_sources after_save :set_cached validates_presence_of :type validates :type, inclusion: {in: ['Source::Bibtex', 'Source::Human', 'Source::Verbatim']} # TODO: not needed accepts_nested_attributes_for :project_sources, reject_if: :reject_project_sources soft_validate( :sv_cached_names, set: :cached_names, fix: :sv_fix_cached_names, name: 'Cached names', description: 'Check if cached values need to be updated' ) soft_validate( :sv_stated_year, set: :stated_year, fix: :sv_fix_stated_year, name: 'Stated year', description: "'Stated year' is not needed if identical to 'year'" ) soft_validate( :sv_html_tags, set: :html_tags, name: 'html tags', description: 'Check if html has both open and close tags' ) # Redirect type here # @param [String] file # @return [[Array, message]] # TODO: return a more informative response? def self.batch_preview(file) begin bibliography = BibTeX::Bibliography.parse(file.read.force_encoding('UTF-8'), filter: :latex) sources = [] bibliography.each do |record| a = Source::Bibtex.new_from_bibtex(record) sources.push(a) end return sources, nil rescue BibTeX::ParseError => e return [], e. rescue raise end end # @return [String] # A string that represents the authors last_names and year (no suffix) def return 'not yet calculated' if new_record? [, year].compact.join(', ') end # @param [String] file # @return [Array, Boolean] def self.batch_create(file) sources = [] valid = 0 begin # error_msg = [] Source.transaction do bibliography = BibTeX::Bibliography.parse(file.read.force_encoding('UTF-8'), filter: :latex) bibliography.each do |record| a = Source::Bibtex.new_from_bibtex(record) if a.valid? if a.save valid += 1 end else # error_msg = a.errors.messages.to_s end sources.push(a) end end rescue return false end return {records: sources, count: valid} end # @param used_on [String] a model name # @return [Scope] # the max 10 most recently used (1 week, could parameterize) TaxonName, as used def self.used_recently(user_id, project_id, used_on = 'TaxonName') Source.select('sources.id'). joins(:citations) .where(citations: {updated_by_id: user_id, project_id:, citation_object_type: used_on, updated_at: 1.week.ago..}) .order('citations.updated_at DESC') .pluck(:id).uniq end # @params target [String] a citable model name # @return [Hash] sources optimized for user selection def self.select_optimized(user_id, project_id, target = 'TaxonName') r = used_recently(user_id, project_id, target) h = { quick: [], pinboard: Source.pinned_by(user_id).where(pinboard_items: {project_id:}).to_a, recent: [] } if r.empty? h[:recent] = Source.where(created_by_id: user_id, updated_at: 2.hours.ago..Time.now ) .order('created_at DESC') .limit(5).order(:cached).to_a h[:quick] = Source.pinned_by(user_id).pinboard_inserted.where(pinboard_items: {project_id:}).to_a else h[:recent] = (Source.where(created_by_id: user_id, updated_at: 2.hours.ago..Time.now ) .order('created_at DESC') .limit(5).order(:cached).to_a + Source.where('"sources"."id" IN (?)', r.first(6) ).to_a).uniq h[:quick] = ( Source.pinned_by(user_id).pinboard_inserted.where(pinboard_items: {project_id:}).to_a + Source.where('"sources"."id" IN (?)', r.first(4) ).to_a).uniq end h end # @return [Array] # objects this source is linked to through citations def cited_objects self.citations.collect { |t| t.citation_object } end # @return [Boolean] def is_bibtex? type == 'Source::Bibtex' end # @return [Boolean] def is_in_project?(project_id) projects.where(id: project_id).any? end # Month handling allows values from bibtex like 'may' to be handled # @return [Date] def nomenclature_date Utilities::Dates.nomenclature_date(day, Utilities::Dates.month_index(month), year)&.to_date end # @return [Source] def clone s = dup m = "[CLONE of #{id}] " case type when 'Source::Verbatim' s.verbatim = m + verbatim.to_s when 'Source::Bibtex' s.title = m + title.to_s end roles.reload.each do |r| s.roles << Role.new(person: r.person, type: r.type, position: r.position ) end s.year_suffix = nil s.save s end protected # Defined in subclasses # @return [Nil] def set_cached end # Defined in subclasses def get_cached end # @param [Hash] attributed # @return [Boolean] def reject_project_sources(attributed) return true if attributed['project_id'].blank? return true if ProjectSource.where(project_id: attributed['project_id'], source_id: id).any? end def sv_cached_names true # see validation in subclasses end def sv_fix_cached_names begin Source.transaction do self.set_cached end true rescue false end end def sv_stated_year soft_validations.add( :base, "'Stated year' is not needed if identical to 'year'; applying the Fix will delete it", success_message: "'Stated year' was deleted", failure_message: "Failed to delete 'Stated year'") if year.to_s == stated_year.to_s end def sv_fix_stated_year begin Source.transaction do self.stated_year = nil self.save end true rescue false end end def if title.present? str = title.squish.gsub(/\<i>[^<>]*?<\/i>/, '') soft_validations.add(:title, 'The title contains unmatched html tags') if str.include?('<i>') || str.include?('</i>') end end end |
#type ⇒ String
Returns An exception to the 1:1 modelling. We retain for Rails STI usage. Either Source::Verbatim or Source::Bibtex. The former can only consist of a single field (the full citation as a string). The latter is a Bibtex model. See “bibtex_type” for the bibtex attribute “type”.
188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 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 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 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 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 |
# File 'app/models/source.rb', line 188 class Source < ApplicationRecord include Housekeeping::Users include Housekeeping::Timestamps include Shared::AlternateValues include Shared::DataAttributes include Shared::Documentation include Shared::Identifiers include Shared::Notes include Shared::SharedAcrossProjects include Shared::Tags include Shared::HasPapertrail include SoftValidation include Shared::IsData # !! Must not have Shared::Depictions ignore_whitespace_on(:verbatim_contents) ALTERNATE_VALUES_FOR = [ :address, :annote, :booktitle, :edition, :editor, :institution, :journal, :note, :organization, :publisher, :school, :title, :doi, :abstract, :language, :translator, :author, :url].freeze # @return [Boolean, nil] # When true, cached values are not built attr_accessor :no_year_suffix_validation # Keep this order for citations/topics has_many :citations, inverse_of: :source, dependent: :restrict_with_error has_many :origin_citations, -> {where(citations: {is_original: true})}, class_name: 'Citation', dependent: :restrict_with_error, inverse_of: :source has_many :citation_topics, through: :citations, inverse_of: :source has_many :topics, through: :citation_topics, inverse_of: :sources has_many :project_sources, inverse_of: :source, dependent: :destroy has_many :projects, inverse_of: :sources, through: :project_sources after_save :set_cached validates_presence_of :type validates :type, inclusion: {in: ['Source::Bibtex', 'Source::Human', 'Source::Verbatim']} # TODO: not needed accepts_nested_attributes_for :project_sources, reject_if: :reject_project_sources soft_validate( :sv_cached_names, set: :cached_names, fix: :sv_fix_cached_names, name: 'Cached names', description: 'Check if cached values need to be updated' ) soft_validate( :sv_stated_year, set: :stated_year, fix: :sv_fix_stated_year, name: 'Stated year', description: "'Stated year' is not needed if identical to 'year'" ) soft_validate( :sv_html_tags, set: :html_tags, name: 'html tags', description: 'Check if html has both open and close tags' ) # Redirect type here # @param [String] file # @return [[Array, message]] # TODO: return a more informative response? def self.batch_preview(file) begin bibliography = BibTeX::Bibliography.parse(file.read.force_encoding('UTF-8'), filter: :latex) sources = [] bibliography.each do |record| a = Source::Bibtex.new_from_bibtex(record) sources.push(a) end return sources, nil rescue BibTeX::ParseError => e return [], e. rescue raise end end # @return [String] # A string that represents the authors last_names and year (no suffix) def return 'not yet calculated' if new_record? [, year].compact.join(', ') end # @param [String] file # @return [Array, Boolean] def self.batch_create(file) sources = [] valid = 0 begin # error_msg = [] Source.transaction do bibliography = BibTeX::Bibliography.parse(file.read.force_encoding('UTF-8'), filter: :latex) bibliography.each do |record| a = Source::Bibtex.new_from_bibtex(record) if a.valid? if a.save valid += 1 end else # error_msg = a.errors.messages.to_s end sources.push(a) end end rescue return false end return {records: sources, count: valid} end # @param used_on [String] a model name # @return [Scope] # the max 10 most recently used (1 week, could parameterize) TaxonName, as used def self.used_recently(user_id, project_id, used_on = 'TaxonName') Source.select('sources.id'). joins(:citations) .where(citations: {updated_by_id: user_id, project_id:, citation_object_type: used_on, updated_at: 1.week.ago..}) .order('citations.updated_at DESC') .pluck(:id).uniq end # @params target [String] a citable model name # @return [Hash] sources optimized for user selection def self.select_optimized(user_id, project_id, target = 'TaxonName') r = used_recently(user_id, project_id, target) h = { quick: [], pinboard: Source.pinned_by(user_id).where(pinboard_items: {project_id:}).to_a, recent: [] } if r.empty? h[:recent] = Source.where(created_by_id: user_id, updated_at: 2.hours.ago..Time.now ) .order('created_at DESC') .limit(5).order(:cached).to_a h[:quick] = Source.pinned_by(user_id).pinboard_inserted.where(pinboard_items: {project_id:}).to_a else h[:recent] = (Source.where(created_by_id: user_id, updated_at: 2.hours.ago..Time.now ) .order('created_at DESC') .limit(5).order(:cached).to_a + Source.where('"sources"."id" IN (?)', r.first(6) ).to_a).uniq h[:quick] = ( Source.pinned_by(user_id).pinboard_inserted.where(pinboard_items: {project_id:}).to_a + Source.where('"sources"."id" IN (?)', r.first(4) ).to_a).uniq end h end # @return [Array] # objects this source is linked to through citations def cited_objects self.citations.collect { |t| t.citation_object } end # @return [Boolean] def is_bibtex? type == 'Source::Bibtex' end # @return [Boolean] def is_in_project?(project_id) projects.where(id: project_id).any? end # Month handling allows values from bibtex like 'may' to be handled # @return [Date] def nomenclature_date Utilities::Dates.nomenclature_date(day, Utilities::Dates.month_index(month), year)&.to_date end # @return [Source] def clone s = dup m = "[CLONE of #{id}] " case type when 'Source::Verbatim' s.verbatim = m + verbatim.to_s when 'Source::Bibtex' s.title = m + title.to_s end roles.reload.each do |r| s.roles << Role.new(person: r.person, type: r.type, position: r.position ) end s.year_suffix = nil s.save s end protected # Defined in subclasses # @return [Nil] def set_cached end # Defined in subclasses def get_cached end # @param [Hash] attributed # @return [Boolean] def reject_project_sources(attributed) return true if attributed['project_id'].blank? return true if ProjectSource.where(project_id: attributed['project_id'], source_id: id).any? end def sv_cached_names true # see validation in subclasses end def sv_fix_cached_names begin Source.transaction do self.set_cached end true rescue false end end def sv_stated_year soft_validations.add( :base, "'Stated year' is not needed if identical to 'year'; applying the Fix will delete it", success_message: "'Stated year' was deleted", failure_message: "Failed to delete 'Stated year'") if year.to_s == stated_year.to_s end def sv_fix_stated_year begin Source.transaction do self.stated_year = nil self.save end true rescue false end end def if title.present? str = title.squish.gsub(/\<i>[^<>]*?<\/i>/, '') soft_validations.add(:title, 'The title contains unmatched html tags') if str.include?('<i>') || str.include?('</i>') end end end |
#url ⇒ String
188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 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 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 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 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 |
# File 'app/models/source.rb', line 188 class Source < ApplicationRecord include Housekeeping::Users include Housekeeping::Timestamps include Shared::AlternateValues include Shared::DataAttributes include Shared::Documentation include Shared::Identifiers include Shared::Notes include Shared::SharedAcrossProjects include Shared::Tags include Shared::HasPapertrail include SoftValidation include Shared::IsData # !! Must not have Shared::Depictions ignore_whitespace_on(:verbatim_contents) ALTERNATE_VALUES_FOR = [ :address, :annote, :booktitle, :edition, :editor, :institution, :journal, :note, :organization, :publisher, :school, :title, :doi, :abstract, :language, :translator, :author, :url].freeze # @return [Boolean, nil] # When true, cached values are not built attr_accessor :no_year_suffix_validation # Keep this order for citations/topics has_many :citations, inverse_of: :source, dependent: :restrict_with_error has_many :origin_citations, -> {where(citations: {is_original: true})}, class_name: 'Citation', dependent: :restrict_with_error, inverse_of: :source has_many :citation_topics, through: :citations, inverse_of: :source has_many :topics, through: :citation_topics, inverse_of: :sources has_many :project_sources, inverse_of: :source, dependent: :destroy has_many :projects, inverse_of: :sources, through: :project_sources after_save :set_cached validates_presence_of :type validates :type, inclusion: {in: ['Source::Bibtex', 'Source::Human', 'Source::Verbatim']} # TODO: not needed accepts_nested_attributes_for :project_sources, reject_if: :reject_project_sources soft_validate( :sv_cached_names, set: :cached_names, fix: :sv_fix_cached_names, name: 'Cached names', description: 'Check if cached values need to be updated' ) soft_validate( :sv_stated_year, set: :stated_year, fix: :sv_fix_stated_year, name: 'Stated year', description: "'Stated year' is not needed if identical to 'year'" ) soft_validate( :sv_html_tags, set: :html_tags, name: 'html tags', description: 'Check if html has both open and close tags' ) # Redirect type here # @param [String] file # @return [[Array, message]] # TODO: return a more informative response? def self.batch_preview(file) begin bibliography = BibTeX::Bibliography.parse(file.read.force_encoding('UTF-8'), filter: :latex) sources = [] bibliography.each do |record| a = Source::Bibtex.new_from_bibtex(record) sources.push(a) end return sources, nil rescue BibTeX::ParseError => e return [], e. rescue raise end end # @return [String] # A string that represents the authors last_names and year (no suffix) def return 'not yet calculated' if new_record? [, year].compact.join(', ') end # @param [String] file # @return [Array, Boolean] def self.batch_create(file) sources = [] valid = 0 begin # error_msg = [] Source.transaction do bibliography = BibTeX::Bibliography.parse(file.read.force_encoding('UTF-8'), filter: :latex) bibliography.each do |record| a = Source::Bibtex.new_from_bibtex(record) if a.valid? if a.save valid += 1 end else # error_msg = a.errors.messages.to_s end sources.push(a) end end rescue return false end return {records: sources, count: valid} end # @param used_on [String] a model name # @return [Scope] # the max 10 most recently used (1 week, could parameterize) TaxonName, as used def self.used_recently(user_id, project_id, used_on = 'TaxonName') Source.select('sources.id'). joins(:citations) .where(citations: {updated_by_id: user_id, project_id:, citation_object_type: used_on, updated_at: 1.week.ago..}) .order('citations.updated_at DESC') .pluck(:id).uniq end # @params target [String] a citable model name # @return [Hash] sources optimized for user selection def self.select_optimized(user_id, project_id, target = 'TaxonName') r = used_recently(user_id, project_id, target) h = { quick: [], pinboard: Source.pinned_by(user_id).where(pinboard_items: {project_id:}).to_a, recent: [] } if r.empty? h[:recent] = Source.where(created_by_id: user_id, updated_at: 2.hours.ago..Time.now ) .order('created_at DESC') .limit(5).order(:cached).to_a h[:quick] = Source.pinned_by(user_id).pinboard_inserted.where(pinboard_items: {project_id:}).to_a else h[:recent] = (Source.where(created_by_id: user_id, updated_at: 2.hours.ago..Time.now ) .order('created_at DESC') .limit(5).order(:cached).to_a + Source.where('"sources"."id" IN (?)', r.first(6) ).to_a).uniq h[:quick] = ( Source.pinned_by(user_id).pinboard_inserted.where(pinboard_items: {project_id:}).to_a + Source.where('"sources"."id" IN (?)', r.first(4) ).to_a).uniq end h end # @return [Array] # objects this source is linked to through citations def cited_objects self.citations.collect { |t| t.citation_object } end # @return [Boolean] def is_bibtex? type == 'Source::Bibtex' end # @return [Boolean] def is_in_project?(project_id) projects.where(id: project_id).any? end # Month handling allows values from bibtex like 'may' to be handled # @return [Date] def nomenclature_date Utilities::Dates.nomenclature_date(day, Utilities::Dates.month_index(month), year)&.to_date end # @return [Source] def clone s = dup m = "[CLONE of #{id}] " case type when 'Source::Verbatim' s.verbatim = m + verbatim.to_s when 'Source::Bibtex' s.title = m + title.to_s end roles.reload.each do |r| s.roles << Role.new(person: r.person, type: r.type, position: r.position ) end s.year_suffix = nil s.save s end protected # Defined in subclasses # @return [Nil] def set_cached end # Defined in subclasses def get_cached end # @param [Hash] attributed # @return [Boolean] def reject_project_sources(attributed) return true if attributed['project_id'].blank? return true if ProjectSource.where(project_id: attributed['project_id'], source_id: id).any? end def sv_cached_names true # see validation in subclasses end def sv_fix_cached_names begin Source.transaction do self.set_cached end true rescue false end end def sv_stated_year soft_validations.add( :base, "'Stated year' is not needed if identical to 'year'; applying the Fix will delete it", success_message: "'Stated year' was deleted", failure_message: "Failed to delete 'Stated year'") if year.to_s == stated_year.to_s end def sv_fix_stated_year begin Source.transaction do self.stated_year = nil self.save end true rescue false end end def if title.present? str = title.squish.gsub(/\<i>[^<>]*?<\/i>/, '') soft_validations.add(:title, 'The title contains unmatched html tags') if str.include?('<i>') || str.include?('</i>') end end end |
#verbatim ⇒ String
Returns the full citation, used only for type = SourceVerbatim.
188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 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 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 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 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 |
# File 'app/models/source.rb', line 188 class Source < ApplicationRecord include Housekeeping::Users include Housekeeping::Timestamps include Shared::AlternateValues include Shared::DataAttributes include Shared::Documentation include Shared::Identifiers include Shared::Notes include Shared::SharedAcrossProjects include Shared::Tags include Shared::HasPapertrail include SoftValidation include Shared::IsData # !! Must not have Shared::Depictions ignore_whitespace_on(:verbatim_contents) ALTERNATE_VALUES_FOR = [ :address, :annote, :booktitle, :edition, :editor, :institution, :journal, :note, :organization, :publisher, :school, :title, :doi, :abstract, :language, :translator, :author, :url].freeze # @return [Boolean, nil] # When true, cached values are not built attr_accessor :no_year_suffix_validation # Keep this order for citations/topics has_many :citations, inverse_of: :source, dependent: :restrict_with_error has_many :origin_citations, -> {where(citations: {is_original: true})}, class_name: 'Citation', dependent: :restrict_with_error, inverse_of: :source has_many :citation_topics, through: :citations, inverse_of: :source has_many :topics, through: :citation_topics, inverse_of: :sources has_many :project_sources, inverse_of: :source, dependent: :destroy has_many :projects, inverse_of: :sources, through: :project_sources after_save :set_cached validates_presence_of :type validates :type, inclusion: {in: ['Source::Bibtex', 'Source::Human', 'Source::Verbatim']} # TODO: not needed accepts_nested_attributes_for :project_sources, reject_if: :reject_project_sources soft_validate( :sv_cached_names, set: :cached_names, fix: :sv_fix_cached_names, name: 'Cached names', description: 'Check if cached values need to be updated' ) soft_validate( :sv_stated_year, set: :stated_year, fix: :sv_fix_stated_year, name: 'Stated year', description: "'Stated year' is not needed if identical to 'year'" ) soft_validate( :sv_html_tags, set: :html_tags, name: 'html tags', description: 'Check if html has both open and close tags' ) # Redirect type here # @param [String] file # @return [[Array, message]] # TODO: return a more informative response? def self.batch_preview(file) begin bibliography = BibTeX::Bibliography.parse(file.read.force_encoding('UTF-8'), filter: :latex) sources = [] bibliography.each do |record| a = Source::Bibtex.new_from_bibtex(record) sources.push(a) end return sources, nil rescue BibTeX::ParseError => e return [], e. rescue raise end end # @return [String] # A string that represents the authors last_names and year (no suffix) def return 'not yet calculated' if new_record? [, year].compact.join(', ') end # @param [String] file # @return [Array, Boolean] def self.batch_create(file) sources = [] valid = 0 begin # error_msg = [] Source.transaction do bibliography = BibTeX::Bibliography.parse(file.read.force_encoding('UTF-8'), filter: :latex) bibliography.each do |record| a = Source::Bibtex.new_from_bibtex(record) if a.valid? if a.save valid += 1 end else # error_msg = a.errors.messages.to_s end sources.push(a) end end rescue return false end return {records: sources, count: valid} end # @param used_on [String] a model name # @return [Scope] # the max 10 most recently used (1 week, could parameterize) TaxonName, as used def self.used_recently(user_id, project_id, used_on = 'TaxonName') Source.select('sources.id'). joins(:citations) .where(citations: {updated_by_id: user_id, project_id:, citation_object_type: used_on, updated_at: 1.week.ago..}) .order('citations.updated_at DESC') .pluck(:id).uniq end # @params target [String] a citable model name # @return [Hash] sources optimized for user selection def self.select_optimized(user_id, project_id, target = 'TaxonName') r = used_recently(user_id, project_id, target) h = { quick: [], pinboard: Source.pinned_by(user_id).where(pinboard_items: {project_id:}).to_a, recent: [] } if r.empty? h[:recent] = Source.where(created_by_id: user_id, updated_at: 2.hours.ago..Time.now ) .order('created_at DESC') .limit(5).order(:cached).to_a h[:quick] = Source.pinned_by(user_id).pinboard_inserted.where(pinboard_items: {project_id:}).to_a else h[:recent] = (Source.where(created_by_id: user_id, updated_at: 2.hours.ago..Time.now ) .order('created_at DESC') .limit(5).order(:cached).to_a + Source.where('"sources"."id" IN (?)', r.first(6) ).to_a).uniq h[:quick] = ( Source.pinned_by(user_id).pinboard_inserted.where(pinboard_items: {project_id:}).to_a + Source.where('"sources"."id" IN (?)', r.first(4) ).to_a).uniq end h end # @return [Array] # objects this source is linked to through citations def cited_objects self.citations.collect { |t| t.citation_object } end # @return [Boolean] def is_bibtex? type == 'Source::Bibtex' end # @return [Boolean] def is_in_project?(project_id) projects.where(id: project_id).any? end # Month handling allows values from bibtex like 'may' to be handled # @return [Date] def nomenclature_date Utilities::Dates.nomenclature_date(day, Utilities::Dates.month_index(month), year)&.to_date end # @return [Source] def clone s = dup m = "[CLONE of #{id}] " case type when 'Source::Verbatim' s.verbatim = m + verbatim.to_s when 'Source::Bibtex' s.title = m + title.to_s end roles.reload.each do |r| s.roles << Role.new(person: r.person, type: r.type, position: r.position ) end s.year_suffix = nil s.save s end protected # Defined in subclasses # @return [Nil] def set_cached end # Defined in subclasses def get_cached end # @param [Hash] attributed # @return [Boolean] def reject_project_sources(attributed) return true if attributed['project_id'].blank? return true if ProjectSource.where(project_id: attributed['project_id'], source_id: id).any? end def sv_cached_names true # see validation in subclasses end def sv_fix_cached_names begin Source.transaction do self.set_cached end true rescue false end end def sv_stated_year soft_validations.add( :base, "'Stated year' is not needed if identical to 'year'; applying the Fix will delete it", success_message: "'Stated year' was deleted", failure_message: "Failed to delete 'Stated year'") if year.to_s == stated_year.to_s end def sv_fix_stated_year begin Source.transaction do self.stated_year = nil self.save end true rescue false end end def if title.present? str = title.squish.gsub(/\<i>[^<>]*?<\/i>/, '') soft_validations.add(:title, 'The title contains unmatched html tags') if str.include?('<i>') || str.include?('</i>') end end end |
#verbatim_contents ⇒ String
188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 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 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 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 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 |
# File 'app/models/source.rb', line 188 class Source < ApplicationRecord include Housekeeping::Users include Housekeeping::Timestamps include Shared::AlternateValues include Shared::DataAttributes include Shared::Documentation include Shared::Identifiers include Shared::Notes include Shared::SharedAcrossProjects include Shared::Tags include Shared::HasPapertrail include SoftValidation include Shared::IsData # !! Must not have Shared::Depictions ignore_whitespace_on(:verbatim_contents) ALTERNATE_VALUES_FOR = [ :address, :annote, :booktitle, :edition, :editor, :institution, :journal, :note, :organization, :publisher, :school, :title, :doi, :abstract, :language, :translator, :author, :url].freeze # @return [Boolean, nil] # When true, cached values are not built attr_accessor :no_year_suffix_validation # Keep this order for citations/topics has_many :citations, inverse_of: :source, dependent: :restrict_with_error has_many :origin_citations, -> {where(citations: {is_original: true})}, class_name: 'Citation', dependent: :restrict_with_error, inverse_of: :source has_many :citation_topics, through: :citations, inverse_of: :source has_many :topics, through: :citation_topics, inverse_of: :sources has_many :project_sources, inverse_of: :source, dependent: :destroy has_many :projects, inverse_of: :sources, through: :project_sources after_save :set_cached validates_presence_of :type validates :type, inclusion: {in: ['Source::Bibtex', 'Source::Human', 'Source::Verbatim']} # TODO: not needed accepts_nested_attributes_for :project_sources, reject_if: :reject_project_sources soft_validate( :sv_cached_names, set: :cached_names, fix: :sv_fix_cached_names, name: 'Cached names', description: 'Check if cached values need to be updated' ) soft_validate( :sv_stated_year, set: :stated_year, fix: :sv_fix_stated_year, name: 'Stated year', description: "'Stated year' is not needed if identical to 'year'" ) soft_validate( :sv_html_tags, set: :html_tags, name: 'html tags', description: 'Check if html has both open and close tags' ) # Redirect type here # @param [String] file # @return [[Array, message]] # TODO: return a more informative response? def self.batch_preview(file) begin bibliography = BibTeX::Bibliography.parse(file.read.force_encoding('UTF-8'), filter: :latex) sources = [] bibliography.each do |record| a = Source::Bibtex.new_from_bibtex(record) sources.push(a) end return sources, nil rescue BibTeX::ParseError => e return [], e. rescue raise end end # @return [String] # A string that represents the authors last_names and year (no suffix) def return 'not yet calculated' if new_record? [, year].compact.join(', ') end # @param [String] file # @return [Array, Boolean] def self.batch_create(file) sources = [] valid = 0 begin # error_msg = [] Source.transaction do bibliography = BibTeX::Bibliography.parse(file.read.force_encoding('UTF-8'), filter: :latex) bibliography.each do |record| a = Source::Bibtex.new_from_bibtex(record) if a.valid? if a.save valid += 1 end else # error_msg = a.errors.messages.to_s end sources.push(a) end end rescue return false end return {records: sources, count: valid} end # @param used_on [String] a model name # @return [Scope] # the max 10 most recently used (1 week, could parameterize) TaxonName, as used def self.used_recently(user_id, project_id, used_on = 'TaxonName') Source.select('sources.id'). joins(:citations) .where(citations: {updated_by_id: user_id, project_id:, citation_object_type: used_on, updated_at: 1.week.ago..}) .order('citations.updated_at DESC') .pluck(:id).uniq end # @params target [String] a citable model name # @return [Hash] sources optimized for user selection def self.select_optimized(user_id, project_id, target = 'TaxonName') r = used_recently(user_id, project_id, target) h = { quick: [], pinboard: Source.pinned_by(user_id).where(pinboard_items: {project_id:}).to_a, recent: [] } if r.empty? h[:recent] = Source.where(created_by_id: user_id, updated_at: 2.hours.ago..Time.now ) .order('created_at DESC') .limit(5).order(:cached).to_a h[:quick] = Source.pinned_by(user_id).pinboard_inserted.where(pinboard_items: {project_id:}).to_a else h[:recent] = (Source.where(created_by_id: user_id, updated_at: 2.hours.ago..Time.now ) .order('created_at DESC') .limit(5).order(:cached).to_a + Source.where('"sources"."id" IN (?)', r.first(6) ).to_a).uniq h[:quick] = ( Source.pinned_by(user_id).pinboard_inserted.where(pinboard_items: {project_id:}).to_a + Source.where('"sources"."id" IN (?)', r.first(4) ).to_a).uniq end h end # @return [Array] # objects this source is linked to through citations def cited_objects self.citations.collect { |t| t.citation_object } end # @return [Boolean] def is_bibtex? type == 'Source::Bibtex' end # @return [Boolean] def is_in_project?(project_id) projects.where(id: project_id).any? end # Month handling allows values from bibtex like 'may' to be handled # @return [Date] def nomenclature_date Utilities::Dates.nomenclature_date(day, Utilities::Dates.month_index(month), year)&.to_date end # @return [Source] def clone s = dup m = "[CLONE of #{id}] " case type when 'Source::Verbatim' s.verbatim = m + verbatim.to_s when 'Source::Bibtex' s.title = m + title.to_s end roles.reload.each do |r| s.roles << Role.new(person: r.person, type: r.type, position: r.position ) end s.year_suffix = nil s.save s end protected # Defined in subclasses # @return [Nil] def set_cached end # Defined in subclasses def get_cached end # @param [Hash] attributed # @return [Boolean] def reject_project_sources(attributed) return true if attributed['project_id'].blank? return true if ProjectSource.where(project_id: attributed['project_id'], source_id: id).any? end def sv_cached_names true # see validation in subclasses end def sv_fix_cached_names begin Source.transaction do self.set_cached end true rescue false end end def sv_stated_year soft_validations.add( :base, "'Stated year' is not needed if identical to 'year'; applying the Fix will delete it", success_message: "'Stated year' was deleted", failure_message: "Failed to delete 'Stated year'") if year.to_s == stated_year.to_s end def sv_fix_stated_year begin Source.transaction do self.stated_year = nil self.save end true rescue false end end def if title.present? str = title.squish.gsub(/\<i>[^<>]*?<\/i>/, '') soft_validations.add(:title, 'The title contains unmatched html tags') if str.include?('<i>') || str.include?('</i>') end end end |
#verbatim_keywords ⇒ String
188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 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 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 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 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 |
# File 'app/models/source.rb', line 188 class Source < ApplicationRecord include Housekeeping::Users include Housekeeping::Timestamps include Shared::AlternateValues include Shared::DataAttributes include Shared::Documentation include Shared::Identifiers include Shared::Notes include Shared::SharedAcrossProjects include Shared::Tags include Shared::HasPapertrail include SoftValidation include Shared::IsData # !! Must not have Shared::Depictions ignore_whitespace_on(:verbatim_contents) ALTERNATE_VALUES_FOR = [ :address, :annote, :booktitle, :edition, :editor, :institution, :journal, :note, :organization, :publisher, :school, :title, :doi, :abstract, :language, :translator, :author, :url].freeze # @return [Boolean, nil] # When true, cached values are not built attr_accessor :no_year_suffix_validation # Keep this order for citations/topics has_many :citations, inverse_of: :source, dependent: :restrict_with_error has_many :origin_citations, -> {where(citations: {is_original: true})}, class_name: 'Citation', dependent: :restrict_with_error, inverse_of: :source has_many :citation_topics, through: :citations, inverse_of: :source has_many :topics, through: :citation_topics, inverse_of: :sources has_many :project_sources, inverse_of: :source, dependent: :destroy has_many :projects, inverse_of: :sources, through: :project_sources after_save :set_cached validates_presence_of :type validates :type, inclusion: {in: ['Source::Bibtex', 'Source::Human', 'Source::Verbatim']} # TODO: not needed accepts_nested_attributes_for :project_sources, reject_if: :reject_project_sources soft_validate( :sv_cached_names, set: :cached_names, fix: :sv_fix_cached_names, name: 'Cached names', description: 'Check if cached values need to be updated' ) soft_validate( :sv_stated_year, set: :stated_year, fix: :sv_fix_stated_year, name: 'Stated year', description: "'Stated year' is not needed if identical to 'year'" ) soft_validate( :sv_html_tags, set: :html_tags, name: 'html tags', description: 'Check if html has both open and close tags' ) # Redirect type here # @param [String] file # @return [[Array, message]] # TODO: return a more informative response? def self.batch_preview(file) begin bibliography = BibTeX::Bibliography.parse(file.read.force_encoding('UTF-8'), filter: :latex) sources = [] bibliography.each do |record| a = Source::Bibtex.new_from_bibtex(record) sources.push(a) end return sources, nil rescue BibTeX::ParseError => e return [], e. rescue raise end end # @return [String] # A string that represents the authors last_names and year (no suffix) def return 'not yet calculated' if new_record? [, year].compact.join(', ') end # @param [String] file # @return [Array, Boolean] def self.batch_create(file) sources = [] valid = 0 begin # error_msg = [] Source.transaction do bibliography = BibTeX::Bibliography.parse(file.read.force_encoding('UTF-8'), filter: :latex) bibliography.each do |record| a = Source::Bibtex.new_from_bibtex(record) if a.valid? if a.save valid += 1 end else # error_msg = a.errors.messages.to_s end sources.push(a) end end rescue return false end return {records: sources, count: valid} end # @param used_on [String] a model name # @return [Scope] # the max 10 most recently used (1 week, could parameterize) TaxonName, as used def self.used_recently(user_id, project_id, used_on = 'TaxonName') Source.select('sources.id'). joins(:citations) .where(citations: {updated_by_id: user_id, project_id:, citation_object_type: used_on, updated_at: 1.week.ago..}) .order('citations.updated_at DESC') .pluck(:id).uniq end # @params target [String] a citable model name # @return [Hash] sources optimized for user selection def self.select_optimized(user_id, project_id, target = 'TaxonName') r = used_recently(user_id, project_id, target) h = { quick: [], pinboard: Source.pinned_by(user_id).where(pinboard_items: {project_id:}).to_a, recent: [] } if r.empty? h[:recent] = Source.where(created_by_id: user_id, updated_at: 2.hours.ago..Time.now ) .order('created_at DESC') .limit(5).order(:cached).to_a h[:quick] = Source.pinned_by(user_id).pinboard_inserted.where(pinboard_items: {project_id:}).to_a else h[:recent] = (Source.where(created_by_id: user_id, updated_at: 2.hours.ago..Time.now ) .order('created_at DESC') .limit(5).order(:cached).to_a + Source.where('"sources"."id" IN (?)', r.first(6) ).to_a).uniq h[:quick] = ( Source.pinned_by(user_id).pinboard_inserted.where(pinboard_items: {project_id:}).to_a + Source.where('"sources"."id" IN (?)', r.first(4) ).to_a).uniq end h end # @return [Array] # objects this source is linked to through citations def cited_objects self.citations.collect { |t| t.citation_object } end # @return [Boolean] def is_bibtex? type == 'Source::Bibtex' end # @return [Boolean] def is_in_project?(project_id) projects.where(id: project_id).any? end # Month handling allows values from bibtex like 'may' to be handled # @return [Date] def nomenclature_date Utilities::Dates.nomenclature_date(day, Utilities::Dates.month_index(month), year)&.to_date end # @return [Source] def clone s = dup m = "[CLONE of #{id}] " case type when 'Source::Verbatim' s.verbatim = m + verbatim.to_s when 'Source::Bibtex' s.title = m + title.to_s end roles.reload.each do |r| s.roles << Role.new(person: r.person, type: r.type, position: r.position ) end s.year_suffix = nil s.save s end protected # Defined in subclasses # @return [Nil] def set_cached end # Defined in subclasses def get_cached end # @param [Hash] attributed # @return [Boolean] def reject_project_sources(attributed) return true if attributed['project_id'].blank? return true if ProjectSource.where(project_id: attributed['project_id'], source_id: id).any? end def sv_cached_names true # see validation in subclasses end def sv_fix_cached_names begin Source.transaction do self.set_cached end true rescue false end end def sv_stated_year soft_validations.add( :base, "'Stated year' is not needed if identical to 'year'; applying the Fix will delete it", success_message: "'Stated year' was deleted", failure_message: "Failed to delete 'Stated year'") if year.to_s == stated_year.to_s end def sv_fix_stated_year begin Source.transaction do self.stated_year = nil self.save end true rescue false end end def if title.present? str = title.squish.gsub(/\<i>[^<>]*?<\/i>/, '') soft_validations.add(:title, 'The title contains unmatched html tags') if str.include?('<i>') || str.include?('</i>') end end end |
#volume ⇒ String
Returns see en.wikipedia.org/wiki/BibTeX#Field_types.
188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 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 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 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 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 |
# File 'app/models/source.rb', line 188 class Source < ApplicationRecord include Housekeeping::Users include Housekeeping::Timestamps include Shared::AlternateValues include Shared::DataAttributes include Shared::Documentation include Shared::Identifiers include Shared::Notes include Shared::SharedAcrossProjects include Shared::Tags include Shared::HasPapertrail include SoftValidation include Shared::IsData # !! Must not have Shared::Depictions ignore_whitespace_on(:verbatim_contents) ALTERNATE_VALUES_FOR = [ :address, :annote, :booktitle, :edition, :editor, :institution, :journal, :note, :organization, :publisher, :school, :title, :doi, :abstract, :language, :translator, :author, :url].freeze # @return [Boolean, nil] # When true, cached values are not built attr_accessor :no_year_suffix_validation # Keep this order for citations/topics has_many :citations, inverse_of: :source, dependent: :restrict_with_error has_many :origin_citations, -> {where(citations: {is_original: true})}, class_name: 'Citation', dependent: :restrict_with_error, inverse_of: :source has_many :citation_topics, through: :citations, inverse_of: :source has_many :topics, through: :citation_topics, inverse_of: :sources has_many :project_sources, inverse_of: :source, dependent: :destroy has_many :projects, inverse_of: :sources, through: :project_sources after_save :set_cached validates_presence_of :type validates :type, inclusion: {in: ['Source::Bibtex', 'Source::Human', 'Source::Verbatim']} # TODO: not needed accepts_nested_attributes_for :project_sources, reject_if: :reject_project_sources soft_validate( :sv_cached_names, set: :cached_names, fix: :sv_fix_cached_names, name: 'Cached names', description: 'Check if cached values need to be updated' ) soft_validate( :sv_stated_year, set: :stated_year, fix: :sv_fix_stated_year, name: 'Stated year', description: "'Stated year' is not needed if identical to 'year'" ) soft_validate( :sv_html_tags, set: :html_tags, name: 'html tags', description: 'Check if html has both open and close tags' ) # Redirect type here # @param [String] file # @return [[Array, message]] # TODO: return a more informative response? def self.batch_preview(file) begin bibliography = BibTeX::Bibliography.parse(file.read.force_encoding('UTF-8'), filter: :latex) sources = [] bibliography.each do |record| a = Source::Bibtex.new_from_bibtex(record) sources.push(a) end return sources, nil rescue BibTeX::ParseError => e return [], e. rescue raise end end # @return [String] # A string that represents the authors last_names and year (no suffix) def return 'not yet calculated' if new_record? [, year].compact.join(', ') end # @param [String] file # @return [Array, Boolean] def self.batch_create(file) sources = [] valid = 0 begin # error_msg = [] Source.transaction do bibliography = BibTeX::Bibliography.parse(file.read.force_encoding('UTF-8'), filter: :latex) bibliography.each do |record| a = Source::Bibtex.new_from_bibtex(record) if a.valid? if a.save valid += 1 end else # error_msg = a.errors.messages.to_s end sources.push(a) end end rescue return false end return {records: sources, count: valid} end # @param used_on [String] a model name # @return [Scope] # the max 10 most recently used (1 week, could parameterize) TaxonName, as used def self.used_recently(user_id, project_id, used_on = 'TaxonName') Source.select('sources.id'). joins(:citations) .where(citations: {updated_by_id: user_id, project_id:, citation_object_type: used_on, updated_at: 1.week.ago..}) .order('citations.updated_at DESC') .pluck(:id).uniq end # @params target [String] a citable model name # @return [Hash] sources optimized for user selection def self.select_optimized(user_id, project_id, target = 'TaxonName') r = used_recently(user_id, project_id, target) h = { quick: [], pinboard: Source.pinned_by(user_id).where(pinboard_items: {project_id:}).to_a, recent: [] } if r.empty? h[:recent] = Source.where(created_by_id: user_id, updated_at: 2.hours.ago..Time.now ) .order('created_at DESC') .limit(5).order(:cached).to_a h[:quick] = Source.pinned_by(user_id).pinboard_inserted.where(pinboard_items: {project_id:}).to_a else h[:recent] = (Source.where(created_by_id: user_id, updated_at: 2.hours.ago..Time.now ) .order('created_at DESC') .limit(5).order(:cached).to_a + Source.where('"sources"."id" IN (?)', r.first(6) ).to_a).uniq h[:quick] = ( Source.pinned_by(user_id).pinboard_inserted.where(pinboard_items: {project_id:}).to_a + Source.where('"sources"."id" IN (?)', r.first(4) ).to_a).uniq end h end # @return [Array] # objects this source is linked to through citations def cited_objects self.citations.collect { |t| t.citation_object } end # @return [Boolean] def is_bibtex? type == 'Source::Bibtex' end # @return [Boolean] def is_in_project?(project_id) projects.where(id: project_id).any? end # Month handling allows values from bibtex like 'may' to be handled # @return [Date] def nomenclature_date Utilities::Dates.nomenclature_date(day, Utilities::Dates.month_index(month), year)&.to_date end # @return [Source] def clone s = dup m = "[CLONE of #{id}] " case type when 'Source::Verbatim' s.verbatim = m + verbatim.to_s when 'Source::Bibtex' s.title = m + title.to_s end roles.reload.each do |r| s.roles << Role.new(person: r.person, type: r.type, position: r.position ) end s.year_suffix = nil s.save s end protected # Defined in subclasses # @return [Nil] def set_cached end # Defined in subclasses def get_cached end # @param [Hash] attributed # @return [Boolean] def reject_project_sources(attributed) return true if attributed['project_id'].blank? return true if ProjectSource.where(project_id: attributed['project_id'], source_id: id).any? end def sv_cached_names true # see validation in subclasses end def sv_fix_cached_names begin Source.transaction do self.set_cached end true rescue false end end def sv_stated_year soft_validations.add( :base, "'Stated year' is not needed if identical to 'year'; applying the Fix will delete it", success_message: "'Stated year' was deleted", failure_message: "Failed to delete 'Stated year'") if year.to_s == stated_year.to_s end def sv_fix_stated_year begin Source.transaction do self.stated_year = nil self.save end true rescue false end end def if title.present? str = title.squish.gsub(/\<i>[^<>]*?<\/i>/, '') soft_validations.add(:title, 'The title contains unmatched html tags') if str.include?('<i>') || str.include?('</i>') end end end |
#year ⇒ Integer
Returns see en.wikipedia.org/wiki/BibTeX#Field_types.
188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 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 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 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 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 |
# File 'app/models/source.rb', line 188 class Source < ApplicationRecord include Housekeeping::Users include Housekeeping::Timestamps include Shared::AlternateValues include Shared::DataAttributes include Shared::Documentation include Shared::Identifiers include Shared::Notes include Shared::SharedAcrossProjects include Shared::Tags include Shared::HasPapertrail include SoftValidation include Shared::IsData # !! Must not have Shared::Depictions ignore_whitespace_on(:verbatim_contents) ALTERNATE_VALUES_FOR = [ :address, :annote, :booktitle, :edition, :editor, :institution, :journal, :note, :organization, :publisher, :school, :title, :doi, :abstract, :language, :translator, :author, :url].freeze # @return [Boolean, nil] # When true, cached values are not built attr_accessor :no_year_suffix_validation # Keep this order for citations/topics has_many :citations, inverse_of: :source, dependent: :restrict_with_error has_many :origin_citations, -> {where(citations: {is_original: true})}, class_name: 'Citation', dependent: :restrict_with_error, inverse_of: :source has_many :citation_topics, through: :citations, inverse_of: :source has_many :topics, through: :citation_topics, inverse_of: :sources has_many :project_sources, inverse_of: :source, dependent: :destroy has_many :projects, inverse_of: :sources, through: :project_sources after_save :set_cached validates_presence_of :type validates :type, inclusion: {in: ['Source::Bibtex', 'Source::Human', 'Source::Verbatim']} # TODO: not needed accepts_nested_attributes_for :project_sources, reject_if: :reject_project_sources soft_validate( :sv_cached_names, set: :cached_names, fix: :sv_fix_cached_names, name: 'Cached names', description: 'Check if cached values need to be updated' ) soft_validate( :sv_stated_year, set: :stated_year, fix: :sv_fix_stated_year, name: 'Stated year', description: "'Stated year' is not needed if identical to 'year'" ) soft_validate( :sv_html_tags, set: :html_tags, name: 'html tags', description: 'Check if html has both open and close tags' ) # Redirect type here # @param [String] file # @return [[Array, message]] # TODO: return a more informative response? def self.batch_preview(file) begin bibliography = BibTeX::Bibliography.parse(file.read.force_encoding('UTF-8'), filter: :latex) sources = [] bibliography.each do |record| a = Source::Bibtex.new_from_bibtex(record) sources.push(a) end return sources, nil rescue BibTeX::ParseError => e return [], e. rescue raise end end # @return [String] # A string that represents the authors last_names and year (no suffix) def return 'not yet calculated' if new_record? [, year].compact.join(', ') end # @param [String] file # @return [Array, Boolean] def self.batch_create(file) sources = [] valid = 0 begin # error_msg = [] Source.transaction do bibliography = BibTeX::Bibliography.parse(file.read.force_encoding('UTF-8'), filter: :latex) bibliography.each do |record| a = Source::Bibtex.new_from_bibtex(record) if a.valid? if a.save valid += 1 end else # error_msg = a.errors.messages.to_s end sources.push(a) end end rescue return false end return {records: sources, count: valid} end # @param used_on [String] a model name # @return [Scope] # the max 10 most recently used (1 week, could parameterize) TaxonName, as used def self.used_recently(user_id, project_id, used_on = 'TaxonName') Source.select('sources.id'). joins(:citations) .where(citations: {updated_by_id: user_id, project_id:, citation_object_type: used_on, updated_at: 1.week.ago..}) .order('citations.updated_at DESC') .pluck(:id).uniq end # @params target [String] a citable model name # @return [Hash] sources optimized for user selection def self.select_optimized(user_id, project_id, target = 'TaxonName') r = used_recently(user_id, project_id, target) h = { quick: [], pinboard: Source.pinned_by(user_id).where(pinboard_items: {project_id:}).to_a, recent: [] } if r.empty? h[:recent] = Source.where(created_by_id: user_id, updated_at: 2.hours.ago..Time.now ) .order('created_at DESC') .limit(5).order(:cached).to_a h[:quick] = Source.pinned_by(user_id).pinboard_inserted.where(pinboard_items: {project_id:}).to_a else h[:recent] = (Source.where(created_by_id: user_id, updated_at: 2.hours.ago..Time.now ) .order('created_at DESC') .limit(5).order(:cached).to_a + Source.where('"sources"."id" IN (?)', r.first(6) ).to_a).uniq h[:quick] = ( Source.pinned_by(user_id).pinboard_inserted.where(pinboard_items: {project_id:}).to_a + Source.where('"sources"."id" IN (?)', r.first(4) ).to_a).uniq end h end # @return [Array] # objects this source is linked to through citations def cited_objects self.citations.collect { |t| t.citation_object } end # @return [Boolean] def is_bibtex? type == 'Source::Bibtex' end # @return [Boolean] def is_in_project?(project_id) projects.where(id: project_id).any? end # Month handling allows values from bibtex like 'may' to be handled # @return [Date] def nomenclature_date Utilities::Dates.nomenclature_date(day, Utilities::Dates.month_index(month), year)&.to_date end # @return [Source] def clone s = dup m = "[CLONE of #{id}] " case type when 'Source::Verbatim' s.verbatim = m + verbatim.to_s when 'Source::Bibtex' s.title = m + title.to_s end roles.reload.each do |r| s.roles << Role.new(person: r.person, type: r.type, position: r.position ) end s.year_suffix = nil s.save s end protected # Defined in subclasses # @return [Nil] def set_cached end # Defined in subclasses def get_cached end # @param [Hash] attributed # @return [Boolean] def reject_project_sources(attributed) return true if attributed['project_id'].blank? return true if ProjectSource.where(project_id: attributed['project_id'], source_id: id).any? end def sv_cached_names true # see validation in subclasses end def sv_fix_cached_names begin Source.transaction do self.set_cached end true rescue false end end def sv_stated_year soft_validations.add( :base, "'Stated year' is not needed if identical to 'year'; applying the Fix will delete it", success_message: "'Stated year' was deleted", failure_message: "Failed to delete 'Stated year'") if year.to_s == stated_year.to_s end def sv_fix_stated_year begin Source.transaction do self.stated_year = nil self.save end true rescue false end end def if title.present? str = title.squish.gsub(/\<i>[^<>]*?<\/i>/, '') soft_validations.add(:title, 'The title contains unmatched html tags') if str.include?('<i>') || str.include?('</i>') end end end |
#year_suffix ⇒ String
Returns Arbitrary user-provided suffix to the year. Use is highly discouraged.
188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 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 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 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 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 |
# File 'app/models/source.rb', line 188 class Source < ApplicationRecord include Housekeeping::Users include Housekeeping::Timestamps include Shared::AlternateValues include Shared::DataAttributes include Shared::Documentation include Shared::Identifiers include Shared::Notes include Shared::SharedAcrossProjects include Shared::Tags include Shared::HasPapertrail include SoftValidation include Shared::IsData # !! Must not have Shared::Depictions ignore_whitespace_on(:verbatim_contents) ALTERNATE_VALUES_FOR = [ :address, :annote, :booktitle, :edition, :editor, :institution, :journal, :note, :organization, :publisher, :school, :title, :doi, :abstract, :language, :translator, :author, :url].freeze # @return [Boolean, nil] # When true, cached values are not built attr_accessor :no_year_suffix_validation # Keep this order for citations/topics has_many :citations, inverse_of: :source, dependent: :restrict_with_error has_many :origin_citations, -> {where(citations: {is_original: true})}, class_name: 'Citation', dependent: :restrict_with_error, inverse_of: :source has_many :citation_topics, through: :citations, inverse_of: :source has_many :topics, through: :citation_topics, inverse_of: :sources has_many :project_sources, inverse_of: :source, dependent: :destroy has_many :projects, inverse_of: :sources, through: :project_sources after_save :set_cached validates_presence_of :type validates :type, inclusion: {in: ['Source::Bibtex', 'Source::Human', 'Source::Verbatim']} # TODO: not needed accepts_nested_attributes_for :project_sources, reject_if: :reject_project_sources soft_validate( :sv_cached_names, set: :cached_names, fix: :sv_fix_cached_names, name: 'Cached names', description: 'Check if cached values need to be updated' ) soft_validate( :sv_stated_year, set: :stated_year, fix: :sv_fix_stated_year, name: 'Stated year', description: "'Stated year' is not needed if identical to 'year'" ) soft_validate( :sv_html_tags, set: :html_tags, name: 'html tags', description: 'Check if html has both open and close tags' ) # Redirect type here # @param [String] file # @return [[Array, message]] # TODO: return a more informative response? def self.batch_preview(file) begin bibliography = BibTeX::Bibliography.parse(file.read.force_encoding('UTF-8'), filter: :latex) sources = [] bibliography.each do |record| a = Source::Bibtex.new_from_bibtex(record) sources.push(a) end return sources, nil rescue BibTeX::ParseError => e return [], e. rescue raise end end # @return [String] # A string that represents the authors last_names and year (no suffix) def return 'not yet calculated' if new_record? [, year].compact.join(', ') end # @param [String] file # @return [Array, Boolean] def self.batch_create(file) sources = [] valid = 0 begin # error_msg = [] Source.transaction do bibliography = BibTeX::Bibliography.parse(file.read.force_encoding('UTF-8'), filter: :latex) bibliography.each do |record| a = Source::Bibtex.new_from_bibtex(record) if a.valid? if a.save valid += 1 end else # error_msg = a.errors.messages.to_s end sources.push(a) end end rescue return false end return {records: sources, count: valid} end # @param used_on [String] a model name # @return [Scope] # the max 10 most recently used (1 week, could parameterize) TaxonName, as used def self.used_recently(user_id, project_id, used_on = 'TaxonName') Source.select('sources.id'). joins(:citations) .where(citations: {updated_by_id: user_id, project_id:, citation_object_type: used_on, updated_at: 1.week.ago..}) .order('citations.updated_at DESC') .pluck(:id).uniq end # @params target [String] a citable model name # @return [Hash] sources optimized for user selection def self.select_optimized(user_id, project_id, target = 'TaxonName') r = used_recently(user_id, project_id, target) h = { quick: [], pinboard: Source.pinned_by(user_id).where(pinboard_items: {project_id:}).to_a, recent: [] } if r.empty? h[:recent] = Source.where(created_by_id: user_id, updated_at: 2.hours.ago..Time.now ) .order('created_at DESC') .limit(5).order(:cached).to_a h[:quick] = Source.pinned_by(user_id).pinboard_inserted.where(pinboard_items: {project_id:}).to_a else h[:recent] = (Source.where(created_by_id: user_id, updated_at: 2.hours.ago..Time.now ) .order('created_at DESC') .limit(5).order(:cached).to_a + Source.where('"sources"."id" IN (?)', r.first(6) ).to_a).uniq h[:quick] = ( Source.pinned_by(user_id).pinboard_inserted.where(pinboard_items: {project_id:}).to_a + Source.where('"sources"."id" IN (?)', r.first(4) ).to_a).uniq end h end # @return [Array] # objects this source is linked to through citations def cited_objects self.citations.collect { |t| t.citation_object } end # @return [Boolean] def is_bibtex? type == 'Source::Bibtex' end # @return [Boolean] def is_in_project?(project_id) projects.where(id: project_id).any? end # Month handling allows values from bibtex like 'may' to be handled # @return [Date] def nomenclature_date Utilities::Dates.nomenclature_date(day, Utilities::Dates.month_index(month), year)&.to_date end # @return [Source] def clone s = dup m = "[CLONE of #{id}] " case type when 'Source::Verbatim' s.verbatim = m + verbatim.to_s when 'Source::Bibtex' s.title = m + title.to_s end roles.reload.each do |r| s.roles << Role.new(person: r.person, type: r.type, position: r.position ) end s.year_suffix = nil s.save s end protected # Defined in subclasses # @return [Nil] def set_cached end # Defined in subclasses def get_cached end # @param [Hash] attributed # @return [Boolean] def reject_project_sources(attributed) return true if attributed['project_id'].blank? return true if ProjectSource.where(project_id: attributed['project_id'], source_id: id).any? end def sv_cached_names true # see validation in subclasses end def sv_fix_cached_names begin Source.transaction do self.set_cached end true rescue false end end def sv_stated_year soft_validations.add( :base, "'Stated year' is not needed if identical to 'year'; applying the Fix will delete it", success_message: "'Stated year' was deleted", failure_message: "Failed to delete 'Stated year'") if year.to_s == stated_year.to_s end def sv_fix_stated_year begin Source.transaction do self.stated_year = nil self.save end true rescue false end end def if title.present? str = title.squish.gsub(/\<i>[^<>]*?<\/i>/, '') soft_validations.add(:title, 'The title contains unmatched html tags') if str.include?('<i>') || str.include?('</i>') end end end |
Class Method Details
.batch_create(file) ⇒ Array, Boolean
278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 |
# File 'app/models/source.rb', line 278 def self.batch_create(file) sources = [] valid = 0 begin # error_msg = [] Source.transaction do bibliography = BibTeX::Bibliography.parse(file.read.force_encoding('UTF-8'), filter: :latex) bibliography.each do |record| a = Source::Bibtex.new_from_bibtex(record) if a.valid? if a.save valid += 1 end else # error_msg = a.errors.messages.to_s end sources.push(a) end end rescue return false end return {records: sources, count: valid} end |
.batch_preview(file) ⇒ [Array, message]
Returns TODO: return a more informative response?.
253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 |
# File 'app/models/source.rb', line 253 def self.batch_preview(file) begin bibliography = BibTeX::Bibliography.parse(file.read.force_encoding('UTF-8'), filter: :latex) sources = [] bibliography.each do |record| a = Source::Bibtex.new_from_bibtex(record) sources.push(a) end return sources, nil rescue BibTeX::ParseError => e return [], e. rescue raise end end |
.select_optimized(user_id, project_id, target = 'TaxonName') ⇒ Hash
Returns sources optimized for user selection.
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 |
# File 'app/models/source.rb', line 319 def self.select_optimized(user_id, project_id, target = 'TaxonName') r = used_recently(user_id, project_id, target) h = { quick: [], pinboard: Source.pinned_by(user_id).where(pinboard_items: {project_id:}).to_a, recent: [] } if r.empty? h[:recent] = Source.where(created_by_id: user_id, updated_at: 2.hours.ago..Time.now ) .order('created_at DESC') .limit(5).order(:cached).to_a h[:quick] = Source.pinned_by(user_id).pinboard_inserted.where(pinboard_items: {project_id:}).to_a else h[:recent] = (Source.where(created_by_id: user_id, updated_at: 2.hours.ago..Time.now ) .order('created_at DESC') .limit(5).order(:cached).to_a + Source.where('"sources"."id" IN (?)', r.first(6) ).to_a).uniq h[:quick] = ( Source.pinned_by(user_id).pinboard_inserted.where(pinboard_items: {project_id:}).to_a + Source.where('"sources"."id" IN (?)', r.first(4) ).to_a).uniq end h end |
.used_recently(user_id, project_id, used_on = 'TaxonName') ⇒ Scope
Returns the max 10 most recently used (1 week, could parameterize) TaxonName, as used.
306 307 308 309 310 311 312 313 314 315 |
# File 'app/models/source.rb', line 306 def self.used_recently(user_id, project_id, used_on = 'TaxonName') Source.select('sources.id'). joins(:citations) .where(citations: {updated_by_id: user_id, project_id:, citation_object_type: used_on, updated_at: 1.week.ago..}) .order('citations.updated_at DESC') .pluck(:id).uniq end |
Instance Method Details
#author_year ⇒ String
Returns A string that represents the authors last_names and year (no suffix).
271 272 273 274 |
# File 'app/models/source.rb', line 271 def return 'not yet calculated' if new_record? [, year].compact.join(', ') end |
#cited_objects ⇒ Array
Returns objects this source is linked to through citations.
347 348 349 |
# File 'app/models/source.rb', line 347 def cited_objects self.citations.collect { |t| t.citation_object } end |
#clone ⇒ Source
368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 |
# File 'app/models/source.rb', line 368 def clone s = dup m = "[CLONE of #{id}] " case type when 'Source::Verbatim' s.verbatim = m + verbatim.to_s when 'Source::Bibtex' s.title = m + title.to_s end roles.reload.each do |r| s.roles << Role.new(person: r.person, type: r.type, position: r.position ) end s.year_suffix = nil s.save s end |
#get_cached ⇒ Object (protected)
Defined in subclasses
397 398 |
# File 'app/models/source.rb', line 397 def get_cached end |
#is_bibtex? ⇒ Boolean
352 353 354 |
# File 'app/models/source.rb', line 352 def is_bibtex? type == 'Source::Bibtex' end |
#is_in_project?(project_id) ⇒ Boolean
357 358 359 |
# File 'app/models/source.rb', line 357 def is_in_project?(project_id) projects.where(id: project_id).any? end |
#nomenclature_date ⇒ Date
Month handling allows values from bibtex like ‘may’ to be handled
363 364 365 |
# File 'app/models/source.rb', line 363 def nomenclature_date Utilities::Dates.nomenclature_date(day, Utilities::Dates.month_index(month), year)&.to_date end |
#reject_project_sources(attributed) ⇒ Boolean (protected)
402 403 404 405 |
# File 'app/models/source.rb', line 402 def reject_project_sources(attributed) return true if attributed['project_id'].blank? return true if ProjectSource.where(project_id: attributed['project_id'], source_id: id).any? end |
#set_cached ⇒ Nil (protected)
Defined in subclasses
393 394 |
# File 'app/models/source.rb', line 393 def set_cached end |
#sv_cached_names ⇒ Object (protected)
407 408 409 |
# File 'app/models/source.rb', line 407 def sv_cached_names true # see validation in subclasses end |
#sv_fix_cached_names ⇒ Object (protected)
411 412 413 414 415 416 417 418 419 420 |
# File 'app/models/source.rb', line 411 def sv_fix_cached_names begin Source.transaction do self.set_cached end true rescue false end end |
#sv_fix_stated_year ⇒ Object (protected)
429 430 431 432 433 434 435 436 437 438 439 |
# File 'app/models/source.rb', line 429 def sv_fix_stated_year begin Source.transaction do self.stated_year = nil self.save end true rescue false end end |
#sv_html_tags ⇒ Object (protected)
442 443 444 445 446 447 |
# File 'app/models/source.rb', line 442 def if title.present? str = title.squish.gsub(/\<i>[^<>]*?<\/i>/, '') soft_validations.add(:title, 'The title contains unmatched html tags') if str.include?('<i>') || str.include?('</i>') end end |
#sv_stated_year ⇒ Object (protected)
422 423 424 425 426 427 |
# File 'app/models/source.rb', line 422 def sv_stated_year soft_validations.add( :base, "'Stated year' is not needed if identical to 'year'; applying the Fix will delete it", success_message: "'Stated year' was deleted", failure_message: "Failed to delete 'Stated year'") if year.to_s == stated_year.to_s end |