javaeye3/script/g_sitemap.rb

263 lines
11 KiB
Ruby
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/usr/bin/env ruby
# -*- coding: utf-8 -*-
ENV['RAILS_ENV'] ||= 'production'
require File.dirname(__FILE__) + '/../config/environment'
require 'open-uri'
require 'pathname'
frequency = ARGV[0] || 'daily'
MAX_SIZE = 50000
SITEMAP_FILE_PATH = "#{RAILS_ROOT}/public/sitemaps/"
SITEMAP_URL_PATH = "http://www.#{DEFAULT_ROOT_DOMAIN}/sitemaps/"
# config each channel infos to generate sitemap
# [:name] 区分不同的sitemap也是对应sitemap文件的一部分
# [:channel_class] 数据的model class, 使用其findfind_in_batches方法来查询数据
# [:query_options] 查询参数,daily的查询条件会据此结合:daily_field来形成自己的查询条件
# [:init_proc] 准备sitemap文件生成所需的数据格式定义url、更新时间
channels =
[{ :name => "topics", :channel_class => Topic,
:query_options => { :conditions => ["topics.forum_id IS NOT NULL AND topics.status_flag <> 'hidden' AND topics.status_flag <> 'delete'"], :joins => "join posts on topics.last_post_id = posts.id" },
:daily_field => "topics.created_at",
:init_proc => Proc.new{ Topic.class_eval do
def loc
format 'http://www.%s/topic/%s', DEFAULT_ROOT_DOMAIN, self.id
end
def lastmod
(self.last_post || self).created_at.xmlschema
end
end}},
{ :name => "blogs", :channel_class => Blog,
:query_options => { :conditions => ['blogs.status <> ? and blogs.status <> ?', Blog.status[:draft], Blog.status[:deleted]], :joins => "join users on blogs.user_id = users.id" },
:daily_field => "blogs.created_at",
:init_proc => Proc.new{ Blog.class_eval do
def loc
format '%sblog/%s', self.user.homepage, self.id
end
def lastmod
created_at.xmlschema
end
end}},
{ :name => "problems", :channel_class => Problem,
:query_options => { :conditions => ["1=1"]},
:daily_field => "created_at",
:init_proc => Proc.new{ Problem.class_eval do
def loc
format 'http://www.%s/problems/%s', DEFAULT_ROOT_DOMAIN, self.id
end
def lastmod
source = self.solutions.max_by{|solution| solution.created_at } || self
source.created_at.xmlschema
end
end}},
{ :name => "news", :channel_class => News,
:query_options => { :conditions => ["status = 'approved'"]},
:daily_field => "created_at",
:init_proc => Proc.new{ News.class_eval do
def loc
format 'http://www.%s/news/%s-%s', DEFAULT_ROOT_DOMAIN, self.id, URI.escape(self.slug_url.to_s)
end
def lastmod
created_at.xmlschema
end
end}},
{ :name => "magazines", :channel_class => Essence,
:query_options => { :conditions => ["status = 'approved'"]},
:daily_field => "created_at",
:init_proc => Proc.new{ Essence.class_eval do
def loc
format 'http://www.%s/magazines/%s-%s', DEFAULT_ROOT_DOMAIN, self.id, URI.escape(self.slug_url.to_s)
end
def lastmod
created_at.xmlschema
end
end}},
{ :name => "group", :channel_class => Group,
:query_options => { :conditions => ["active = ? and id not in (?)", true, Group::HIDDEN_GROUP_IDS]},
:daily_field => "created_at",
:init_proc => Proc.new{ Group.class_eval do
def loc
format 'http://%s.group.%s', URI.escape(self.domain), DEFAULT_ROOT_DOMAIN
end
def lastmod
created_at.xmlschema
end
end}},
{ :name => "group_topic", :channel_class => GroupTopic,
:query_options => { :conditions => ["1=1"], :joins => "join group_posts on group_topics.last_post_id = group_posts.id left join groups on groups.id=group_topics.group_id"},
:daily_field => "group_topics.created_at",
:init_proc => Proc.new{ GroupTopic.class_eval do
def loc
format 'http://%s.group.%s/group/topic/%s', URI.escape(self.group.domain), DEFAULT_ROOT_DOMAIN, self.id
end
def lastmod
created_at.xmlschema
end
end}},
{ :name => "group_wiki", :channel_class => Page,
:query_options => { :conditions => ["pages.group_id is not null and pages.group_id not in (?) and pages.status <> 'delete'", Group::HIDDEN_GROUP_IDS], :joins => "join groups on groups.id=pages.group_id"},
:daily_field => "pages.created_at",
:init_proc => Proc.new{ Page.class_eval do
def loc
format 'http://%s.group.%s%s', URI.escape(self.group.domain), DEFAULT_ROOT_DOMAIN, URI.escape(self.slug_url)
end
def lastmod
created_at.xmlschema
end
end}},
{ :name => "magazine_type", :channel_class => Essence,
:query_options => { :select => "essence_type as id, max(created_at) as created_at", :conditions => ["1=1"], :group => "essence_type"},
:init_proc => Proc.new{ Essence.class_eval do
def loc
format 'http://www.%s/magazines/%s', DEFAULT_ROOT_DOMAIN, Essence::ESSENCE_TYPE[self.id].try(:last)
end
end}},
{ :name => "problem_category", :channel_class => Forum,
:query_options => { :conditions => ["1=1"]},
:init_proc => Proc.new{ Forum.class_eval do
def loc
format 'http://www.%s/problems/tags/%s', DEFAULT_ROOT_DOMAIN, URI.escape(self.urlname)
end
end}},
{ :name => "whole_tag", :channel_class => Whole::Tag,
:query_options => { :select => "id, name", :conditions => "category_id > 0" },
:init_proc => Proc.new { Whole::Tag.class_eval do
def loc
[format('http://www.%s/news/tag/%s', DEFAULT_ROOT_DOMAIN, URI.escape(self.name)),
format('http://www.%s/magazines/tag/%s', DEFAULT_ROOT_DOMAIN, URI.escape(self.name)),
format('http://www.%s/blogs/tag/%s', DEFAULT_ROOT_DOMAIN, URI.escape(self.name)) ]
end
end }},
{ :name => "whole_category", :channel_class => WholeCategory,
:query_options => { :select => "id, urlname" },
:init_proc => Proc.new { WholeCategory.class_eval do
def loc
[format('http://www.%s/news/category/%s', DEFAULT_ROOT_DOMAIN, URI.escape(self.urlname)),
format('http://www.%s/magazines/category/%s', DEFAULT_ROOT_DOMAIN, URI.escape(self.urlname)),
format('http://www.%s/blogs/category/%s', DEFAULT_ROOT_DOMAIN, URI.escape(self.urlname)) ]
end
end }},
{ :name => "problem_tag", :channel_class => SysTag,
:query_options => { :select => "sys_tags.id, sys_tags.urlname", :conditions => "sys_tags.forum_id is not null", :joins => "join problems on sys_tags.id = problems.sys_tag_id", :group => "sys_tags.id" },
:init_proc => Proc.new{ SysTag.class_eval do
def loc
format 'http://www.%s/problems/tags/%s', DEFAULT_ROOT_DOMAIN, URI.escape(self.urlname)
end
end}}
]
# 跳过生成daily范围的网站地图
without_daily_channels = ["whole_category", "whole_tag", "magazine_type", "problem_category"]
# 需要二级域名访问的地址
subdomain_channels = %w(blogs group group_topic group_wiki)
module Enumerable
def to_sitemap
builder = Builder::XmlMarkup.new(:indent => 2)
builder.instruct!
sitemap = builder.urlset :xmlns => 'http://www.sitemaps.org/schemas/sitemap/0.9' do
each do |item|
if item.loc.is_a? Array
item.loc.each do |loc|
builder.url do
builder.loc loc
end
end
else
builder.url do
builder.loc item.loc
builder.lastmod item.lastmod if defined?(item.lastmod)
end
end
end
end
sitemap
end
def write_sitemap(prefix, last = false)
FileUtils.mkdir_p(SITEMAP_FILE_PATH)
unless empty?
file_name = last ? "#{SITEMAP_FILE_PATH}#{prefix}_last.xml" : "#{SITEMAP_FILE_PATH}#{prefix}_#{first.id}.xml"
# puts "====== writing sitemap file : #{file_name}..."
File.open(file_name, 'w'){ |f| f.write to_sitemap}
# puts "====== writed sitemap file : #{file_name}"
end
end
end
def sitemaps
Pathname.new(SITEMAP_FILE_PATH).children
end
def gzip_sitemaps
`gzip #{SITEMAP_FILE_PATH}*.xml`
`rm #{SITEMAP_FILE_PATH}sitemap_index.xml.gz` # 索引文件不压缩
end
def clean_sitemaps
sitemaps.each {|s| s.delete unless s.to_s =~ /last/}
end
def clean_daily_sitemaps
sitemaps.each {|s| s.delete if s.to_s =~ /last/}
end
def generate_sitemap_index
File.open("#{SITEMAP_FILE_PATH}sitemap_index.xml", 'w') do |f|
builder = Builder::XmlMarkup.new(:indent => 2)
builder.instruct!
sitemap = builder.sitemapindex :xmlns => 'http://www.sitemaps.org/schemas/sitemap/0.9' do
sitemaps.each do |file|
if ['.xml','.gz'].include?(file.extname) && file.basename.to_s != 'sitemap_index.xml'
builder.sitemap do
builder.loc "#{SITEMAP_URL_PATH}#{file.basename}"
end
end
end
end
f.write sitemap
end
end
case frequency
when 'weekly'
clean_sitemaps
puts "== generating sitemaps weekly: #{Time.now()}..."
puts " channels: #{(channels.collect{|e| e[:name]} - subdomain_channels).join(', ')}"
channels.each do |channel|
next if (subdomain_channels).include?(channel[:name])
channel[:init_proc].call
channel[:channel_class].find_in_batches({ :batch_size => MAX_SIZE}.merge(channel[:query_options])) do |records|
puts "==== generating sitemap #{channel[:name]} (weekly) : record count(#{records.count}) ,#{Time.now.to_s(:db)}..."
records.write_sitemap(channel[:name])
puts " generated sitemap #{channel[:name]} (weekly) : record count(#{records.count}) ,#{Time.now.to_s(:db)}"
end
end
puts "== generated sitemaps weekly: #{Time.now.to_s(:db)}"
when 'daily'
clean_daily_sitemaps
puts "== generating sitemaps daily: #{Time.now()}"
puts " channels: #{(channels.collect{|e| e[:name]} - subdomain_channels - without_daily_channels).join(', ')}"
channels.each do |channel|
next if (subdomain_channels + without_daily_channels).include?(channel[:name])
channel[:init_proc].call
daily_conditions = channel[:query_options][:conditions]
daily_conditions[0] << " and #{channel[:daily_field]} > ? " and daily_conditions << Time.now.beginning_of_week if channel[:daily_field]
records = channel[:channel_class].all(channel[:query_options].merge({ :conditions => daily_conditions}))
puts "==== generating sitemap #{channel[:name]} (daily) : record count(#{records.count}) , #{Time.now.to_s(:db)}..."
records.write_sitemap(channel[:name], true)
puts " generated sitemap #{channel[:name]} (daily) : record count(#{records.count}) , #{Time.now.to_s(:db)}"
end
puts "== generated sitemaps daily: #{Time.now.to_s(:db)}"
else
puts 'xxxx Unknown update frequency , use \'daily\'(default) or \'weekly\'.'
end
gzip_sitemaps
generate_sitemap_index