• R/O
  • HTTP
  • SSH
  • HTTPS

Commit

Tags
No Tags

Frequently used words (click to add to your profile)

javac++androidlinuxc#windowsobjective-ccocoa誰得qtpythonphprubygameguibathyscaphec計画中(planning stage)翻訳omegatframeworktwitterdomtestvb.netdirectxゲームエンジンbtronarduinopreviewer

news4 - RSS aggrigation system


Commit MetaInfo

Revisãodfb2a0929cc61bf4d186cfed707e85d67e04e92a (tree)
Hora2012-09-24 22:43:51
Autorhylom <hylom@hylo...>
Commiterhylom

Mensagem de Log

add filter, fix css

Mudança Sumário

Diff

--- a/README
+++ b/README
@@ -4,4 +4,7 @@ requires:
44 mako (Makotenplate library for Python)
55
66 feedparser (feed library for Python)
7- http://code.google.com/p/feedparser
\ No newline at end of file
7+ http://code.google.com/p/feedparser
8+
9+ python-dateutil
10+
\ No newline at end of file
--- a/css/gnews.css
+++ b/css/gnews.css
@@ -3,6 +3,28 @@
33 .entry-header{}
44 .entry-body{}
55 .entry-continue {
6- margin-bottom: 1em;
6+ margin-bottom: 1em;
77 }
8-.entry-footer{}
\ No newline at end of file
8+.entry-footer{
9+ color: gray;
10+}
11+
12+header {
13+ border-bottom: 1px solid gray;
14+}
15+
16+.main-contents h3 {
17+ font-size: 130%;
18+}
19+
20+.sidebar-left h3 {
21+ font-size: 100%;
22+ font-weight: normal;
23+}
24+
25+.sidebar-right h3 {
26+ font-size: 100%;
27+ font-weight: normal;
28+}
29+
30+
\ No newline at end of file
--- a/fetcher.py
+++ b/fetcher.py
@@ -1,6 +1,10 @@
1+#-*- coding: utf-8 -*-
12 'fetcher.py - RSS fetcher'
23
4+import re
5+
36 import feedparser
7+from config import config as config, target_rss as target_rss
48
59 class FeedFetcher(object):
610 'Feed fetching and parsing'
@@ -13,6 +17,7 @@ class FeedFetcher(object):
1317 entries = []
1418 for e in f['entries']:
1519 entry = {
20+# 'title': e.title.decode('utf8') if isinstance(e.title, str) else e.title,
1621 'title': e.title,
1722 'link': e.link,
1823 'body': e.description,
@@ -22,7 +27,41 @@ class FeedFetcher(object):
2227 entries.append(entry)
2328 return entries
2429
30+ def _embeded_filter(self, entry):
31+ # remove PR entry
32+ if re.search(u'^PR', entry['title']):
33+ print 'delete PR entry - %s' % entry['title']
34+ return None
35+ return entry
36+
2537 def get_entries(self):
2638 'get entries'
27- return self._fetch()
39+ entries = self._fetch()
40+ entries = [self._embeded_filter(x) for x in entries]
41+ entries = [x for x in entries if x]
42+
43+ if 'filter' in self._feed:
44+ entry_filter = self._get_filter()
45+ entries = [entry_filter(x) for x in entries]
46+ # remove entry which is None
47+ entries = [x for x in entries if x]
48+ return entries
49+
50+ def _get_filter(self):
51+ 'load filter by seed settings'
52+ filter_name = self._feed.get('filter', None)
53+
54+ # fallback when filter isn't defined
55+ if filter_name is None:
56+ return lambda x:x
57+
58+ # import module
59+ mods = __import__(config['filter_directory'],
60+ globals(),
61+ locals(),
62+ [filter_name,])
63+ mod = mods.__getattribute__(filter_name)
2864
65+ # return module's entry_filter function
66+ return mod.entry_filter
67+
--- /dev/null
+++ b/filters/__init__.py
@@ -0,0 +1 @@
1+# __init__.py stub
--- /dev/null
+++ b/filters/slashdotjp.py
@@ -0,0 +1,16 @@
1+# filter for slashdot.jp
2+# -*- coding: utf-8 -*-
3+
4+import re
5+
6+
7+def entry_filter(entry):
8+ # すべて読む、関連ストーリーを削除
9+ body = entry['body']
10+ body = re.sub(ur'<p> <a href=.*>\s*すべて読む</a>.*?</p>', '', body)
11+ body = re.sub(ur'<p>\s*関連ストーリー:.*?</p>', '', body)
12+ entry['body'] = body
13+
14+ return entry
15+
16+
--- a/gnews.py
+++ b/gnews.py
@@ -8,14 +8,18 @@ import os.path
88
99 def main():
1010 "TODO: argv check"
11+
12+ # fetch rss feed
1113 entries = []
1214 for feed in target_rss:
1315 f = fetcher.FeedFetcher(feed)
1416 e = f.get_entries()
1517 entries.extend(e)
1618
19+ # sort by date
1720 entries.sort(lambda x,y: -cmp(x["date"],y["date"]))
1821
22+ # render entries
1923 r = renderer.Renderer()
2024 for (output_file, tmpl) in config["templates"]:
2125 output_fullpath = os.path.join(
--- a/renderer.py
+++ b/renderer.py
@@ -1,12 +1,19 @@
11 'renderer.py - rendering html and some items'
22
3+import datetime
4+
35 from mako.template import Template
46 from mako.lookup import TemplateLookup
57 from mako.exceptions import RichTraceback
8+import dateutil.parser
69
710 from config import config, target_rss
811 from propertizer import propertize
912
13+def date_format(date):
14+ dt = dateutil.parser.parse(date)
15+ return dt.strftime('%Y/%m/%d %H:%M')
16+
1017 class Renderer(object):
1118 def __init__(self):
1219 self.template_dir = config['template_directory']
@@ -25,7 +32,10 @@ class Renderer(object):
2532 t = self._get_template(template)
2633
2734
28- kwargs = { 'entries': entries }
35+ kwargs = {
36+ 'entries': entries,
37+ 'date_format': date_format,
38+ }
2939 kwargs.update(config["site_parameter"])
3040 for key in kwargs:
3141 d = propertize(kwargs[key])
--- a/templates/index.tmpl.html
+++ b/templates/index.tmpl.html
@@ -1,44 +1,87 @@
11 <!DOCTYPE html>
22 <html lang="ja">
33 <head>
4- <meta charset="UTF-8">
4+ <meta charset="UTF-8">
55 <title>${site_name}</title>
66 <!-- Bootstrap -->
77 <link href="${css_directory}/bootstrap.min.css" rel="stylesheet">
88 <link href="${css_directory}/gnews.css" rel="stylesheet">
99 </head>
10+
1011 <body class="wrap">
11- <div class="containter">
12- <div class="row">
13- <div class="span12">
14- <h1>${site_name}</h1>
15- </div>
16- </div>
17- <div class="row">
18- <div class="span3"></div>
19- <div class="span9">
20- <script src="http://code.jquery.com/jquery-latest.js"></script>
21- <script src="${js_directory}/bootstrap.min.js"></script>
22-
23- % for entry in entries:
24- <div class="entry">
25- <div class="entry-header">
26- <h3>
27- <a href='${entry.link}' target="_blank_">${entry.title}</a>
28- </h3>
29- </div>
30- <div class="entry-body">${entry.body}</div>
31- <div class="entry-footer">
32- <div class="entry-continue">
33- <a href='${entry.link}'>[続きを読む]</a>
34- </div>
35- <div class="entry-footer">
36- <span>情報元:<a href='${entry.feed.source_url}'>${entry.feed.name}</a></span>
37- <span>${entry.date}</span></div>
38- </div>
39- % endfor
40- </div>
41- </div>
12+ <div class="container">
13+
14+ <!-- タイトル -->
15+ <div class="row">
16+ <div class="span12">
17+ <header>
18+ <h1>${site_name}</h1>
19+ </header>
4220 </div>
21+ </div>
22+
23+ <!-- コンテンツ本体 -->
24+ <div class="row">
25+
26+ <!-- 左サイドバー -->
27+ <div class="span2 sidebar-left">
28+ <div class="keywords">
29+ <h3>キーワード:</h3>
30+ <ul class="nav nav-pills nav-stacked">
31+ <li><a href="#">iPhone</a></li>
32+ <li><a href="#">iOS</a></li>
33+ <li><a href="#">Internet</a></li>
34+ <li><a href="#">Firefox</a></li>
35+ <li><a href="#">Facebook</a></li>
36+ </ul>
37+ </div>
38+ </div>
39+
40+ <!-- メインコンテンツ -->
41+ <div class="span7 main-contents">
42+ <script src="http://code.jquery.com/jquery-latest.js"></script>
43+ <script src="${js_directory}/bootstrap.min.js"></script>
44+
45+ % for entry in entries:
46+ <div class="entry">
47+ <!-- ヘッダ -->
48+ <div class="entry-header">
49+ <h3>
50+ <a href='${entry.link}' target="_blank_">${entry.title}</a>
51+ </h3>
52+ </div>
53+
54+
55+ <!-- 本文テキスト -->
56+ <div class="entry-body">${entry.body}</div>
57+
58+ <!-- フッタ -->
59+ <div class="entry-footer">
60+ <div class="entry-continue">
61+ <a href='${entry.link}'>[続きを読む]</a>
62+ </div>
63+ <div class="entry-footer">
64+ <span>情報元:<a href='${entry.feed.source_url}'>${entry.feed.name}</a></span>
65+ <span>(${date_format(entry.date)})</span></div>
66+ </div>
67+ </div>
68+ % endfor
69+
70+ </div><!-- main-contents -->
71+
72+ <!-- 右サイドバー -->
73+ <div class="span3 sidebar-right">
74+ <div class="feed-provider">
75+ <h3>情報提供サイト:</h3>
76+ <ul class="nav nav-pills nav-stacked">
77+ <li><a href="#">Slashdot Japan</a></li>
78+ <li><a href="#">SourceForge.JP Magazine</a></li>
79+ </ul>
80+ </div>
81+ </div>
82+
83+ </div><!-- .row -->
84+
85+ </div><!-- .container -->
4386 </body>
4487 </html>