sufia 4.1.0 → 4.2.0
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/History.md +14 -3
- data/SUFIA_VERSION +1 -1
- data/app/assets/javascripts/sufia/batch_edit.js +50 -39
- data/app/jobs/content_depositor_change_event_job.rb +7 -14
- data/app/views/_head_tag_extras.html.erb +1 -0
- data/app/views/homepage/_featured.html.erb +1 -1
- data/app/views/homepage/_featured_fields.html.erb +1 -1
- data/app/views/homepage/_recent_document.html.erb +1 -1
- data/app/views/homepage/_tagcloud.html.erb +1 -2
- data/app/views/layouts/_head_tag_content.html.erb +11 -3
- data/lib/sufia/version.rb +1 -1
- data/spec/controllers/generic_files_controller_spec.rb +1 -1
- data/spec/jobs/event_jobs_spec.rb +16 -1
- data/spec/models/file_download_stat_spec.rb +86 -0
- data/spec/models/file_usage_spec.rb +41 -21
- data/spec/models/file_view_stat_spec.rb +84 -0
- data/spec/support/features.rb +1 -0
- data/spec/support/statistic_helper.rb +9 -0
- data/sufia-models/app/models/concerns/sufia/file_stat_utils.rb +35 -0
- data/sufia-models/app/models/file_download_stat.rb +18 -0
- data/sufia-models/app/models/file_usage.rb +6 -35
- data/sufia-models/app/models/file_view_stat.rb +18 -0
- data/sufia-models/lib/generators/sufia/models/cached_stats_generator.rb +53 -0
- data/sufia-models/lib/generators/sufia/models/install_generator.rb +8 -4
- data/sufia-models/lib/generators/sufia/models/templates/migrations/create_file_download_stats.rb +12 -0
- data/sufia-models/lib/generators/sufia/models/templates/migrations/create_file_view_stats.rb +12 -0
- data/sufia-models/lib/sufia/models/version.rb +1 -1
- data/sufia-models/sufia-models.gemspec +5 -0
- metadata +18 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: f0becb3e785f413597d7424ec1ed417c1712a740
|
4
|
+
data.tar.gz: a7c8c1607bb853f638cf2be54eaf105feeb80cea
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: afe990f4ee1a1b7cef86f77919fcf79808399553de7627057f34bae0f54f3782f89d4a5b9f07267d57222269f5451918493063b43bbc299a39e8d8b929c29739
|
7
|
+
data.tar.gz: b708364b4863882be9f7376c83a99d7202d1d9b9784844e550ebac24db019f9228b24fbaf4492e7fd884813ea07fac81b975118940a8b733f5d0da6905798a5b
|
data/History.md
CHANGED
@@ -1,10 +1,21 @@
|
|
1
1
|
# History of Sufia releases
|
2
2
|
|
3
|
+
## 4.2.0
|
4
|
+
|
5
|
+
* Caches google analytics data in the database so we do not have to retrieve them each time the page is loaded [Carolyn Cole]
|
6
|
+
* Allows adopters to inject content into the layout's head block, needed by ScholarSphere to add a favicon [Mike Giarlo]
|
7
|
+
* Removes redundant title attributes for featured and recent works, fixes orphaned labels [Michael Tribone]
|
8
|
+
* Pins mini_magick for rubies < 2.1 [Carolyn Cole]
|
9
|
+
* Changes the way we log depositor change events [Mike Giarlo]
|
10
|
+
* Breaks cached stats migrations into dedicated generator [Mike Giarlo]
|
11
|
+
* Fixes bug with proxy setup in the install generator [Mike Giarlo]
|
12
|
+
* Fixes bug in batch editing javascript [Carolyn Cole]
|
13
|
+
|
3
14
|
## 4.1.0
|
4
15
|
|
5
|
-
* Adds proxy deposit, "sticky" proxies, and transfers of ownership (from ScholarSphere) [
|
6
|
-
* Fixes bug with form fields attached to single-valued terms [
|
7
|
-
* Converts specs to use RSpec 3 style [
|
16
|
+
* Adds proxy deposit, "sticky" proxies, and transfers of ownership (from ScholarSphere) [Mike Giarlo]
|
17
|
+
* Fixes bug with form fields attached to single-valued terms [Carolyn Cole]
|
18
|
+
* Converts specs to use RSpec 3 style [Mike Giarlo]
|
8
19
|
|
9
20
|
## 4.0.1
|
10
21
|
|
data/SUFIA_VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
4.
|
1
|
+
4.2.0
|
@@ -20,9 +20,13 @@ function batch_edit_init () {
|
|
20
20
|
var Result = {};
|
21
21
|
while (i--) {
|
22
22
|
var Pair = decodeURIComponent(Data[i]).split("=");
|
23
|
-
var
|
24
|
-
var
|
25
|
-
Result[
|
23
|
+
var key = Pair[0];
|
24
|
+
var val = Pair[1];
|
25
|
+
if (Result[key] != null) {
|
26
|
+
if(!$.isArray(Result[key])) Result[key] = [Result[key]];
|
27
|
+
Result[key].push(val);
|
28
|
+
} else
|
29
|
+
Result[key] = val;
|
26
30
|
}
|
27
31
|
return Result;
|
28
32
|
}
|
@@ -46,54 +50,65 @@ function batch_edit_init () {
|
|
46
50
|
},
|
47
51
|
run: function () {
|
48
52
|
running = true;
|
49
|
-
var self = this
|
50
|
-
orgSuc;
|
53
|
+
var self = this;
|
51
54
|
|
52
55
|
if (requests.length) {
|
53
|
-
oriSuc = requests[0].complete;
|
54
56
|
|
55
57
|
// combine data from multiple requests
|
56
58
|
if (requests.length > 1) {
|
57
|
-
|
58
|
-
form = [requests[0].form]
|
59
|
-
for (var i = requests.length - 1; i > 0; i--) {
|
60
|
-
req = requests.pop();
|
61
|
-
adata = deserialize(req.data.replace(/\+/g, " "));
|
62
|
-
for (key in Object.keys(adata)) {
|
63
|
-
curKey = Object.keys(adata)[key];
|
64
|
-
if (curKey.slice(0, 12) == "generic_file") {
|
65
|
-
data[curKey] = adata[curKey];
|
66
|
-
form.push(req.form);
|
67
|
-
}
|
68
|
-
}
|
69
|
-
}
|
70
|
-
requests[0].data = $.param(data);
|
71
|
-
requests[0].form = form;
|
59
|
+
requests = this.combine_requests(requests);
|
72
60
|
}
|
73
61
|
|
74
|
-
requests
|
75
|
-
if (typeof oriSuc === 'function') oriSuc();
|
76
|
-
if (typeof requests[0].form === 'object') {
|
77
|
-
for (f in form) {
|
78
|
-
form_id = form[f];
|
79
|
-
after_ajax(form_id);
|
80
|
-
}
|
81
|
-
}
|
82
|
-
requests.shift();
|
83
|
-
self.run.apply(self, []);
|
84
|
-
};
|
85
|
-
|
62
|
+
requests = this.setup_request_complete(requests);
|
86
63
|
$.ajax(requests[0]);
|
87
64
|
} else {
|
88
65
|
self.tid = setTimeout(function () {
|
89
66
|
self.run.apply(self, []);
|
90
67
|
}, 500);
|
68
|
+
running = false;
|
91
69
|
}
|
92
|
-
running = false;
|
93
70
|
},
|
94
71
|
stop: function () {
|
95
72
|
requests = [];
|
96
73
|
clearTimeout(this.tid);
|
74
|
+
},
|
75
|
+
setup_request_complete: function (requests) {
|
76
|
+
oriComp = requests[0].complete;
|
77
|
+
|
78
|
+
requests[0].complete = [ function (e) {
|
79
|
+
req = requests.shift();
|
80
|
+
if (typeof req.form === 'object') {
|
81
|
+
for (f in req.form) {
|
82
|
+
form_id = form[f];
|
83
|
+
after_ajax(form_id);
|
84
|
+
}
|
85
|
+
}
|
86
|
+
this.tid = setTimeout(function () {
|
87
|
+
ajaxManager.run.apply(ajaxManager, []);
|
88
|
+
}, 50);
|
89
|
+
return true;
|
90
|
+
}];
|
91
|
+
if (typeof oriComp === 'function') requests[0].complete.push(oriComp);
|
92
|
+
return requests;
|
93
|
+
},
|
94
|
+
combine_requests: function (requests) {
|
95
|
+
var data = deserialize(requests[0].data.replace(/\+/g, " "));
|
96
|
+
form = [requests[0].form]
|
97
|
+
for (var i = requests.length - 1; i > 0; i--) {
|
98
|
+
req = requests.pop();
|
99
|
+
adata = deserialize(req.data.replace(/\+/g, " "));
|
100
|
+
|
101
|
+
for (key in Object.keys(adata)) {
|
102
|
+
curKey = Object.keys(adata)[key];
|
103
|
+
if (curKey.slice(0, 12) == "generic_file") {
|
104
|
+
data[curKey] = adata[curKey];
|
105
|
+
form.push(req.form);
|
106
|
+
}
|
107
|
+
}
|
108
|
+
}
|
109
|
+
requests[0].data = $.param(data);
|
110
|
+
requests[0].form = form;
|
111
|
+
return requests;
|
97
112
|
}
|
98
113
|
};
|
99
114
|
}());
|
@@ -135,11 +150,7 @@ function batch_edit_init () {
|
|
135
150
|
dataType: "json",
|
136
151
|
type: form.attr("method").toUpperCase(),
|
137
152
|
data: form.serialize(),
|
138
|
-
|
139
|
-
eval(e.responseText);
|
140
|
-
after_ajax(form_id);
|
141
|
-
},
|
142
|
-
error: function (e) {
|
153
|
+
complete: function (e) {
|
143
154
|
after_ajax(form_id);
|
144
155
|
if (e.status == 200) {
|
145
156
|
eval(e.responseText);
|
@@ -23,7 +23,7 @@ class ContentDepositorChangeEventJob < EventJob
|
|
23
23
|
file.apply_depositor_metadata(login)
|
24
24
|
file.save!
|
25
25
|
|
26
|
-
action = "User #{link_to_profile file.proxy_depositor} has transferred #{link_to file.title.first, Sufia::Engine.routes.url_helpers.generic_file_path(file.noid)} to
|
26
|
+
action = "User #{link_to_profile file.proxy_depositor} has transferred #{link_to file.title.first, Sufia::Engine.routes.url_helpers.generic_file_path(file.noid)} to user #{link_to_profile login}"
|
27
27
|
timestamp = Time.now.to_i
|
28
28
|
depositor = ::User.find_by_user_key(file.depositor)
|
29
29
|
proxy_depositor = ::User.find_by_user_key(file.proxy_depositor)
|
@@ -31,19 +31,12 @@ class ContentDepositorChangeEventJob < EventJob
|
|
31
31
|
event = proxy_depositor.create_event(action, timestamp)
|
32
32
|
# Log the event to the GF's stream
|
33
33
|
file.log_event(event)
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
#
|
39
|
-
|
40
|
-
end
|
41
|
-
|
42
|
-
def log_depositor_event(event, depositor, gf)
|
43
|
-
# Log the event to the depositor's profile stream
|
44
|
-
depositor.log_profile_event(event)
|
45
|
-
# Fan out the event to all followers who have access
|
46
|
-
depositor.followers.select { |user| user.can? :read, gf }.each do |follower|
|
34
|
+
# log the event to the proxy depositor's profile
|
35
|
+
proxy_depositor.log_profile_event(event)
|
36
|
+
# log the event to the depositor's dashboard
|
37
|
+
depositor.log_event(event)
|
38
|
+
# Fan out the event to the depositor's followers who have access
|
39
|
+
depositor.followers.select { |user| user.can? :read, file }.each do |follower|
|
47
40
|
follower.log_event(event)
|
48
41
|
end
|
49
42
|
end
|
@@ -0,0 +1 @@
|
|
1
|
+
<%# Override this partial in your application to insert extra HEAD content into every page %>
|
@@ -2,7 +2,7 @@
|
|
2
2
|
<li class="featured-item" data-id="<%= solr_document.noid %>">
|
3
3
|
<div class="main row">
|
4
4
|
<div class="col-sm-3">
|
5
|
-
<%= link_to sufia.generic_file_path(solr_document) do %>
|
5
|
+
<%= link_to sufia.generic_file_path(solr_document), "aria-hidden" => true do %>
|
6
6
|
<%= render_thumbnail_tag solr_document, {width: 90} %>
|
7
7
|
<% end %>
|
8
8
|
</div>
|
@@ -1,6 +1,6 @@
|
|
1
1
|
<h3>
|
2
2
|
<span class="sr-only">Title</span>
|
3
|
-
<%= link_to truncate(featured.title_or_label, length: 28, separator: ' '), sufia.generic_file_path(featured)
|
3
|
+
<%= link_to truncate(featured.title_or_label, length: 28, separator: ' '), sufia.generic_file_path(featured) %>
|
4
4
|
</h3>
|
5
5
|
<div>
|
6
6
|
<span class="sr-only">Depositor</span>
|
@@ -9,7 +9,7 @@
|
|
9
9
|
<% end %>
|
10
10
|
<td>
|
11
11
|
<h3>
|
12
|
-
<span class="sr-only">Title</span><%= link_to truncate(recent_document.title_or_label, length: 28, separator: ' '), sufia.generic_file_path(recent_document.noid)
|
12
|
+
<span class="sr-only">Title</span><%= link_to truncate(recent_document.title_or_label, length: 28, separator: ' '), sufia.generic_file_path(recent_document.noid) %>
|
13
13
|
<% if display_access %>
|
14
14
|
<% if recent_document.registered? %>
|
15
15
|
<span class="label label-info" title="<%=t('sufia.institution_name') %>"><%=t('sufia.institution_name') %></span>
|
@@ -1,6 +1,5 @@
|
|
1
1
|
<a role="button" aria-hidden="true" class="btn btn-default tag-toggle-list">List</a>
|
2
|
-
<
|
3
|
-
<div id="tag-cloud" class="btn-group btn-group-justified tag-sort">
|
2
|
+
<div id="tag-cloud" class="btn-group btn-group-justified tag-sort" aria-label="Sort By">
|
4
3
|
<a role="button" class="btn btn-default tag-sort-za">Z-A</a>
|
5
4
|
<a role="button" class="btn btn-default tag-sort-az">A-Z</a>
|
6
5
|
<a role="button" class="btn btn-default tag-sort-numerical">Rank</a>
|
@@ -2,18 +2,26 @@
|
|
2
2
|
<link href='https://fonts.googleapis.com/css?family=Lato:300,400' rel='stylesheet' type='text/css'>
|
3
3
|
<meta charset="utf-8" />
|
4
4
|
|
5
|
-
<!-- added for use on small devices like phones
|
5
|
+
<!-- added for use on small devices like phones -->
|
6
6
|
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
|
7
7
|
|
8
|
-
|
9
|
-
<%= yield :
|
8
|
+
<!-- Twitter card metadata -->
|
9
|
+
<%= yield :twitter_meta %>
|
10
|
+
<!-- Google Scholar metadata -->
|
11
|
+
<%= yield :gscholar_meta %>
|
10
12
|
|
11
13
|
<title><%= h(@page_title || application_name) %></title>
|
14
|
+
|
12
15
|
<!-- application css -->
|
13
16
|
<%= stylesheet_link_tag 'application' %>
|
14
17
|
<%= yield(:css_head) %>
|
15
18
|
|
16
19
|
<!-- application js -->
|
17
20
|
<%= javascript_include_tag 'application' %>
|
21
|
+
<%= yield(:js_head) %>
|
18
22
|
|
23
|
+
<!-- Google Analytics -->
|
19
24
|
<%= render partial: '/ga', formats: [:html] %>
|
25
|
+
|
26
|
+
<!-- for extras, e.g., a favicon -->
|
27
|
+
<%= render partial: '/head_tag_extras', formats: [:html] %>
|
data/lib/sufia/version.rb
CHANGED
@@ -304,7 +304,7 @@ describe GenericFilesController, :type => :controller do
|
|
304
304
|
|
305
305
|
download_query = double('query')
|
306
306
|
allow(download_query).to receive(:for_file).and_return([
|
307
|
-
OpenStruct.new(eventCategory: "Files", eventAction: "Downloaded", eventLabel: "sufia:123456789", totalEvents: "3")
|
307
|
+
OpenStruct.new(date: Date.today.strftime("%Y%m%d"), eventCategory: "Files", eventAction: "Downloaded", eventLabel: "sufia:123456789", totalEvents: "3")
|
308
308
|
])
|
309
309
|
allow(download_query).to receive(:map).and_return(download_query.for_file.map(&:marshal_dump))
|
310
310
|
allow(profile).to receive(:sufia__download).and_return(download_query)
|
@@ -86,6 +86,22 @@ describe 'event jobs' do
|
|
86
86
|
expect(@gf.events.length).to eq(1)
|
87
87
|
expect(@gf.events.first).to eq(event)
|
88
88
|
end
|
89
|
+
it "logs content depositor change events" do
|
90
|
+
# ContentDepositorChange should log the event to the proxy depositor's profile, the depositor's dashboard, followers' dashboards, and the GF
|
91
|
+
@third_user.follow(@another_user)
|
92
|
+
allow_any_instance_of(User).to receive(:can?).and_return(true)
|
93
|
+
allow(Time).to receive(:now).at_least(:once).and_return(1)
|
94
|
+
event = {action: 'User <a href="/users/jilluser@example-dot-com">jilluser@example.com</a> has transferred <a href="/files/123">Hamlet</a> to user <a href="/users/archivist1@example-dot-com">archivist1@example.com</a>', timestamp: '1' }
|
95
|
+
ContentDepositorChangeEventJob.new('test:123', @another_user.user_key).run
|
96
|
+
expect(@user.profile_events.length).to eq(1)
|
97
|
+
expect(@user.profile_events.first).to eq(event)
|
98
|
+
expect(@another_user.events.length).to eq(1)
|
99
|
+
expect(@another_user.events.first).to eq(event)
|
100
|
+
expect(@third_user.events.length).to eq(1)
|
101
|
+
expect(@third_user.events.first).to eq(event)
|
102
|
+
expect(@gf.events.length).to eq(1)
|
103
|
+
expect(@gf.events.first).to eq(event)
|
104
|
+
end
|
89
105
|
it "should log content update events" do
|
90
106
|
# ContentUpdate should log the event to the depositor's profile, followers' dashboards, and the GF
|
91
107
|
@another_user.follow(@user)
|
@@ -188,4 +204,3 @@ describe 'event jobs' do
|
|
188
204
|
expect(@gf.events.first).to eq(event)
|
189
205
|
end
|
190
206
|
end
|
191
|
-
|
@@ -0,0 +1,86 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
RSpec.describe FileDownloadStat, :type => :model do
|
4
|
+
let (:file_id) {"99"}
|
5
|
+
let (:date) {DateTime.new}
|
6
|
+
let (:file_stat) {FileDownloadStat.create(downloads:"2", date: date, file_id: file_id)}
|
7
|
+
|
8
|
+
it "has attributes" do
|
9
|
+
expect(file_stat).to respond_to(:downloads)
|
10
|
+
expect(file_stat).to respond_to(:date)
|
11
|
+
expect(file_stat).to respond_to(:file_id)
|
12
|
+
expect(file_stat.file_id).to eq("99")
|
13
|
+
expect(file_stat.date).to eq(date)
|
14
|
+
expect(file_stat.downloads).to eq(2)
|
15
|
+
end
|
16
|
+
|
17
|
+
|
18
|
+
describe "#get_float_statistics" do
|
19
|
+
|
20
|
+
let(:dates) {
|
21
|
+
ldates = []
|
22
|
+
4.downto(0) {|idx| ldates << (Date.today-idx.day) }
|
23
|
+
ldates
|
24
|
+
}
|
25
|
+
let(:date_strs) {
|
26
|
+
dates.map {|date| date.strftime("%Y%m%d") }
|
27
|
+
}
|
28
|
+
|
29
|
+
let(:download_output) {
|
30
|
+
[[statistic_date(dates[0]), 1], [statistic_date(dates[1]), 1], [statistic_date(dates[2]), 2], [statistic_date(dates[3]), 3]]
|
31
|
+
}
|
32
|
+
|
33
|
+
# This is what the data looks like that's returned from Google Analytics (GA) via the Legato gem
|
34
|
+
# Due to the nature of querying GA, testing this data in an automated fashion is problematc.
|
35
|
+
# Sample data structures were created by sending real events to GA from a test instance of
|
36
|
+
# Scholarsphere. The data below are essentially a "cut and paste" from the output of query
|
37
|
+
# results from the Legato gem.
|
38
|
+
let(:sample_download_statistics) {
|
39
|
+
[
|
40
|
+
OpenStruct.new(eventCategory: "Files", eventAction: "Downloaded", eventLabel: "sufia:x920fw85p", date: date_strs[0], totalEvents: "1"),
|
41
|
+
OpenStruct.new(eventCategory: "Files", eventAction: "Downloaded", eventLabel: "sufia:x920fw85p", date: date_strs[1], totalEvents: "1"),
|
42
|
+
OpenStruct.new(eventCategory: "Files", eventAction: "Downloaded", eventLabel: "sufia:x920fw85p", date: date_strs[2], totalEvents: "2"),
|
43
|
+
OpenStruct.new(eventCategory: "Files", eventAction: "Downloaded", eventLabel: "sufia:x920fw85p", date: date_strs[3], totalEvents: "3"),
|
44
|
+
#OpenStruct.new(eventCategory: "Files", eventAction: "Downloaded", eventLabel: "sufia:x920fw85p", date: date_strs[4], totalEvents: "5"),
|
45
|
+
]
|
46
|
+
}
|
47
|
+
|
48
|
+
describe "cache empty" do
|
49
|
+
let (:stats) {
|
50
|
+
expect(FileDownloadStat).to receive(:ga_statistics).and_return(sample_download_statistics)
|
51
|
+
FileDownloadStat.statistics(file_id,Date.today-4.day)
|
52
|
+
}
|
53
|
+
|
54
|
+
it "includes cached ga data" do
|
55
|
+
expect(FileDownloadStat.to_flots stats).to include(*download_output)
|
56
|
+
end
|
57
|
+
|
58
|
+
it "caches data" do
|
59
|
+
expect(FileDownloadStat.to_flots stats).to include(*download_output)
|
60
|
+
|
61
|
+
# at this point all data should be cached
|
62
|
+
allow(FileDownloadStat).to receive(:ga_statistics).with(Date.today, file_id).and_raise("We should not call Google Analytics All data should be cached!")
|
63
|
+
|
64
|
+
stats2 = FileDownloadStat.statistics(file_id,Date.today-4.day)
|
65
|
+
expect(FileDownloadStat.to_flots stats2).to include(*download_output)
|
66
|
+
end
|
67
|
+
|
68
|
+
end
|
69
|
+
|
70
|
+
describe "cache loaded" do
|
71
|
+
|
72
|
+
let!(:file_download_stat) { FileDownloadStat.create(date: (Date.today-5.day).to_datetime, file_id: file_id, downloads:"25")}
|
73
|
+
|
74
|
+
let (:stats) {
|
75
|
+
expect(FileDownloadStat).to receive(:ga_statistics).and_return(sample_download_statistics)
|
76
|
+
FileDownloadStat.statistics(file_id,Date.today-5.day)
|
77
|
+
}
|
78
|
+
|
79
|
+
it "includes cached data" do
|
80
|
+
expect(FileDownloadStat.to_flots stats).to include([file_download_stat.date.to_i*1000,file_download_stat.downloads],*download_output)
|
81
|
+
end
|
82
|
+
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
end
|
@@ -12,6 +12,25 @@ describe FileUsage, :type => :model do
|
|
12
12
|
@file.delete
|
13
13
|
end
|
14
14
|
|
15
|
+
let(:dates) {
|
16
|
+
ldates = []
|
17
|
+
4.downto(0) {|idx| ldates << (Date.today-idx.day) }
|
18
|
+
ldates
|
19
|
+
}
|
20
|
+
let(:date_strs) {
|
21
|
+
ldate_strs = []
|
22
|
+
dates.each {|date| ldate_strs << date.strftime("%Y%m%d") }
|
23
|
+
ldate_strs
|
24
|
+
}
|
25
|
+
|
26
|
+
let(:view_output) {
|
27
|
+
[[statistic_date(dates[0]), 4], [statistic_date(dates[1]), 8], [statistic_date(dates[2]), 6], [statistic_date(dates[3]), 10], [statistic_date(dates[4]), 2]]
|
28
|
+
}
|
29
|
+
|
30
|
+
let(:download_output) {
|
31
|
+
[[statistic_date(dates[0]), 1], [statistic_date(dates[1]), 1], [statistic_date(dates[2]), 2], [statistic_date(dates[3]), 3], [statistic_date(dates[4]), 5]]
|
32
|
+
}
|
33
|
+
|
15
34
|
# This is what the data looks like that's returned from Google Analytics (GA) via the Legato gem
|
16
35
|
# Due to the nature of querying GA, testing this data in an automated fashion is problematc.
|
17
36
|
# Sample data structures were created by sending real events to GA from a test instance of
|
@@ -20,27 +39,28 @@ describe FileUsage, :type => :model do
|
|
20
39
|
|
21
40
|
let(:sample_download_statistics) {
|
22
41
|
[
|
23
|
-
OpenStruct.new(eventCategory: "Files", eventAction: "Downloaded", eventLabel: "sufia:x920fw85p", date:
|
24
|
-
OpenStruct.new(eventCategory: "Files", eventAction: "Downloaded", eventLabel: "sufia:x920fw85p", date:
|
25
|
-
OpenStruct.new(eventCategory: "Files", eventAction: "Downloaded", eventLabel: "sufia:x920fw85p", date:
|
26
|
-
OpenStruct.new(eventCategory: "Files", eventAction: "Downloaded", eventLabel: "sufia:x920fw85p", date:
|
27
|
-
OpenStruct.new(eventCategory: "Files", eventAction: "Downloaded", eventLabel: "sufia:x920fw85p", date:
|
42
|
+
OpenStruct.new(eventCategory: "Files", eventAction: "Downloaded", eventLabel: "sufia:x920fw85p", date: date_strs[0], totalEvents: "1"),
|
43
|
+
OpenStruct.new(eventCategory: "Files", eventAction: "Downloaded", eventLabel: "sufia:x920fw85p", date: date_strs[1], totalEvents: "1"),
|
44
|
+
OpenStruct.new(eventCategory: "Files", eventAction: "Downloaded", eventLabel: "sufia:x920fw85p", date: date_strs[2], totalEvents: "2"),
|
45
|
+
OpenStruct.new(eventCategory: "Files", eventAction: "Downloaded", eventLabel: "sufia:x920fw85p", date: date_strs[3], totalEvents: "3"),
|
46
|
+
OpenStruct.new(eventCategory: "Files", eventAction: "Downloaded", eventLabel: "sufia:x920fw85p", date: date_strs[4], totalEvents: "5"),
|
28
47
|
]
|
29
48
|
}
|
30
49
|
|
31
50
|
let(:sample_pageview_statistics) {
|
32
51
|
[
|
33
|
-
OpenStruct.new(date:
|
34
|
-
OpenStruct.new(date:
|
35
|
-
OpenStruct.new(date:
|
36
|
-
OpenStruct.new(date:
|
37
|
-
OpenStruct.new(date:
|
52
|
+
OpenStruct.new(date: date_strs[0], pageviews: 4),
|
53
|
+
OpenStruct.new(date: date_strs[1], pageviews: 8),
|
54
|
+
OpenStruct.new(date: date_strs[2], pageviews: 6),
|
55
|
+
OpenStruct.new(date: date_strs[3], pageviews: 10),
|
56
|
+
OpenStruct.new(date: date_strs[4], pageviews: 2)
|
38
57
|
]
|
39
58
|
}
|
40
59
|
|
41
60
|
let(:usage) {
|
42
|
-
allow_any_instance_of(
|
43
|
-
|
61
|
+
allow_any_instance_of(GenericFile).to receive(:create_date).and_return((Date.today-4.day).to_s)
|
62
|
+
expect(FileDownloadStat).to receive(:ga_statistics).and_return(sample_download_statistics)
|
63
|
+
expect(FileViewStat).to receive(:ga_statistics).and_return(sample_pageview_statistics)
|
44
64
|
FileUsage.new(@file.id)
|
45
65
|
}
|
46
66
|
|
@@ -82,8 +102,8 @@ describe FileUsage, :type => :model do
|
|
82
102
|
it "should return an array of hashes for use with JQuery Flot" do
|
83
103
|
expect(usage.to_flot[0][:label]).to eq("Pageviews")
|
84
104
|
expect(usage.to_flot[1][:label]).to eq("Downloads")
|
85
|
-
expect(usage.to_flot[0][:data]).to include(
|
86
|
-
expect(usage.to_flot[1][:data]).to include(
|
105
|
+
expect(usage.to_flot[0][:data]).to include(*view_output)
|
106
|
+
expect(usage.to_flot[1][:data]).to include(*download_output)
|
87
107
|
end
|
88
108
|
|
89
109
|
let(:create_date) {DateTime.new(2014, 01, 01)}
|
@@ -100,8 +120,8 @@ describe FileUsage, :type => :model do
|
|
100
120
|
describe "create date before earliest date set" do
|
101
121
|
let(:usage) {
|
102
122
|
allow_any_instance_of(GenericFile).to receive(:create_date).and_return(create_date.to_s)
|
103
|
-
|
104
|
-
|
123
|
+
expect(FileDownloadStat).to receive(:ga_statistics).and_return(sample_download_statistics)
|
124
|
+
expect(FileViewStat).to receive(:ga_statistics).and_return(sample_pageview_statistics)
|
105
125
|
FileUsage.new(@file.id)
|
106
126
|
}
|
107
127
|
it "should set the created date to the earliest date not the created date" do
|
@@ -112,8 +132,9 @@ describe FileUsage, :type => :model do
|
|
112
132
|
|
113
133
|
describe "create date after earliest" do
|
114
134
|
let(:usage) {
|
115
|
-
allow_any_instance_of(
|
116
|
-
|
135
|
+
allow_any_instance_of(GenericFile).to receive(:create_date).and_return((Date.today-4.day).to_s)
|
136
|
+
expect(FileDownloadStat).to receive(:ga_statistics).and_return(sample_download_statistics)
|
137
|
+
expect(FileViewStat).to receive(:ga_statistics).and_return(sample_pageview_statistics)
|
117
138
|
Sufia.config.analytic_start_date = earliest
|
118
139
|
FileUsage.new(@file.id)
|
119
140
|
}
|
@@ -129,8 +150,8 @@ describe FileUsage, :type => :model do
|
|
129
150
|
|
130
151
|
let(:usage) {
|
131
152
|
allow_any_instance_of(GenericFile).to receive(:create_date).and_return(create_date.to_s)
|
132
|
-
|
133
|
-
|
153
|
+
expect(FileDownloadStat).to receive(:ga_statistics).and_return(sample_download_statistics)
|
154
|
+
expect(FileViewStat).to receive(:ga_statistics).and_return(sample_pageview_statistics)
|
134
155
|
FileUsage.new(@file.id)
|
135
156
|
}
|
136
157
|
it "should set the created date to the earliest date not the created date" do
|
@@ -139,5 +160,4 @@ describe FileUsage, :type => :model do
|
|
139
160
|
|
140
161
|
end
|
141
162
|
end
|
142
|
-
|
143
163
|
end
|
@@ -0,0 +1,84 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
RSpec.describe FileViewStat, :type => :model do
|
4
|
+
let (:file_id) {"99"}
|
5
|
+
let (:date) {DateTime.new}
|
6
|
+
let (:file_stat) {FileViewStat.create(views:"25", date: date, file_id: file_id)}
|
7
|
+
|
8
|
+
it "has attributes" do
|
9
|
+
expect(file_stat).to respond_to(:views)
|
10
|
+
expect(file_stat).to respond_to(:date)
|
11
|
+
expect(file_stat).to respond_to(:file_id)
|
12
|
+
expect(file_stat.file_id).to eq("99")
|
13
|
+
expect(file_stat.date).to eq(date)
|
14
|
+
expect(file_stat.views).to eq(25)
|
15
|
+
end
|
16
|
+
|
17
|
+
describe "#get_float_statistics" do
|
18
|
+
|
19
|
+
let(:dates) {
|
20
|
+
ldates = []
|
21
|
+
4.downto(0) {|idx| ldates << (Date.today-idx.day) }
|
22
|
+
ldates
|
23
|
+
}
|
24
|
+
let(:date_strs) {
|
25
|
+
dates.map {|date| date.strftime("%Y%m%d") }
|
26
|
+
}
|
27
|
+
|
28
|
+
let(:view_output) {
|
29
|
+
[[statistic_date(dates[0]), 4], [statistic_date(dates[1]), 8], [statistic_date(dates[2]), 6], [statistic_date(dates[3]), 10]]
|
30
|
+
}
|
31
|
+
|
32
|
+
# This is what the data looks like that's returned from Google Analytics (GA) via the Legato gem
|
33
|
+
# Due to the nature of querying GA, testing this data in an automated fashion is problematc.
|
34
|
+
# Sample data structures were created by sending real events to GA from a test instance of
|
35
|
+
# Scholarsphere. The data below are essentially a "cut and paste" from the output of query
|
36
|
+
# results from the Legato gem.
|
37
|
+
let(:sample_pageview_statistics) {
|
38
|
+
[
|
39
|
+
OpenStruct.new(date: date_strs[0], pageviews: 4),
|
40
|
+
OpenStruct.new(date: date_strs[1], pageviews: 8),
|
41
|
+
OpenStruct.new(date: date_strs[2], pageviews: 6),
|
42
|
+
OpenStruct.new(date: date_strs[3], pageviews: 10),
|
43
|
+
#OpenStruct.new(date: date_strs[4], pageviews: 2)
|
44
|
+
]
|
45
|
+
}
|
46
|
+
describe "cache empty" do
|
47
|
+
let (:stats) {
|
48
|
+
expect(FileViewStat).to receive(:ga_statistics).and_return(sample_pageview_statistics)
|
49
|
+
FileViewStat.statistics(file_id,Date.today-4.day)
|
50
|
+
}
|
51
|
+
|
52
|
+
it "includes cached ga data" do
|
53
|
+
expect(FileViewStat.to_flots stats).to include(*view_output)
|
54
|
+
end
|
55
|
+
|
56
|
+
it "caches data" do
|
57
|
+
expect(FileViewStat.to_flots stats).to include(*view_output)
|
58
|
+
|
59
|
+
# at this point all data should be cached
|
60
|
+
allow(FileViewStat).to receive(:ga_statistics).with(Date.today, file_id).and_raise("We should not call Google Analytics All data should be cached!")
|
61
|
+
|
62
|
+
stats2 = FileViewStat.statistics(file_id,Date.today-5.day)
|
63
|
+
expect(FileViewStat.to_flots stats2).to include(*view_output)
|
64
|
+
end
|
65
|
+
|
66
|
+
end
|
67
|
+
|
68
|
+
describe "cache loaded" do
|
69
|
+
|
70
|
+
let!(:file_view_stat) { FileViewStat.create(date: (Date.today-5.day).to_datetime, file_id: file_id, views:"25")}
|
71
|
+
|
72
|
+
let (:stats) {
|
73
|
+
expect(FileViewStat).to receive(:ga_statistics).and_return(sample_pageview_statistics)
|
74
|
+
FileViewStat.statistics(file_id,Date.today-5.day)
|
75
|
+
}
|
76
|
+
|
77
|
+
it "includes cached data" do
|
78
|
+
expect(FileViewStat.to_flots stats).to include([file_view_stat.date.to_i*1000,file_view_stat.views],*view_output)
|
79
|
+
end
|
80
|
+
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
data/spec/support/features.rb
CHANGED
@@ -6,6 +6,7 @@ require File.expand_path('../proxies', __FILE__)
|
|
6
6
|
require File.expand_path('../locations', __FILE__)
|
7
7
|
require File.expand_path('../poltergeist', __FILE__)
|
8
8
|
require File.expand_path('../cleaner', __FILE__)
|
9
|
+
require File.expand_path('../statistic_helper', __FILE__)
|
9
10
|
|
10
11
|
RSpec.configure do |config|
|
11
12
|
config.include Features::SessionHelpers, type: :feature
|
@@ -0,0 +1,35 @@
|
|
1
|
+
module Sufia
|
2
|
+
module FileStatUtils
|
3
|
+
|
4
|
+
def to_flots stats
|
5
|
+
stats.map {|stat| stat.to_flot}
|
6
|
+
end
|
7
|
+
|
8
|
+
def convert_date date_time
|
9
|
+
date_time.to_datetime.to_i * 1000
|
10
|
+
end
|
11
|
+
|
12
|
+
private
|
13
|
+
|
14
|
+
def cached_stats(file_id, start_date, method)
|
15
|
+
stats = self.where(file_id:file_id).order(date: :asc)
|
16
|
+
ga_start_date = stats.size > 0 ? stats[stats.size-1].date + 1.day : start_date.to_date
|
17
|
+
{ga_start_date: ga_start_date, cached_stats: stats.to_a }
|
18
|
+
end
|
19
|
+
|
20
|
+
def combined_stats file_id, start_date, object_method, ga_key
|
21
|
+
stat_cache_info = cached_stats( file_id, start_date, object_method)
|
22
|
+
stats = stat_cache_info[:cached_stats]
|
23
|
+
if stat_cache_info[:ga_start_date] < Date.today
|
24
|
+
ga_stats = ga_statistics(stat_cache_info[:ga_start_date], file_id)
|
25
|
+
ga_stats.each do |stat|
|
26
|
+
lstat = self.new file_id:file_id, date: stat[:date], object_method => stat[ga_key]
|
27
|
+
lstat.save unless Date.parse(stat[:date]) == Date.today
|
28
|
+
stats << lstat
|
29
|
+
end
|
30
|
+
end
|
31
|
+
stats
|
32
|
+
end
|
33
|
+
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
class FileDownloadStat < ActiveRecord::Base
|
2
|
+
extend Sufia::FileStatUtils
|
3
|
+
|
4
|
+
def to_flot
|
5
|
+
[ self.class.convert_date(date), downloads ]
|
6
|
+
end
|
7
|
+
|
8
|
+
def self.statistics file_id, start_date
|
9
|
+
combined_stats file_id, start_date, :downloads, :totalEvents
|
10
|
+
end
|
11
|
+
|
12
|
+
# Sufia::Download is sent to Sufia::Analytics.profile as #sufia__download
|
13
|
+
# see Legato::ProfileMethods.method_name_from_klass
|
14
|
+
def self.ga_statistics start_date, file_id
|
15
|
+
Sufia::Analytics.profile.sufia__download(sort: 'date', start_date: start_date, end_date: Date.yesterday).for_file(file_id)
|
16
|
+
end
|
17
|
+
|
18
|
+
end
|
@@ -8,52 +8,23 @@ class FileUsage
|
|
8
8
|
earliest = Sufia.config.analytic_start_date
|
9
9
|
self.created = DateTime.parse(::GenericFile.find(id).create_date)
|
10
10
|
self.created = earliest > created ? earliest : created unless earliest.blank?
|
11
|
-
self.downloads =
|
12
|
-
self.pageviews =
|
11
|
+
self.downloads = FileDownloadStat.to_flots FileDownloadStat.statistics(id, created)
|
12
|
+
self.pageviews = FileViewStat.to_flots FileViewStat.statistics(id, created)
|
13
13
|
end
|
14
14
|
|
15
15
|
def total_downloads
|
16
|
-
self.downloads.
|
16
|
+
self.downloads.reduce(0) { |total, result| total + result[1].to_i }
|
17
17
|
end
|
18
18
|
|
19
19
|
def total_pageviews
|
20
|
-
self.pageviews.
|
20
|
+
self.pageviews.reduce(0) { |total, result| total + result[1].to_i }
|
21
21
|
end
|
22
22
|
|
23
23
|
# Package data for visualization using JQuery Flot
|
24
24
|
def to_flot
|
25
25
|
[
|
26
|
-
{ label: "Pageviews", data:
|
27
|
-
{ label: "Downloads", data:
|
26
|
+
{ label: "Pageviews", data: pageviews },
|
27
|
+
{ label: "Downloads", data: downloads }
|
28
28
|
]
|
29
29
|
end
|
30
|
-
|
31
|
-
private
|
32
|
-
|
33
|
-
# Sufia::Download is sent to Sufia::Analytics.profile as #sufia__download
|
34
|
-
# see Legato::ProfileMethods.method_name_from_klass
|
35
|
-
def download_statistics
|
36
|
-
Sufia::Analytics.profile.sufia__download(sort: 'date', start_date: created).for_file(self.id)
|
37
|
-
end
|
38
|
-
|
39
|
-
# Sufia::Pageview is sent to Sufia::Analytics.profile as #sufia__pageview
|
40
|
-
# see Legato::ProfileMethods.method_name_from_klass
|
41
|
-
def pageview_statistics
|
42
|
-
Sufia::Analytics.profile.sufia__pageview(sort: 'date', start_date: created).for_path(self.path)
|
43
|
-
end
|
44
|
-
|
45
|
-
def pageviews_to_flot values = Array.new
|
46
|
-
self.pageviews.map(&:marshal_dump).map do |result_hash|
|
47
|
-
values << [ (Date.parse(result_hash[:date]).to_time.to_i * 1000), result_hash[:pageviews].to_i ]
|
48
|
-
end
|
49
|
-
return values
|
50
|
-
end
|
51
|
-
|
52
|
-
def downloads_to_flot values = Array.new
|
53
|
-
self.downloads.map(&:marshal_dump).map do |result_hash|
|
54
|
-
values << [ (Date.parse(result_hash[:date]).to_time.to_i * 1000), result_hash[:totalEvents].to_i ]
|
55
|
-
end
|
56
|
-
return values
|
57
|
-
end
|
58
|
-
|
59
30
|
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
class FileViewStat < ActiveRecord::Base
|
2
|
+
extend Sufia::FileStatUtils
|
3
|
+
|
4
|
+
def to_flot
|
5
|
+
[ self.class.convert_date(date), views ]
|
6
|
+
end
|
7
|
+
|
8
|
+
def self.statistics file_id, start_date
|
9
|
+
combined_stats file_id, start_date, :views, :pageviews
|
10
|
+
end
|
11
|
+
|
12
|
+
# Sufia::Download is sent to Sufia::Analytics.profile as #sufia__download
|
13
|
+
# see Legato::ProfileMethods.method_name_from_klass
|
14
|
+
def self.ga_statistics start_date, file_id
|
15
|
+
path = Sufia::Engine.routes.url_helpers.generic_file_path(Sufia::Noid.noidify(file_id))
|
16
|
+
Sufia::Analytics.profile.sufia__pageview(sort: 'date', start_date: start_date).for_path(path)
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,53 @@
|
|
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
|
7
|
+
|
8
|
+
source_root File.expand_path('../templates', __FILE__)
|
9
|
+
|
10
|
+
desc """
|
11
|
+
This generator adds the ability to cache usage stats to your application:
|
12
|
+
1. Creates several database migrations if they do not exist in /db/migrate
|
13
|
+
"""
|
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
|
+
|
29
|
+
def banner
|
30
|
+
say_status("warning", "ADDING STATS CACHING-RELATED SUFIA MODELS", :yellow)
|
31
|
+
end
|
32
|
+
|
33
|
+
# Setup the database migrations
|
34
|
+
def copy_migrations
|
35
|
+
# Can't get this any more DRY, because we need this order.
|
36
|
+
[
|
37
|
+
'create_file_view_stats.rb',
|
38
|
+
'create_file_download_stats.rb'
|
39
|
+
].each do |file|
|
40
|
+
better_migration_template file
|
41
|
+
end
|
42
|
+
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
|
+
end
|
@@ -18,6 +18,7 @@ This generator makes the following changes to your application:
|
|
18
18
|
6. Installs Blacklight gallery
|
19
19
|
7. Runs full-text generator
|
20
20
|
8. Runs proxies generator
|
21
|
+
9. Runs cached stats generator
|
21
22
|
"""
|
22
23
|
|
23
24
|
# Implement the required interface for Rails::Generators::Migration.
|
@@ -56,9 +57,7 @@ This generator makes the following changes to your application:
|
|
56
57
|
'add_linkedin_to_users.rb',
|
57
58
|
'create_tinymce_assets.rb',
|
58
59
|
'create_content_blocks.rb',
|
59
|
-
'create_featured_works.rb'
|
60
|
-
'create_proxy_deposit_requests.rb',
|
61
|
-
'create_proxy_deposit_rights.rb'
|
60
|
+
'create_featured_works.rb'
|
62
61
|
].each do |file|
|
63
62
|
better_migration_template file
|
64
63
|
end
|
@@ -106,10 +105,15 @@ This generator makes the following changes to your application:
|
|
106
105
|
end
|
107
106
|
|
108
107
|
# Sets up proxies and transfers
|
109
|
-
def
|
108
|
+
def proxies
|
110
109
|
generate "sufia:models:proxies"
|
111
110
|
end
|
112
111
|
|
112
|
+
# Sets up cached usage stats
|
113
|
+
def cached_stats
|
114
|
+
generate 'sufia:models:cached_stats'
|
115
|
+
end
|
116
|
+
|
113
117
|
private
|
114
118
|
|
115
119
|
def better_migration_template(file)
|
@@ -42,4 +42,9 @@ Gem::Specification.new do |spec|
|
|
42
42
|
spec.add_dependency 'google-api-client', '~> 0.7'
|
43
43
|
spec.add_dependency 'legato', '~> 0.3'
|
44
44
|
spec.add_dependency 'activerecord-import', '~> 0.5'
|
45
|
+
if RUBY_VERSION < '2.1.0'
|
46
|
+
spec.add_dependency 'mini_magick', '< 4'
|
47
|
+
else
|
48
|
+
spec.add_dependency 'mini_magick'
|
49
|
+
end
|
45
50
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: sufia
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 4.
|
4
|
+
version: 4.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Justin Coyne
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2014-
|
12
|
+
date: 2014-11-25 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: sufia-models
|
@@ -17,14 +17,14 @@ dependencies:
|
|
17
17
|
requirements:
|
18
18
|
- - '='
|
19
19
|
- !ruby/object:Gem::Version
|
20
|
-
version: 4.
|
20
|
+
version: 4.2.0
|
21
21
|
type: :runtime
|
22
22
|
prerelease: false
|
23
23
|
version_requirements: !ruby/object:Gem::Requirement
|
24
24
|
requirements:
|
25
25
|
- - '='
|
26
26
|
- !ruby/object:Gem::Version
|
27
|
-
version: 4.
|
27
|
+
version: 4.2.0
|
28
28
|
- !ruby/object:Gem::Dependency
|
29
29
|
name: blacklight_advanced_search
|
30
30
|
requirement: !ruby/object:Gem::Requirement
|
@@ -629,6 +629,7 @@ files:
|
|
629
629
|
- app/views/_flash_msg.html.erb
|
630
630
|
- app/views/_footer.html.erb
|
631
631
|
- app/views/_ga.html.erb
|
632
|
+
- app/views/_head_tag_extras.html.erb
|
632
633
|
- app/views/_logo.html.erb
|
633
634
|
- app/views/_masthead.html.erb
|
634
635
|
- app/views/_user_util_links.html.erb
|
@@ -977,7 +978,9 @@ files:
|
|
977
978
|
- spec/models/featured_work_list_spec.rb
|
978
979
|
- spec/models/featured_work_spec.rb
|
979
980
|
- spec/models/file_content_datastream_spec.rb
|
981
|
+
- spec/models/file_download_stat_spec.rb
|
980
982
|
- spec/models/file_usage_spec.rb
|
983
|
+
- spec/models/file_view_stat_spec.rb
|
981
984
|
- spec/models/fits_datastream_spec.rb
|
982
985
|
- spec/models/generic_file/reload_on_save_spec.rb
|
983
986
|
- spec/models/generic_file/visibility_spec.rb
|
@@ -1007,6 +1010,7 @@ files:
|
|
1007
1010
|
- spec/support/poltergeist.rb
|
1008
1011
|
- spec/support/proxies.rb
|
1009
1012
|
- spec/support/selectors.rb
|
1013
|
+
- spec/support/statistic_helper.rb
|
1010
1014
|
- spec/support/uploaded_file_monkeypatch.rb
|
1011
1015
|
- spec/test_app_templates/lib/generators/test_app_generator.rb
|
1012
1016
|
- spec/views/batch/edit.html.erb_spec.rb
|
@@ -1044,6 +1048,7 @@ files:
|
|
1044
1048
|
- sufia-models/app/models/collection.rb
|
1045
1049
|
- sufia-models/app/models/concerns/sufia/ability.rb
|
1046
1050
|
- sufia-models/app/models/concerns/sufia/collection.rb
|
1051
|
+
- sufia-models/app/models/concerns/sufia/file_stat_utils.rb
|
1047
1052
|
- sufia-models/app/models/concerns/sufia/generic_file.rb
|
1048
1053
|
- sufia-models/app/models/concerns/sufia/generic_file/accessible_attributes.rb
|
1049
1054
|
- sufia-models/app/models/concerns/sufia/generic_file/audit.rb
|
@@ -1072,7 +1077,9 @@ files:
|
|
1072
1077
|
- sufia-models/app/models/datastreams/properties_datastream.rb
|
1073
1078
|
- sufia-models/app/models/domain_term.rb
|
1074
1079
|
- sufia-models/app/models/featured_work.rb
|
1080
|
+
- sufia-models/app/models/file_download_stat.rb
|
1075
1081
|
- sufia-models/app/models/file_usage.rb
|
1082
|
+
- sufia-models/app/models/file_view_stat.rb
|
1076
1083
|
- sufia-models/app/models/follow.rb
|
1077
1084
|
- sufia-models/app/models/generic_file.rb
|
1078
1085
|
- sufia-models/app/models/geo_names_resource.rb
|
@@ -1093,6 +1100,7 @@ files:
|
|
1093
1100
|
- sufia-models/app/services/sufia/id_service.rb
|
1094
1101
|
- sufia-models/app/services/sufia/noid.rb
|
1095
1102
|
- sufia-models/config/locales/sufia.en.yml
|
1103
|
+
- sufia-models/lib/generators/sufia/models/cached_stats_generator.rb
|
1096
1104
|
- sufia-models/lib/generators/sufia/models/fulltext_generator.rb
|
1097
1105
|
- sufia-models/lib/generators/sufia/models/install_generator.rb
|
1098
1106
|
- sufia-models/lib/generators/sufia/models/proxies_generator.rb
|
@@ -1117,6 +1125,8 @@ files:
|
|
1117
1125
|
- sufia-models/lib/generators/sufia/models/templates/migrations/create_checksum_audit_logs.rb
|
1118
1126
|
- sufia-models/lib/generators/sufia/models/templates/migrations/create_content_blocks.rb
|
1119
1127
|
- sufia-models/lib/generators/sufia/models/templates/migrations/create_featured_works.rb
|
1128
|
+
- sufia-models/lib/generators/sufia/models/templates/migrations/create_file_download_stats.rb
|
1129
|
+
- sufia-models/lib/generators/sufia/models/templates/migrations/create_file_view_stats.rb
|
1120
1130
|
- sufia-models/lib/generators/sufia/models/templates/migrations/create_local_authorities.rb
|
1121
1131
|
- sufia-models/lib/generators/sufia/models/templates/migrations/create_proxy_deposit_requests.rb
|
1122
1132
|
- sufia-models/lib/generators/sufia/models/templates/migrations/create_proxy_deposit_rights.rb
|
@@ -1206,7 +1216,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
1206
1216
|
version: '0'
|
1207
1217
|
requirements: []
|
1208
1218
|
rubyforge_project:
|
1209
|
-
rubygems_version: 2.4.
|
1219
|
+
rubygems_version: 2.4.3
|
1210
1220
|
signing_key:
|
1211
1221
|
specification_version: 4
|
1212
1222
|
summary: Sufia was extracted from ScholarSphere developed by Penn State University
|
@@ -1321,7 +1331,9 @@ test_files:
|
|
1321
1331
|
- spec/models/featured_work_list_spec.rb
|
1322
1332
|
- spec/models/featured_work_spec.rb
|
1323
1333
|
- spec/models/file_content_datastream_spec.rb
|
1334
|
+
- spec/models/file_download_stat_spec.rb
|
1324
1335
|
- spec/models/file_usage_spec.rb
|
1336
|
+
- spec/models/file_view_stat_spec.rb
|
1325
1337
|
- spec/models/fits_datastream_spec.rb
|
1326
1338
|
- spec/models/generic_file/reload_on_save_spec.rb
|
1327
1339
|
- spec/models/generic_file/visibility_spec.rb
|
@@ -1351,6 +1363,7 @@ test_files:
|
|
1351
1363
|
- spec/support/poltergeist.rb
|
1352
1364
|
- spec/support/proxies.rb
|
1353
1365
|
- spec/support/selectors.rb
|
1366
|
+
- spec/support/statistic_helper.rb
|
1354
1367
|
- spec/support/uploaded_file_monkeypatch.rb
|
1355
1368
|
- spec/test_app_templates/lib/generators/test_app_generator.rb
|
1356
1369
|
- spec/views/batch/edit.html.erb_spec.rb
|