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.
- checksums.yaml +4 -4
- data/app/actors/sufia/generic_file/actor.rb +10 -7
- data/app/jobs/active_fedora_pid_based_job.rb +3 -2
- data/app/jobs/audit_job.rb +31 -28
- data/app/jobs/batch_update_job.rb +8 -9
- data/app/jobs/import_url_job.rb +2 -2
- data/app/models/batch.rb +12 -11
- data/app/models/checksum_audit_log.rb +8 -7
- data/app/models/concerns/sufia/ability.rb +6 -4
- data/app/models/concerns/sufia/collection.rb +5 -4
- data/app/models/concerns/sufia/file_stat_utils.rb +3 -3
- data/app/models/concerns/sufia/generic_file.rb +14 -16
- data/app/models/concerns/sufia/generic_file/audit.rb +31 -50
- data/app/models/concerns/sufia/generic_file/characterization.rb +3 -3
- data/app/models/concerns/sufia/generic_file/derivatives.rb +5 -5
- data/app/models/concerns/sufia/generic_file/full_text_indexing.rb +2 -2
- data/app/models/concerns/sufia/generic_file/metadata.rb +11 -82
- data/app/models/concerns/sufia/generic_file/proxy_deposit.rb +3 -12
- data/app/models/concerns/sufia/generic_file/reload_on_save.rb +18 -0
- data/app/models/concerns/sufia/generic_file/versions.rb +4 -1
- data/app/models/concerns/sufia/generic_file/web_form.rb +6 -13
- data/app/models/concerns/sufia/model_methods.rb +9 -11
- data/app/models/concerns/sufia/properties_datastream_behavior.rb +32 -0
- data/app/models/concerns/sufia/user.rb +33 -11
- data/app/models/concerns/sufia/user_usage_stats.rb +15 -0
- data/app/models/datastreams/batch_rdf_datastream.rb +6 -0
- data/app/models/datastreams/file_content_datastream.rb +1 -1
- data/app/models/datastreams/fits_datastream.rb +1 -1
- data/app/models/datastreams/generic_file_rdf_datastream.rb +69 -0
- data/app/models/datastreams/paranoid_rights_datastream.rb +22 -0
- data/app/models/datastreams/properties_datastream.rb +4 -0
- data/app/models/file_download_stat.rb +2 -2
- data/app/models/file_usage.rb +9 -5
- data/app/models/file_view_stat.rb +2 -2
- data/app/models/local_authority.rb +2 -2
- data/app/models/proxy_deposit_request.rb +1 -1
- data/app/models/sufia/orcid_validator.rb +8 -0
- data/app/models/user_stat.rb +2 -0
- data/app/services/sufia/id_service.rb +5 -5
- data/app/services/sufia/noid.rb +7 -10
- data/lib/generators/sufia/models/abstract_migration_generator.rb +30 -0
- data/lib/generators/sufia/models/cached_stats_generator.rb +2 -31
- data/lib/generators/sufia/models/install_generator.rb +11 -31
- data/lib/generators/sufia/models/orcid_field_generator.rb +19 -0
- data/lib/generators/sufia/models/proxies_generator.rb +2 -31
- data/lib/generators/sufia/models/templates/config/sufia.rb +3 -10
- data/lib/generators/sufia/models/templates/migrations/add_orcid_to_users.rb +5 -0
- data/lib/generators/sufia/models/templates/migrations/create_user_stats.rb +19 -0
- data/lib/generators/sufia/models/upgrade400_generator.rb +2 -33
- data/lib/generators/sufia/models/user_stats_generator.rb +31 -0
- data/lib/sufia/models/engine.rb +4 -13
- data/lib/sufia/models/file_content/versions.rb +8 -12
- data/lib/sufia/models/stats/user_stat_importer.rb +89 -0
- data/lib/sufia/models/version.rb +1 -1
- data/lib/sufia/permissions/writable.rb +16 -34
- data/lib/tasks/stats_tasks.rake +12 -0
- data/sufia-models.gemspec +2 -4
- metadata +78 -90
@@ -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
|
@@ -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
|
data/app/models/file_usage.rb
CHANGED
@@ -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 =
|
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,
|
12
|
+
predicate = opts.fetch(:predicate, RDF::SKOS.prefLabel)
|
13
13
|
entries = []
|
14
14
|
sources.each do |uri|
|
15
|
-
|
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::
|
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
|
@@ -9,11 +9,11 @@ module Sufia
|
|
9
9
|
|
10
10
|
@minter = ::Noid::Minter.new(template: noid_template)
|
11
11
|
@pid = $$
|
12
|
-
|
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
|
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
|
-
|
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
|
-
|
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
|
-
|
44
|
+
return pid
|
45
45
|
end
|
46
46
|
end
|
47
47
|
end
|
data/app/services/sufia/noid.rb
CHANGED
@@ -2,14 +2,16 @@ module Sufia
|
|
2
2
|
module Noid
|
3
3
|
extend ActiveSupport::Concern
|
4
4
|
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
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(
|
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
|
-
|
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
|
-
|
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
|
-
|
97
|
+
# Adds orcid field to user model
|
98
|
+
def orcid_field
|
99
|
+
generate 'sufia:models:orcid_field'
|
100
|
+
end
|
118
101
|
|
119
|
-
|
120
|
-
|
121
|
-
|
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
|