1 from collections
import namedtuple
3 from pkg_resources
import resource_filename
9 from spline
.lib
import helpers
10 from spline
.lib
.plugin
import PluginBase
, PluginLink
, Priority
12 import splinext
.frontpage
.controllers
.frontpage
14 class FrontPageUpdate(object):
15 """Base class ('interface') for an updated thing that may appear on the
18 Subclasses should implement the `time` and `template` properties.
23 RSS_SUMMARY_LENGTH
= 1000
25 FrontPageRSS
= namedtuple('FrontPageRSS',
26 ['time', 'entry', 'template', 'category', 'content', 'icon'])
28 def rss_hook(limit
, max_age
, url
, title
=None, icon
=None):
29 """Front page handler for news feeds."""
30 feed
= feedparser
.parse(url
)
33 title
= feed
.feed
.title
36 for entry
in feed
.entries
[:limit
]:
37 # Grab a date -- Atom has published, RSS usually just has updated.
38 # Both come out as time tuples, which datetime.datetime() can read
40 timestamp_tuple
= entry
.published_parsed
41 except AttributeError:
42 timestamp_tuple
= entry
.updated_parsed
43 timestamp
= datetime
.datetime(*timestamp_tuple
[:6])
45 if max_age
and timestamp
< max_age
:
46 # Entries should be oldest-first, so we can bail after the first
50 # Try to find something to show! Default to the summary, if there is
51 # one, or try to generate one otherwise
53 if 'summary' in entry
:
54 # If there be a summary, cheerfully trust that it's actually a
56 content
= entry
.summary
57 elif 'content' in entry
:
58 # Full content is way too much, especially for my giant blog posts.
59 # Cut this down to some arbitrary number of characters, then feed
60 # it to lxml.html to fix tag nesting
61 broken_html
= entry
.content
[0].value
[:RSS_SUMMARY_LENGTH
]
62 fragment
= lxml
.html
.fromstring(broken_html
)
64 # Insert an ellipsis at the end of the last node with text
67 # Need to find the last node with a tail, OR the last node with
69 for node
in fragment
.iter():
77 if last_text_node
is not None:
78 last_text_node
.text
+= '...'
79 if last_tail_node
is not None:
80 last_tail_node
.tail
+= '...'
83 content
= lxml
.html
.tostring(fragment
)
85 content
= helpers
.literal(content
)
87 update
= FrontPageRSS(
90 template
= '/front_page/rss.mako',
95 updates
.append(update
)
100 FrontPageGit
= namedtuple('FrontPageGit',
101 ['time', 'gitweb', 'log', 'tag', 'template', 'category', 'icon'])
102 FrontPageGitCommit
= namedtuple('FrontPageGitCommit',
103 ['hash', 'author', 'time', 'subject', 'repo'])
105 def git_hook(limit
, max_age
, title
, gitweb
, repo_paths
, repo_names
,
106 tag_pattern
=None, icon
=None):
108 """Front page handler for repository history."""
109 # Repo stuff can be space-delimited lists...
110 repo_paths
= repo_paths
.split()
111 repo_names
= repo_names
.split()
113 # Fetch the main repo's git tags
116 '--git-dir=' + repo_paths
[0],
120 args
.append(tag_pattern
)
122 proc
= subprocess
.Popen(args
, stdout
=subprocess
.PIPE
)
123 git_output
, _
= proc
.communicate()
124 tags
= git_output
.strip().split('\n')
126 # Tags come out in alphabetical order, which means earliest first. Reverse
127 # it to make the slicing easier
129 # Only history from tag to tag is actually interesting, so get the most
130 # recent $limit tags but skip the earliest
131 interesting_tags
= tags
[:-1][:limit
]
134 for tag
, since_tag
in zip(interesting_tags
, tags
[1:]):
135 # Get the date when this tag was actually created
138 '--git-dir=' + repo_paths
[0],
140 '--format=%(taggerdate:raw)',
143 tag_timestamp
, _
= subprocess
.Popen(args
, stdout
=subprocess
.PIPE
) \
145 tag_unixtime
, tag_timezone
= tag_timestamp
.split(None, 1)
146 tagged_timestamp
= datetime
.datetime
.fromtimestamp(int(tag_unixtime
))
148 if max_age
and tagged_timestamp
< max_age
:
153 for repo_path
, repo_name
in zip(repo_paths
, repo_names
):
154 # Grab an easily-parsed history: fields delimited by nulls.
155 # Hash, author's name, commit timestamp, subject.
158 '--git-dir=' + repo_path
,
160 '--pretty=%h%x00%an%x00%at%x00%s',
161 "{0}..{1}".format(since_tag
, tag
),
163 proc
= subprocess
.Popen(git_log_args
, stdout
=subprocess
.PIPE
)
164 for line
in proc
.stdout
:
165 hash, author
, time
, subject
= line
.strip().split('\x00')
170 time
= datetime
.datetime
.fromtimestamp(int(time
)),
176 update
= FrontPageGit(
177 time
= tagged_timestamp
,
180 template
= '/front_page/git.mako',
185 updates
.append(update
)
190 def add_routes_hook(map, *args
, **kwargs
):
191 """Hook to inject some of our behavior into the routes configuration."""
192 map.connect('/', controller
='frontpage', action
='index')
195 class FrontPagePlugin(PluginBase
):
196 def controllers(self
):
198 frontpage
= splinext
.frontpage
.controllers
.frontpage
.FrontPageController
,
201 def template_dirs(self
):
203 (resource_filename(__name__
, 'templates'), Priority
.FIRST
)
208 ('routes_mapping', Priority
.NORMAL
, add_routes_hook
),
209 ('frontpage_updates_rss', Priority
.NORMAL
, rss_hook
),
210 ('frontpage_updates_git', Priority
.NORMAL
, git_hook
),