From a5b119a9bc63ff9d3913dcbc03958650730888c5 Mon Sep 17 00:00:00 2001 From: Matchu Date: Sat, 6 Nov 2010 11:52:58 -0400 Subject: [PATCH] contributions viewing --- app/controllers/contributions_controller.rb | 7 ++ app/helpers/application_helper.rb | 4 + app/helpers/contribution_helper.rb | 40 +++++++++ app/models/contribution.rb | 84 +++++++++++++++++++ app/models/item.rb | 28 +++++++ app/models/pet_attribute.rb | 10 +++ app/models/pet_type.rb | 24 ++++++ app/models/swf_asset.rb | 4 + app/models/user.rb | 2 + app/stylesheets/contributions/_index.sass | 38 +++++++++ app/stylesheets/screen.sass | 1 + .../contributions/_contribution.html.haml | 10 +++ app/views/contributions/index.html.haml | 9 ++ app/views/layouts/application.html.haml | 8 +- config/colors.txt | 2 + public/stylesheets/compiled/screen.css | 53 ++++++++++++ 16 files changed, 323 insertions(+), 1 deletion(-) create mode 100644 app/controllers/contributions_controller.rb create mode 100644 app/helpers/contribution_helper.rb create mode 100644 app/models/contribution.rb create mode 100644 app/stylesheets/contributions/_index.sass create mode 100644 app/views/contributions/_contribution.html.haml create mode 100644 app/views/contributions/index.html.haml diff --git a/app/controllers/contributions_controller.rb b/app/controllers/contributions_controller.rb new file mode 100644 index 00000000..600d8b5d --- /dev/null +++ b/app/controllers/contributions_controller.rb @@ -0,0 +1,7 @@ +class ContributionsController < ApplicationController + def index + @contributions = Contribution.recent.paginate :page => params[:page], + :include => :user + Contribution.preload_contributeds_and_parents @contributions + end +end diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb index 34fc42ae..248b1313 100644 --- a/app/helpers/application_helper.rb +++ b/app/helpers/application_helper.rb @@ -39,4 +39,8 @@ module ApplicationHelper def origin_tag(value) hidden_field_tag 'origin', value, :id => nil end + + def title(value) + content_for :title, value + end end diff --git a/app/helpers/contribution_helper.rb b/app/helpers/contribution_helper.rb new file mode 100644 index 00000000..9e7f417d --- /dev/null +++ b/app/helpers/contribution_helper.rb @@ -0,0 +1,40 @@ +module ContributionHelper + def contributed_description(contributed) + case contributed + when Item + contributed_item(contributed, 'for the first time') + when SwfAsset + contributed_item(contributed.item, 'on a new body type') + when PetType + contributed_pet_type(contributed, :after => 'for the first time') + when PetState + contributed_pet_type(contributed.pet_type, :before => 'a new pose for') + end + end + + def contributed_item(item, adverbial) + output do |html| + html << 'the' + html << link_to(item.name, item, :class => 'contributed-name') + html << adverbial + html << image_tag(item.thumbnail_url) + end + end + + PET_TYPE_IMAGE_FORMAT = 'http://pets.neopets.com/cp/%s/1/3.png' + def contributed_pet_type(pet_type, options) + options[:before] ||= 'the' + output do |html| + html << options[:before] + html << content_tag(:span, pet_type.human_name, :class => 'contributed-name') + html << options[:after] if options[:after] + html << image_tag(sprintf(PET_TYPE_IMAGE_FORMAT, pet_type.image_hash)) + end + end + + private + + def output(&block) + raw([].tap(&block).join(' ')) + end +end diff --git a/app/models/contribution.rb b/app/models/contribution.rb new file mode 100644 index 00000000..b3479764 --- /dev/null +++ b/app/models/contribution.rb @@ -0,0 +1,84 @@ +class Contribution < ActiveRecord::Base + POINT_VALUES = { + 'Item' => 3, + 'SwfAsset' => 2, + 'PetType' => 15, + 'PetState' => 10 + } + + attr_accessor :contributed + + belongs_to :user + + scope :recent, order('id DESC') + + cattr_reader :per_page + @@per_page = 30 + + def point_value + POINT_VALUES[contributed_type] + end + + CONTRIBUTED_RELATIONSHIPS = { + 'SwfAsset' => 'Item', + 'PetState' => 'PetType' + } + CONTRIBUTED_CHILDREN = CONTRIBUTED_RELATIONSHIPS.keys + CONTRIBUTED_TYPES = CONTRIBUTED_CHILDREN + CONTRIBUTED_RELATIONSHIPS.values + CONTRIBUTED_BASES = {} + CONTRIBUTED_TYPES.each do |type| + base = type == 'SwfAsset' ? SwfAsset.object_assets : type.constantize + CONTRIBUTED_BASES[type] = base + end + def self.preload_contributeds_and_parents(contributions) + # Initialize the groups we'll be using for quick access + contributions_by_type = {} + contributed_by_type = {} + contributed_by_type_and_id = {} + needed_ids_by_type = {} + CONTRIBUTED_TYPES.each do |type| + contributions_by_type[type] = [] + contributed_by_type[type] = [] + contributed_by_type_and_id[type] = {} + needed_ids_by_type[type] = [] + end + + # Go through the contributions to sort them for future contributed + # assignment, and so we can know what immediate contributed items we'll + # need to look up + contributions.each do |contribution| + type = contribution.contributed_type + contributions_by_type[type] << contribution + needed_ids_by_type[type] << contribution.contributed_id + end + + # Load contributed objects without parents, prepare them for easy access + # for future assignment to contributions and looking up parents + CONTRIBUTED_CHILDREN.each do |type| + base = CONTRIBUTED_BASES[type] + base.find(needed_ids_by_type[type]).each do |contributed| + contributed_by_type[type] << contributed + contributed_by_type_and_id[type][contributed.id] = contributed + end + end + + # Load both parents of the children we just got, and immediately + # contributed objects of that class. all_by_ids_or_children properly + # assigns parents to children, as well + CONTRIBUTED_RELATIONSHIPS.each do |child_type, type| + base = CONTRIBUTED_BASES[type] + ids = needed_ids_by_type[type] + children = contributed_by_type[child_type] + base.all_by_ids_or_children(ids, children).each do |contributed| + contributed_by_type_and_id[type][contributed.id] = contributed + end + end + + # Assign contributed objects to contributions + contributions.each do |contribution| + type = contribution.contributed_type + id = contribution.contributed_id + contribution.contributed = contributed_by_type_and_id[type][id] + end + end +end diff --git a/app/models/item.rb b/app/models/item.rb index 8253bcea..3952b6ba 100644 --- a/app/models/item.rb +++ b/app/models/item.rb @@ -149,6 +149,34 @@ class Item < ActiveRecord::Base @parent_swf_asset_relationships_to_update = rels end + def self.all_by_ids_or_children(ids, swf_assets) + swf_asset_ids = [] + swf_assets_by_id = {} + swf_assets_by_parent_id = {} + swf_assets.each do |swf_asset| + id = swf_asset.id + swf_assets_by_id[id] = swf_asset + swf_asset_ids << id + end + SwfAsset.select('id, parent_id').object_assets.joins(:object_asset_relationships). + where(SwfAsset.arel_table[:id].in(swf_asset_ids)).each do |row| + item_id = row.parent_id.to_i + swf_assets_by_parent_id[item_id] ||= [] + swf_assets_by_parent_id[item_id] << swf_assets_by_id[row.id.to_i] + ids << item_id + end + find(ids).tap do |items| + items.each do |item| + swf_assets = swf_assets_by_parent_id[item.id] + if swf_assets + swf_assets.each do |swf_asset| + swf_asset.item = item + end + end + end + end + end + def self.collection_from_pet_type_and_registries(pet_type, info_registry, asset_registry) # bear in mind that registries are arrays with many nil elements, # due to how the parser works diff --git a/app/models/pet_attribute.rb b/app/models/pet_attribute.rb index b131573b..4da5523d 100644 --- a/app/models/pet_attribute.rb +++ b/app/models/pet_attribute.rb @@ -10,6 +10,16 @@ class PetAttribute < StaticResource self.name.capitalize end + def self.find(id) + attribute = super + unless attribute + attribute = new + attribute.id = id + attribute.name = "color \##{id}" + end + attribute + end + def self.find_by_name(name) @objects_by_name[name.downcase] end diff --git a/app/models/pet_type.rb b/app/models/pet_type.rb index 5e579207..fee2144a 100644 --- a/app/models/pet_type.rb +++ b/app/models/pet_type.rb @@ -64,6 +64,10 @@ class PetType < ActiveRecord::Base self['image_hash'] || BasicHashes[species.name][color.name] end + def human_name + self.color.human_name + ' ' + self.species.human_name + end + def add_pet_state_from_biology!(biology) pet_state = PetState.from_pet_type_and_biology_info(self, biology) self.pet_states << pet_state @@ -92,4 +96,24 @@ class PetType < ActiveRecord::Base end end end + + def self.all_by_ids_or_children(ids, pet_states) + pet_states_by_pet_type_id = {} + pet_states.each do |pet_state| + id = pet_state.pet_type_id + ids << id + pet_states_by_pet_type_id[id] ||= [] + pet_states_by_pet_type_id[id] << pet_state + end + find(ids).tap do |pet_types| + pet_types.each do |pet_type| + pet_states = pet_states_by_pet_type_id[pet_type.id] + if pet_states + pet_states.each do |pet_state| + pet_state.pet_type = pet_type + end + end + end + end + end end diff --git a/app/models/swf_asset.rb b/app/models/swf_asset.rb index c9f8fd53..51bd4e43 100644 --- a/app/models/swf_asset.rb +++ b/app/models/swf_asset.rb @@ -3,6 +3,8 @@ class SwfAsset < ActiveRecord::Base LOCAL_ASSET_DIR = Rails.root.join('public', PUBLIC_ASSET_DIR) set_inheritance_column 'inheritance_type' + attr_accessor :item + has_many :object_asset_relationships, :class_name => 'ParentSwfAssetRelationship', :conditions => {:swf_asset_type => 'object'} @@ -17,6 +19,8 @@ class SwfAsset < ActiveRecord::Base where(arel_table[:body_id].in(BodyIdsFittingStandard)) } + scope :object_assets, where(arel_table[:type].eq('object')) + def local_url '/' + File.join(PUBLIC_ASSET_DIR, local_path_within_outfit_swfs) end diff --git a/app/models/user.rb b/app/models/user.rb index 6aec9d37..1f607637 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -1,6 +1,8 @@ class User < ActiveRecord::Base DefaultAuthServerId = 1 + has_many :contributions + scope :top_contributors, order('points DESC') def self.find_or_create_from_remote_auth_data(user_data) diff --git a/app/stylesheets/contributions/_index.sass b/app/stylesheets/contributions/_index.sass new file mode 100644 index 00000000..6319c5e6 --- /dev/null +++ b/app/stylesheets/contributions/_index.sass @@ -0,0 +1,38 @@ +body.contributions-index + text-align: center + + .contributions + li + list-style: none + height: 80px + overflow: hidden + padding: 1em 0 0 100px + position: relative + text-align: left + .point-value + +header-text + color: #fff + font-size: 80px + left: 0 + line-height: 1 + position: absolute + text-align: center + text-shadow: 2px 2px 0 #000 + top: 0 + width: 80px + z-index: 3 + &:hover + +opacity(0.5) + img + height: 80px + left: 0 + position: absolute + top: 0 + width: 80px + z-index: 2 + span + &.username, &.contributed-name + font-weight: bold + &.time-ago + display: block + font-size: 75% diff --git a/app/stylesheets/screen.sass b/app/stylesheets/screen.sass index bc169f8e..82652df5 100644 --- a/app/stylesheets/screen.sass +++ b/app/stylesheets/screen.sass @@ -4,6 +4,7 @@ @import layout +@import contributions/index @import items @import items/index @import items/show diff --git a/app/views/contributions/_contribution.html.haml b/app/views/contributions/_contribution.html.haml new file mode 100644 index 00000000..59873b8b --- /dev/null +++ b/app/views/contributions/_contribution.html.haml @@ -0,0 +1,10 @@ +- contributed = contribution.contributed += content_tag_for :li, contribution do + %span.point-value= contribution.point_value + %span.username + = link_to contribution.user.name, user_contributions_path(contribution.user) + showed us + = contributed_description contributed + %span.time-ago{:title => contribution.created_at.to_s} + = time_ago_in_words(contribution.created_at) + ago diff --git a/app/views/contributions/index.html.haml b/app/views/contributions/index.html.haml new file mode 100644 index 00000000..28864361 --- /dev/null +++ b/app/views/contributions/index.html.haml @@ -0,0 +1,9 @@ +- title 'Recent Contributions' +%ul.buttons + %li= link_to 'Top Contributors', top_contributors_path, :class => 'button' +- if @contributions.empty? + %p No contributions to see here! +- else + = will_paginate @contributions + %ul.contributions= render @contributions + = will_paginate @contributions diff --git a/app/views/layouts/application.html.haml b/app/views/layouts/application.html.haml index 43590f53..1ef20106 100644 --- a/app/views/layouts/application.html.haml +++ b/app/views/layouts/application.html.haml @@ -1,11 +1,17 @@ !!! 5 %html %head - %title Dress to Impress + %title + Dress to Impress + - if content_for? :title + — + = yield :title = stylesheet_link_tag "compiled/screen" = javascript_include_tag "http://#{RemoteImpressHost}/assets/js/analytics.js" %body{:class => body_class} #container + - if content_for? :title + %h1#title= yield :title = yield :before_flashes = flashes diff --git a/config/colors.txt b/config/colors.txt index 07d1df3f..c6cb035f 100644 --- a/config/colors.txt +++ b/config/colors.txt @@ -86,3 +86,5 @@ Zombie Onion Magma Relic +Woodland +Transparent diff --git a/public/stylesheets/compiled/screen.css b/public/stylesheets/compiled/screen.css index 256b2c07..231e1e8a 100644 --- a/public/stylesheets/compiled/screen.css +++ b/public/stylesheets/compiled/screen.css @@ -367,6 +367,59 @@ dd { src: local("Droid Sans"), url("http://themes.googleusercontent.com/font?kit=POVDFY-UUf0WFR9DIMCU8g") format("truetype"); } +/* line 1, ../../../app/stylesheets/contributions/_index.sass */ +body.contributions-index { + text-align: center; +} +/* line 5, ../../../app/stylesheets/contributions/_index.sass */ +body.contributions-index .contributions li { + list-style: none; + height: 80px; + overflow: hidden; + padding: 1em 0 0 100px; + position: relative; + text-align: left; +} +/* line 12, ../../../app/stylesheets/contributions/_index.sass */ +body.contributions-index .contributions .point-value { + font-family: Delicious, Helvetica, Arial, Verdana, sans-serif; + color: white; + font-size: 80px; + left: 0; + line-height: 1; + position: absolute; + text-align: center; + text-shadow: 2px 2px 0 black; + top: 0; + width: 80px; + z-index: 3; +} +/* line 24, ../../../app/stylesheets/contributions/_index.sass */ +body.contributions-index .contributions .point-value:hover { + -moz-opacity: 0.5; + -webkit-opacity: 0.5; + -o-opacity: 0.5; + -khtml-opacity: 0.5; +} +/* line 26, ../../../app/stylesheets/contributions/_index.sass */ +body.contributions-index .contributions img { + height: 80px; + left: 0; + position: absolute; + top: 0; + width: 80px; + z-index: 2; +} +/* line 34, ../../../app/stylesheets/contributions/_index.sass */ +body.contributions-index .contributions span.username, body.contributions-index .contributions span.contributed-name { + font-weight: bold; +} +/* line 36, ../../../app/stylesheets/contributions/_index.sass */ +body.contributions-index .contributions span.time-ago { + display: block; + font-size: 75%; +} + /* line 1, ../../../app/stylesheets/_items.sass */ body.items { text-align: center;