Skip to content

Commit

Permalink
better news visualization
Browse files Browse the repository at this point in the history
  • Loading branch information
antirez committed Oct 17, 2011
1 parent 1816189 commit f41ac48
Show file tree
Hide file tree
Showing 3 changed files with 93 additions and 22 deletions.
7 changes: 3 additions & 4 deletions README
Original file line number Diff line number Diff line change
Expand Up @@ -66,11 +66,10 @@ The hash has the following fields:
rank -> News score adjusted by age: RANK = SCORE / AGE^ALPHA
up -> Counter with number of upvotes
down -> Counter with number of downvotes
comments -> number of comments

the "up" and "down" fields are in the news just as a fast way to access
this information, as it is one of the things we show when we render the news
into HTML. Otherwise this data is also available in the sorted sets
news.up:<id> and news.down:<id> as you'll see below.
Note: up, down, comments fields are also available in other ways but we
denormalize for speed.

Also recently posted urls have a key named "url:<actual full url>" with TTL 48
hours and set to the news ID of a recently posted news having this url.
Expand Down
76 changes: 63 additions & 13 deletions app.rb
Original file line number Diff line number Diff line change
Expand Up @@ -43,9 +43,17 @@

get '/' do
H.set_title "Top News - #{SiteName}"
topnews = get_top_news
news = get_top_news
H.page {
H.h2 {"Top news"}+news_list_to_html(topnews)
H.h2 {"Top news"}+news_list_to_html(news)
}
end

get '/latest' do
H.set_title "Latest news - #{SiteName}"
news = get_latest_news
H.page {
H.h2 {"Latest news"}+news_list_to_html(news)
}
end

Expand Down Expand Up @@ -326,7 +334,10 @@ def check_user_credentials(username,password)
# Redis pipelining.
def get_news_by_id(news_ids,opt={})
result = []
news_ids = [news_ids] if !news_ids.is_a? Array
if !news_ids.is_a? Array
opt[:single] = true
news_ids = [news_ids]
end
news = $r.pipelined {
news_ids.each{|nid|
$r.hgetall("news:#{nid}")
Expand Down Expand Up @@ -355,7 +366,28 @@ def get_news_by_id(news_ids,opt={})
result.each_with_index{|n,i|
n["username"] = usernames[i]
}
result

# Load $User vote information if we are in the context of a
# registered user.
if $user
votes = $r.pipelined {
result.each{|n|
$r.zscore("news.up:#{n["id"]}",$user["id"])
$r.zscore("news.down:#{n["id"]}",$user["id"])
}
}
result.each_with_index{|n,i|
if votes[i*2]
n["voted"] = :up
elsif votes[(i*2)+1]
n["voted"] = :down
end
}
end

# Return an array if we got an array as input, otherwise
# the single element the caller requested.
opt[:single] ? result[0] : result
end

# Vote the specified news in the context of a given user.
Expand Down Expand Up @@ -443,7 +475,8 @@ def submit_news(title,url,text,user_id)
"score", 0,
"rank", 0,
"up", 0,
"down", 0)
"down", 0,
"comments", 0)
# The posting user virtually upvoted the news posting it
vote_news(news_id,user_id,:up)
news = get_news_by_id(news_id)
Expand All @@ -467,12 +500,21 @@ def submit_news(title,url,text,user_id)
end

# Turn the news into its HTML representation, that is
# a linked title with buttons to up/down vote.
# a linked title with buttons to up/down vote plus additional info.
# This function expects as input a news entry as obtained from
# the get_news_by_id function.
def news_to_html(news)
su = news["url"].split("/")
domain = (su[0] == "text:") ? "comment" : su[2]
H.news {
H.uparrow {
if news["voted"] == :up
upclass = "voted"
downclass = "disabled"
elsif news["voted"] == :down
downclass = "voted"
upclass = "disabled"
end
H.news(:id => news["id"]) {
H.uparrow(:class => upclass) {
"&#9650;"
}+" "+
H.h2 {
Expand All @@ -481,9 +523,9 @@ def news_to_html(news)
}
}+" "+
H.address {
"("+H.entities(domain)+")"
"at "+H.entities(domain)
}+" "+
H.downarrow {
H.downarrow(:class => downclass) {
"&#9660;"
}+
H.p {
Expand All @@ -492,10 +534,13 @@ def news_to_html(news)
H.a(:href=>"/user/"+H.urlencode(news["username"])) {
news["username"]
}
}+" "+str_elapsed(news["ctime"].to_i)
}+" "+str_elapsed(news["ctime"].to_i)+" "+
H.a(:href => "/news/#{news["id"]}") {
news["comments"]+" comments"
}
}
#+news["score"]+","+news["rank"]+","+compute_news_rank(news).to_s
}
}+"\n"
end

# If 'news' is a list of news entries (Ruby hashes with the same fields of
Expand Down Expand Up @@ -538,13 +583,18 @@ def update_news_rank_if_needed(n)
# score since this is done incrementally when there are pageviews on the
# site.
def get_top_news
result = []
news_ids = $r.zrevrange("news.top",0,NewsPerPage-1)
result = get_news_by_id(news_ids,:update_rank => true)
# Sort by rank before returning, since we adjusted ranks during iteration.
result.sort{|a,b| b["rank"].to_f <=> a["rank"].to_f}
end

# Get news in chronological order.
def get_latest_news
news_ids = $r.zrevrange("news.cron",0,NewsPerPage-1)
result = get_news_by_id(news_ids,:update_rank => true)
end

###############################################################################
# Utilit functions
###############################################################################
Expand Down
32 changes: 27 additions & 5 deletions public/css/style.css
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@ a {

h2 {
margin-bottom:20px;
font-size:18px;
font-size:22px;
color:#333;
}

header {
Expand Down Expand Up @@ -94,24 +95,33 @@ news {

news h2 {
display: inline;
font-size:20px;
}

news address {
display: inline;
color: #999;
font-size:14px;
}

news a {
news h2 a {
color: #3333ee;
}

news a:visited {
news h2 a:visited {
color: #6666aa;
}

news uparrow, news downarrow {
font-size: 16px;
color:#666;
}

news uparrow {
color:#999;
}

news downarrow {
color:#ccc;
}

news p {
Expand All @@ -120,6 +130,18 @@ news p {
margin-left:20px;
}

username a {
news p a {
color: #666;
}

news uparrow.voted {
color:#99dd99;
}

news downarrow.voted {
color:#dd9999;
}

news downarrow.disabled, uparrow.disabled {
visibility: hidden;
}

0 comments on commit f41ac48

Please sign in to comment.