Created
March 3, 2013 20:09
-
-
Save cgat/5078052 to your computer and use it in GitHub Desktop.
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 CaptureImage < ActiveRecord::Base | |
include NumberHelper | |
include ImageHelper | |
is_filesystemable | |
attr_accessible :captureable_id, :captureable_type, :bit_depth, :image, :image_remote, :file_size, :hash_key, :image_state, :x_dim, :y_dim, :remote, :image_secure_token, :comments, :legacy_path | |
attr_accessible :image_cache | |
attr_accessor :remote, :original_image_state | |
#important ordering here. move_filesystem_object but come before save_metadata (otherwise the metadata function will look in the wrong place for the image file) | |
before_update :save_metadata | |
before_create :save_metadata | |
before_save :config_for_remote_upload, if: :remote_state_changed? | |
before_validation :convert_image_state_number_to_string | |
#forces record save and callbacks if only the remote attribute has changed. | |
before_validation :change_record_updated_at, if: :remote_state_changed?, if: "!self.changed?" | |
before_destroy :convert_image_state_number_to_string | |
after_destroy :clean_up | |
belongs_to :captureable, polymorphic: true | |
alias :parent :captureable | |
mount_uploader :image, ImageUploader | |
store_in_background :image | |
mount_uploader :image_remote, ImageRemoteUploader | |
validates :image_state, inclusion: { in: IMAGE_STATES } | |
#validates :image, presence: true, if: 'self.new_record?' | |
validates :captureable_type, inclusion: { in: ['Capture','HistoricCapture']} | |
validate :allowable_formats_for_remote, if: :remote_state? | |
def file_present? | |
if image.present? | |
File.exists?(self.image.file.path) | |
elsif image_tmp.present? | |
true | |
else | |
false | |
end | |
end | |
def display_name | |
if image.blank? && image_tmp.blank? | |
"UNKNOWN" | |
elsif image_tmp.present? && image_tmp=~/.*\/([^\/]+)$/ | |
$1 #if we are here, then image is processing | |
elsif image_secure_token.present? | |
self.image.file.filename.sub("_"+self.image_secure_token, "") | |
else | |
self.image.file.filename | |
end | |
end | |
def allowable_formats_for_remote | |
if image.present? && image.file.filename=~/\.(3FR)$/i | |
errors.add(:remote, "Remote copying of 3FR images is not currently supported. If you need to serve this image remotely, please add the tiff version (interim) to the capture and add that file remotely.") | |
end | |
end | |
def convert_image_state_number_to_string | |
if !self.image_state.nil? && integer?(self.image_state) | |
self.image_state = IMAGE_STATES[self.image_state.to_i] | |
end | |
end | |
def remote_state? | |
self.remote==true || self.remote==1 || self.remote=="1" | |
end | |
def remote_state_changed? | |
((self.remote==true || self.remote==1 || self.remote=="1") && self.image_remote.blank?) || \ | |
((self.remote==false || self.remote==0 || self.remote=="0") && !self.image_remote.blank?) | |
end | |
#this is used to force the record to save to db, even when attributes have not changed yet. | |
#this is because the remote attribute, which is not a column, may have changed. If it has changed | |
#the image_remote column will be configured in the config_for_remote_upload before_save callback. | |
def change_record_updated_at | |
self.updated_at = Time.current | |
end | |
#note that carrierwave will naturally delete the file in its callback | |
#but in order to clean the parent directory, we first need to remove | |
#the file ourselves | |
def clean_up | |
self.remove_image! | |
self.clean(self.filesystem_dir) | |
end | |
def save_metadata | |
if File.exists?(self.image.file.path) || File.exists?(self.fs_path) | |
begin | |
path = File.exists?(self.image.file.path) ? self.image.file.path : self.fs_path | |
@exif = MiniExiftool.new(path) | |
rescue ArgumentError => e | |
#with some files it miniexiftool throws an exception when there in invalid UTF-8 characters in the metadata | |
if e.message == "invalid byte sequence in UTF-8" | |
return true #silently fail. | |
else | |
raise e | |
end | |
end | |
else | |
return true #silently fail when file is missing | |
end | |
self.x_dim = @exif.imagewidth | |
self.y_dim = @exif.imageheight | |
self.bit_depth = @exif.bitspersample | |
self.file_size = self.image.file.size | |
if self.captureable_type=='Capture' | |
capture = Capture.find_by_id(self.captureable_id) | |
if !capture.nil? && (capture.capture_images.empty? || !capture.metadata_set?) | |
update_hash = {} | |
#exif can produce results as strings or as numerics. For us, it benefits to use both. By default, the miniexiftool will output string, so do this, then change to numerics | |
if @exif.datetimeoriginal | |
update_hash[:capture_datetime] = @exif.datetimeoriginal | |
end | |
update_hash[:shutter_speed] = (@exif.shutterspeed ||= @exif.exposuretime) | |
update_hash[:shutter_speed] = update_hash[:shutter_speed].to_s #store as a string instead of a rational | |
update_hash[:camera_make] = @exif.make | |
update_hash[:camera_model] = @exif.model | |
@exif.numerical = true | |
@exif.reload | |
update_hash[:iso] = @exif.iso | |
update_hash[:f_stop] = @exif.aperture ||= @exif.fnumber | |
update_hash[:focal_length] = @exif.focallength | |
update_hash[:lat] = @exif.gpslatitude | |
update_hash[:long] = @exif.gpslongitude | |
update_hash[:elevation] = @exif.gpsaltitude | |
capture.update_attributes(update_hash) | |
true #return, even if the capture update fails, allowing the capture image to be saved regardless of whether the capture update is successfully. | |
end | |
end | |
end | |
def filesystem_name | |
self.image.file.filename | |
end | |
def filesystem_parent | |
if self.captureable_type=='Capture' | |
capture_owner = self.captureable.capture_owner | |
case capture_owner.class.name | |
when "Location" | |
return capture_owner.visit | |
when "Visit","Station","SurveySeason", "Survey", "Project" | |
return capture_owner | |
else | |
raise StandardError, "The filesystem parent of this capture image is a #{capture_owner.class.name}, which is not permitted (or there is an error in the code)." | |
end | |
elsif self.captureable_type=='HistoricCapture' | |
return self.captureable.hcapture_owner | |
else | |
raise StandardError, "The parent of this capture image is a #{self.captureable_type}, which is not permitted." | |
end | |
end | |
def filesystem_dir | |
parent = self.filesystem_parent | |
File.join(parent.filesystem_path,self.image_state.humanize) | |
end | |
# def update_fs_path | |
# if self.fs_path != self.image.store_path | |
# update_column(:fs_path, self.image.store_path) | |
# end | |
# end | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment