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"