Skip to content

Instantly share code, notes, and snippets.

@cgat
Created March 3, 2013 20:09
Show Gist options
  • Save cgat/5078052 to your computer and use it in GitHub Desktop.
Save cgat/5078052 to your computer and use it in GitHub Desktop.
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