Commit 45697591 by Truong Ba Dieu

Done basic feature

parent 1606b3bc
......@@ -53,6 +53,7 @@ gem "slim"
gem "thin"
gem 'figaro', '1.1.1'
gem 'vacuum'
gem "breadcrumbs_on_rails"
group :test do
# gem 'capybara' # Integration test tool to simulate a user on a website.
# gem 'capybara_minitest_spec' # MiniTest::Spec expectations for Capybara node matchers.
......
......@@ -47,6 +47,7 @@ GEM
bootstrap-sass (3.3.4.1)
autoprefixer-rails (>= 5.0.0.1)
sass (>= 3.2.19)
breadcrumbs_on_rails (2.3.0)
builder (3.2.2)
byebug (5.0.0)
columnize (= 0.9.0)
......@@ -224,6 +225,7 @@ PLATFORMS
DEPENDENCIES
bootstrap-sass (= 3.3.4.1)
breadcrumbs_on_rails
byebug
coffee-rails (~> 4.1.0)
database_cleaner
......
.navbar-brand{
padding: 2px;
img{
height: 45px;
display: inline-block;
margin-right: 10px;
}
}
.logo{
margin: 0;
line-height: 25px;
font-size: 22px;
font-family: serif;
}
.wrap-img{
display: inline-block;
}
#search-bar{
margin: 20px 0;
input[type="text"]{
height: 35px;
}
}
\ No newline at end of file
.recommend{
}
.products{
.wrap-img{
float: left;
}
.title{
padding-left: 60px;
}
}
\ No newline at end of file
.mar-top-20{
margin-top: 20px;
}
.mar-bot-20{
margin-bottom: 20px;
}
.mar-r-10{
margin-right: 10px;
}
.text-center{
text-align: center;
}
.pad-0{
padding: 0;
}
\ No newline at end of file
......@@ -2,4 +2,8 @@ class ApplicationController < ActionController::Base
# Prevent CSRF attacks by raising an exception.
# For APIs, you may want to use :null_session instead.
protect_from_forgery with: :exception
def get_categories
@categories = Category.all
end
end
class CategoriesController < ApplicationController
before_filter :get_categories
def show
@category = Category.find(params[:id])
end
end
\ No newline at end of file
class HomeController < ApplicationController
add_breadcrumb "Home", :root_path
before_filter :get_categories
before_filter :get_recommend
def index
params[:per_page] ||= 5
params[:page] ||= 1
@products = Product.page(params[:page]).per(params[:per_page])
end
def search
add_breadcrumb "Search", :search_path
@products = Product.search(params)
end
private
def get_recommend
@recommend_products = Product.recommend
end
end
\ No newline at end of file
class ProductsController < ApplicationController
before_filter :get_categories
def show
@product = Product.find(params[:id])
end
end
\ No newline at end of file
module ApplicationHelper
def currency_number(price=0, currency="usd")
"#{currency}#{price}"
end
end
class Category < ActiveRecord::Base
has_many :products
end
class Product < ActiveRecord::Base
belongs_to :category
belongs_to :user
dragonfly_accessor :image
validates :pid, :title, :price, :category_id, presence: true
validates :pid, uniqueness: true
scope :recommend, -> {where(recommend: true)}
def self.search(params)
params[:per_page] ||= 5
params[:page] ||= 1
Product.order("release_date DESC").page(params[:page]).per(params[:per_page])
end
end
......@@ -3,4 +3,6 @@ class User < ActiveRecord::Base
# :confirmable, :lockable, :timeoutable and :omniauthable
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable
has_many :products
end
class DatetimeService
def self.strptime(str = nil, type="%Y-%m-%d")
begin
DateTime.strptime(str, type)
rescue
DateTime.now
end
end
end
\ No newline at end of file
class VacuumAwsService
def self.item_search(options={})
options[:country_code] ||='GB'
options[:params] ||={}
options[:query] ||={}
request = Vacuum.new(options[:country_code])
request.configure(
......@@ -11,7 +11,164 @@ class VacuumAwsService
)
request.item_search(
options[:params]
query: options[:query]
)
end
end
\ No newline at end of file
def self.parse_items(response)
response = response.to_h
if response["ItemSearchResponse"].present? && response["ItemSearchResponse"]["Items"].present? && response["ItemSearchResponse"]["Items"]["Item"].present?
response["ItemSearchResponse"]["Items"]["Item"]
else
[]
end
end
def self.parse_item(item)
# only get product have pid, title, price
if item.present? && item["ASIN"].present? && item["ItemAttributes"]["Title"].present? && item["ItemAttributes"]["ListPrice"].present?
{
pid: item["ASIN"],
title: item["ItemAttributes"]["Title"],
author: item["ItemAttributes"]["Author"],
publisher: item["ItemAttributes"]["Publisher"],
studio: item["ItemAttributes"]["Studio"],
price: item["ItemAttributes"]["ListPrice"]["FormattedPrice"][1..-1].to_f,
currency: item["ItemAttributes"]["ListPrice"]["FormattedPrice"].to_s.first,
release_date: DatetimeService.strptime(item["ItemAttributes"]["ReleaseDate"], "%Y-%m-%d"),
public_date: DatetimeService.strptime(item["ItemAttributes"]["PublicationDate"], "%Y-%m-%d"),
image_url: item["LargeImage"]["URL"]
}
end
end
end
# Sample item response
# {"ASIN"=>"B00000G43U", "DetailPageURL"=>"http://www.amazon.co.uk/All-Saints-Remix-Album-Saints/dp/B00000G43U%3FSubscriptionId%3DAKIAJ77C4CTZOP7TUVWQ%26tag%3Dzigexn6400-22%26linkCode%3Dxm2%26camp%3D2025%26creative%3D165953%26creativeASIN%3DB00000G43U",
# "ItemLinks"=>{
# "ItemLink"=>[
# {"Description"=>"Add To Wishlist", "URL"=>"http://www.amazon.co.uk/gp/registry/wishlist/add-item.html%3Fasin.0%3DB00000G43U%26SubscriptionId%3DAKIAJ77C4CTZOP7TUVWQ%26tag%3Dzigexn6400-22%26linkCode%3Dxm2%26camp%3D2025%26creative%3D12734%26creativeASIN%3DB00000G43U"},
# {"Description"=>"Tell A Friend", "URL"=>"http://www.amazon.co.uk/gp/pdp/taf/B00000G43U%3FSubscriptionId%3DAKIAJ77C4CTZOP7TUVWQ%26tag%3Dzigexn6400-22%26linkCode%3Dxm2%26camp%3D2025%26creative%3D12734%26creativeASIN%3DB00000G43U"},
# {"Description"=>"All Customer Reviews", "URL"=>"http://www.amazon.co.uk/review/product/B00000G43U%3FSubscriptionId%3DAKIAJ77C4CTZOP7TUVWQ%26tag%3Dzigexn6400-22%26linkCode%3Dxm2%26camp%3D2025%26creative%3D12734%26creativeASIN%3DB00000G43U"},
# {"Description"=>"All Offers", "URL"=>"http://www.amazon.co.uk/gp/offer-listing/B00000G43U%3FSubscriptionId%3DAKIAJ77C4CTZOP7TUVWQ%26tag%3Dzigexn6400-22%26linkCode%3Dxm2%26camp%3D2025%26creative%3D12734%26creativeASIN%3DB00000G43U"}
# ]
# },
# "ItemAttributes"=>{"Artist"=>"All Saints", "Manufacturer"=>"CD", "ProductGroup"=>"Music", "Title"=>"All Saints-Remix Album"}
# }
# {
# "ASIN"=>"0008138303",
# "DetailPageURL"=>"http://www.amazon.co.uk/All-Light-We-Cannot-See/dp/0008138303%3FSubscriptionId%3DAKIAJ77C4CTZOP7TUVWQ%26tag%3Dzigexn6400-22%26linkCode%3Dxm2%26camp%3D2025%26creative%3D165953%26creativeASIN%3D0008138303",
# "ItemLinks"=>{
# "ItemLink"=>[
# {"Description"=>"Add To Wishlist", "URL"=>"http://www.amazon.co.uk/gp/registry/wishlist/add-item.html%3Fasin.0%3D0008138303%26SubscriptionId%3DAKIAJ77C4CTZOP7TUVWQ%26tag%3Dzigexn6400-22%26linkCode%3Dxm2%26camp%3D2025%26creative%3D12734%26creativeASIN%3D0008138303"},
# {"Description"=>"Tell A Friend", "URL"=>"http://www.amazon.co.uk/gp/pdp/taf/0008138303%3FSubscriptionId%3DAKIAJ77C4CTZOP7TUVWQ%26tag%3Dzigexn6400-22%26linkCode%3Dxm2%26camp%3D2025%26creative%3D12734%26creativeASIN%3D0008138303"},
# {"Description"=>"All Customer Reviews", "URL"=>"http://www.amazon.co.uk/review/product/0008138303%3FSubscriptionId%3DAKIAJ77C4CTZOP7TUVWQ%26tag%3Dzigexn6400-22%26linkCode%3Dxm2%26camp%3D2025%26creative%3D12734%26creativeASIN%3D0008138303"},
# {"Description"=>"All Offers", "URL"=>"http://www.amazon.co.uk/gp/offer-listing/0008138303%3FSubscriptionId%3DAKIAJ77C4CTZOP7TUVWQ%26tag%3Dzigexn6400-22%26linkCode%3Dxm2%26camp%3D2025%26creative%3D12734%26creativeASIN%3D0008138303"}
# ]
# },
# "SalesRank"=>"41",
# "SmallImage"=>{
# "URL"=>"http://ecx.images-amazon.com/images/I/511qpsWcTyL._SL75_.jpg",
# "Height"=>{"__content__"=>"75", "Units"=>"pixels"},
# "Width"=>{"__content__"=>"49", "Units"=>"pixels"}
# },
# "MediumImage"=>{
# "URL"=>"http://ecx.images-amazon.com/images/I/511qpsWcTyL._SL160_.jpg",
# "Height"=>{"__content__"=>"160", "Units"=>"pixels"},
# "Width"=>{"__content__"=>"104", "Units"=>"pixels"}
# },
# "LargeImage"=>{
# "URL"=>"http://ecx.images-amazon.com/images/I/511qpsWcTyL.jpg",
# "Height"=>{"__content__"=>"500", "Units"=>"pixels"},
# "Width"=>{"__content__"=>"326", "Units"=>"pixels"}
# },
# "ImageSets"=>{
# "ImageSet"=>{
# "SwatchImage"=>{
# "URL"=>"http://ecx.images-amazon.com/images/I/511qpsWcTyL._SL30_.jpg",
# "Height"=>{"__content__"=>"30", "Units"=>"pixels"},
# "Width"=>{"__content__"=>"20", "Units"=>"pixels"}
# },
# "SmallImage"=>{
# "URL"=>"http://ecx.images-amazon.com/images/I/511qpsWcTyL._SL75_.jpg",
# "Height"=>{"__content__"=>"75", "Units"=>"pixels"},
# "Width"=>{"__content__"=>"49", "Units"=>"pixels"}
# },
# "ThumbnailImage"=>{
# "URL"=>"http://ecx.images-amazon.com/images/I/511qpsWcTyL._SL75_.jpg",
# "Height"=>{"__content__"=>"75", "Units"=>"pixels"},
# "Width"=>{"__content__"=>"49", "Units"=>"pixels"}
# },
# "TinyImage"=>{
# "URL"=>"http://ecx.images-amazon.com/images/I/511qpsWcTyL._SL110_.jpg",
# "Height"=>{"__content__"=>"110", "Units"=>"pixels"},
# "Width"=>{"__content__"=>"72", "Units"=>"pixels"}
# },
# "MediumImage"=>{"URL"=>"http://ecx.images-amazon.com/images/I/511qpsWcTyL._SL160_.jpg",
# "Height"=>{"__content__"=>"160", "Units"=>"pixels"},
# "Width"=>{"__content__"=>"104", "Units"=>"pixels"}
# },
# "LargeImage"=>{
# "URL"=>"http://ecx.images-amazon.com/images/I/511qpsWcTyL.jpg",
# "Height"=>{"__content__"=>"500", "Units"=>"pixels"},
# "Width"=>{"__content__"=>"326", "Units"=>"pixels"}
# }, "Category"=>"primary"
# }
# },
# "ItemAttributes"=>{
# "Author"=>"Anthony Doerr",
# "Binding"=>"Paperback",
# "EAN"=>"9780008138301",
# "EANList"=>{"EANListElement"=>"9780008138301"},
# "Edition"=>"0",
# "ISBN"=>"0008138303",
# "IsEligibleForTradeIn"=>"1",
# "ItemDimensions"=>{
# "Height"=>{"__content__"=>"787", "Units"=>"hundredths-inches"},
# "Length"=>{"__content__"=>"508", "Units"=>"hundredths-inches"},
# "Weight"=>{"__content__"=>"84", "Units"=>"hundredths-pounds"},
# "Width"=>{"__content__"=>"142", "Units"=>"hundredths-inches"}
# },
# "Label"=>"Fourth Estate",
# "Languages"=>{
# "Language"=>[
# {"Name"=>"English", "Type"=>"Published"},
# {"Name"=>"English", "Type"=>"Original Language"}
# ]
# },
# "ListPrice"=>{
# "Amount"=>"899",
# "CurrencyCode"=>"GBP",
# "FormattedPrice"=>"£8.99"
# },
# "Manufacturer"=>"Fourth Estate",
# "NumberOfPages"=>"544",
# "PackageDimensions"=>{
# "Height"=>{"__content__"=>"142", "Units"=>"hundredths-inches"},
# "Length"=>{"__content__"=>"772", "Units"=>"hundredths-inches"},
# "Weight"=>{"__content__"=>"84", "Units"=>"hundredths-pounds"},
# "Width"=>{"__content__"=>"504", "Units"=>"hundredths-inches"}
# },
# "ProductGroup"=>"Book",
# "ProductTypeName"=>"ABIS_BOOK",
# "PublicationDate"=>"2015-04-23",
# "Publisher"=>"Fourth Estate",
# "ReleaseDate"=>"2015-04-23",
# "Studio"=>"Fourth Estate",
# "Title"=>"All the Light We Cannot See",
# "TradeInValue"=>{
# "Amount"=>"50", "CurrencyCode"=>"GBP", "FormattedPrice"=>"£0.50"
# }
# },
# "OfferSummary"=>{
# "LowestNewPrice"=>{
# "Amount"=>"399", "CurrencyCode"=>"GBP", "FormattedPrice"=>"£3.99"
# },
# "LowestUsedPrice"=>{"Amount"=>"263", "CurrencyCode"=>"GBP", "FormattedPrice"=>"£2.63"},
# "TotalNew"=>"20",
# "TotalUsed"=>"7",
# "TotalCollectible"=>"0",
# "TotalRefurbished"=>"0"
# }
# }
\ No newline at end of file
h1 Index page
\ No newline at end of file
- if @recommend_products.present?
.row.recommends
- @recommend_products.each do |product|
= render "products/recommend_item", product: product
- if @products.present?
.products
- @products.each do |product|
= render "products/item", product: product
.text-center
= paginate @products
\ No newline at end of file
- if @products.present?
.products
- @products.each do |product|
= render "products/item", product: product
.text-center
= paginate @products
\ No newline at end of file
......@@ -7,10 +7,13 @@ nav.navbar.navbar-inverse
span.icon-bar
span.icon-bar
span.icon-bar
= link_to 'Home', root_path, class: 'navbar-brand'
h1.logo
a(href="#{root_path}" class='navbar-brand')
= image_tag "logo.png"
'Venshop
.collapse.navbar-collapse
ul.nav.navbar-nav
= render 'layouts/navigation_links'
-#ul.nav.navbar-nav
-# = render 'layouts/navigation_links'
ul.nav.navbar-nav.navbar-right
- if current_user.present?
......
......@@ -17,12 +17,13 @@ html
.row
.col-sm-12.text-center
= render 'shared/messages'
.row
.col-sm-12
= render 'shared/search_bar'
.row
.col-sm-12
= render 'shared/search_bar'
= render_breadcrumbs :separator => ' / '
.row.mar-top-20
.col-sm-4
= render 'shared/categories'
.col-sm-8
= yield
.clearfix
= render 'layouts/footer'
.row.mar-bot-20
.col-lg-7
.wrap-img.mar-r-10
= image_tag product.image.thumb('51x32#').url
.title= link_to product.title, product_path(product)
.col-lg-5
= "Price: #{currency_number(product.currency, product.price)}"
.col-sm-4
h4= product.title
= image_tag product.image.thumb('71x38>')
\ No newline at end of file
ul.list-group
- @categories.each do |cate|
= link_to cate.name, category_path(cate), class: 'list-group-item'
\ No newline at end of file
#search-bar
= form_tag search_path, class: "form-horizontal", method: "GET" do |f|
.row
.col-sm-9
= text_field_tag :keyword, params[:keyword], placeholder: "Keyword", class: "form-control"
.col-sm-3
= submit_tag "Search", class: "btn btn-primary"
require 'dragonfly'
# Configure
Dragonfly.app.configure do
plugin :imagemagick
secret "9a92d67749f82dc4000bbd351d7ffba9fb266682fd2d54efed218ece2497cb21"
url_format "/media/:job/:name"
datastore :file,
root_path: Rails.root.join('public/system/dragonfly', Rails.env),
server_root: Rails.root.join('public')
end
# Logger
Dragonfly.logger = Rails.logger
# Mount as middleware
Rails.application.middleware.use Dragonfly::Middleware
# Add model functionality
if defined?(ActiveRecord::Base)
ActiveRecord::Base.extend Dragonfly::Model
ActiveRecord::Base.extend Dragonfly::Model::Validations
end
......@@ -5,6 +5,10 @@ Rails.application.routes.draw do
# You can have the root of your site routed with "root"
root 'home#index'
get '/search' => "home#search", as: :search
resources :categories, :only => [:show]
resources :products, :only => [:show]
# Example of regular route:
# get 'products/:id' => 'catalog#view'
......
class CreateCategories < ActiveRecord::Migration
def change
create_table :categories do |t|
t.string :name
end
end
end
class CreateProducts < ActiveRecord::Migration
def change
create_table :products do |t|
t.string :pid
t.string :title, :null => false, :default => '', :limit => 1024
t.string :author
t.string :publisher
t.string :studio
t.float :price
t.string :currency
t.integer :category_id
t.integer :user_id
t.string :image_uid
t.datetime :release_date
t.datetime :public_date
end
end
end
class AddRecommendToProducts < ActiveRecord::Migration
def change
add_column :products, :recommend, :boolean, default: false
end
end
......@@ -11,7 +11,27 @@
#
# It's strongly recommended that you check this file into your version control system.
ActiveRecord::Schema.define(version: 20150714072001) do
ActiveRecord::Schema.define(version: 20150715033939) do
create_table "categories", force: :cascade do |t|
t.string "name", limit: 255
end
create_table "products", force: :cascade do |t|
t.string "pid", limit: 255
t.string "title", limit: 1024, default: "", null: false
t.string "author", limit: 255
t.string "publisher", limit: 255
t.string "studio", limit: 255
t.float "price", limit: 24
t.string "currency", limit: 255
t.integer "category_id", limit: 4
t.integer "user_id", limit: 4
t.string "image_uid", limit: 255
t.datetime "release_date"
t.datetime "public_date"
t.boolean "recommend", limit: 1, default: false
end
create_table "users", force: :cascade do |t|
t.string "email", limit: 255, default: "", null: false
......
namespace :crawler do
task :aws => :environment do
# categories name want to craw
# categories = %w(Books Music Electronics Software Kitchen)
categories = %w(Books Music Electronics Software Kitchen)
# categories.each do |cate|
categories.each do |search_cate|
category = Category.find_or_create_by(name: search_cate)
(1..5).each do |page|
next if category.products.count >= 20 # limit 20 products each category
response = VacuumAwsService.item_search({
query: {
'Keywords' => 'All',
'SearchIndex' => search_cate,
'ItemPage' => page,
'ResponseGroup' => 'Medium'
}
})
items = VacuumAwsService.parse_items(response)
# end
items.each do |item|
break if category.products.count >= 20 # limit 20 products each category
param_item = VacuumAwsService.parse_item(item)
category.products.create(param_item) if param_item.present? && category.products.where(pid: param_item[:pid], title: param_item[:title]).limit(1).blank?
end
end
end
response = VacuumAwsService.item_search({
params:{
query: {
'Keywords' => 'Architecture',
'SearchIndex' => 'Books'
}
}
})
puts '---------------------------------------------------------------------------------------------'
puts response.data
end
end
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment