Skip to content

Commit

Permalink
more twisted
Browse files Browse the repository at this point in the history
  • Loading branch information
ojii committed Apr 26, 2012
1 parent c709f1e commit 2cdc22c
Show file tree
Hide file tree
Showing 8 changed files with 191 additions and 33 deletions.
2 changes: 2 additions & 0 deletions MANIFEST.in
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
include LICENSE
include README.rst
149 changes: 148 additions & 1 deletion README.rst
Original file line number Diff line number Diff line change
@@ -1 +1,148 @@
This is not the readme you're looking for.
##########
Leftronicd
##########

A script to periodically post information to leftronic.com


************
Installation
************

* Make a virtualenv
* ``pip install leftronicd``


*************
Configuration
*************

Configuration is done in yaml.

accesskey
=========

Your leftronic API access key.

streams
=======

A list of stream configurations.

Each stream requires following values:

* ``method``: The method that generates the value
* ``type``: The type of value (eg ``number`` or ``leaderboard``)
* ``name``: Name of the stream
* ``verbosename``: Verbose name of the stream
* ``interval``: Interval in seconds when the method should be called.

Any other key-value pairs will be passed into the method.

Example::

accesskey: SECRET
streams:
- method: leftronicd.contrib.github.repo_metric
verbosename: django CMS Watchers
name: django-cms-watchers
type: number
interval: 86400
repo: divio/django-cms
metric: watchers
- method: leftronicd.contrib.github.repo_metric
verbosename: django CMS Forks
type: number
interval: 86400
name: django-cms-forks
repo: divio/django-cms
metric: forks


****************
Built-in methods
****************


``leftronicd.contrib.github.repo_metric``
=========================================

Reports a metric from a github repository.

Configuration:

* ``repo``: The repo name, eg ``ojii/leftronicd``
* ``metric``: Which value to grab from the repo, eg ``forks``

Optional configuration:

* ``username``: The username (for private repos)
* ``password``: The password (for private repos)


*******
Running
*******

``leftronicd <configfile> [-v]``


**************
Custom methods
**************

Custom data collecting methods can be any Python callable that returns a
`Twisted Deferred`_ which calls attached callbacks with the value to be posted
to leftronic.com.

The Python callable is called with all additional configuration parameters
given for a stream.

Example
=======

This example will show the amount of GitHub followers a user has.

Python code (let's assume it's in a module called 'custom')::

from leftronicd.helpers import get_page
import json
def github_followers(username):
def handler(data):
return json.loads(data)['followers']
return get_page(handler, 'https://api.github.com/users/%s' % username)

As you can see, we use the ``leftronicd.helpers.get_page`` helper here, for
details, see below.

Stream configuration::

accesskey: SECRET
streams:
- method: custom.github_followers
verbosename: Github Followers
name: my-github-followers
type: number
interval: 300
username: ojii


*******
Helpers
*******

``leftronicd.helpers.get_page``
===============================

A wrapper around ``twisted.web.client.getPage``. Takes a handler function as
first argument which is called with the content of the page if the page is
loaded successful. The handler function should then return the value to be
passed to leftronic. ``get_page`` returns a deferred which can be returned from
your custom methods.

All arguments after the handler argument are the same as in
``twisted.web.client.getPage``.


.. _Twisted Deferred: http://twistedmatrix.com/documents/current/core/howto/defer.html
2 changes: 1 addition & 1 deletion leftronicd/__init__.py
Original file line number Diff line number Diff line change
@@ -1 +1 @@
__version__ = '1.0'
__version__ = '1.1'
21 changes: 11 additions & 10 deletions leftronicd/contrib/github.py
Original file line number Diff line number Diff line change
@@ -1,21 +1,22 @@
# -*- coding: utf-8 -*-
from leftronicd.helpers import get_page
from twisted.python import log
import base64
import json
import requests

def repo_metric(repo, metric, username=None, password=None):
auth = None
headers = {}
if username and password:
auth = (username, password)
headers['Authorization'] = 'Basic %s' % (base64.b64encode('%s:%s' % (username, password)))
log.msg('[github.repo_metric] Using authentication')
else:
log.msg('[github.repo_metric] No authentication')
url = 'https://api.github.com/repos/%s' % repo
log.msg('[github.repo_metric] Sending request to %s' % url)
response = requests.get(url, auth=auth)
log.msg('[github.repo_metric] Got response: %s' % response.status_code)
response.raise_for_status()
log.msg('[github.repo_metric] Loading data')
data = json.loads(response.content)
log.msg('[github.repo_metric] Returning value')
return data[metric]
def handler(data):
log.msg('[github.repo_metric] Got response')
log.msg('[github.repo_metric] Loading data')
data = json.loads(data)
log.msg('[github.repo_metric] Returning value')
return data[metric]
return get_page(handler, url, headers=headers)
15 changes: 9 additions & 6 deletions leftronicd/helpers.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,17 @@
# -*- coding: utf-8 -*-
from importlib import import_module
from twisted.internet.defer import Deferred
from twisted.web.client import getPage

def load(method_path):
module_name, method_name = method_path.rsplit('.', 1)
module = import_module(module_name)
return getattr(module, method_name)

def get_interval(name):
return {
'daily': 24 * 60 * 60,
'hourly': 60 * 60,
'minutely': 60,
}.get(name, int(name))
def get_page(handler, *args, **kwargs):
deferred = Deferred()
def callback(data):
output = handler(data)
deferred.callback(output)
getPage(*args, **kwargs).addCallback(callback)
return deferred
12 changes: 8 additions & 4 deletions leftronicd/main.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
# -*- coding: utf-8 -*-
from leftronic import Leftronic
from leftronicd.constants import IDLE, RUNNING
from leftronicd.helpers import get_interval, load
from leftronicd.helpers import load
from twisted.internet import task, reactor
from twisted.python import log
from twisted.web.client import getPage
import json
try:
import json
except ImportError:
import simplejson as json
import sys
import yaml

Expand Down Expand Up @@ -38,7 +41,7 @@ class Stream(object):
"""
def __init__(self, daemon, config):
self.daemon = daemon
self.interval = get_interval(config.pop('interval'))
self.interval = int(config.pop('interval'))
self.method_path = config.pop('method')
self.method = load(self.method_path)
self.name = config.pop('name')
Expand All @@ -57,7 +60,8 @@ def execute(self):
return
log.msg("[Stream(%s).execute] Calling %s(**%s)" % (self.name, self.method_path, self.kwargs))
self.state = RUNNING
self.callback(self.method(**self.kwargs))
deferred = self.method(**self.kwargs)
deferred.addCallback(self.callback)

def callback(self, value):
log.msg("[Stream(%s).callback] Got value: %s" % (self.name, value))
Expand Down
9 changes: 0 additions & 9 deletions requirements.txt

This file was deleted.

14 changes: 12 additions & 2 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,17 @@
from setuptools import setup, find_packages


with open('requirements.txt') as fobj:
INSTALL_REQUIRES = [line.strip() for line in fobj.readlines() if line.strip()]
INSTALL_REQUIRES = [
'PyYAML==3.10',
'Twisted==12.0.0',
'certifi==0.0.8',
'chardet==1.0.1',
'leftronic==1.4',
'pyOpenSSL==0.13',
'requests==0.11.1',
'wsgiref==0.1.2',
'zope.interface==3.8.0',
]

try:
import json
Expand Down Expand Up @@ -34,6 +43,7 @@
packages=find_packages(),
license='BSD',
platforms=['OS Independent'],
include_package_data=True,
install_requires=INSTALL_REQUIRES,
entry_points="""
[console_scripts]
Expand Down

0 comments on commit 2cdc22c

Please sign in to comment.