• R/O
  • HTTP
  • SSH
  • HTTPS

提交

标签
No Tags

Frequently used words (click to add to your profile)

javaandroidc++linuxc#objective-ccocoa誰得qtrubypythongamewindowsbathyscaphephpguic翻訳omegattwitterframeworktestbtronarduinovb.net計画中(planning stage)directxpreviewerゲームエンジンdom

news4 - RSS aggrigation system


Commit MetaInfo

修订版dc4c829ca4a9caf6d049130634e7d5c7335d0438 (tree)
时间2012-11-06 20:30:32
作者hylom <hylom@user...>
Commiterhylom

Log Message

Merge remote branch 'remotes/origin/live' into live

更改概述

差异

--- a/README
+++ b/README
@@ -14,9 +14,12 @@
1414  gnewsでサイトを生成するために、下記の設定ファイルが必要です。
1515
1616 === config.py ===
17- HTMLの生成先やサイト名、RSSの取得先などを設定するファイルです。PythonのDictionaryおよびArray形式で記述されています。
17+ HTMLの生成先やサイト名などを設定するファイルです。PythonのDictionaryおよびArray形式で記述されています。
1818  config.py.sampleをコピーしてconfig.pyを作成し、編集します。
1919
20+=== sources.ini ===
21+ RSSの取得先を設定するファイルです。ini形式で記述されています。セクション名が表示されるサイト名、urlパラメータがサイトのURL、sourcesパラメータがRSSの取得先、filtersが使用するフィルタ一覧(カンマ区切り)となります。
22+
2023 === install.conf ===
2124  関連ファイルのコピー先を指定するファイルです。
2225  install.conf.sampleをコピーしてintall.confを作成し、編集します。
--- a/config.py.sample
+++ b/config.py.sample
@@ -35,81 +35,3 @@ config = {
3535 ],
3636 }
3737
38-target_rss = [
39- {
40- 'name': 'SourceForge.JP Magazine',
41- 'url': 'http://rss.rssad.jp/rss/sourceforge/magazine/rss',
42- 'source_url': 'http://sourceforge.jp/magazine/',
43- },
44- {
45- 'name': 'Slashdot Japan',
46- 'url': 'http://rss.rssad.jp/rss/slashdot/slashdot.rss',
47- 'source_url': 'http://slashdot.jp/',
48- 'filter': ['slashdotjp',],
49- },
50- {
51- 'name': 'ITmedia',
52- 'url': 'http://rss.rssad.jp/rss/itmtop/2.0/itmedia_all.xml',
53- 'source_url': 'http://www.itmedia.co.jp/',
54- 'filter': ['tagging', 'itmedia'],
55- },
56- {
57- 'name': 'So-netセキュリティ通信',
58- 'url': 'http://security-t.blog.so-net.ne.jp/index.rdf',
59- 'source_url': 'http://security-t.blog.so-net.ne.jp/',
60- 'filter': ['tagging',],
61- },
62- {
63- 'name': 'Engadget Japanese',
64- 'url': 'http://japanese.engadget.com/rss.xml',
65- 'source_url': 'http://japanese.engadget.com/',
66- 'filter': ['tagging',],
67- },
68- {
69- 'name': 'ギズモード・ジャパン',
70- 'url': 'http://feeds.gizmodo.jp/rss/gizmodo/index.xml',
71- 'source_url': 'http://www.gizmodo.jp/',
72- 'filter': ['tagging',],
73- },
74- {
75- 'name': 'TechCrunch Japan',
76- 'url': 'http://jp.techcrunch.com/feed/',
77- 'source_url': 'http://jp.techcrunch.com/',
78- 'filter': ['tagging',],
79- },
80- {
81- 'name': 'Impress Watch',
82- 'url': 'http://rss.rssad.jp/rss/headline/headline.rdf',
83- 'source_url': 'http://www.watch.impress.co.jp/',
84- 'filter': ['tagging',],
85- },
86- {
87- 'name': 'WIRED.jp',
88- 'url': 'ihttp://rss.rssad.jp/rss/h/wired/feed.rdf',
89- 'source_url': 'http://wired.jp/',
90- 'filter': ['tagging',],
91- },
92- {
93- 'name': 'CNET Japan',
94- 'url': 'http://feeds.japan.cnet.com/rss/cnet/all.rdf',
95- 'source_url': 'http://japan.cnet.com/',
96- 'filter': ['tagging',],
97- },
98- {
99- 'name': 'japan.internet.com',
100- 'url': 'http://rss.internetcom.jp/rss/japaninternetcom/index.rdf',
101- 'source_url': 'http://japan.internet.com/',
102- 'filter': ['tagging',],
103- },
104-
105- ]
106-
107-
108-"""Template:
109- {
110- 'name': '',
111- 'url': '',
112- 'source_url': '',
113- 'filter': ['tagging',],
114- },
115-"""
--- a/css/gnews.css
+++ b/css/gnews.css
@@ -22,17 +22,21 @@ a {
2222 margin-bottom: 1em;
2323 }
2424 .entry-footer{
25- color: gray;
25+ color: #888;
2626 }
2727
2828 #site-header {
29- border-bottom: 1px solid gray;
29+ border-bottom: 1px solid #888;
3030 margin-bottom: 10px;
3131 }
3232
33+#site-header .last-update {
34+ color: #888;
35+}
36+
3337 #site-footer {
3438 margin-top: 10px;
35- color: gray;
39+ color: #888;
3640 text-align: center;
3741 }
3842
--- a/fetcher.py
+++ b/fetcher.py
@@ -21,7 +21,7 @@ class FeedFetcher(object):
2121 entry = {
2222 # 'title': e.title.decode('utf8') if isinstance(e.title, str) else e.title,
2323 'title': e.title,
24- 'link': e.link,
24+ 'url': e.link,
2525 'body': e.description,
2626 'date': dateutil.parser.parse(e.updated),
2727 'feed': self._feed,
--- a/gnews.py
+++ b/gnews.py
@@ -15,7 +15,6 @@ def main():
1515 "gnews's main function"
1616 # TODO: argv check
1717
18-
1918 # fetch RSS feed
2019 entries = []
2120 for feed in sources:
@@ -49,8 +48,14 @@ def main():
4948 for e in entries:
5049 log(e["date"])
5150
51+ call_plugin('pre_render', entries)
52+
5253 # do rendering
53- params = {'tags':tags, 'page':{}, 'sorted_tags':sorted_tags}
54+ params = {
55+ 'tags':tags,
56+ 'page':{},
57+ 'sorted_tags':sorted_tags
58+ }
5459
5560 # render index page
5661 do_rendering('index', 'index%s.html', entries, params)
@@ -61,6 +66,38 @@ def main():
6166 do_rendering('tags', tag + '%s.html', subentries, params)
6267
6368
69+def call_plugin(function_name, entries):
70+ "call plugin"
71+ for plugin in config['plugins']:
72+ mod = _get_plugin(plugin)
73+ f = mod.__getattribute__(function_name)
74+ f(entries)
75+
76+class PluginError(Exception):
77+ def __init__(self, value):
78+ self.value = value
79+ def __str__(self):
80+ return 'plugin "' + self.value + '" is not found.'
81+
82+def _get_plugin(plugin_name):
83+ 'load plugin by config settings'
84+
85+ # fallback when filter isn't defined
86+ if plugin_name is None:
87+ return lambda x:x
88+
89+ # import module
90+ mods = __import__(config['plugin_directory'],
91+ globals(),
92+ locals(),
93+ [plugin_name,])
94+ try:
95+ mod = mods.__getattribute__(plugin_name)
96+ except AttributeError:
97+ raise PluginError(plugin_name)
98+
99+ return mod
100+
64101
65102 def do_rendering(page_type, filename, entries, params):
66103 "rendering page"
--- /dev/null
+++ b/plugins/hatebu_counter.py
@@ -0,0 +1,40 @@
1+#!/usr/bin/python
2+# -*- coding: utf-8
3+'plugin for hatena bookmark counter'
4+
5+#from __future__ import with_statement
6+
7+import xmlrpclib
8+import datetime
9+import time
10+import sys
11+
12+# see http://d.hatena.ne.jp/keyword/%a4%cf%a4%c6%a4%ca%a5%d6%a5%c3%a5%af%a5%de%a1%bc%a5%af%b7%ef%bf%f4%bc%e8%c6%c0API?kid=146686
13+
14+urls = []
15+counts = {}
16+
17+def pre_fetch():
18+ pass
19+
20+def pre_tag_aggregate(entries):
21+ pass
22+
23+def pre_render(entries):
24+ counts = []
25+ for i in range(0, len(entries), 50):
26+ urls = [x['url'] for x in entries[i:i+50]]
27+ c = _get_count(urls)
28+ for j in range(0, len(c)):
29+ entries[i+j]['url'] = c[j]
30+
31+def pre_quit(entries):
32+ pass
33+
34+def _get_count(urls):
35+ # urls can have max 50 items
36+ uri = "http://b.hatena.ne.jp/xmlrpc"
37+ server = xmlrpclib.ServerProxy(uri)
38+ t = server.bookmark.getCount(*urls)
39+ return t
40+
--- a/renderer.py
+++ b/renderer.py
@@ -12,7 +12,6 @@ from propertizer import propertize
1212 from logger import log
1313
1414 def date_format(date):
15- #dt = dateutil.parser.parse(date)
1615 return date.strftime('%Y/%m/%d %H:%M')
1716
1817 class Renderer(object):
@@ -41,6 +40,7 @@ class Renderer(object):
4140 'site': config['site_parameter'],
4241 'sources': self._sources,
4342 }
43+ kwargs['site']['last_update'] = datetime.datetime.utcnow()
4444 for key in kwargs:
4545 d = propertize(kwargs[key])
4646 kwargs[key] = d
--- a/templates/index.tmpl.html
+++ b/templates/index.tmpl.html
@@ -25,14 +25,17 @@ s.parentNode.insertBefore(ga, s);
2525 <div class="container">
2626
2727 <!-- タイトル -->
28- <div class="row">
29- <div class="span12">
30- <header id="site-header">
28+ <div class="row" id="site-header">
29+ <div class="span9">
30+ <header>
3131 <a href="${site.root}">
3232 <img id="sitelogo" src="${site.img_directory}/themesjp.png" alt="Themes.JP"> α
3333 </a>
3434 </header>
3535 </div>
36+ <div class="span3 last-update">
37+ last update: ${date_format(site.last_update)}
38+ </div>
3639 </div>
3740
3841 <!-- コンテンツ本体 -->
@@ -63,7 +66,7 @@ s.parentNode.insertBefore(ga, s);
6366 <img class="thumbnail" src="${entry.images[0]}">
6467 % endif
6568 <h3>
66- <a href='${entry.link}' target="_blank_">${entry.title}</a>
69+ <a href='${entry.url}' target="_blank_">${entry.title}</a>
6770 </h3>
6871 </div>
6972
@@ -76,7 +79,7 @@ s.parentNode.insertBefore(ga, s);
7679 <!-- フッタ -->
7780 <div class="entry-footer">
7881 <div class="entry-continue">
79- <a href='${entry.link}'>[続きを読む]</a>
82+ <a href='${entry.url}'>[続きを読む]</a>
8083 </div>
8184 <div class="information">
8285 <span>情報元:<a href='${entry.feed.url}'>${entry.feed.name}</a></span>