Skip to content

Instantly share code, notes, and snippets.

@soeffing
Last active August 29, 2015 14:12
Show Gist options
  • Save soeffing/ea811324890331bed021 to your computer and use it in GitHub Desktop.
Save soeffing/ea811324890331bed021 to your computer and use it in GitHub Desktop.
Ad Model
require 'open-uri'
class Ad < ActiveRecord::Base
# NOTE: These need to come before inclusion of CountsEntries module
STATES = [:uploaded, :encoded, :published, :invalid]
AVAILABLE_TYPES = %w(Video Print Radio Banner Concept)
include CountsEntries
include AASM
include ZooppaAnalytics
aasm_column :state
aasm.initial_state :uploaded
aasm.state :uploaded, enter: :do_upload
aasm.state :encoded, enter: :do_encode
aasm.state :published, enter: :do_publish
aasm.state :invalid, enter: :do_invalid
# Just formal event to support the 'upload!' method without a possibility to actually change the state
aasm.event :upload do
transitions to: :uploaded, from: :uploaded
end
aasm.event :encode do
transitions to: :encoded, from: :uploaded, guard: proc { |ad| ad.resource.is_a?(Video) }
end
aasm.event :publish do
transitions to: :published, from: :uploaded, guard: proc { |ad| ad.valid? && ad.resource.valid? }
transitions to: :published, from: :encoded, guard: proc { |ad| ad.resource.flv_encoded? && ad.resource.mp4_encoded? && ad.resource.ogg_encoded? && ad.valid? && ad.resource.valid? }
transitions to: :published, from: :published
end
aasm.event :invalid do
transitions to: :invalid, from: :encoded, guard: proc { |ad| ad.resource.is_a?(Video) }
transitions to: :invalid, from: :published, guard: proc { |ad| ad.resource.is_a?(Video) }
end
acts_as_url :title, url_attribute: :permalink
attr_accessible :title, :description, :copyright_disclaimer, :submission_agreement,
:user, :state, :muted, :hp_featured, :approved,
:facebook_upload, :new_clearance_attributes, :existing_clearance_attributes,
:clearance_video_author, :clearance_video_hire, :clearance_video_licensed,
:clearance_audio_author, :clearance_audio_hire, :clearance_audio_licensed, :clearance_audio_not_applicable,
:clearance_stills_author, :clearance_stills_hire, :clearance_stills_licensed, :clearance_stills_not_applicable,
:clearance_talent_release, :clearance_talent_not_applicable,
:clearance_meets_special_age_requirements,
:incorrect_grammar, :moderation_flags, :moderation_flags_attributes,
:contest_id, :display, :user_id, :resource_id, :resource_type, :recommended, :average_rating
attr_readonly :votes_count
delegate :login, :email, :platform, to: :user, prefix: true
delegate :country, to: 'user.profile', prefix: 'user'
belongs_to :resource, polymorphic: true
belongs_to :contest, class_name: '::Contest'
belongs_to :user, touch: true, class_name: '::User'
has_many :votes, dependent: :delete_all
has_many :awards, class_name: '::Award'
has_many :comments, dependent: :destroy
has_many :tickets
has_many :clearances, dependent: :destroy
has_many :moderation_flags, dependent: :destroy, class_name: 'Moderation::Flag'
has_many :shortlists, dependent: :destroy
has_many :ad_ratings, dependent: :destroy
has_many :my_favorites, dependent: :destroy
accepts_nested_attributes_for :clearances, :moderation_flags
validates :copyright_disclaimer, acceptance: true, on: :create, allow_nil: false
validates :submission_agreement, acceptance: true, on: :create, allow_nil: false,
if: proc { |ad| ad.contest.controlling_platform.submission_agreement.present? }
validates :user, :contest, :title, :description, presence: true
validates :title, length: { maximum: 40 }
validates_associated :clearances
before_create :set_display_flag
before_update :check_display_flag
after_create :encode_or_publish
after_create :upload_event_segment_com
after_create :create_moderation_flags
after_update :save_clearances
AVAILABLE_TYPES.each do |t|
scope t.downcase.pluralize.to_sym, -> { where resource_type: t }
end
scope :visible, -> { where display: true }
scope :from_facebook, -> { where facebook_upload: true }
def encode_or_publish
resource.is_a?(Video) ? encode! : publish!
user.update_seniority(contest.controlling)
end
def to_param
permalink
end
class << self
def detail(resource_type, permalink, contest_permalink, page, admin = false)
conditions = { resource_type: resource_type, permalink: permalink, contests: { permalink: contest_permalink } }
conditions.merge!(ads: { display: true, state: %w(published encoded) }) unless admin
ad = includes(:contest, :comments).references(:ads, :contests, :comments).find_by!(conditions)
comments = ad.comments.includes(:user).order('created_at DESC').paginate(page: page, per_page: 20)
related = ad.related(8)
p, n = ad.prev, ad.next
[ad.decorate, comments, related, p, n]
end
end
def update_ad_and_resource(user, params)
check_if_state_changed(params[:ad][:state]) if user.admin?
resource.update_attributes!(params[:ad][:resource]) if params[:ad][:resource].present?
update_attributes!(params[:ad].except(:resource))
# Allow re-encoding only for published Ads
if resource_type == 'Video' && published? && params[:ad][:resource].present? && params[:ad][:resource][:original].present?
update_encoded
end
end
def can_be_voted?(user)
user && contest.opened? && user.active? && UserMiscellaneousPolicy.new(user).can_vote?(self) && !voted?(user)
end
def vote(user_id)
votes.find_by_user_id(user_id).rate
end
def voted?(user)
return false if user.nil?
votes.any? { |vote| vote.user_id == user.id }
end
def others
contest.ads.published.visible.where(resource_type: resource_type)
end
def related(n)
others.order('RAND()').limit(n)
end
def prev
others.order('created_at DESC').find_by('created_at < ?', created_at)
end
def next
others.order('created_at ASC').find_by('created_at > ?', created_at)
end
def position
index = others.order('score DESC').where(resource_type: resource_type).index(self) || 0
index + 1
end
def new_clearance_attributes=(clearance_attributes)
clearance_attributes = clearance_attributes.map { |k, v| v } if clearance_attributes.is_a?(Hash)
clearance_attributes.each do |attributes|
clearances.build(attributes)
end
end
def existing_clearance_attributes=(clearance_attributes)
clearances.reject(&:new_record?).each do |clearance|
attributes = clearance_attributes[clearance.id.to_s]
if attributes
clearance.attributes = attributes
else
clearances.delete(clearance)
end
end
end
def requires_video_clearance?
%w(Video).include?(resource_type)
end
def requires_audio_clearance?
%w(Video Radio).include?(resource_type)
end
def requires_stills_clearance?
%w(Video Print Banner).include?(resource_type)
end
def requires_talent_clearance?
%w(Video Radio Print Banner).include?(resource_type)
end
def requires_clearances?
requires_video_clearance? || requires_audio_clearance? || requires_stills_clearance? || requires_talent_clearance?
end
def save_clearances
clearances.each do |clearance|
clearance.save(validate: false)
end
end
Clearance::ASSET_TYPES.each do |type|
define_method "#{ type }_clearances_count" do
send("requires_#{ type }_clearance?") ? clearances.send(type).count : 0
end
end
def check_if_state_changed(new_state)
return nil if state == new_state
case new_state
when 'uploaded'
upload!
when 'encoded'
encode!
when 'published'
publish!
when 'invalid'
invalid!
end
end
def update_encoded
# Here 'E198OGRLS9PMVD' is CloudFront dist ID for encoded
if Rails.env.production? && !CloudFrontInvalidator.new('E198OGRLS9PMVD').invalidate([resource.url, resource.url('ogg'), resource.url('mp4')])
Rails.logger.error "[CLOUDFRONT VIDEO] Attachment invalidation failed: #{resource.original.url}"
end
do_encode
end
def path(contest_link = contest.permalink)
host = ApplicationConfig['host']
path = "en-us/ads/#{ contest_link }/#{ resource_type.pluralize.downcase }/#{ permalink }"
"http://#{ host }/#{ path }"
end
def get_moderation_flag_type(type)
moderation_flags.find_or_initialize_by(flag_type: type)
end
def clearances_counter
v = 0
Clearance::ASSET_TYPES.each do |type|
v += send("#{type}_clearances_count")
end
v
end
def update_moderation_flags(params)
params[:ad][:moderation_flags].each do |flag_type, value|
flag = Moderation::Flag.where(ad_id: self.id, flag_type: flag_type).first
if flag
flag.update_state(flag, value)
else
new_flag = Moderation::Flag.new(ad_id: self.id, user_id: self.user_id, flag_type: flag_type)
self.save!
new_flag.update_state(new_flag, value)
self.save!
end
end
end
def moderation_status
if moderation_flags.empty?
'under review'
elsif moderation_flags.any?{ |f| f.state == 'rejected' }
'incomplete'
elsif moderation_flags.all?{ |f| f.state == 'accepted' }
'complete'
else
'under review'
end
end
def moderation_status_available?
!moderation_flags.empty?
end
protected
def set_display_flag
self.display = false if contest.moderated?
nil
end
def check_display_flag
PublicationNotifier.new(self).perform if display_changed? && display? && contest.moderated?
end
def self.add_view!(resource_type, permalink_or_id)
# Flash players use ad ID while API and new video player use permalink
numeric = permalink_or_id.to_s =~ /^[0-9]+$/
if numeric
ad = Ad.find_by_resource_type_and_resource_id!(resource_type, permalink_or_id)
else
ad = Ad.find_by_resource_type_and_permalink!(resource_type, permalink_or_id)
end
Ad.increment_counter(:views, ad.id)
end
private
def upload_event_segment_com
ZooppaAnalytics.upload(self.contest, self, self.user) if Rails.env.production?
end
def create_moderation_flags
Moderation::Flag::AVAILABLE_TYPES.each do |type|
Moderation::Flag.create(ad_id: self.id, user_id: self.user_id, flag_type: type)
end
end
def do_upload
UploadNotifier.new(self).perform
self.display = false if resource.is_a?(Video)
end
def do_encode
VideoEncoder.new(resource).perform
self.display = false
end
def do_publish
PublicationNotifier.new(self).perform
if resource.is_a?(Video)
self.display = !self.contest.moderated
end
end
def do_invalid
InvalidationNotifier.new(self).perform
self.display = false
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment