record last_inline_view time for attachments

to collect data about when scribd (or crocodoc) documents
are not being used and can be purged from third party servers
to save money

fixes CNVS-5310

note: this data is not yet exposed; it can only be queried
from the database manually for now

test plan:  make sure the last_inline_view attribute for an
Attachment is updated whenever the attachment is viewed via
scribd or crocodoc. this can be done in several ways:
 - via the preview icon that appears next to a document link
   in rich text
 - on the files page
 - for student submissions on SpeedGrader

the easiest way to check this is in the Rails console.
get the attachment id from the canvas url, then,
in the console (after switching to the correct shard),
do

  Attachment.find(id).last_inline_view

Change-Id: I137ab631ad4eefdf4d9af353dd11191b7f2ca140
Reviewed-on: https://gerrit.instructure.com/20810
Tested-by: Jenkins <jenkins@instructure.com>
Reviewed-by: James Williams  <jamesw@instructure.com>
Reviewed-by: Bracken Mosbacker <bracken@instructure.com>
Product-Review: Bracken Mosbacker <bracken@instructure.com>
QA-Review: Bracken Mosbacker <bracken@instructure.com>
This commit is contained in:
Jeremy Stanley 2013-05-21 13:02:44 -06:00
parent 2ecc8bc16b
commit aa120ecd78
10 changed files with 63 additions and 10 deletions

View File

@ -289,6 +289,7 @@ class FilesController < ApplicationController
generate_new_page_view
@attachment.context_module_action(@current_user, :read) if @current_user
log_asset_access(@attachment, 'files', 'files')
@attachment.record_inline_view
render :json => {:ok => true}.to_json
else
render_attachment(@attachment)
@ -313,10 +314,11 @@ class FilesController < ApplicationController
# Right now we assume if they ask for json data on the attachment
# which includes the scribd doc data, then that means they have
# viewed or are about to view the file in some form.
attachment.context_module_action(@current_user, :read) if @current_user && (
(feature_enabled?(:scribd) && attachment.scribd_doc) ||
(service_enabled?(:google_docs_previews) && attachment.authenticated_s3_url)
)
if @current_user && ((feature_enabled?(:scribd) && attachment.scribd_doc) ||
(service_enabled?(:google_docs_previews) && attachment.authenticated_s3_url))
attachment.context_module_action(@current_user, :read)
@attachment.record_inline_view
end
options[:methods] = :authenticated_s3_url if service_enabled?(:google_docs_previews) && attachment.authenticated_s3_url
log_asset_access(@attachment, "files", "files")
end

View File

@ -38,6 +38,7 @@ module AttachmentHelper
end
attrs[:attachment_id] = attachment.id
attrs[:mimetype] = attachment.mimetype
attrs[:attachment_view_inline_ping_url] = context_url(self.context, :context_file_inline_view_url, self.id)
attrs.inject("") { |s,(attr,val)| s << "data-#{attr}=#{val} " }
end
end

View File

@ -1159,6 +1159,10 @@ class Assignment < ActiveRecord::Base
:media_comment_type, :media_comment_id,
:cached_attachments, :attachments]
attachment_fields = [:id, :comment_id, :content_type, :context_id, :context_type,
:crocodoc_available?, :display_name, :filename, :mime_class,
:scribd_doc, :scribdable?, :size, :submitter_id, :workflow_state]
res = as_json(
:include => {
:context => { :only => :id },
@ -1197,7 +1201,16 @@ class Assignment < ActiveRecord::Base
},
:only => submission_fields,
:methods => [:versioned_attachments, :late]
)
).tap do |s|
if s['submission'] && s['submission']['versioned_attachments']
s['submission']['versioned_attachments'].map! do |a|
a.as_json(
:only => attachment_fields,
:methods => [:view_inline_ping_url]
)
end
end
end
end
end
json

View File

@ -1622,4 +1622,12 @@ class Attachment < ActiveRecord::Base
def crocodoc_available?
crocodoc_document.try(:available?)
end
def view_inline_ping_url
"/#{context_url_prefix}/files/#{self.id}/inline_view"
end
def record_inline_view
update_attribute(:last_inline_view, Time.now)
end
end

View File

@ -41,7 +41,7 @@
<span style="font-size: 1.2em;">
<%= link_to "Download #{@attachment.display_name}", download_url %>
</span> (<%= @attachment.readable_size %>)
<div id="doc_preview" <%= doc_preview_attributes(@attachment, :attachment_view_inline_ping_url => context_url(@context, :context_file_inline_view_url, @attachment.id)) %>></div>
<div id="doc_preview" <%= doc_preview_attributes(@attachment) %>></div>
<% end %>
<% end %>
<%= render :partial => 'shared/sequence_footer', :locals => {:asset => @attachment} %>

View File

@ -0,0 +1,11 @@
class AddLastInlineViewToAttachments < ActiveRecord::Migration
tag :predeploy
def self.up
add_column :attachments, :last_inline_view, :datetime
end
def self.down
remove_column :attachments, :last_inline_view
end
end

View File

@ -311,10 +311,10 @@ define([
// what value is there in knowing the student looked at the file browser,
// we just care if they access files.
},
viewFile: function(context_string, id) {
viewInlinePingUrl: function(context_string, id) {
var url = $("#file_context_links ." + context_string + "_inline_view_attachment_url").attr('href');
url = $.replaceTags(url, 'id', id);
$.ajaxJSON(url, 'POST', {}, function() { }, function() { });
return url;
},
selectFolder: function($original_node) {
if(!files.selectFolder.forceRefresh && ($original_node.hasClass('active-node') || $original_node.hasClass('active-leaf'))) { return; }
@ -1578,9 +1578,9 @@ define([
height: '100%',
crocodoc_session_url: data.crocodocSession,
scribd_doc_id: data.scribd_doc && data.scribd_doc.attributes && data.scribd_doc.attributes.doc_id,
scribd_access_key: data.scribd_doc && data.scribd_doc.attributes && data.scribd_doc.attributes.access_key
scribd_access_key: data.scribd_doc && data.scribd_doc.attributes && data.scribd_doc.attributes.access_key,
attachment_view_inline_ping_url: files.viewInlinePingUrl(data.context_string, data.id)
});
files.viewFile(data.context_string, data.id);
};
if (data.permissions && data.permissions.download && $.isPreviewable(data.content_type)) {
if (data['crocodoc_available?'] && !data.crocodocSession) {

View File

@ -1243,6 +1243,7 @@ define([
mimeType: attachment.content_type,
attachment_id: attachment.id,
submission_id: this.currentStudent.submission.id,
attachment_view_inline_ping_url: attachment.view_inline_ping_url,
ready: function(){
EG.resizeFullHeight();
}

View File

@ -272,6 +272,7 @@ describe FilesController do
get 'show', :course_id => @course.id, :id => @file.id, :download => 1
@module.reload
@module.evaluate_for(@user, true, true).state.should eql(:completed)
@file.reload.last_inline_view.should be_nil
end
it "should not mark a file as viewed for module progressions if the file is locked" do
@ -281,6 +282,7 @@ describe FilesController do
get 'show', :course_id => @course.id, :id => @file.id, :download => 1
@module.reload
@module.evaluate_for(@user, true, true).state.should eql(:unlocked)
@file.reload.last_inline_view.should be_nil
end
it "should not mark a file as viewed for module progressions just because the files#show view is rendered" do
@ -290,6 +292,7 @@ describe FilesController do
get 'show', :course_id => @course.id, :id => @file.id
@module.reload
@module.evaluate_for(@user, true, true).state.should eql(:unlocked)
@file.reload.last_inline_view.should be_nil
end
it "should mark files as viewed for module progressions if the file is previewed inline" do
@ -298,6 +301,7 @@ describe FilesController do
json_parse.should == {'ok' => true}
@module.reload
@module.evaluate_for(@user, true, true).state.should eql(:completed)
@file.reload.last_inline_view.should > 1.minute.ago
end
it "should mark files as viewed for module progressions if the file data is requested and it includes the scribd_doc data" do
@ -307,6 +311,7 @@ describe FilesController do
get 'show', :course_id => @course.id, :id => @file.id, :format => :json
@module.reload
@module.evaluate_for(@user, true, true).state.should eql(:completed)
@file.reload.last_inline_view.should > 1.minute.ago
end
it "should not mark files as viewed for module progressions if the file data is requested and it doesn't include the scribd_doc data (meaning it got viewed in scribd inline) and google docs preview is disabled" do
@ -322,6 +327,7 @@ describe FilesController do
get 'show', :course_id => @course.id, :id => @file.id, :format => :json
@module.reload
@module.evaluate_for(@user, true, true).state.should eql(:unlocked)
@file.reload.last_inline_view.should be_nil
end
it "should redirect to an existing attachment with the same path as a deleted attachment" do

View File

@ -2316,6 +2316,17 @@ describe Assignment do
submission[:late].should == user.submissions.first.late?
end
end
it "should include inline view pingback url for files" do
course_with_teacher :active_all => true
student_in_course :active_all => true
assignment = @course.assignments.create! :submission_types => ['online_upload']
attachment = @student.attachments.create! :uploaded_data => dummy_io, :filename => 'doc.doc', :display_name => 'doc.doc', :context => @student
submission = assignment.submit_homework @student, :submission_type => :online_upload, :attachments => [attachment]
json = assignment.speed_grader_json @teacher
attachment_json = json['submissions'][0]['submission_history'][0]['submission']['versioned_attachments'][0]['attachment']
attachment_json['view_inline_ping_url'].should match %r{/users/#{@student.id}/files/#{attachment.id}/inline_view\z}
end
end
describe "update_student_submissions" do