sufia-models 5.0.0.beta1 → 5.0.0.rc1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (58) hide show
  1. checksums.yaml +4 -4
  2. data/app/actors/sufia/generic_file/actor.rb +10 -7
  3. data/app/jobs/active_fedora_pid_based_job.rb +3 -2
  4. data/app/jobs/audit_job.rb +31 -28
  5. data/app/jobs/batch_update_job.rb +8 -9
  6. data/app/jobs/import_url_job.rb +2 -2
  7. data/app/models/batch.rb +12 -11
  8. data/app/models/checksum_audit_log.rb +8 -7
  9. data/app/models/concerns/sufia/ability.rb +6 -4
  10. data/app/models/concerns/sufia/collection.rb +5 -4
  11. data/app/models/concerns/sufia/file_stat_utils.rb +3 -3
  12. data/app/models/concerns/sufia/generic_file.rb +14 -16
  13. data/app/models/concerns/sufia/generic_file/audit.rb +31 -50
  14. data/app/models/concerns/sufia/generic_file/characterization.rb +3 -3
  15. data/app/models/concerns/sufia/generic_file/derivatives.rb +5 -5
  16. data/app/models/concerns/sufia/generic_file/full_text_indexing.rb +2 -2
  17. data/app/models/concerns/sufia/generic_file/metadata.rb +11 -82
  18. data/app/models/concerns/sufia/generic_file/proxy_deposit.rb +3 -12
  19. data/app/models/concerns/sufia/generic_file/reload_on_save.rb +18 -0
  20. data/app/models/concerns/sufia/generic_file/versions.rb +4 -1
  21. data/app/models/concerns/sufia/generic_file/web_form.rb +6 -13
  22. data/app/models/concerns/sufia/model_methods.rb +9 -11
  23. data/app/models/concerns/sufia/properties_datastream_behavior.rb +32 -0
  24. data/app/models/concerns/sufia/user.rb +33 -11
  25. data/app/models/concerns/sufia/user_usage_stats.rb +15 -0
  26. data/app/models/datastreams/batch_rdf_datastream.rb +6 -0
  27. data/app/models/datastreams/file_content_datastream.rb +1 -1
  28. data/app/models/datastreams/fits_datastream.rb +1 -1
  29. data/app/models/datastreams/generic_file_rdf_datastream.rb +69 -0
  30. data/app/models/datastreams/paranoid_rights_datastream.rb +22 -0
  31. data/app/models/datastreams/properties_datastream.rb +4 -0
  32. data/app/models/file_download_stat.rb +2 -2
  33. data/app/models/file_usage.rb +9 -5
  34. data/app/models/file_view_stat.rb +2 -2
  35. data/app/models/local_authority.rb +2 -2
  36. data/app/models/proxy_deposit_request.rb +1 -1
  37. data/app/models/sufia/orcid_validator.rb +8 -0
  38. data/app/models/user_stat.rb +2 -0
  39. data/app/services/sufia/id_service.rb +5 -5
  40. data/app/services/sufia/noid.rb +7 -10
  41. data/lib/generators/sufia/models/abstract_migration_generator.rb +30 -0
  42. data/lib/generators/sufia/models/cached_stats_generator.rb +2 -31
  43. data/lib/generators/sufia/models/install_generator.rb +11 -31
  44. data/lib/generators/sufia/models/orcid_field_generator.rb +19 -0
  45. data/lib/generators/sufia/models/proxies_generator.rb +2 -31
  46. data/lib/generators/sufia/models/templates/config/sufia.rb +3 -10
  47. data/lib/generators/sufia/models/templates/migrations/add_orcid_to_users.rb +5 -0
  48. data/lib/generators/sufia/models/templates/migrations/create_user_stats.rb +19 -0
  49. data/lib/generators/sufia/models/upgrade400_generator.rb +2 -33
  50. data/lib/generators/sufia/models/user_stats_generator.rb +31 -0
  51. data/lib/sufia/models/engine.rb +4 -13
  52. data/lib/sufia/models/file_content/versions.rb +8 -12
  53. data/lib/sufia/models/stats/user_stat_importer.rb +89 -0
  54. data/lib/sufia/models/version.rb +1 -1
  55. data/lib/sufia/permissions/writable.rb +16 -34
  56. data/lib/tasks/stats_tasks.rake +12 -0
  57. data/sufia-models.gemspec +2 -4
  58. metadata +78 -90
@@ -0,0 +1,6 @@
1
+ class BatchRdfDatastream < ActiveFedora::NtriplesRDFDatastream
2
+ property :part, predicate: RDF::DC.hasPart
3
+ property :creator, predicate: RDF::DC.creator
4
+ property :title, predicate: RDF::DC.title
5
+ property :status, predicate: RDF::DC.type
6
+ end
@@ -1,4 +1,4 @@
1
- class FileContentDatastream < ActiveFedora::File
1
+ class FileContentDatastream < ActiveFedora::Datastream
2
2
  include Hydra::Derivatives::ExtractMetadata
3
3
  include Sufia::FileContent::Versions
4
4
  end
@@ -1,7 +1,7 @@
1
1
  class FitsDatastream < ActiveFedora::OmDatastream
2
2
  include OM::XML::Document
3
3
 
4
- def prefix(_)
4
+ def prefix
5
5
  ""
6
6
  end
7
7
 
@@ -0,0 +1,69 @@
1
+ class GenericFileRdfDatastream < ActiveFedora::NtriplesRDFDatastream
2
+ property :part_of, predicate: RDF::DC.isPartOf
3
+ property :resource_type, predicate: RDF::DC.type do |index|
4
+ index.as :stored_searchable, :facetable
5
+ end
6
+ property :title, predicate: RDF::DC.title do |index|
7
+ index.as :stored_searchable, :facetable
8
+ end
9
+ property :creator, predicate: RDF::DC.creator do |index|
10
+ index.as :stored_searchable, :facetable
11
+ end
12
+ property :contributor, predicate: RDF::DC.contributor do |index|
13
+ index.as :stored_searchable, :facetable
14
+ end
15
+ property :description, predicate: RDF::DC.description do |index|
16
+ index.type :text
17
+ index.as :stored_searchable
18
+ end
19
+ property :tag, predicate: RDF::DC.relation do |index|
20
+ index.as :stored_searchable, :facetable
21
+ end
22
+ property :rights, predicate: RDF::DC.rights do |index|
23
+ index.as :stored_searchable
24
+ end
25
+ property :publisher, predicate: RDF::DC.publisher do |index|
26
+ index.as :stored_searchable, :facetable
27
+ end
28
+ property :date_created, predicate: RDF::DC.created do |index|
29
+ index.as :stored_searchable
30
+ end
31
+ property :date_uploaded, predicate: RDF::DC.dateSubmitted do |index|
32
+ index.type :date
33
+ index.as :stored_sortable
34
+ end
35
+ property :date_modified, predicate: RDF::DC.modified do |index|
36
+ index.type :date
37
+ index.as :stored_sortable
38
+ end
39
+ property :subject, predicate: RDF::DC.subject do |index|
40
+ index.as :stored_searchable, :facetable
41
+ end
42
+ property :language, predicate: RDF::DC.language do |index|
43
+ index.as :stored_searchable, :facetable
44
+ end
45
+ property :identifier, predicate: RDF::DC.identifier do |index|
46
+ index.as :stored_searchable
47
+ end
48
+ property :based_near, predicate: RDF::FOAF.based_near do |index|
49
+ index.as :stored_searchable, :facetable
50
+ end
51
+ property :related_url, predicate: RDF::RDFS.seeAlso do |index|
52
+ index.as :stored_searchable
53
+ end
54
+ property :bibliographic_citation, predicate: RDF::DC.bibliographicCitation do |index|
55
+ index.as :stored_searchable
56
+ end
57
+ property :source, predicate: RDF::DC.source do |index|
58
+ index.as :stored_searchable
59
+ end
60
+
61
+ # TODO: Move this somewhere more appropriate
62
+ begin
63
+ LocalAuthority.register_vocabulary(self, "subject", "lc_subjects")
64
+ LocalAuthority.register_vocabulary(self, "language", "lexvo_languages")
65
+ LocalAuthority.register_vocabulary(self, "tag", "lc_genres")
66
+ rescue
67
+ puts "tables for vocabularies missing"
68
+ end
69
+ end
@@ -0,0 +1,22 @@
1
+ # subclass built-in Hydra RightsDatastream and build in extra model-level validation
2
+ class ParanoidRightsDatastream < Hydra::Datastream::RightsMetadata
3
+ use_terminology Hydra::Datastream::RightsMetadata
4
+
5
+ VALIDATIONS = [
6
+ {key: :edit_users, message: 'Depositor must have edit access', condition: lambda { |obj| !obj.edit_users.include?(obj.depositor) }},
7
+ {key: :edit_groups, message: 'Public cannot have edit access', condition: lambda { |obj| obj.edit_groups.include?('public') }},
8
+ {key: :edit_groups, message: 'Registered cannot have edit access', condition: lambda { |obj| obj.edit_groups.include?('registered') }}
9
+ ]
10
+
11
+ def validate(object)
12
+ valid = true
13
+ VALIDATIONS.each do |validation|
14
+ if validation[:condition].call(object)
15
+ object.errors[validation[:key]] ||= []
16
+ object.errors[validation[:key]] << validation[:message]
17
+ valid = false
18
+ end
19
+ end
20
+ return valid
21
+ end
22
+ end
@@ -0,0 +1,4 @@
1
+ # properties datastream: catch-all for info that didn't have another home. Particularly depositor.
2
+ class PropertiesDatastream < ActiveFedora::OmDatastream
3
+ include Sufia::PropertiesDatastreamBehavior
4
+ end
@@ -5,8 +5,8 @@ class FileDownloadStat < ActiveRecord::Base
5
5
  [ self.class.convert_date(date), downloads ]
6
6
  end
7
7
 
8
- def self.statistics file_id, start_date
9
- combined_stats file_id, start_date, :downloads, :totalEvents
8
+ def self.statistics file_id, start_date, user_id=nil
9
+ combined_stats file_id, start_date, :downloads, :totalEvents, user_id
10
10
  end
11
11
 
12
12
  # Sufia::Download is sent to Sufia::Analytics.profile as #sufia__download
@@ -3,13 +3,17 @@ class FileUsage
3
3
  attr_accessor :id, :created, :path, :downloads, :pageviews
4
4
 
5
5
  def initialize id
6
+ file = ::GenericFile.find(id)
7
+ user = User.where(email: file.depositor).first
8
+ user_id = user ? user.id : nil
9
+
6
10
  self.id = id
7
11
  self.path = Sufia::Engine.routes.url_helpers.generic_file_path(Sufia::Noid.noidify(id))
8
12
  earliest = Sufia.config.analytic_start_date
9
- self.created = ::GenericFile.find(id).create_date
13
+ self.created = DateTime.parse(file.create_date)
10
14
  self.created = earliest > created ? earliest : created unless earliest.blank?
11
- self.downloads = FileDownloadStat.to_flots FileDownloadStat.statistics(id, created)
12
- self.pageviews = FileViewStat.to_flots FileViewStat.statistics(id, created)
15
+ self.downloads = FileDownloadStat.to_flots FileDownloadStat.statistics(id, created, user_id)
16
+ self.pageviews = FileViewStat.to_flots FileViewStat.statistics(id, created, user_id)
13
17
  end
14
18
 
15
19
  def total_downloads
@@ -19,8 +23,8 @@ class FileUsage
19
23
  def total_pageviews
20
24
  self.pageviews.reduce(0) { |total, result| total + result[1].to_i }
21
25
  end
22
-
23
- # Package data for visualization using JQuery Flot
26
+
27
+ # Package data for visualization using JQuery Flot
24
28
  def to_flot
25
29
  [
26
30
  { label: "Pageviews", data: pageviews },
@@ -5,8 +5,8 @@ class FileViewStat < ActiveRecord::Base
5
5
  [ self.class.convert_date(date), views ]
6
6
  end
7
7
 
8
- def self.statistics file_id, start_date
9
- combined_stats file_id, start_date, :views, :pageviews
8
+ def self.statistics file_id, start_date, user_id=nil
9
+ combined_stats file_id, start_date, :views, :pageviews, user_id
10
10
  end
11
11
 
12
12
  # Sufia::Download is sent to Sufia::Analytics.profile as #sufia__download
@@ -9,10 +9,10 @@ class LocalAuthority < ActiveRecord::Base
9
9
  return unless self.where(name: name).empty?
10
10
  authority = self.create(name: name)
11
11
  format = opts.fetch(:format, :ntriples)
12
- predicate = opts.fetch(:predicate, ::RDF::SKOS.prefLabel)
12
+ predicate = opts.fetch(:predicate, RDF::SKOS.prefLabel)
13
13
  entries = []
14
14
  sources.each do |uri|
15
- ::RDF::Reader.open(uri, format: format) do |reader|
15
+ RDF::Reader.open(uri, format: format) do |reader|
16
16
  reader.each_statement do |statement|
17
17
  if statement.predicate == predicate
18
18
  entries << LocalAuthorityEntry.new(local_authority: authority,
@@ -78,7 +78,7 @@ class ProxyDepositRequest < ActiveRecord::Base
78
78
 
79
79
  def title
80
80
  return 'file not found' if deleted_file?
81
- query = ActiveFedora::SolrQueryBuilder.construct_query_for_ids([pid])
81
+ query = ActiveFedora::SolrService.construct_query_for_pids([pid])
82
82
  solr_response = ActiveFedora::SolrService.query(query, raw: true)
83
83
  SolrDocument.new(solr_response['response']['docs'].first, solr_response).title
84
84
  end
@@ -0,0 +1,8 @@
1
+ module Sufia
2
+ class OrcidValidator < ActiveModel::Validator
3
+ def validate(record)
4
+ return if record.orcid.blank?
5
+ record.errors.add(:orcid, 'must be a string of 19 characters, e.g., "0000-0000-0000-0000"') unless /\d{4}-\d{4}-\d{4}-\d{4}/.match(record.orcid)
6
+ end
7
+ end
8
+ end
@@ -0,0 +1,2 @@
1
+ class UserStat < ActiveRecord::Base
2
+ end
@@ -9,11 +9,11 @@ module Sufia
9
9
 
10
10
  @minter = ::Noid::Minter.new(template: noid_template)
11
11
  @pid = $$
12
- #@namespace = Sufia.config.id_namespace
12
+ @namespace = Sufia.config.id_namespace
13
13
  @semaphore = Mutex.new
14
14
  def self.valid?(identifier)
15
15
  # remove the fedora namespace since it's not part of the noid
16
- noid = identifier#.split(":").last
16
+ noid = identifier.split(":").last
17
17
  return @minter.valid? noid
18
18
  end
19
19
  def self.mint
@@ -28,20 +28,20 @@ module Sufia
28
28
  protected
29
29
 
30
30
  def self.next_id
31
- id = ''
31
+ pid = ''
32
32
  File.open(Sufia.config.minter_statefile, File::RDWR|File::CREAT, 0644) do |f|
33
33
  f.flock(File::LOCK_EX)
34
34
  yaml = YAML::load(f.read)
35
35
  yaml = {template: noid_template} unless yaml
36
36
  minter = ::Noid::Minter.new(yaml)
37
- id = minter.mint
37
+ pid = "#{@namespace}:#{minter.mint}"
38
38
  f.rewind
39
39
  yaml = YAML::dump(minter.dump)
40
40
  f.write yaml
41
41
  f.flush
42
42
  f.truncate(f.pos)
43
43
  end
44
- id
44
+ return pid
45
45
  end
46
46
  end
47
47
  end
@@ -2,14 +2,16 @@ module Sufia
2
2
  module Noid
3
3
  extend ActiveSupport::Concern
4
4
 
5
- ## This overrides the default behavior, which is to ask Fedora for a pid
6
- # @see ActiveFedora::Sharding.assign_pid
7
- def assign_pid
8
- Sufia::IdService.mint
5
+ module ClassMethods
6
+ ## This overrides the default behavior, which is to ask Fedora for a pid
7
+ # @see ActiveFedora::Sharding.assign_pid
8
+ def assign_pid(_)
9
+ Sufia::IdService.mint
10
+ end
9
11
  end
10
12
 
11
13
  def noid
12
- Noid.noidify(id)
14
+ Noid.noidify(self.pid)
13
15
  end
14
16
 
15
17
  # Redefine this for more intuitive keys in Redis
@@ -22,11 +24,6 @@ module Sufia
22
24
  String(identifier).split(":").last
23
25
  end
24
26
 
25
- # Create a pairtree like path for the given identifier
26
- def treeify(identifier)
27
- (identifier.scan(/..?/).first(4) + [identifier]).join('/')
28
- end
29
-
30
27
  def namespaceize(identifier)
31
28
  return identifier if identifier.include?(':')
32
29
  "#{namespace}:#{identifier}"
@@ -0,0 +1,30 @@
1
+ # -*- encoding : utf-8 -*-
2
+ require 'rails/generators'
3
+ require 'rails/generators/migration'
4
+
5
+ class Sufia::Models::AbstractMigrationGenerator < Rails::Generators::Base
6
+ include Rails::Generators::Migration
7
+
8
+ # Implement the required interface for Rails::Generators::Migration.
9
+ # taken from http://github.com/rails/rails/blob/master/activerecord/lib/generators/active_record.rb
10
+ def self.next_migration_number(path)
11
+ if @prev_migration_nr
12
+ @prev_migration_nr += 1
13
+ else
14
+ if last_migration = Dir[File.join(path, '*.rb')].sort.last
15
+ @prev_migration_nr = last_migration.sub(File.join(path, '/'), '').to_i + 1
16
+ else
17
+ @prev_migration_nr = Time.now.utc.strftime("%Y%m%d%H%M%S").to_i
18
+ end
19
+ end
20
+ @prev_migration_nr.to_s
21
+ end
22
+
23
+ protected
24
+
25
+ def better_migration_template(file)
26
+ migration_template "migrations/#{file}", "db/migrate/#{file}"
27
+ rescue Rails::Generators::Error => e
28
+ say_status("warning", e.message, :yellow)
29
+ end
30
+ end
@@ -1,30 +1,12 @@
1
- # -*- encoding : utf-8 -*-
2
- require 'rails/generators'
3
- require 'rails/generators/migration'
4
-
5
- class Sufia::Models::CachedStatsGenerator < Rails::Generators::Base
6
- include Rails::Generators::Migration
1
+ require_relative 'abstract_migration_generator'
7
2
 
3
+ class Sufia::Models::CachedStatsGenerator < Sufia::Models::AbstractMigrationGenerator
8
4
  source_root File.expand_path('../templates', __FILE__)
9
5
 
10
6
  desc """
11
7
  This generator adds the ability to cache usage stats to your application:
12
8
  1. Creates several database migrations if they do not exist in /db/migrate
13
9
  """
14
- # Implement the required interface for Rails::Generators::Migration.
15
- # taken from http://github.com/rails/rails/blob/master/activerecord/lib/generators/active_record.rb
16
- def self.next_migration_number(path)
17
- if @prev_migration_nr
18
- @prev_migration_nr += 1
19
- else
20
- if last_migration = Dir[File.join(path, '*.rb')].sort.last
21
- @prev_migration_nr = last_migration.sub(File.join(path, '/'), '').to_i + 1
22
- else
23
- @prev_migration_nr = Time.now.utc.strftime("%Y%m%d%H%M%S").to_i
24
- end
25
- end
26
- @prev_migration_nr.to_s
27
- end
28
10
 
29
11
  def banner
30
12
  say_status("warning", "ADDING STATS CACHING-RELATED SUFIA MODELS", :yellow)
@@ -32,7 +14,6 @@ This generator adds the ability to cache usage stats to your application:
32
14
 
33
15
  # Setup the database migrations
34
16
  def copy_migrations
35
- # Can't get this any more DRY, because we need this order.
36
17
  [
37
18
  'create_file_view_stats.rb',
38
19
  'create_file_download_stats.rb'
@@ -40,14 +21,4 @@ This generator adds the ability to cache usage stats to your application:
40
21
  better_migration_template file
41
22
  end
42
23
  end
43
-
44
- private
45
-
46
- def better_migration_template(file)
47
- begin
48
- migration_template "migrations/#{file}", "db/migrate/#{file}"
49
- rescue Rails::Generators::Error => e
50
- say_status("warning", e.message, :yellow)
51
- end
52
- end
53
24
  end
@@ -1,12 +1,7 @@
1
- # -*- encoding : utf-8 -*-
2
- require 'rails/generators'
3
- require 'rails/generators/migration'
4
-
5
- class Sufia::Models::InstallGenerator < Rails::Generators::Base
6
- include Rails::Generators::Migration
1
+ require_relative 'abstract_migration_generator'
7
2
 
3
+ class Sufia::Models::InstallGenerator < Sufia::Models::AbstractMigrationGenerator
8
4
  source_root File.expand_path('../templates', __FILE__)
9
-
10
5
  argument :model_name, type: :string , default: "user"
11
6
  desc """
12
7
  This generator makes the following changes to your application:
@@ -19,30 +14,15 @@ This generator makes the following changes to your application:
19
14
  7. Runs full-text generator
20
15
  8. Runs proxies generator
21
16
  9. Runs cached stats generator
17
+ 10. Runs ORCID field generator
18
+ 11. Runs user stats generator
22
19
  """
23
-
24
- # Implement the required interface for Rails::Generators::Migration.
25
- # taken from http://github.com/rails/rails/blob/master/activerecord/lib/generators/active_record.rb
26
- def self.next_migration_number(path)
27
- if @prev_migration_nr
28
- @prev_migration_nr += 1
29
- else
30
- if last_migration = Dir[File.join(path, '*.rb')].sort.last
31
- @prev_migration_nr = last_migration.sub(File.join(path, '/'), '').to_i + 1
32
- else
33
- @prev_migration_nr = Time.now.utc.strftime("%Y%m%d%H%M%S").to_i
34
- end
35
- end
36
- @prev_migration_nr.to_s
37
- end
38
-
39
20
  def banner
40
21
  say_status("warning", "GENERATING SUFIA MODELS", :yellow)
41
22
  end
42
23
 
43
24
  # Setup the database migrations
44
25
  def copy_migrations
45
- # Can't get this any more DRY, because we need this order.
46
26
  [
47
27
  "acts_as_follower_migration.rb",
48
28
  "add_social_to_users.rb",
@@ -114,13 +94,13 @@ This generator makes the following changes to your application:
114
94
  generate 'sufia:models:cached_stats'
115
95
  end
116
96
 
117
- private
97
+ # Adds orcid field to user model
98
+ def orcid_field
99
+ generate 'sufia:models:orcid_field'
100
+ end
118
101
 
119
- def better_migration_template(file)
120
- begin
121
- migration_template "migrations/#{file}", "db/migrate/#{file}"
122
- rescue Rails::Generators::Error => e
123
- say_status("warning", e.message, :yellow)
124
- end
102
+ # Adds user stats-related migration & methods
103
+ def user_stats
104
+ generate 'sufia:models:user_stats'
125
105
  end
126
106
  end