From 513711bf600990e194dd8151f35b4283cfaa097f Mon Sep 17 00:00:00 2001 From: Matchu Date: Tue, 2 Aug 2011 22:42:56 -0400 Subject: [PATCH] import sdb as well as closet --- app/controllers/closet_pages_controller.rb | 67 --------------- app/controllers/neopets_pages_controller.rb | 79 ++++++++++++++++++ app/models/closet_page.rb | 82 ++++++++++++++----- app/models/safety_deposit_page.rb | 39 +++++++++ .../{closet_pages => neopets_pages}/_new.sass | 2 +- app/stylesheets/screen.sass | 2 +- app/views/closet_hangers/index.html.haml | 5 +- .../new.html.haml | 30 +++---- config/routes.rb | 8 +- public/stylesheets/compiled/screen.css | 60 +++++++------- 10 files changed, 236 insertions(+), 138 deletions(-) delete mode 100644 app/controllers/closet_pages_controller.rb create mode 100644 app/controllers/neopets_pages_controller.rb create mode 100644 app/models/safety_deposit_page.rb rename app/stylesheets/{closet_pages => neopets_pages}/_new.sass (92%) rename app/views/{closet_pages => neopets_pages}/new.html.haml (67%) diff --git a/app/controllers/closet_pages_controller.rb b/app/controllers/closet_pages_controller.rb deleted file mode 100644 index c0912883..00000000 --- a/app/controllers/closet_pages_controller.rb +++ /dev/null @@ -1,67 +0,0 @@ -class ClosetPagesController < ApplicationController - include ActionView::Helpers::TextHelper - - before_filter :authenticate_user!, :build_closet_page - - rescue_from ClosetPage::ParseError, :with => :on_parse_error - - def create - if params[:closet_page] && params[:closet_page][:source] - @closet_page.index = params[:closet_page][:index] - @closet_page.source = params[:closet_page][:source] - - saved_counts = @closet_page.save_hangers! - - any_created = saved_counts[:created] > 0 - any_updated = saved_counts[:updated] > 0 - if any_created || any_updated - message = "Page #{@closet_page.index} saved! We " - message << "added " + pluralize(saved_counts[:created], 'item') + " to your closet" if any_created - message << " and " if any_created && any_updated - message << "updated the count on " + pluralize(saved_counts[:updated], 'item') if any_updated - message << ". " - else - message = "Success! We checked that page, and we already had all this data recorded. " - end - - unless @closet_page.unknown_item_names.empty? - message << "We also found " + - pluralize(@closet_page.unknown_item_names.size, 'item') + - " we didn't recognize: " + - @closet_page.unknown_item_names.to_sentence + - ". Please put each item on your pet and type its name in on the " + - "home page so we can have a record of it. Thanks! " - end - - if @closet_page.last? - message << "That was the last page of your Neopets closet." - destination = user_closet_hangers_path(current_user) - else - message << "Now the frame should contain page #{@closet_page.index + 1}. Paste that source code over, too." - destination = {:action => :new, :index => (@closet_page.index + 1)} - end - - flash[:success] = message - redirect_to destination - else - redirect_to :action => :new - end - end - - def new - @closet_page.index ||= 1 - end - - protected - - def build_closet_page - @closet_page = ClosetPage.new(current_user) - @closet_page.index = params[:index] - end - - def on_parse_error - flash[:alert] = "We had trouble reading your source code. Is it a valid HTML document? Make sure you pasted the computery-looking result of clicking View Frame Source, and not the pretty page itself." - render :action => :new - end -end - diff --git a/app/controllers/neopets_pages_controller.rb b/app/controllers/neopets_pages_controller.rb new file mode 100644 index 00000000..a930789d --- /dev/null +++ b/app/controllers/neopets_pages_controller.rb @@ -0,0 +1,79 @@ +class NeopetsPagesController < ApplicationController + include ActionView::Helpers::TextHelper + + before_filter :authenticate_user!, :build_neopets_page + + rescue_from ClosetPage::ParseError, :with => :on_parse_error + + def create + if @page_params && @page_params[:source] + @neopets_page.index = @page_params[:index] + @neopets_page.source = @page_params[:source] + + saved_counts = @neopets_page.save_hangers! + + any_created = saved_counts[:created] > 0 + any_updated = saved_counts[:updated] > 0 + if any_created || any_updated + message = "Page #{@neopets_page.index} saved! We " + message << "added " + pluralize(saved_counts[:created], 'item') + " to the items you own" if any_created + message << " and " if any_created && any_updated + message << "updated the count on " + pluralize(saved_counts[:updated], 'item') if any_updated + message << ". " + elsif @neopets_page.hangers.size > 1 + message = "Success! We checked that page, and we already had all this data recorded. " + else + message = "Success! We checked that page, and there were no wearables to add. " + end + + unless @neopets_page.unknown_item_names.empty? + message << "We also found " + + pluralize(@neopets_page.unknown_item_names.size, 'item') + + " we didn't recognize: " + + @neopets_page.unknown_item_names.to_sentence + + ". Please put each item on your pet and type its name in on the " + + "home page so we can have a record of it. Thanks! " + end + + if @neopets_page.last? + message << "That was the last page of your Neopets #{@neopets_page.name}." + destination = user_closet_hangers_path(current_user) + else + message << "Now the frame should contain page #{@neopets_page.index + 1}. Paste that source code over, too." + destination = {:action => :new, :index => (@neopets_page.index + 1)} + end + + flash[:success] = message + redirect_to destination + else + redirect_to :action => :new + end + end + + def new + @neopets_page.index ||= 1 + end + + protected + + TYPES = { + 'closet' => ClosetPage, + 'sdb' => SafetyDepositPage + } + def build_neopets_page + type_class = TYPES[params[:type]] + + @neopets_page = type_class.new(current_user) + @neopets_page.index = params[:index] + @page_params = params[type_class.model_name.singular] + end + + def on_parse_error(e) + Rails.logger.info "Neopets page parse error: #{e.message}" + flash[:alert] = "We had trouble reading your source code. Is it a valid " + + "HTML document? Make sure you pasted the computery-looking result of " + + "clicking View Frame Source, and not the pretty page itself. " + render :action => :new + end +end + diff --git a/app/models/closet_page.rb b/app/models/closet_page.rb index 9ac91838..f58a466f 100644 --- a/app/models/closet_page.rb +++ b/app/models/closet_page.rb @@ -4,7 +4,7 @@ class ClosetPage include ActiveModel::Conversion extend ActiveModel::Naming - SELECTORS = { + @selectors = { :items => "form[action=\"process_closet.phtml\"] tr[bgcolor!=silver][bgcolor!=\"#E4E4E4\"]", :item_thumbnail => "img", :item_name => "td:nth-child(2)", @@ -26,6 +26,10 @@ class ClosetPage @index == @total_pages end + def name + 'closet' + end + def persisted? false end @@ -58,47 +62,79 @@ class ClosetPage protected def element(selector_name, parent) - parent.at_css(SELECTORS[selector_name]) || - raise(ParseError, "Closet #{selector_name} element not found in #{parent.inspect}") + parent.at_css(self.class.selectors[selector_name]) || + raise(ParseError, "#{selector_name} element not found") end def elements(selector_name, parent) - parent.css(SELECTORS[selector_name]) + parent.css(self.class.selectors[selector_name]) + end + + def find_id(row) + element(:item_remove, row)['name'] + end + + def find_index(page_selector) + element(:selected, page_selector)['value'].to_i + end + + def find_items(doc) + elements(:items, doc) + end + + def find_name(row) + # For normal items, the td contains essentially: + # NAME
OPTIONAL ADJECTIVE
+ # For PB items, the td contains: + # NAME
OPTIONAL ADJECTIVE + # So, we want the first text node. If it's a PB item, that's the first + # child. If it's a normal item, it's the first child 's child. + name_el = element(:item_name, row).children[0] + name_el = name_el.children[0] if name_el.name == 'b' + name_el.text + end + + def find_page_selector(doc) + element(:page_select, doc) + end + + def find_quantity(row) + element(:item_quantity, row).text.to_i + end + + def find_thumbnail_url(row) + element(:item_thumbnail, row)['src'] + end + + def find_total_pages(page_selector) + page_selector.children.size end def parse_source!(source) doc = Nokogiri::HTML(source) - page_selector = element(:page_select, doc) - @total_pages = page_selector.children.size - @index = element(:selected, page_selector)['value'].to_i + page_selector = find_page_selector(doc) + @total_pages = find_total_pages(page_selector) + @index = find_index(page_selector) items_data = { :id => {}, :thumbnail_url => {} } - # Go through the items, and find the ID/thumbnail for each and data with it - elements(:items, doc).each do |row| - # For normal items, the td contains essentially: - # NAME
OPTIONAL ADJECTIVE
- # For PB items, the td contains: - # NAME
OPTIONAL ADJECTIVE - # So, we want the first text node. If it's a PB item, that's the first - # child. If it's a normal item, it's the first child 's child. - name_el = element(:item_name, row).children[0] - name_el = name_el.children[0] if name_el.name == 'b' + # Go through the items, and find the ID/thumbnail for each and data with it + find_items(doc).each do |row| data = { - :name => name_el.text, - :quantity => element(:item_quantity, row).text.to_i + :name => find_name(row), + :quantity => find_quantity(row) } - if id = element(:item_remove, row)['name'] + if id = find_id(row) id = id.to_i items_data[:id][id] = data else # if this is a pb item, which does not give ID, go by thumbnail - thumbnail_url = element(:item_thumbnail, row)['src'] + thumbnail_url = find_thumbnail_url(row) items_data[:thumbnail_url][thumbnail_url] = data end end @@ -133,6 +169,10 @@ class ClosetPage end end + def self.selectors + @selectors + end + class ParseError < RuntimeError;end end diff --git a/app/models/safety_deposit_page.rb b/app/models/safety_deposit_page.rb new file mode 100644 index 00000000..09619fdf --- /dev/null +++ b/app/models/safety_deposit_page.rb @@ -0,0 +1,39 @@ +class SafetyDepositPage < ClosetPage + @selectors = { + :items => "#content tr[bgcolor=\"#DFEAF7\"]", + :item_thumbnail => "img", + :item_name => "td:nth-child(2)", + :item_quantity => "td:nth-child(5)", + :item_remove => "input", + :page_select => "select[name=offset]", + :selected => "option[selected]" + } + + def name + 'SDB' + end + + def url + "http://www.neopets.com/safetydeposit.phtml?offset=#{offset}" + end + + protected + + REMOVE_NAME_REGEX = /\[([0-9]+)\]/ + def find_id(*args) + name = super + unless match = name.match(REMOVE_NAME_REGEX) + raise ParseError, "Remove Item input name format was unexpected: #{name}.inspect" + end + match[1] + end + + def find_index(*args) + (super / 30) + 1 + end + + def offset + @index ? (@index.to_i - 1) * 30 : 0 + end +end + diff --git a/app/stylesheets/closet_pages/_new.sass b/app/stylesheets/neopets_pages/_new.sass similarity index 92% rename from app/stylesheets/closet_pages/_new.sass rename to app/stylesheets/neopets_pages/_new.sass index ceb49654..9a95aa8c 100644 --- a/app/stylesheets/closet_pages/_new.sass +++ b/app/stylesheets/neopets_pages/_new.sass @@ -1,4 +1,4 @@ -body.closet_pages-new, body.closet_pages-create +body.neopets_pages-new, body.neopets_pages-create #title float: left diff --git a/app/stylesheets/screen.sass b/app/stylesheets/screen.sass index c8db393f..289e15f3 100644 --- a/app/stylesheets/screen.sass +++ b/app/stylesheets/screen.sass @@ -9,7 +9,7 @@ @import closet_hangers/index @import closet_hangers/petpage @import closet_lists/form -@import closet_pages/new +@import neopets_pages/new @import contributions/index @import items @import items/index diff --git a/app/views/closet_hangers/index.html.haml b/app/views/closet_hangers/index.html.haml index 91509480..c8aa24a4 100644 --- a/app/views/closet_hangers/index.html.haml +++ b/app/views/closet_hangers/index.html.haml @@ -74,8 +74,9 @@ %label{:for => 'closet-hangers-share-box'} Public URL: %input#closet-hangers-share-box{:type => 'text', :value => user_closet_hangers_url(@user), :readonly => true} - = link_to "Import from Neopets closet", new_closet_page_path - = link_to "Export to Neopets petpage", petpage_user_closet_hangers_path(@user) + = link_to "Import from closet", new_closet_page_path + = link_to "Import from SDB", new_safety_deposit_page_path + = link_to "Export to petpage", petpage_user_closet_hangers_path(@user) diff --git a/app/views/closet_pages/new.html.haml b/app/views/neopets_pages/new.html.haml similarity index 67% rename from app/views/closet_pages/new.html.haml rename to app/views/neopets_pages/new.html.haml index e2f6f5d6..2850f517 100644 --- a/app/views/closet_pages/new.html.haml +++ b/app/views/neopets_pages/new.html.haml @@ -1,26 +1,26 @@ -- title "Import from closet, Page #{@closet_page.index}" +- title "Import from #{@neopets_page.name}, Page #{@neopets_page.index}" - content_for :before_flashes do = link_to 'Back to Your Items', user_closet_hangers_path(current_user), :id => 'back-to-items' -= form_for @closet_page, :html => {:id => 'closet-page-form'} do |f| += form_for @neopets_page, :html => {:id => 'closet-page-form'} do |f| = f.hidden_field :index #closet-page-frame-wrapper %span - %strong Page #{@closet_page.index} - of your closet - %iframe#closet-page-frame{:src => @closet_page.url} + %strong Page #{@neopets_page.index} + of your #{@neopets_page.name} + %iframe#closet-page-frame{:src => @neopets_page.url} #closet-page-source = f.label :source, "Paste source code below" = f.text_area :source - = f.submit 'Add items to closet' + = f.submit 'Import items' :markdown - **Welcome to the bulk closet importer!** We're going to make it as - easy as possible to import your Neopets.com closet data into your Dress to - Impress closet. Here's how it works. + **Welcome to the bulk #{@neopets_page.name} importer!** We're going to make it as + easy as possible to import your Neopets.com #{@neopets_page.name} data into your Dress to + Impress items list. Here's how it works. 1. Check the framed Neopets.com window on the left, pointing to - [page #{@closet_page.index} of your closet][cp]. + [page #{@neopets_page.index} of your #{@neopets_page.name}][cp]. * **Confirm that you're logged in.** If you're logged into Neopets, but the above frame says that you're not, try enabling "third-party cookies" in your browser. (Most have that on by default.) @@ -29,7 +29,7 @@ web programmer pro who can check that the frame does, in fact, point to Neopets.com. To be safe, #{link_to_neopets_login "pull up another window, check the URL, and log in safely"}. - * **Confirm that the page is, in fact, your closet.** Similarly, don't + * **Confirm that the page is, in fact, your #{@neopets_page.name}.** Similarly, don't just trust a website when they tell you to copy-paste the source code of another site. Instead, check that the page is what it is supposed to be and does not contain any information you didn't mean to give out. @@ -39,7 +39,7 @@ * **In Firefox,** right-click the frame, choose **This Frame**, then **View Frame Source**. * In other browsers, right-click, and look for something similar. If you're still having trouble, try - #{link_to "viewing the page in a new window", @closet_page.url, :target => "_blank"}, + #{link_to "viewing the page in a new window", @neopets_page.url, :target => "_blank"}, right-clicking, and choosing View Source. 3. Highlight the entire source code, and copy-paste it into the box on the right. @@ -48,10 +48,10 @@ 4. Submit! * We'll analyze the code you sent us, grab exclusively the identity and - quantity of items in your closet, and add that to your Dress to Impress - closet. I promise it's all safe, but, if you're concerned, find a + quantity of items in your #{@neopets_page.name}, and add that to your Dress to Impress + items list. I promise it's all safe, but, if you're concerned, find a programmer buddy and [check out the source code to be sure][source]. - [cp]: #{@closet_page.url} + [cp]: #{@neopets_page.url} [source]: http://github.com/matchu/openneo-impress-rails diff --git a/config/routes.rb b/config/routes.rb index 7a19db34..a0b6dc4e 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -30,7 +30,13 @@ OpenneoImpressItems::Application.routes.draw do |map| resources :pet_attributes, :only => [:index] resources :swf_assets, :only => [:index, :show] - resources :closet_pages, :only => [:new, :create], :path => 'closet/pages' + scope 'import' do + resources :closet_pages, :only => [:new, :create], + :controller => 'neopets_pages', :path => 'closet/pages', :type => 'closet' + + resources :safety_deposit_pages, :only => [:new, :create], + :controller => 'neopets_pages', :path => 'sdb/pages', :type => 'sdb' + end match '/users/current-user/outfits' => 'outfits#index', :as => :current_user_outfits diff --git a/public/stylesheets/compiled/screen.css b/public/stylesheets/compiled/screen.css index 329855cc..45d43f7e 100644 --- a/public/stylesheets/compiled/screen.css +++ b/public/stylesheets/compiled/screen.css @@ -1348,16 +1348,16 @@ body.closet_lists-new form ul.fields .hint, body.closet_lists-create form ul.fie font-size: 85%; } -/* line 3, ../../../app/stylesheets/closet_pages/_new.sass */ -body.closet_pages-new #title, body.closet_pages-create #title { +/* line 3, ../../../app/stylesheets/neopets_pages/_new.sass */ +body.neopets_pages-new #title, body.neopets_pages-create #title { float: left; } -/* line 6, ../../../app/stylesheets/closet_pages/_new.sass */ -body.closet_pages-new .flash, body.closet_pages-create .flash { +/* line 6, ../../../app/stylesheets/neopets_pages/_new.sass */ +body.neopets_pages-new .flash, body.neopets_pages-create .flash { clear: both; } -/* line 9, ../../../app/stylesheets/closet_pages/_new.sass */ -body.closet_pages-new #back-to-items, body.closet_pages-create #back-to-items { +/* line 9, ../../../app/stylesheets/neopets_pages/_new.sass */ +body.neopets_pages-new #back-to-items, body.neopets_pages-create #back-to-items { /* http://www.zurb.com/blog_uploads/0000/0617/buttons-03.html */ -moz-border-radius: 5px; -webkit-border-radius: 5px; @@ -1378,69 +1378,69 @@ body.closet_pages-new #back-to-items, body.closet_pages-create #back-to-items { margin-top: 0.75em; } /* line 34, ../../../app/stylesheets/partials/clean/_mixins.sass */ -body.closet_pages-new #back-to-items:hover, body.closet_pages-create #back-to-items:hover { +body.neopets_pages-new #back-to-items:hover, body.neopets_pages-create #back-to-items:hover { background-color: #005300; } /* line 53, ../../../app/stylesheets/partials/clean/_mixins.sass */ -body.closet_pages-new #back-to-items:hover, body.closet_pages-create #back-to-items:hover { +body.neopets_pages-new #back-to-items:hover, body.neopets_pages-create #back-to-items:hover { color: white; } /* line 55, ../../../app/stylesheets/partials/clean/_mixins.sass */ -body.closet_pages-new #back-to-items:active, body.closet_pages-create #back-to-items:active { +body.neopets_pages-new #back-to-items:active, body.neopets_pages-create #back-to-items:active { top: 1px; } -/* line 15, ../../../app/stylesheets/closet_pages/_new.sass */ -body.closet_pages-new #closet-page-form, body.closet_pages-create #closet-page-form { +/* line 15, ../../../app/stylesheets/neopets_pages/_new.sass */ +body.neopets_pages-new #closet-page-form, body.neopets_pages-create #closet-page-form { overflow: hidden; display: inline-block; clear: both; margin-bottom: 1em; } /* line 8, ../../../app/stylesheets/partials/clean/_mixins.sass */ -body.closet_pages-new #closet-page-form, body.closet_pages-create #closet-page-form { +body.neopets_pages-new #closet-page-form, body.neopets_pages-create #closet-page-form { display: block; } -/* line 20, ../../../app/stylesheets/closet_pages/_new.sass */ -body.closet_pages-new #closet-page-frame-wrapper, body.closet_pages-create #closet-page-frame-wrapper { +/* line 20, ../../../app/stylesheets/neopets_pages/_new.sass */ +body.neopets_pages-new #closet-page-frame-wrapper, body.neopets_pages-create #closet-page-frame-wrapper { float: left; margin-right: 2%; width: 48%; } -/* line 25, ../../../app/stylesheets/closet_pages/_new.sass */ -body.closet_pages-new #closet-page-frame, body.closet_pages-create #closet-page-frame { +/* line 25, ../../../app/stylesheets/neopets_pages/_new.sass */ +body.neopets_pages-new #closet-page-frame, body.neopets_pages-create #closet-page-frame { height: 19em; width: 100%; } -/* line 29, ../../../app/stylesheets/closet_pages/_new.sass */ -body.closet_pages-new #closet-page-source, body.closet_pages-create #closet-page-source { +/* line 29, ../../../app/stylesheets/neopets_pages/_new.sass */ +body.neopets_pages-new #closet-page-source, body.neopets_pages-create #closet-page-source { float: left; width: 50%; } -/* line 33, ../../../app/stylesheets/closet_pages/_new.sass */ -body.closet_pages-new #closet-page-source label, body.closet_pages-create #closet-page-source label { +/* line 33, ../../../app/stylesheets/neopets_pages/_new.sass */ +body.neopets_pages-new #closet-page-source label, body.neopets_pages-create #closet-page-source label { font-weight: bold; } -/* line 36, ../../../app/stylesheets/closet_pages/_new.sass */ -body.closet_pages-new #closet-page-source textarea, body.closet_pages-create #closet-page-source textarea { +/* line 36, ../../../app/stylesheets/neopets_pages/_new.sass */ +body.neopets_pages-new #closet-page-source textarea, body.neopets_pages-create #closet-page-source textarea { height: 19em; } -/* line 40, ../../../app/stylesheets/closet_pages/_new.sass */ -body.closet_pages-new ol, body.closet_pages-create ol { +/* line 40, ../../../app/stylesheets/neopets_pages/_new.sass */ +body.neopets_pages-new ol, body.neopets_pages-create ol { padding-left: 1em; } -/* line 43, ../../../app/stylesheets/closet_pages/_new.sass */ -body.closet_pages-new ol > li, body.closet_pages-create ol > li { +/* line 43, ../../../app/stylesheets/neopets_pages/_new.sass */ +body.neopets_pages-new ol > li, body.neopets_pages-create ol > li { margin-bottom: 1em; } -/* line 46, ../../../app/stylesheets/closet_pages/_new.sass */ -body.closet_pages-new ol ul, body.closet_pages-create ol ul { +/* line 46, ../../../app/stylesheets/neopets_pages/_new.sass */ +body.neopets_pages-new ol ul, body.neopets_pages-create ol ul { font-size: 85%; margin-bottom: 1em; margin-top: 0; padding-left: 1em; } -/* line 53, ../../../app/stylesheets/closet_pages/_new.sass */ -body.closet_pages-new ol p, body.closet_pages-create ol p { +/* line 53, ../../../app/stylesheets/neopets_pages/_new.sass */ +body.neopets_pages-new ol p, body.neopets_pages-create ol p { margin: 0; }