#!/usr/bin/ruby
# (c) 2005-2006 Frederik Deweerdt frederik.deweerdt@gmail.com
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
require 'net/ftp'
class RGBColor
def initialize(rgb, factor=0.9)
@r = Float((rgb & 0xff0000) >> 16)
@g = Float((rgb & 0x00ff00) >> 8)
@b = Float((rgb & 0x0000ff))
@factor = Float(factor)
end#def initialize
def lighten!
@r +=1; @g += 1; @b += 1
@r = (@r / @factor) % 255
@g = (@g / @factor) % 255
@b = (@b / @factor) % 255
return self
end#def lighten!
def darken!
@r -=1; @g -= 1; @b -= 1
@r = (@r * @factor) % 255
@g = (@g * @factor) % 255
@b = (@b * @factor) % 255
return self
end#def darken!
def to_s
return sprintf("%02x%02x%02x", @r.to_i, @g.to_i, @b.to_i);
end#def to_s
end#class RGBColor
class Rectangle
attr_writer :width,:height,:area
attr_reader :width,:height,:area,:news_topic
def initialize (width, height, news_topic)
@width = Float(width)
@height = Float(height)
@news_topic = news_topic
end#intialize
def area
return @width * @height
end#def area
def ratio
ret = @width / @height
ret = 1 / ret if ret < 1
return ret
end#def ratio
def to_s
return "[#{@width},#{@height},@#{area},r#{ratio}]"
end#to_s
end#class Rectangle
class Row
attr_reader :kind,:elements
HORIZ = 0
VERT = 1
def initialize(kind,length)
@kind = kind
@fixed_side_length = length
@elements = Array.new
@ratio = 9999
end#initialize
def area
area = 0.0
@elements.each { |r| area += r.area }
return area
end
def width
return area / @fixed_side_length if @kind == VERT
return @fixed_side_length
end#width
def height
return area / @fixed_side_length if @kind == HORIZ
return @fixed_side_length
end#height
def recalculate_layout
#puts "#{area} / #{@fixed_side_length}"
variable_side_lenth = area / @fixed_side_length
new_elem = Array.new
if @kind == VERT
then
@elements.each { |r| new_elem.push(Rectangle.new(variable_side_lenth, r.area / variable_side_lenth,r.news_topic)) }
else
@elements.each { |r| new_elem.push(Rectangle.new(r.area / variable_side_lenth, variable_side_lenth, r.news_topic)) }
end#if
@elements = new_elem
end#recalculate_layout
public
def push(o)
@elements.push(o)
recalculate_layout
better = false
better = true if @elements.last.ratio < @ratio
@ratio = @elements.last.ratio
return better
end#push
def pop()
o = @elements.pop()
recalculate_layout
return o
end#pop
def to_s
ret = String.new
ret += "=====\n"
@elements.each { |r| ret += "#{r.to_s}\n" }
ret += "-----\n"
end#dump_row
end#class Row
class Frame
def initialize(org_x, org_y, width, height)
@org_x = org_x
@org_y = org_y
@width = width
@height = height
@free_x = 0
@free_y = 0
@current_row = free_width > free_height ? Row.new(Row::VERT, free_height) : Row.new(Row::HORIZ,free_width)
@rows = Array.new
end#initialize
def free_width
@width - @free_x
end#free_width
def free_height
@height - @free_y
end#free_width
def add(area, nt)
r = Rectangle.new(area, 1, nt)
if not @current_row.push(r)
then
@current_row.pop()
@rows.push(@current_row)
@free_x += @current_row.width if @current_row.kind == Row::VERT
@free_y += @current_row.height if @current_row.kind == Row::HORIZ
@current_row = free_width > free_height ? Row.new(Row::VERT, free_height) : Row.new(Row::HORIZ,free_width)
@current_row.push(r)
end#if
end#add
def to_s
@rows.each do
|row|
puts row
end#each
puts @current_row
end#to_s
public
def to_html(colors_by_topic)
s = ""
pencil_x = @org_x
pencil_y = @org_y
previous_pencil_x = pencil_x
previous_pencil_y = pencil_y
zoom = 1
border = 0
@rows.push(@current_row)
@rows.each do
|row|
previous_pencil_x = pencil_x
previous_pencil_y = pencil_y
row.elements.each do
|r|
color = colors_by_topic[r.news_topic.category];
colors_by_topic[r.news_topic.category] = color.darken!
news_title = r.news_topic.text.gsub(/"/,'"').gsub(/\[\d*\]/, '')
font_size = Integer(Math.log(pus(r.area,6) / r.news_topic.title.length)/6)
s += "
\n"
pencil_y += Integer(r.height*zoom) + border if row.kind == Row::VERT
pencil_x += Integer(r.width*zoom) + border if row.kind == Row::HORIZ
end#do row.elements.each
if row.kind == Row::VERT && row.elements.last != nil
then
pencil_x += Integer(row.elements.last.width * zoom) + border
pencil_y = previous_pencil_y
end#if
if row.kind == Row::HORIZ && row.elements.last != nil
then
pencil_y += Integer(row.elements.last.height * zoom) + border
pencil_x = previous_pencil_x
end#if
end#each
@rows.pop()
return s
end#to_html
def pus (int, pow)
res = int
while pow > 1 do
res *= int
pow -= 1
end#while
return res
end#pus
end#class Frame
class NewsLang
private
def initialize(end_of_gn, end_of_article, in_the_news, related_articles, topic_separator, \
top_stories, more_top_stories, command, customize_begin, customize_end)
@end_of_gn = end_of_gn
@end_of_article = end_of_article
@in_the_news = in_the_news
@related_articles = related_articles
@topic_separator = topic_separator
@top_stories = top_stories
@more_top_stories = more_top_stories
@command = command
@customize_begin = customize_begin
@customize_end = customize_end
end#def initialize
public
attr_reader :end_of_gn, :related_articles,:end_of_article,:in_the_news,:top_stories,:command, :topic_separator, :more_top_stories, :customize_begin, :customize_end;
FR = NewsLang.new("Versions internationales de Google Actualités disponibles", \
"articles connexes", \
"Google Actualit.s", \
'\[(\d+)\]et (\d+) articles connexes', \
'(\[\d+\]([\w|\/|\(|\)| ]+)\s\s>>)|(la une \(suite\))', \
"À la une", \
". la une .suite.", \
'|lynx -cfg /dev/null -width 2000 -image_links -dump http://news.google.com/news?ned=fr', \
#'|wget --user-agent=Mozilla -O /dev/stdout http://news.google.com/news?ned=fr 2> /dev/null|lynx -width 200 -image_links -stdin -dump|iconv -c -f ISO_8859-1 -t UTF-8', \
#'|wget --user-agent=Mozilla -O /dev/stdout http://news.google.fr/news?ned=fr 2> /dev/null|lynx -width 200 -image_links -stdin -dump', \
#'|cat fr.txt', \
'Personnaliser cette page', \
'Personnaliser cette page fermer');
EN = NewsLang.new("International versions of Google News available in", \
'related', \
"Google News", \
'\[(\d+)\]all ([\d|,]+) related', \
'(\[\d+\]([\w|\/|\(|\)| |\.]+)\s\s»)|(More Top Stories)', \
'Top Stories', \
'More Top Stories', \
'|lynx -cfg /dev/null -width 2000 -image_links -dump http://news.google.com/news', \
'Customize this page', \
'Customize this page close');
# ES = NewsLang.new("Versiones internacionales de Google Noticias disponibles en",
# "culos relacionados",
# "Es noticia",
# '\[(\d+)\]y (\d+) art.culos relacionados',
# '(\[\d+\]([\w|\/|\(|\)| ]+)\s\s>>)|(noticias destacadas)');
end#class NewsLang
class NewsPage
attr_reader :articles,:no_articles,:display_as_text,:no_articles_by_topic,:articles_by_topic,:topics
LinesToSkip = 35
def initialize(lines, limit=0, ns=NewsLang::EN)
end_of_article = false
skip=LinesToSkip
topic = ""
@articles = Array.new
@no_articles = 0
@links = Array.new
@topics = Array.new
@ns = ns
category = @ns.top_stories
lines.each do
|line|
if line =~ /(\d+)\. (http:\/\/.*)/
then
@links[$1.to_i] = $2
end#if
end#each
image_index = nil
image_url_index = nil
in_the_news = false
in_customize_html = false
expecting_category = true
lines.each {
|line|
in_the_news = true if line =~ /#{ns.in_the_news}/
next if not in_the_news
in_customize_html = true if line =~ /#{ns.customize_begin}/
in_customize_html = false if line =~ /#{ns.customize_end}/
next if in_customize_html
# Category?
if line =~ /cleardot\.gif/
then
expecting_category = true
next
end#if
# Category found
if expecting_category and in_the_news
then
expecting_category = false
if line =~ /\[\d+\]([\w|\/| ]+) / or line =~ /(#{ns.more_top_stories})/
then
category = $1
end#if
next
end#if
# Article found
if line =~ /#{ns.related_articles}/
then
related_link = @links[$1.to_i]
related_count = $2.strip.gsub(',','').to_i
art_line_array = Array.new
tmp_image_line_array = Array.new
search_for_image = false
image_url = nil
image_link = nil
lines.slice(0,lines.index(line)+1).reverse.each {
|l|
search_for_image = true if not search_for_image and l.strip.empty?
# Search the image
if search_for_image
then
tmp_image_line_array.push(l)
break if tmp_image_line_array.size > 3
if l =~ /\[(.*)\]\[.*\]-\[(.*)\]\[.*\]/
then
image_link = @links[$1.to_i]
image_url = @links[$2.to_i]
break;
end#if
else
art_line_array.push(l.strip)
end#if
}
art_line_array.reverse[0] =~ /\[(\d+)\](.*)/
title = $2.strip unless $2 == nil
title_link = @links[$1.to_i]
text = art_line_array.reverse.grep(/^[^\[]/).slice(1,4).join
nt = NewsTopic.new(title, title_link, text, image_url, image_link, related_link, related_count, category)
@articles.push(nt)
@no_articles += nt.related_count
end#if
}
#
# Assign percentages
#
articles.each do
|art|
art.pct = art.related_count * 100 / @no_articles
end#do
#
# Sort articles by topic
#
@articles_by_topic = Hash.new
@no_articles_by_topic = Hash.new
articles.each do
|art|
if not @articles_by_topic.has_key?(art.category)
then
@articles_by_topic[art.category] = Array.new
@no_articles_by_topic[art.category] = 0
@topics.push(art.category)
end#if
@articles_by_topic[art.category].push(art)
@no_articles_by_topic[art.category] += art.related_count
end#do
end#initialize
def display_as_text
pct = 0.0
articles.sort.each {
|art|
pct += art.no_articles.to_f * 100.0 / @no_articles.to_f
#puts "#{art.no_articles.to_f * 100.0 / @no_articles.to_f}%"
puts art.to_s
}
end#display_as_text
end#class NewsPage
class NewsTopic
attr_reader :title, :title_link, :text, :image_url, :image_link, :related_link, :related_count, :category, :pct
attr_writer :pct
def initialize(title, title_link, text, image_url, image_link, related_link, related_count, category)
@title = title
@title_link = title_link
@text = text
@image_url = image_url
@image_link = image_link
@related_link = related_link
@related_count = related_count
@category = category
@pct = nil
end#initialize
def <=>(nt)
return @related_count <=> nt.related_count
if @category != nt.category
then
return @category <=> nt.category
else
return @related_count <=> nt.related_count
end#if
end# <=>
def to_s
return "*#{@title}*\t #{@category}\t #{@related_count}\n#{@text}"
end#to_s
end#class NewsTopic
def generate_html(fileout, lang, limit)
file = Kernel.open(lang.command)
np = NewsPage.new(file.readlines, limit, lang)
file.close
colors = Array.new
colors.push(RGBColor.new(0xffcc11))
colors.push(RGBColor.new(0x111188))
colors.push(RGBColor.new(0x118111))
colors.push(RGBColor.new(0xcc11111))
colors.push(RGBColor.new(0xff6611))
colors.push(RGBColor.new(0x663399))
colors.push(RGBColor.new(0xcccccc))
colors.push(RGBColor.new(0x669999))
colors.push(RGBColor.new(0xfedcba))
coltop = Hash.new
i = 0
np.topics.each {
|t|
coltop[t] = colors[i]
i += 1
}
fileout.puts <
Open News Map
Legend
EOF
coltop.each_key { |topic| fileout.puts "#{topic} " }
fileout.puts < |
|
|
|
EOF
width = 600
height = 800
pencil_x = 130
pencil_y = 150
area = width * height
som_sqrt = 0
border = 5
f = Frame.new(pencil_x, pencil_y, width,height)
border = 10
#fileout.puts ""
np.articles.sort.reverse.each do
|art|
next if art.related_count == 0
som_sqrt += Math.sqrt(art.related_count)
end#each
np.articles.sort.reverse.each do
|art|
next if art.related_count == 0
f.add(Math.sqrt(art.related_count)*area / som_sqrt, art)
end#each_key
fileout.puts f.to_html(coltop)
fileout.puts "© 2005 - open-news.net. Tous droits reservés
"
fileout.puts ""
end#def generate_html
generate_html(File.new('index.html','w+'), NewsLang::EN, 30)
generate_html(File.new('index_fr.html','w+'), NewsLang::FR, 10)