From 675e244864e1eb998bf377f3efcac5238e844dce Mon Sep 17 00:00:00 2001 From: Coleman Date: Sat, 11 Oct 2008 19:16:22 -0500 Subject: [PATCH] adding cached vote stats fields and hall of famers --- app/controllers/home.rb | 2 + app/models/photo.rb | 47 ++++------------- app/models/vote.rb | 29 ++++++++++- app/views/home/hall_of_fame.html.haml | 52 ++++++++++++++++++- app/views/photos/show.html.haml | 2 +- app/views/votes/_stats_for_user.html.haml | 8 +-- .../006_cached_vote_fields_migration.rb | 15 ++++++ schema/schema.rb | 6 ++- 8 files changed, 115 insertions(+), 46 deletions(-) create mode 100644 schema/migrations/006_cached_vote_fields_migration.rb diff --git a/app/controllers/home.rb b/app/controllers/home.rb index 028f685..8a885e4 100644 --- a/app/controllers/home.rb +++ b/app/controllers/home.rb @@ -8,6 +8,8 @@ class Home < Application end 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 end diff --git a/app/models/photo.rb b/app/models/photo.rb index 4ab83bd..3243e39 100644 --- a/app/models/photo.rb +++ b/app/models/photo.rb @@ -8,52 +8,16 @@ class Photo < ActiveRecord::Base before_create :validate_image_sanity before_create :hashify_email + before_save :set_oneness after_create :create_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. # def relative_directory - fkey = id.to_s[0,2] - skey = id.to_s[0,4] - "/photos/#{fkey}/#{skey}/#{id}" + "/photos/#{id.to_s[0,2]}/#{id.to_s[0,4]}/#{id}" end ## @@ -163,4 +127,11 @@ class Photo < ActiveRecord::Base email_hash = User.salted_string(email) unless email.to_s.empty? true 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 diff --git a/app/models/vote.rb b/app/models/vote.rb index b682c52..4b10b9b 100644 --- a/app/models/vote.rb +++ b/app/models/vote.rb @@ -8,6 +8,9 @@ class Vote < ActiveRecord::Base attr_protected :user_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. # @@ -54,7 +57,9 @@ class Vote < ActiveRecord::Base 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) c = if user.respond_to?('id') "votes.user_id = #{user.id}" @@ -66,6 +71,9 @@ class Vote < ActiveRecord::Base protected + ## + # Make sure you can't vote on a photo more than once. + # def unique_for_user if self.user.to_s.empty? and self.session_id.empty? 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') 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 diff --git a/app/views/home/hall_of_fame.html.haml b/app/views/home/hall_of_fame.html.haml index 8543ea4..560aba6 100644 --- a/app/views/home/hall_of_fame.html.haml +++ b/app/views/home/hall_of_fame.html.haml @@ -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' } \ No newline at end of file diff --git a/app/views/photos/show.html.haml b/app/views/photos/show.html.haml index 63b5b0c..73fed15 100644 --- a/app/views/photos/show.html.haml +++ b/app/views/photos/show.html.haml @@ -32,7 +32,7 @@ %p %strong Oneness %br - %tt= @photo.oneness + %tt== #{@photo.oneness}% %br{ :style => 'clear: both' } diff --git a/app/views/votes/_stats_for_user.html.haml b/app/views/votes/_stats_for_user.html.haml index 219edeb..01c374e 100644 --- a/app/views/votes/_stats_for_user.html.haml +++ b/app/views/votes/_stats_for_user.html.haml @@ -6,20 +6,20 @@ - if @votes[0] %a{ :href => url(:photo, @votes[0].photo), :onclick => 'window.open(this.href);return false;' } %img{ :src => photo_url(@votes[0].photo, dim, dim) } - %p== #{@votes[0].to_i} / #{@votes[0].photo.oneness} + %p== #{@votes[0].to_i} / #{@votes[0].photo.oneness}% %td{ :style => "width: 140px; height: 150px;" } - if @votes[1] %a{ :href => url(:photo, @votes[1].photo), :onclick => 'window.open(this.href);return false;' } %img{ :src => photo_url(@votes[1].photo, dim, dim) } - %p== #{@votes[1].to_i} / #{@votes[1].photo.oneness} + %p== #{@votes[1].to_i} / #{@votes[1].photo.oneness}% %tr %td{ :style => "width: 140px; height: 150px;" } - if @votes[2] %a{ :href => url(:photo, @votes[2].photo), :onclick => 'window.open(this.href);return false;' } %img{ :src => photo_url(@votes[2].photo, dim, dim) } - %p== #{@votes[2].to_i} / #{@votes[2].photo.oneness} + %p== #{@votes[2].to_i} / #{@votes[2].photo.oneness}% %td{ :style => "width: 140px; height: 150px;" } - if @votes[3] %a{ :href => url(:photo, @votes[3].photo), :onclick => 'window.open(this.href);return false;' } %img{ :src => photo_url(@votes[3].photo, dim, dim) } - %p== #{@votes[3].to_i} / #{@votes[3].photo.oneness} + %p== #{@votes[3].to_i} / #{@votes[3].photo.oneness}% diff --git a/schema/migrations/006_cached_vote_fields_migration.rb b/schema/migrations/006_cached_vote_fields_migration.rb new file mode 100644 index 0000000..34ad4f3 --- /dev/null +++ b/schema/migrations/006_cached_vote_fields_migration.rb @@ -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 diff --git a/schema/schema.rb b/schema/schema.rb index d72f24e..3df8c8e 100644 --- a/schema/schema.rb +++ b/schema/schema.rb @@ -9,7 +9,7 @@ # # 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| t.integer "photo_id" @@ -27,6 +27,10 @@ ActiveRecord::Schema.define(:version => 5) do t.integer "height" t.datetime "created_at" 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 add_index "photos", ["email_hash"], :name => "index_photos_on_email_hash"