adding cached vote stats fields and hall of famers
parent
e8e0cb98d0
commit
675e244864
|
@ -8,6 +8,8 @@ class Home < Application
|
||||||
end
|
end
|
||||||
|
|
||||||
def hall_of_fame
|
def hall_of_fame
|
||||||
|
@top_oneness = Photo.find :all, :order => 'oneness DESC, id DESC', :limit => 10, :conditions => 'oneness > 0'
|
||||||
|
@top_voted = Photo.find :all, :order => 'votes_count DESC, id DESC', :limit => 10, :conditions => 'votes_count > 0'
|
||||||
render
|
render
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -8,52 +8,16 @@ class Photo < ActiveRecord::Base
|
||||||
|
|
||||||
before_create :validate_image_sanity
|
before_create :validate_image_sanity
|
||||||
before_create :hashify_email
|
before_create :hashify_email
|
||||||
|
before_save :set_oneness
|
||||||
|
|
||||||
after_create :create_directories
|
after_create :create_directories
|
||||||
before_destroy :destroy_directories
|
before_destroy :destroy_directories
|
||||||
|
|
||||||
##
|
|
||||||
# Returns the biggest dimension of this model.
|
|
||||||
#
|
|
||||||
def max_dimension
|
|
||||||
if self.width > self.height
|
|
||||||
self.width
|
|
||||||
else
|
|
||||||
self.height
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
##
|
|
||||||
# Determines how 'oneable' this photo is. Should be cached in model later.
|
|
||||||
#
|
|
||||||
def oneness
|
|
||||||
os = one_votes
|
|
||||||
"%.1f%%" % (os.to_f / self.votes.size.to_f * 100.0)
|
|
||||||
end
|
|
||||||
|
|
||||||
##
|
|
||||||
# Abstraction to determine the number of positive votes on this photo. Should
|
|
||||||
# be replaced with a cached counter variable.
|
|
||||||
#
|
|
||||||
def one_votes
|
|
||||||
self.votes.count :id, :conditions => [ 'vote = ?', true ]
|
|
||||||
end
|
|
||||||
|
|
||||||
##
|
|
||||||
# Abstraction to determine the number of negative votes on this photo. Should
|
|
||||||
# be replaced with a cached counter variable.
|
|
||||||
#
|
|
||||||
def zero_votes
|
|
||||||
self.votes.size - self.one_votes
|
|
||||||
end
|
|
||||||
|
|
||||||
##
|
##
|
||||||
# Returns the path of the image relative to Merb's root.
|
# Returns the path of the image relative to Merb's root.
|
||||||
#
|
#
|
||||||
def relative_directory
|
def relative_directory
|
||||||
fkey = id.to_s[0,2]
|
"/photos/#{id.to_s[0,2]}/#{id.to_s[0,4]}/#{id}"
|
||||||
skey = id.to_s[0,4]
|
|
||||||
"/photos/#{fkey}/#{skey}/#{id}"
|
|
||||||
end
|
end
|
||||||
|
|
||||||
##
|
##
|
||||||
|
@ -163,4 +127,11 @@ class Photo < ActiveRecord::Base
|
||||||
email_hash = User.salted_string(email) unless email.to_s.empty?
|
email_hash = User.salted_string(email) unless email.to_s.empty?
|
||||||
true
|
true
|
||||||
end
|
end
|
||||||
|
|
||||||
|
##
|
||||||
|
# Regenerates the calculated value of oneness.
|
||||||
|
#
|
||||||
|
def set_oneness
|
||||||
|
self.oneness = (self.one_votes.to_f / self.votes_count.to_f * 100.0)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -8,6 +8,9 @@ class Vote < ActiveRecord::Base
|
||||||
attr_protected :user_id
|
attr_protected :user_id
|
||||||
attr_protected :session_id
|
attr_protected :session_id
|
||||||
|
|
||||||
|
after_create :update_photo_stats
|
||||||
|
after_destroy :update_photo_stats
|
||||||
|
|
||||||
##
|
##
|
||||||
# Checks if this vote is anonymous, or not an authenticated User vote.
|
# Checks if this vote is anonymous, or not an authenticated User vote.
|
||||||
#
|
#
|
||||||
|
@ -54,7 +57,9 @@ class Vote < ActiveRecord::Base
|
||||||
end
|
end
|
||||||
|
|
||||||
##
|
##
|
||||||
# Does a quick find and collect on the cast votes so you can find a
|
# Does a quick find and collect on the cast votes so you can find all voted
|
||||||
|
# photo ids.
|
||||||
|
#
|
||||||
def self.voted_photo_ids(user)
|
def self.voted_photo_ids(user)
|
||||||
c = if user.respond_to?('id')
|
c = if user.respond_to?('id')
|
||||||
"votes.user_id = #{user.id}"
|
"votes.user_id = #{user.id}"
|
||||||
|
@ -66,6 +71,9 @@ class Vote < ActiveRecord::Base
|
||||||
|
|
||||||
protected
|
protected
|
||||||
|
|
||||||
|
##
|
||||||
|
# Make sure you can't vote on a photo more than once.
|
||||||
|
#
|
||||||
def unique_for_user
|
def unique_for_user
|
||||||
if self.user.to_s.empty? and self.session_id.empty?
|
if self.user.to_s.empty? and self.session_id.empty?
|
||||||
self.errors.add(:vote, 'must have an owner')
|
self.errors.add(:vote, 'must have an owner')
|
||||||
|
@ -75,4 +83,23 @@ class Vote < ActiveRecord::Base
|
||||||
self.errors.add(:anonymous, 'vote has already been collected')
|
self.errors.add(:anonymous, 'vote has already been collected')
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
##
|
||||||
|
# Recalc the stats in the Photo for specific stats don't have to query the
|
||||||
|
# database.
|
||||||
|
#
|
||||||
|
def update_photo_stats
|
||||||
|
v = if self.frozen?
|
||||||
|
-1
|
||||||
|
else
|
||||||
|
1
|
||||||
|
end
|
||||||
|
self.photo.votes_count += v
|
||||||
|
if self.zero?
|
||||||
|
self.photo.zero_votes += v
|
||||||
|
else
|
||||||
|
self.photo.one_votes += v
|
||||||
|
end
|
||||||
|
self.photo.save
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,2 +1,52 @@
|
||||||
%h1 Hall of famers!
|
- dim = 100
|
||||||
|
%style{ :type => 'text/css' }
|
||||||
|
:sass
|
||||||
|
.top_rated_container
|
||||||
|
:width 600px
|
||||||
|
:margin-left auto
|
||||||
|
:margin-right auto
|
||||||
|
.top_rated
|
||||||
|
:width 150px
|
||||||
|
:height 130px
|
||||||
|
:margin 10px
|
||||||
|
:padding 5px
|
||||||
|
:border 1px solid #c17d11
|
||||||
|
:background-color #e9b96e
|
||||||
|
:float left
|
||||||
|
img
|
||||||
|
:display block
|
||||||
|
:margin 5px auto 5px auto
|
||||||
|
:margin-left auto
|
||||||
|
:margin-right auto
|
||||||
|
h3
|
||||||
|
:text-align center
|
||||||
|
.top_rated_number
|
||||||
|
:float left
|
||||||
|
:font-size 12px
|
||||||
|
:font-weight bold
|
||||||
|
:padding 3px
|
||||||
|
:background-color #ffffff
|
||||||
|
|
||||||
|
%h2 Top oneness
|
||||||
|
|
||||||
|
- @top_oneness.each_with_index do |p, index|
|
||||||
|
%div.top_rated_container
|
||||||
|
%div.top_rated
|
||||||
|
%span.top_rated_number= index + 1
|
||||||
|
%a{ :href => url(:photo, p) }
|
||||||
|
%img{ :src => photo_url(p, dim, dim) }
|
||||||
|
%h3== #{p.oneness}%
|
||||||
|
|
||||||
|
%br{ :style => 'clear: both' }
|
||||||
|
|
||||||
|
%h2 Top voted
|
||||||
|
|
||||||
|
- @top_voted.each_with_index do |p, index|
|
||||||
|
%div.top_rated_container
|
||||||
|
%div.top_rated
|
||||||
|
%span.top_rated_number= index + 1
|
||||||
|
%a{ :href => url(:photo, p) }
|
||||||
|
%img{ :src => photo_url(p, dim, dim) }
|
||||||
|
%h3= p.votes_count
|
||||||
|
|
||||||
|
%br{ :style => 'clear: both' }
|
|
@ -32,7 +32,7 @@
|
||||||
%p
|
%p
|
||||||
%strong Oneness
|
%strong Oneness
|
||||||
%br
|
%br
|
||||||
%tt= @photo.oneness
|
%tt== #{@photo.oneness}%
|
||||||
|
|
||||||
%br{ :style => 'clear: both' }
|
%br{ :style => 'clear: both' }
|
||||||
|
|
||||||
|
|
|
@ -6,20 +6,20 @@
|
||||||
- if @votes[0]
|
- if @votes[0]
|
||||||
%a{ :href => url(:photo, @votes[0].photo), :onclick => 'window.open(this.href);return false;' }
|
%a{ :href => url(:photo, @votes[0].photo), :onclick => 'window.open(this.href);return false;' }
|
||||||
%img{ :src => photo_url(@votes[0].photo, dim, dim) }
|
%img{ :src => photo_url(@votes[0].photo, dim, dim) }
|
||||||
%p== <tt>#{@votes[0].to_i} / #{@votes[0].photo.oneness}</tt>
|
%p== <tt>#{@votes[0].to_i} / #{@votes[0].photo.oneness}%</tt>
|
||||||
%td{ :style => "width: 140px; height: 150px;" }
|
%td{ :style => "width: 140px; height: 150px;" }
|
||||||
- if @votes[1]
|
- if @votes[1]
|
||||||
%a{ :href => url(:photo, @votes[1].photo), :onclick => 'window.open(this.href);return false;' }
|
%a{ :href => url(:photo, @votes[1].photo), :onclick => 'window.open(this.href);return false;' }
|
||||||
%img{ :src => photo_url(@votes[1].photo, dim, dim) }
|
%img{ :src => photo_url(@votes[1].photo, dim, dim) }
|
||||||
%p== <tt>#{@votes[1].to_i} / #{@votes[1].photo.oneness}</tt>
|
%p== <tt>#{@votes[1].to_i} / #{@votes[1].photo.oneness}%</tt>
|
||||||
%tr
|
%tr
|
||||||
%td{ :style => "width: 140px; height: 150px;" }
|
%td{ :style => "width: 140px; height: 150px;" }
|
||||||
- if @votes[2]
|
- if @votes[2]
|
||||||
%a{ :href => url(:photo, @votes[2].photo), :onclick => 'window.open(this.href);return false;' }
|
%a{ :href => url(:photo, @votes[2].photo), :onclick => 'window.open(this.href);return false;' }
|
||||||
%img{ :src => photo_url(@votes[2].photo, dim, dim) }
|
%img{ :src => photo_url(@votes[2].photo, dim, dim) }
|
||||||
%p== <tt>#{@votes[2].to_i} / #{@votes[2].photo.oneness}</tt>
|
%p== <tt>#{@votes[2].to_i} / #{@votes[2].photo.oneness}%</tt>
|
||||||
%td{ :style => "width: 140px; height: 150px;" }
|
%td{ :style => "width: 140px; height: 150px;" }
|
||||||
- if @votes[3]
|
- if @votes[3]
|
||||||
%a{ :href => url(:photo, @votes[3].photo), :onclick => 'window.open(this.href);return false;' }
|
%a{ :href => url(:photo, @votes[3].photo), :onclick => 'window.open(this.href);return false;' }
|
||||||
%img{ :src => photo_url(@votes[3].photo, dim, dim) }
|
%img{ :src => photo_url(@votes[3].photo, dim, dim) }
|
||||||
%p== <tt>#{@votes[3].to_i} / #{@votes[3].photo.oneness}</tt>
|
%p== <tt>#{@votes[3].to_i} / #{@votes[3].photo.oneness}%</tt>
|
||||||
|
|
|
@ -0,0 +1,15 @@
|
||||||
|
class CachedVoteFieldsMigration < ActiveRecord::Migration
|
||||||
|
def self.up
|
||||||
|
add_column :photos, :votes_count, :integer, :default => 0
|
||||||
|
add_column :photos, :one_votes, :integer, :default => 0
|
||||||
|
add_column :photos, :zero_votes, :integer, :default => 0
|
||||||
|
add_column :photos, :oneness, :float, :precision => 1
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.down
|
||||||
|
remove_column :photos, :votes_count
|
||||||
|
remove_column :photos, :one_votes
|
||||||
|
remove_column :photos, :zero_votes
|
||||||
|
remove_column :photos, :oneness
|
||||||
|
end
|
||||||
|
end
|
|
@ -9,7 +9,7 @@
|
||||||
#
|
#
|
||||||
# It's strongly recommended to check this file into your version control system.
|
# It's strongly recommended to check this file into your version control system.
|
||||||
|
|
||||||
ActiveRecord::Schema.define(:version => 5) do
|
ActiveRecord::Schema.define(:version => 6) do
|
||||||
|
|
||||||
create_table "photo_favorites", :force => true do |t|
|
create_table "photo_favorites", :force => true do |t|
|
||||||
t.integer "photo_id"
|
t.integer "photo_id"
|
||||||
|
@ -27,6 +27,10 @@ ActiveRecord::Schema.define(:version => 5) do
|
||||||
t.integer "height"
|
t.integer "height"
|
||||||
t.datetime "created_at"
|
t.datetime "created_at"
|
||||||
t.boolean "approved"
|
t.boolean "approved"
|
||||||
|
t.integer "votes_count", :default => 0
|
||||||
|
t.integer "one_votes", :default => 0
|
||||||
|
t.integer "zero_votes", :default => 0
|
||||||
|
t.float "oneness"
|
||||||
end
|
end
|
||||||
|
|
||||||
add_index "photos", ["email_hash"], :name => "index_photos_on_email_hash"
|
add_index "photos", ["email_hash"], :name => "index_photos_on_email_hash"
|
||||||
|
|
Reference in New Issue