module JamRuby class Band < ActiveRecord::Base attr_accessible :name, :website, :biography, :city, :state, :country, :original_fpfile_photo, :cropped_fpfile_photo, :cropped_large_fpfile_photo, :cropped_s3_path_photo, :cropped_large_s3_path_photo, :crop_selection_photo, :photo_url, :large_photo_url attr_accessor :updating_photo, :skip_location_validation self.primary_key = 'id' before_save :stringify_photo_info , :if => :updating_photo validates :biography, no_profanity: true, presence:true, length: {maximum: 4000} validates :name, presence: true validates :country, presence: true, :unless => :skip_location_validation validates :state, presence: true, :unless => :skip_location_validation validates :city, presence: true, :unless => :skip_location_validation validate :validate_photo_info validate :require_at_least_one_genre validate :limit_max_genres before_save :check_lat_lng before_save :check_website_url # musicians has_many :band_musicians, :class_name => "JamRuby::BandMusician" has_many :users, :through => :band_musicians, :class_name => "JamRuby::User" # genres has_many :band_genres, class_name: "JamRuby::BandGenre" has_many :genres, class_name: "JamRuby::Genre", :through => :band_genres # recordings has_many :recordings, :class_name => "JamRuby::Recording", :foreign_key => "band_id" # self.id = likable_id in likes table has_many :likers, :as => :likable, :class_name => "JamRuby::Like", :dependent => :destroy # self.id = followable_id in follows table has_many :followers, :as => :followable, :class_name => "JamRuby::Follow", :dependent => :destroy # invitations has_many :invitations, :inverse_of => :band, :class_name => "JamRuby::BandInvitation", :foreign_key => "band_id" # music_sessions has_many :music_sessions, :class_name => "JamRuby::MusicSession", :foreign_key => "band_id" has_many :music_session_history, :class_name => "JamRuby::MusicSessionHistory", :foreign_key => "band_id", :inverse_of => :band # events has_many :event_sessions, :class_name => "JamRuby::EventSession" include Geokit::ActsAsMappable::Glue unless defined?(acts_as_mappable) acts_as_mappable def liker_count return self.likers.size end def follower_count return self.followers.size end def recording_count return self.recordings.size end def session_count return self.music_sessions.size end def recent_history recordings = Recording.where(:band_id => self.id) .order('created_at DESC') .limit(10) msh = MusicSessionHistory.where(:band_id => self.id) .order('created_at DESC') .limit(10) recordings.concat(msh) recordings.sort! {|a,b| b.created_at <=> a.created_at}.first(5) end def location loc = self.city.blank? ? '' : self.city loc = loc.blank? ? self.state : "#{loc}, #{self.state}" unless self.state.blank? #loc = loc.blank? ? self.country : "#{loc}, #{self.country}" unless self.country.blank? loc end def validate_photo_info if updating_photo # we want to mak sure that original_fpfile and cropped_fpfile seems like real fpfile info objects (i.e, json objects from filepicker.io) errors.add(:original_fpfile_photo, ValidationMessages::INVALID_FPFILE) if self.original_fpfile_photo.nil? || self.original_fpfile_photo["key"].nil? || self.original_fpfile_photo["url"].nil? errors.add(:cropped_fpfile_photo, ValidationMessages::INVALID_FPFILE) if self.cropped_fpfile_photo.nil? || self.cropped_fpfile_photo["key"].nil? || self.cropped_fpfile_photo["url"].nil? errors.add(:cropped_large_fpfile_photo, ValidationMessages::INVALID_FPFILE) if self.cropped_large_fpfile_photo.nil? || self.cropped_large_fpfile_photo["key"].nil? || self.cropped_large_fpfile_photo["url"].nil? end end def add_member(user_id, admin) BandMusician.create(:band_id => self.id, :user_id => user_id, :admin => admin) end def self.musician_index(band_id) @musicians = User.joins(:band_musicians).where(:bands_musicians => {:band_id => "#{band_id}"}) end def self.pending_musicians(band_id) @musicians = User.joins(:received_band_invitations) .where(:band_invitations => {:band_id => "#{band_id}"}) .where(:band_invitations => {:accepted => nil}) end def self.recording_index(current_user, band_id) hide_private = false band = Band.find(band_id) # hide private Recordings from anyone who's not in the Band unless band.users.exists? current_user hide_private = true end if hide_private recordings = Recording.joins(:band_recordings) .where(:bands_recordings => {:band_id => "#{band_id}"}, :public => true) else recordings = Recording.joins(:band_recordings) .where(:bands_recordings => {:band_id => "#{band_id}"}) end return recordings end def self.build_band(user, params) id = params[:id] # ensure person creating this Band is a Musician unless user.musician? raise PermissionError, "must be a musician" end band = id.blank? ? Band.new : Band.find(id) # ensure user updating Band details is a Band member unless band.new_record? || band.users.exists?(user) raise PermissionError, ValidationMessages::USER_NOT_BAND_MEMBER_VALIDATION_ERROR end band.name = params[:name] if params.has_key?(:name) band.website = params[:website] if params.has_key?(:website) band.biography = params[:biography] if params.has_key?(:biography) band.city = params[:city] if params.has_key?(:city) band.state = params[:state] if params.has_key?(:state) band.country = params[:country] if params.has_key?(:country) band.photo_url = params[:photo_url] if params.has_key?(:photo_url) band.logo_url = params[:logo_url] if params.has_key?(:logo_url) if params.has_key?(:genres) && params[:genres] # loop through each genre in the array and save to the db genres = [] params[:genres].each { |genre_id| genres << Genre.find(genre_id) } band.genres = genres end band end # helper method for creating / updating a Band def self.save(user, params) band = build_band(user, params) if band.save # add the creator as the admin BandMusician.create(:band_id => band.id, :user_id => user.id, :admin => true) if params[:id].blank? end band end def escape_filename(path) dir = File.dirname(path) file = File.basename(path) "#{dir}/#{ERB::Util.url_encode(file)}" end def update_photo(original_fpfile, cropped_fpfile, cropped_large_fpfile, crop_selection, aws_bucket) self.updating_photo = true cropped_s3_path = cropped_fpfile["key"] cropped_large_s3_path = cropped_large_fpfile["key"] self.update_attributes( :original_fpfile_photo => original_fpfile, :cropped_fpfile_photo => cropped_fpfile, :cropped_large_fpfile_photo => cropped_large_fpfile, :cropped_s3_path_photo => cropped_s3_path, :cropped_large_s3_path_photo => cropped_large_s3_path, :crop_selection_photo => crop_selection, :photo_url => S3Util.url(aws_bucket, escape_filename(cropped_s3_path), :secure => false), :large_photo_url => S3Util.url(aws_bucket, escape_filename(cropped_large_s3_path), :secure => false)) end def delete_photo(aws_bucket) Band.transaction do unless self.cropped_s3_path_photo.nil? S3Util.delete(aws_bucket, File.dirname(self.cropped_s3_path_photo) + '/cropped.jpg') S3Util.delete(aws_bucket, self.cropped_s3_path_photo) S3Util.delete(aws_bucket, self.cropped_large_s3_path_photo) end return self.update_attributes( :original_fpfile_photo => nil, :cropped_fpfile_photo => nil, :cropped_large_fpfile_photo => nil, :cropped_s3_path_photo => nil, :cropped_large_s3_path_photo => nil, :crop_selection_photo => nil, :photo_url => nil, :large_photo_url => nil) end end def check_lat_lng if (city_changed? || state_changed? || country_changed?) update_lat_lng end true end def update_lat_lng if self.city query = { :city => self.city } query[:region] = self.state unless self.state.blank? query[:country] = self.country unless self.country.blank? if geo = MaxMindGeo.where(query).limit(1).first geo.lat = nil if geo.lat = 0 geo.lng = nil if geo.lng = 0 if geo.lat && geo.lng && (self.lat != geo.lat || self.lng != geo.lng) self.lat, self.lng = geo.lat, geo.lng return true end end end self.lat, self.lng = nil, nil false end def check_website_url if website_changed? && self.website.present? self.website.strip! self.website = "http://#{self.website}" unless self.website =~ /^http/ end true end def to_s name end def in_real_session?(session) b_members = self.users.sort_by(&:id).map(&:id) s_members = session.users.sort_by(&:id).map(&:id) (b_members - s_members).blank? end private def require_at_least_one_genre if self.genres.size < Limits::MIN_GENRES_PER_BAND errors.add(:genres, ValidationMessages::BAND_GENRE_MINIMUM_NOT_MET) end end def limit_max_genres if self.genres.size > Limits::MAX_GENRES_PER_BAND errors.add(:genres, ValidationMessages::BAND_GENRE_LIMIT_EXCEEDED) end end def stringify_photo_info # fpfile comes in as a hash, which is a easy-to-use and validate form. However, we store it as a VARCHAR, # so we need t oconvert it to JSON before storing it (otherwise it gets serialized as a ruby object) # later, when serving this data out to the REST API, we currently just leave it as a string and make a JSON capable # client parse it, because it's very rare when it's needed at all self.original_fpfile_photo = original_fpfile_photo.to_json if !original_fpfile_photo.nil? self.cropped_fpfile_photo = cropped_fpfile_photo.to_json if !cropped_fpfile_photo.nil? self.crop_selection_photo = crop_selection_photo.to_json if !crop_selection_photo.nil? end end end