Created
June 19, 2014 17:57
-
-
Save patrickmcelwee/8c5d5b452afc7f520a1f to your computer and use it in GitHub Desktop.
Automated Vivo Image Uploads
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
require 'vivo_mapper/resource' | |
require 'app/maps/image_map' | |
module VivoMapper | |
class Image < Resource | |
map_with(ImageMap) | |
attr_reader :type, :download_location, :thumb, :content, :image | |
def initialize(image,type='image') | |
@type=type | |
@image = image | |
@download_location = VivoMapper::ImageFileLocation.new(image,type) | |
if type == 'image' | |
@thumb = VivoMapper::Image.new(image.thumb,'thumb') | |
end | |
super() | |
end | |
def uid | |
"file_" + download_location.uid | |
end | |
def person | |
download_location.person | |
end | |
def vivo_id | |
download_location.vivo_id | |
end | |
def mime_type | |
return nil unless image.file | |
case image.file.extension | |
when 'jpg' || 'jpeg' || 'jpe' | |
'image/jpeg' | |
when 'png' | |
'image/png' | |
when 'gif' | |
'image/gif' | |
else | |
'image/jpeg' | |
end | |
end | |
def content | |
# What does this do? | |
image | |
end | |
def file_name | |
download_location.file_name | |
end | |
protected | |
def full_path_and_name | |
download_location.full_path_and_name | |
end | |
end | |
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
require 'vivo_mapper/resource' | |
require 'app/maps/image_file_location_map' | |
module VivoMapper | |
class ImageFileLocation < Resource | |
map_with(ImageFileLocationMap) | |
attr_reader :type, :image | |
def initialize(image,type) | |
@type=type | |
@image = image | |
super() | |
end | |
def vivo_id | |
@vivo_id ||= person.vivo_id | |
end | |
def person | |
@person ||= image.model | |
end | |
def uid | |
"#{type[0]}#{vivo_id}" | |
end | |
def file_name | |
prefix = "#{image.version_name}_" if image.version_name | |
"#{prefix}#{image.filename}" | |
end | |
def full_path_and_name | |
"/file/#{uid}/#{file_name}" | |
end | |
end | |
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
require 'vivo_mapper/rdf_prefixes' | |
require 'date' | |
module ImageFileLocationMap | |
include RdfPrefixes | |
extend self | |
def uri(namespace,f) | |
"#{namespace}#{f.uid}" | |
end | |
def most_specific_type(f) | |
vpublic("FileByteStream") | |
end | |
def properties(f) | |
{ vpublic("directDownloadUrl") => f.full_path_and_name, | |
vitro("modTime") => DateTime.now } | |
end | |
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
require 'vivo_mapper/rdf_prefixes' | |
require "diffable" | |
require 'date' | |
module ImageMap | |
include RdfPrefixes | |
extend Diffable | |
extend self | |
def uri(namespace,f) | |
"#{namespace}#{f.uid}" | |
end | |
def most_specific_type(f) | |
vpublic("File") | |
end | |
def properties(f) | |
properties = { | |
vpublic("filename") => f.file_name, | |
vpublic("downloadLocation") => f.download_location, | |
vpublic("mimeType") => f.mime_type, | |
vitro("modTime") => DateTime.now } | |
if f.type == 'image' | |
properties.merge! ({ | |
vpublic("thumbnailImage") => f.thumb, | |
duke("imageOf") => f.person.as_stub }) | |
end | |
properties | |
end | |
def sparql_constructs(person_uri) | |
sparql =<<-EOS | |
PREFIX vitro-public: <http://vitro.mannlib.cornell.edu/ns/vitro/public#> | |
CONSTRUCT { ?image ?p ?o. | |
?s ?p2 ?image. | |
?location ?p3 ?o2. | |
?s2 ?p4 ?location. | |
?thumb ?p5 ?o3. | |
?s3 ?p6 ?thumb. | |
?t_location ?p7 ?o4. | |
?s4 ?p8 ?t_location. | |
} | |
WHERE { <#{person_uri}> vitro-public:mainImage ?image. | |
?image vitro-public:thumbnailImage ?thumb. | |
?image ?p ?o. | |
?s ?p2 ?image. | |
?image vitro-public:downloadLocation ?location. | |
?location ?p3 ?o2. | |
?s2 ?p4 ?location. | |
?image vitro-public:thumbnailImage ?thumb. | |
?thumb ?p5 ?o3. | |
?s3 ?p6 ?thumb. | |
?thumb vitro-public:downloadLocation ?t_location. | |
?t_location ?p7 ?o4. | |
?s4 ?p8 ?t_location. | |
} | |
EOS | |
Array(sparql) | |
end | |
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
class PersonProfileImageCropper | |
constructor: -> | |
$('#cropbox').Jcrop | |
aspectRatio: 1 | |
setSelect: [0, 0, 600, 600] | |
onSelect: @update | |
onChange: @update | |
update: (coords) => | |
$('#profile_image_crop_x').val(coords.x) | |
$('#profile_image_crop_y').val(coords.y) | |
$('#profile_image_crop_w').val(coords.w) | |
$('#profile_image_crop_h').val(coords.h) | |
@updatePreview(coords) | |
updatePreview: (coords) => | |
$('#preview').css | |
width: Math.round(160/coords.w * $('#cropbox').width()) + 'px' | |
height: Math.round(160/coords.h * $('#cropbox').height()) + 'px' | |
marginLeft: '-' + Math.round(160/coords.w * coords.x) + 'px' | |
marginTop: '-' + Math.round(160/coords.h * coords.y) + 'px' |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
shared_context "profile_image" do | |
let(:krucoff) { FactoryGirl.create(:krucoff) } | |
let(:image_file_name) { "image_#{krucoff.vivo_id}.jpg" } | |
let(:thumb_file_name) { "thumb_image_#{krucoff.vivo_id}.jpg" } | |
let(:dir_path) { "PATH_TO_VIVO_DATA/data/uploads/file_storage_root" } | |
let(:image_base_path) { "#{dir_path}/a~i" } | |
let(:thumb_base_path) { "#{dir_path}/a~t" } | |
let(:image_path) { "#{image_base_path}/036/525/2/#{image_file_name}" } | |
let(:thumb_path) { "#{thumb_base_path}/036/525/2/#{thumb_file_name}" } | |
let(:namespace) { "https://scholars.duke.edu/individual/" } | |
let(:image_location_uid) { "i#{krucoff.vivo_id}" } | |
let(:image_uid) { "file_#{image_location_uid}" } | |
let(:image_uri) { namespace + image_uid } | |
let(:image_location_uri) { namespace + image_location_uid } | |
let(:thumb_location_uid) { "t#{krucoff.vivo_id}" } | |
let(:thumbnail_uid) { "file_#{thumb_location_uid}" } | |
let(:thumbnail_uri) { namespace + thumbnail_uid } | |
let(:thumb_location_uri) { namespace + thumb_location_uid } | |
let(:krucoff_uri) { namespace + "per0365252" } | |
let(:date_regex) { /http:\/\/www.w3.org\/2001\/XMLSchema#dateTime/ } | |
after(:each) do | |
clear_rdf_models %w(Image) | |
[image_base_path, thumb_base_path].each do |base_path| | |
FileUtils.rm_r(base_path + "/036") if Dir.exists?(base_path + "/036") | |
end | |
end | |
def image_attributes_should_be_loaded_correctly_in_rdf | |
image_attrs = query(file_sparql(image_uri)).first | |
image_attrs[:type].split('#')[1].should == "File" | |
image_attrs[:filename].should == image_file_name | |
image_attrs[:download_location].should == image_location_uri | |
image_attrs[:mime_type].should == "image/jpeg" | |
image_attrs[:thumb].should == thumbnail_uri | |
image_attrs[:person].should == krucoff_uri | |
image_attrs[:mod_time].should =~ date_regex | |
end | |
def image_location_attributes_should_be_loaded_correctly_in_rdf | |
image_location_attrs = query(location_sparql(image_location_uri)).first | |
image_location_attrs[:type].should == "http://vitro.mannlib.cornell.edu/ns/vitro/public#FileByteStream" | |
image_location_attrs[:download_url].should == "/file/#{image_location_uid}/#{image_file_name}" | |
image_location_attrs[:mod_time].should =~ date_regex | |
end | |
def thumbnail_attributes_should_be_loaded_correctly_in_rdf | |
thumbnail_attrs = query(file_sparql(thumbnail_uri, false)).first | |
thumbnail_attrs[:type].split('#')[1].should == "File" | |
thumbnail_attrs[:filename].should == thumb_file_name | |
thumbnail_attrs[:download_location].should == thumb_location_uri | |
thumbnail_attrs[:mime_type].should == "image/jpeg" | |
thumbnail_attrs[:mod_time].should =~ date_regex | |
end | |
def thumbnail_location_attributes_should_be_loaded_correctly_in_rdf | |
thumb_loc_attrs = query(location_sparql(thumb_location_uri)).first | |
thumb_loc_attrs[:type].should == "http://vitro.mannlib.cornell.edu/ns/vitro/public#FileByteStream" | |
thumb_loc_attrs[:download_url].should == "/file/#{thumb_location_uid}/#{thumb_file_name}" | |
thumb_loc_attrs[:mod_time].should =~ date_regex | |
end | |
def prefixes | |
<<-EOS | |
PREFIX vitro-public: <http://vitro.mannlib.cornell.edu/ns/vitro/public#> | |
PREFIX vitro: <http://vitro.mannlib.cornell.edu/ns/vitro/0.7#> | |
PREFIX duke: <http://vivo.duke.edu/vivo/ontology/duke-extension#> | |
EOS | |
end | |
def main_image_triples(image_uri, is_main_image) | |
if is_main_image | |
<<-EOS | |
OPTIONAL { <#{image_uri}> vitro-public:thumbnailImage ?thumb.} | |
OPTIONAL {<#{image_uri}> duke:imageOf ?person.} | |
EOS | |
end | |
end | |
def file_sparql(image_uri, is_main_image=true) | |
<<-EOS | |
#{prefixes} | |
SELECT * | |
WHERE { | |
OPTIONAL {<#{image_uri}> vitro:mostSpecificType ?type.} | |
OPTIONAL {<#{image_uri}> vitro-public:filename ?filename.} | |
OPTIONAL {<#{image_uri}> vitro-public:downloadLocation ?download_location.} | |
OPTIONAL {<#{image_uri}> vitro-public:mimeType ?mime_type.} | |
OPTIONAL {<#{image_uri}> vitro:modTime ?mod_time.} | |
OPTIONAL {#{main_image_triples(image_uri, is_main_image)}} | |
} | |
EOS | |
end | |
def location_sparql(image_location_uri) | |
<<-EOS | |
#{prefixes} | |
SELECT * | |
WHERE { | |
OPTIONAL {<#{image_location_uri}> vitro:mostSpecificType ?type.} | |
OPTIONAL {<#{image_location_uri}> vitro-public:directDownloadUrl ?download_url.} | |
OPTIONAL {<#{image_location_uri}> vitro:modTime ?mod_time.} | |
} | |
EOS | |
end | |
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
require 'carrierwave_imagevoodoo' | |
class ProfileImageUploader < CarrierWave::Uploader::Base | |
include CarrierWave::ImageVoodoo | |
# Include the Sprockets helpers for Rails 3.1+ asset pipeline compatibility: | |
# include Sprockets::Helpers::RailsHelper | |
# include Sprockets::Helpers::IsolatedHelper | |
storage :file | |
def store_dir(for_file=filename) | |
if version_name == :large_for_cropping | |
"vivo_admin/uploads/" | |
else | |
literal_root_path + namespacing + vivo_id_to_dir | |
end | |
end | |
version :large_for_cropping do | |
process :resize_to_limit => [600, 600] | |
end | |
version :thumb, from_version: :large_for_cropping do | |
process :crop | |
process :resize_to_fill => [200, 200] | |
end | |
def extension_white_list | |
%w(jpg jpeg gif png) | |
end | |
def crop | |
if model.crop_x.present? | |
manipulate! do |img| | |
x1 = model.crop_x.to_i | |
y1 = model.crop_y.to_i | |
x2 = model.crop_w.to_i + x1 | |
y2 = model.crop_h.to_i + y1 | |
img.with_crop(x1, y1, x2, y2) do |file| | |
file.save(self.current_path) | |
end | |
end | |
end | |
model.import_profile_image | |
end | |
# Override the filename of the uploaded files: | |
# Avoid using model.id or version_name here, see uploader/store.rb for details. | |
def filename | |
"image_#{vivo_id}.#{original_extension}" if original_filename | |
end | |
def url | |
if version_name == :large_for_cropping | |
super | |
else | |
file ? vivo_url : default_url | |
end | |
end | |
private | |
def original_extension | |
original_filename.split(".").last | |
end | |
def default_url | |
'person_thumbnail.jpg' | |
end | |
def vivo_url | |
"#{VivoAdmin::Application.config.base_url}/file/#{version_char}#{vivo_id}/#{file.filename}" | |
end | |
def literal_root_path | |
'/srv/web/apps/vivo/shared/data/uploads/file_storage_root/' | |
end | |
def namespacing | |
"a~#{version_char}/" | |
end | |
def version_char | |
version_name ? version_name.to_s[0] : 'i' | |
end | |
def vivo_id_to_dir | |
vivo_id.scan(/.{1,3}/).join('/') + '/' | |
end | |
def vivo_id | |
model.vivo_id | |
end | |
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
require 'rubygems' | |
require 'carrierwave' | |
require 'active_record' | |
require 'spec_helper_lite' | |
require_relative '../../app/uploaders/profile_image_uploader' | |
stub_class "VivoAdmin::Application" | |
describe ProfileImageUploader do | |
before(:each) do | |
@professor = double(vivo_id: '0365252') | |
described_class.enable_processing = false | |
@uploader = described_class.new(@professor, :profile_image) | |
end | |
context "before image uploaded" do | |
it "gives the correct default url" do | |
@uploader.url.should =~ /person_thumbnail\.jpg/ | |
end | |
end | |
context "when image uploaded" do | |
before(:each) do | |
@base_dir = "PATH_TO_VIVO_DATA/data/uploads/file_storage_root/" | |
@vivo_id_dir = "036/525/2/" | |
FileUtils.mkdir_p @base_dir + 'a~i' + @vivo_id_dir | |
FileUtils.mkdir_p @base_dir + 'a~t' + @vivo_id_dir | |
@uploader.store!(File.open("spec/support/images/person_profile/0117469.jpg")) | |
end | |
after(:each) do | |
@uploader.remove! | |
end | |
it "stores the main image file in the correct place" do | |
image_file_name = "image_#{@professor.vivo_id}.jpg" | |
@uploader.file.file.should == @base_dir + 'a~i/' + @vivo_id_dir + image_file_name | |
end | |
it "stores the thumbnail file in the correct place" do | |
thumb_file_name = "thumb_image_#{@professor.vivo_id}.jpg" | |
@uploader.thumb.file.file.should == @base_dir + 'a~t/' + @vivo_id_dir + thumb_file_name | |
end | |
it "gives the image the correct url" do | |
config = double(base_url: '') | |
VivoAdmin::Application.stub!(:config).and_return(config) | |
@uploader.url.should == "/file/i#{@professor.vivo_id}/image_#{@professor.vivo_id}.jpg" | |
end | |
end | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Here are some of the files that handle the automated image uploads - the 'uploader' files are mostly concerned with cropping and placing the image files in the correct places in the filesystem, using the carrierwave ruby library. The spec files specify the two important parts of the process: 1, where we want those images saved; and 2, what triples we want to create. Creating the large (600x600) and thumbnail (200x200) images is perhaps the third important part of the process.
The map files show the predicates and values we are putting into the RDF. 'image' and 'image_location' are the resources that the map file uses to get its values.
We also have a batch uploader (not included here) that pushes a directory of images through this code.
Hope that's helpful. Cleaner and clearer documentation of what is required to do this would certainly fill a gap. (Or perhaps the file storage could be configurable through the triples themselves, which would make this quite a bit easier, I think.)