news4 - RSS aggrigation system
Révision | 04b45a09f84e27ebe0abbab369f7ee2d1ba7ae99 (tree) |
---|---|
l'heure | 2012-11-07 05:31:45 |
Auteur | hylom <hylom@hylo...> |
Commiter | hylom |
implement plugin system
@@ -22,17 +22,21 @@ a { | ||
22 | 22 | margin-bottom: 1em; |
23 | 23 | } |
24 | 24 | .entry-footer{ |
25 | - color: gray; | |
25 | + color: #888; | |
26 | 26 | } |
27 | 27 | |
28 | 28 | #site-header { |
29 | - border-bottom: 1px solid gray; | |
29 | + border-bottom: 1px solid #888; | |
30 | 30 | margin-bottom: 10px; |
31 | 31 | } |
32 | 32 | |
33 | +#site-header .last-update { | |
34 | + color: #888; | |
35 | +} | |
36 | + | |
33 | 37 | #site-footer { |
34 | 38 | margin-top: 10px; |
35 | - color: gray; | |
39 | + color: #888; | |
36 | 40 | text-align: center; |
37 | 41 | } |
38 | 42 |
@@ -21,7 +21,7 @@ class FeedFetcher(object): | ||
21 | 21 | entry = { |
22 | 22 | # 'title': e.title.decode('utf8') if isinstance(e.title, str) else e.title, |
23 | 23 | 'title': e.title, |
24 | - 'link': e.link, | |
24 | + 'url': e.link, | |
25 | 25 | 'body': e.description, |
26 | 26 | 'date': dateutil.parser.parse(e.updated), |
27 | 27 | 'feed': self._feed, |
@@ -15,7 +15,6 @@ def main(): | ||
15 | 15 | "gnews's main function" |
16 | 16 | # TODO: argv check |
17 | 17 | |
18 | - | |
19 | 18 | # fetch RSS feed |
20 | 19 | entries = [] |
21 | 20 | for feed in sources: |
@@ -49,8 +48,14 @@ def main(): | ||
49 | 48 | for e in entries: |
50 | 49 | log(e["date"]) |
51 | 50 | |
51 | + call_plugin('pre_render', entries) | |
52 | + | |
52 | 53 | # do rendering |
53 | - params = {'tags':tags, 'page':{}, 'sorted_tags':sorted_tags} | |
54 | + params = { | |
55 | + 'tags':tags, | |
56 | + 'page':{}, | |
57 | + 'sorted_tags':sorted_tags | |
58 | + } | |
54 | 59 | |
55 | 60 | # render index page |
56 | 61 | do_rendering('index', 'index%s.html', entries, params) |
@@ -61,6 +66,38 @@ def main(): | ||
61 | 66 | do_rendering('tags', tag + '%s.html', subentries, params) |
62 | 67 | |
63 | 68 | |
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 | + | |
64 | 101 | |
65 | 102 | def do_rendering(page_type, filename, entries, params): |
66 | 103 | "rendering page" |
@@ -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 | + |
@@ -12,7 +12,6 @@ from propertizer import propertize | ||
12 | 12 | from logger import log |
13 | 13 | |
14 | 14 | def date_format(date): |
15 | - #dt = dateutil.parser.parse(date) | |
16 | 15 | return date.strftime('%Y/%m/%d %H:%M') |
17 | 16 | |
18 | 17 | class Renderer(object): |
@@ -41,6 +40,7 @@ class Renderer(object): | ||
41 | 40 | 'site': config['site_parameter'], |
42 | 41 | 'sources': self._sources, |
43 | 42 | } |
43 | + kwargs['site']['last_update'] = datetime.datetime.utcnow() | |
44 | 44 | for key in kwargs: |
45 | 45 | d = propertize(kwargs[key]) |
46 | 46 | kwargs[key] = d |
@@ -12,14 +12,17 @@ | ||
12 | 12 | <div class="container"> |
13 | 13 | |
14 | 14 | <!-- タイトル --> |
15 | - <div class="row"> | |
16 | - <div class="span12"> | |
17 | - <header id="site-header"> | |
15 | + <div class="row" id="site-header"> | |
16 | + <div class="span9"> | |
17 | + <header> | |
18 | 18 | <a href="${site.root}"> |
19 | 19 | <img id="sitelogo" src="${site.img_directory}/themesjp.png" alt="Themes.JP"> α |
20 | 20 | </a> |
21 | 21 | </header> |
22 | 22 | </div> |
23 | + <div class="span3 last-update"> | |
24 | + last update: ${date_format(site.last_update)} | |
25 | + </div> | |
23 | 26 | </div> |
24 | 27 | |
25 | 28 | <!-- コンテンツ本体 --> |
@@ -50,7 +53,7 @@ | ||
50 | 53 | <img class="thumbnail" src="${entry.images[0]}"> |
51 | 54 | % endif |
52 | 55 | <h3> |
53 | - <a href='${entry.link}' target="_blank_">${entry.title}</a> | |
56 | + <a href='${entry.url}' target="_blank_">${entry.title}</a> | |
54 | 57 | </h3> |
55 | 58 | </div> |
56 | 59 |
@@ -63,7 +66,7 @@ | ||
63 | 66 | <!-- フッタ --> |
64 | 67 | <div class="entry-footer"> |
65 | 68 | <div class="entry-continue"> |
66 | - <a href='${entry.link}'>[続きを読む]</a> | |
69 | + <a href='${entry.url}'>[続きを読む]</a> | |
67 | 70 | </div> |
68 | 71 | <div class="information"> |
69 | 72 | <span>情報元:<a href='${entry.feed.url}'>${entry.feed.name}</a></span> |