angelakuo/citydogshare

View on GitHub
app/models/dog.rb

Summary

Maintainability
B
4 hrs
Test Coverage
class Dog < ActiveRecord::Base
  include ActiveModel::ForbiddenAttributesProtection
  # attr_accessible :name, :image, :dob, :gender, :description, :motto, :fixed, :health, :comments, :contact, :availability, :mixes, :likes, :energy_level, :size, :personalities, :photo, :latitude, :longitude, :video

  scope :has_gender, lambda {|genders| filter_gender(genders)}
  scope :has_personalities, lambda {|personalities| filter_personality(personalities)}
  scope :has_likes, lambda {|likes| filter_like(likes)}
  scope :has_mix, lambda {|mix| filter_mix(mix)}
  scope :has_bark, lambda {|bark| filter_bark(bark)}
  scope :has_energy_level, lambda {|energy_levels| filter_energy_level(energy_levels)}
  scope :has_size, lambda {|sizes| filter_size(sizes)}
  scope :in_age_range, lambda {|age_query| filter_age(age_query)}

  belongs_to :user
  belongs_to :energy_level
  belongs_to :size
  
  has_many :stars, :dependent => :destroy
  has_many :dog_mix_linkers
  has_many :dog_like_linkers
  has_many :dog_personality_linkers
  has_many :dog_bark_linkers
  has_many :mixes, :through => :dog_mix_linkers
  has_many :likes, :through => :dog_like_linkers
  has_many :personalities, :through => :dog_personality_linkers
  has_many :barks, :through => :dog_bark_linkers
  has_and_belongs_to_many :events

  geocoded_by :address

  validates :name, :presence => {:message => "Please enter a name"}
  validates :gender, :presence => {:message => "Please select a gender"}
  validates :size, :presence => {:message => "Please select a size"}
  validates :mixes, :presence => {:message => "Please select the mix"}
  validates :personalities, :presence => {:message => "Please select at least one personality"}
  validates_inclusion_of :fixed, in: [true, false], :message => "Please select a response for spayed/neutered"
  validates_inclusion_of :chipped, in: [true, false], :message => "Please select a response for chipped"
  validate :validate_dob
  validate :validate_availability
  validate :validate_photo_size

  #paperclip avatar
  has_attached_file :photo, 
                    :styles => { :small    => '150x',
                                 :medium   => '300x',
                                 :large    => '600x'},
                    :default_url => "",
                    :storage => :s3,
                    :bucket => ENV["AWS_BUCKET_NAME"],
                    :path => "/:class/:images/:id/:style/:basename.:extension"

  validates_attachment_content_type :photo, :content_type => ['image/jpeg', 'image/png']

  #paperclip dog multiple pictures
  has_many :pictures, :dependent => :destroy

  after_validation :geocode

  ## Attribute Access Functions
  def validate_dob
    errors.add(:dob, "Dog's birthday can't be in the future.") if (!dob.nil? and dob > Date.today)
  end
  
  def validate_availability
    errors.add(:availability, "- Please select a proper availability.") if (!availability.nil? and availability != "Available" and availability != "Unavailable")
  end

  def validate_photo_size
    errors[:photo] << "should be less than 3MB" if photo and photo.size and photo.size > 3.megabytes
    pictures.each do |picture|
      if picture.image_file_size > 3.megabytes
        errors[:pictures] << "should be less than 3MB"
        break
      end
    end
  end
  
  def age
    now = Time.now.utc.to_date
    now.year - dob.year - ((now.month > dob.month || (now.month == dob.month && now.day >= dob.day)) ? 0 : 1)
  end

  def age_caption
    y = age
    out = "< 1 year old" if y == 0
    out = "1 year old" if y == 1
    out = "#{y} years old" if y > 1
    out = "" if dob.year == 0
    out
  end

  def tags
    readable_personalities.to_sentence
  end

  def owner
    User.find(self.user_id)
  end
  
  def readable_size
    self.size.value.capitalize
  end
  
  def readable_energy_level
    self.energy_level ? self.energy_level.value.capitalize : ""
  end

  def readable_mixes
    self.mixes.map {|m| m.value}
  end

  def readable_likes
    self.likes.map {|l| l.value.capitalize}
  end

  def readable_personalities
    self.personalities.map {|p| p.value.capitalize}
  end

  def readable_barks
    self.barks.map {|b| b.value.capitalize}
  end
  
  def readable_fixed
    self.fixed ? "Yes" : "No"
  end
  
  def readable_chipped
    self.chipped ? "Yes" : "No"
  end
  
  def readable_shots_to_date
    if !self.shots_to_date.nil?
      self.shots_to_date ? "Yes" : "No"
    else
      "Unknown"
    end
  end

  def address
    user = self.owner
    "#{user.zipcode}"
  end

  def youtube_id
    video.split(%r{v=|&})[1]
  end
  
  def available
    self.availability && self.availability != "Unavailable" ? true : false
  end
  
  def to_form_hash
    return {
      :name => self.name,
      :dob => self.dob.year,
      :size => self.size.id,
      :gender => self.gender,
      :mixes => self.readable_mixes,
      :personalities => self.readable_personalities.map {|val| val.downcase},
      :energy_level => self.energy_level ? self.energy_level.id : 0,
      :likes => self.readable_likes.map {|val| val.downcase},
      :barks => self.readable_barks,
      :fixed => self.fixed,
      :chipped => self.chipped,
      :shots_to_date => self.shots_to_date,
      :health => self.health,
      :status => self.motto,
      :description => self.description,
      :availability => self.availability,
    }
  end
  
  def to_json
    return {
      :id => self.id,
      :name => self.name,
      :dob => self.dob.year,
      :size => self.readable_size,
      :gender => self.gender,
      :mixes => self.readable_mixes,
      :personalities => self.readable_personalities,
      :energy_level => self.readable_energy_level,
      :likes => self.readable_likes,
      :barks => self.readable_barks,
      :fixed => self.fixed,
      :chipped => self.chipped,
      :shots_to_date => self.shots_to_date,
      :health => self.health,
      :status => self.motto,
      :description => self.description,
      :available => self.available,
      :parent => self.user_id
    }
  end

  ## Attribute Possible Values Functions

  def self.genders
    ["Male", "Female"]
  end

  def self.age_ranges
    ["0-2 years", "2-4 years", "5-8 years", "9+ years"]
  end

  ## Filter Functions for Search Results (Dog Index Page)

  def self.filter_gender(genders)
    where("gender" => genders) unless genders.empty?
  end

  def self.filter_personality(personalities)
    joins(:personalities).where("personalities.value" => personalities) unless personalities.empty?
  end

  def self.filter_like(likes)
    joins(:likes).where("likes.value" => likes) unless likes.empty?
  end

  def self.filter_mix(mix)
    joins(:mixes).where("mixes.value" => mix) unless mix == "All Mixes"
  end
  
  def self.filter_bark(barks)
    joins(:barks).where("barks.value" => bark) unless barks.empty?
  end

  def self.filter_energy_level(energy_levels)
    joins(:energy_level).where("energy_levels.value" => energy_levels) unless energy_levels.empty?
  end

  def self.filter_size(sizes)
    joins(:size).where("sizes.value" => sizes) unless sizes.empty?
  end
  
  def self.filter_age(age_query)
    where(age_query) unless age_query == ""
  end

  def self.convert_age_ranges_to_dob_query(age_ranges_indices)
    age_ranges = [[0, 2], [2, 4], [5, 8], [9, 30]]
    age_query = ""
    selected_counter = 0
    num_selected = age_ranges_indices.length
    age_ranges_indices.each do |i|
        index = Dog.age_ranges.index(i)
        base = get_base(index, age_ranges)
        selected_counter += 1
        if selected_counter < num_selected
          age_query += (base + " OR ")
        else
          age_query += base
        end
    end
    age_query
  end

  def self.get_base(i, ranges)
    first = (Time.now - ranges[i][1].years).strftime "%Y-%m-%d %H:%M:%S"
    second = (Time.now - ranges[i][0].years).strftime "%Y-%m-%d %H:%M:%S"
    base = %Q[("dogs"."dob" BETWEEN '#{first}' AND '#{second}')]
  end

  def self.filter_by(criteria)
    dogs = Dog.near(criteria[:zipcode], criteria[:radius])
              .has_mix(criteria[:mix])
              .has_size(criteria[:size])
              .has_likes(criteria[:like])
              .has_personalities(criteria[:personality])
              .has_bark(criteria[:bark])
              .has_gender(criteria[:gender])
              .has_energy_level(criteria[:energy_level])
              .in_age_range(convert_age_ranges_to_dob_query(criteria[:age]))
  end
  
  # Event Methods
  def future_events?
    # for all events, if at least one comes after yesterday, return true
    self.events.where("end_date > ?", 1.day.ago.midnight).pluck('end_date') != []
  end

  def future_events
    self.events.where("end_date > ?", 1.day.ago.midnight).order("start_date ASC")
  end

end