Nitpicks
- Maximum line length should be 72 characters for docstrings.
- For your
old
variable, you may wan to use a set
instead of a list
to check for existence (not in
) as it will be \$O(1)\$ instead of \$O(n)\$.
Reusability
As it stand, I can't use this script because it is too much tied to your OSX only notifier. I'd rather yield
questions from get_notifications
(which will not be greatly named after that) and format them elsewhere, letting other users handle it with other notifications systems (or even email, for that matter).
Something like:
import stackexchange
import time
def pync_notify(question):
from pync import Notifier
Notifier.notify('Votes: {0} | Answers: {1} | Views: {2}'.format(
question.score, len(question.answers), question.view_count),
title='CodeReview: {0}'.format(question.title),
subtitle='Tags: {0}'.format(', '.join(question.tags)),
sound='default', open=question.url)
def gtk_notify(question):
from gi.repository import Notify
Notify.init('stackexchange')
notification = Notify.Notification.new(
'CodeReview: {}'.format(question.title),
'Votes: {} | Answers: {} | Views: {}'.format(
question.score, len(question.answers), question.view_count),
'dialog-information')
notification.show()
def get_questions():
"""Fetch recent questions from CodeReview
This function uses the SE API and yield each
new question that is asked on CR.
"""
cr = stackexchange.Site(stackexchange.CodeReview)
old = {}
while True:
questions = cr.recent_questions(filter='_b')
for question in questions[:1]:
if question.title not in old:
old.add(question.title)
yield question
time.sleep(2)
if __name__ == '__main__':
for question in get_questions():
pync_notify(question)
Should be enough. Note that I import modules each time a new question appears, which is not ideal, but at least it let you easily deal with modules that are not present on the system. And the few extra time spent realizing the module is already loaded is negligible compared to your time.sleep(2)
anyway. You may want to use classes with a __call__
method if you really want to import and initialize only once.
On the reusability side, it could also be good to be able to parametrize the generator with the site the user whish to fetch:
def get_questions(site=stackexchange.CodeReview):
se = stackexchange.Site(site)
…
Better usage of the stackexchange
API
The stackexchange
module let you directly query information using "site classes" (such as stackexchange.CodeReview
) without necessarily relying on a stackexchange.Site
object. So:
CR = stackexchange.CodeReview()
CR.questions()
will be a direct call to the SE API on questions.
From there, two things worth noting:
- The sort method;
- The fromdate parameter.
Sorting results
The advantage of using sort='creation'
in the query parameters is that you only need to store the last asked question in your old
variable. You can then iterate over the result of the query and stop when q.title == old.title
without storing previous questions anymore.
Asking for questions asked after a given date
The fromdate
parameter takes a timestamp and make the query returns only questions asked after that point in time. There is no real need for sorting anymore, all you need to remember is the date at when the last query was made.
Proposed improvements
import stackexchange
import datetime
import time
def pync_notify(question):
from pync import Notifier
Notifier.notify('Votes: {0} | Answers: {1} | Views: {2}'.format(
question.score, len(question.answers), question.view_count),
title='CodeReview: {0}'.format(question.title),
subtitle='Tags: {0}'.format(', '.join(question.tags)),
sound='default', open=question.url)
def gtk_notify(question):
from gi.repository import Notify
Notify.init('stackexchange')
notification = Notify.Notification.new(
'CodeReview: {}'.format(question.title),
'Votes: {} | Answers: {} | Views: {}'.format(
question.score, len(question.answers), question.view_count),
'dialog-information')
notification.show()
def now():
"""Return the current timestamp"""
date = datetime.datetime.now()
return int(date.timestamp())
def get_questions(site=stackexchange.CodeReview, query_delay=2):
"""Fetch recent questions from the Stack Exchange network
This function uses the SE API and yield each
new question that is asked on the given site.
"""
se = site()
last_query = now()
while True:
fromdate, last_query = last_query, now()
questions = se.questions(fromdate=fromdate, sort='creation')
yield from questions
time.sleep(query_delay)
if __name__ == '__main__':
for question in get_questions():
pync_notify(question)
Last minute reading
On the discussion about throttles of the StackExchange API, one can read:
While not strictly a throttle, the Stack Exchange API employs heavy caching and as such no application should make semantically identical requests more than once a minute. This is generally a waste of bandwidth as, more often than not, the exact same result will be returned.
This means that you probably want to increase your time.sleep
delay from 2 to at least 60.