Permalink
Browse files

Added files

  • Loading branch information...
1 parent 3047e95 commit 471e3d64325d585bf26bd8432f30bc42ada640e5 @Lispython Lispython committed Dec 7, 2012
View
@@ -1,4 +1,4 @@
-gottwall is written and maintained by Alexandr Lispython and
+gottwall is written by GottWall Team and maintained by Alexandr Lispython and
various contributors:
Development Lead
@@ -9,4 +9,3 @@ Development Lead
Patches and Suggestions
~~~~~~~~~~~~~~~~~~~~~~~
-- Nicolas D (github@ndevilla)
View
@@ -26,4 +26,8 @@ clean-pyc:
find . -name '*~' -exec rm -f {} +
find-print:
- grep -r --include=*.py --exclude-dir=venv --exclude=fabfile* --exclude=tests.py --exclude-dir=tests --exclude-dir=commands 'print' ./
+ grep -r --include=*.py --exclude-dir=venv --exclude=fabfile* --exclude=tests.py --exclude-dir=tests --exclude-dir=commands 'print' ./
+
+
+debug:
+ python gottwall/app.py --config=examples/config.py
View
@@ -0,0 +1,18 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
+STORAGE = 'gottwall.storages.RedisStorage'
+
+
+REDIS_HOST = 'localhost'
+REDIS_PORT = 6379
+REDIS_PASSWORD = None
+REDIS_DB = 2
+
+
+USERS = []
+
+SECRET_KEY = "dwefwefwefwecwef"
+
+
+PROJECTS = ["test_project"]
View
@@ -0,0 +1,18 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
+import json
+import datetime
+import human_curl as hurl
+
+metric_data = {"name": "my_metric_name",
+ "timestamp": datetime.datetime.utcnow().strftime("%Y-%m-%dT%H:%M:%S"),
+ "filters": {"views": "registered",
+ "clicks": "anonymouse"},
+ "action": "incr",
+ "value": 2}
+
+headers = (("content-type", "application/json"),)
+
+r = hurl.post("http://127.0.0.1:8889/test_project/api/store", data=json.dumps(metric_data), headers=headers)
+
View
@@ -11,60 +11,101 @@
:license: BSD, see LICENSE for more details.
:github: http://github.com/Lispython/gottwall
"""
+import imp
+import os
+import importlib
import os.path
import tornado.ioloop
from tornado.web import Application
from tornado.options import define, options
from tornado import httpserver
from tornado import autoreload
from utils import rel
+from config import Config, default_settings
+
+from handlers import BaseHandler, DashboardHandler, LoginHandler, HomeHandler,\
+ StatsHandler, MetricsHandler
+from backends import HTTPBackend as HTTPBackendHandler
define("port", default=8889, help="run HTTP on the given port", type=int)
define("ssl_port", default=8890, help="run HTTPS on the given port", type=int)
+define("config", help="Configuration file", type=str)
+
+class HTTPApplication(Application):
+ """Base application
+ """
-class LoginHandler():
- def get(self):
- self.render("login.html")
+ def __init__(self, config):
+ self.dirty_handlers = [
+ (r"/login", LoginHandler),
+ (r"/dashboard", DashboardHandler),
+ (r"/(?P<project>.+)/api/stats", StatsHandler),
+ (r"/(?P<project>.+)/api/metrics", MetricsHandler),
+ (r"/", HomeHandler)]
-class HomeHandler():
+ self.config = config
- def get(self):
- self.render("index.html")
+ tornado.web.Application.__init__(self, self.dirty_handlers, **config)
-class Dashboard():
- def get(self):
- self.render("dashboard.html")
+ def configure_app(self):
+ """Configure application backends and storages
+ """
+ self.configure_backends(self.config['BACKENDS'])
+ self.configure_storage(self.config['STORAGE'])
+ def configure_storage(self, storage_path):
+ """Configure data storage by path
-class HTTPApplication(Application):
- """Base application
- """
+ :param storage: storage path
+ """
+ module_path, name = storage_path.rsplit('.', 1)
+ try:
+ module = importlib.import_module(module_path, name)
+ storage = getattr(module, name)
+ except (ImportError, AttributeError), e:
+ raise Exception("Invalid storage: {0}".format(e))
+ storage.setup(self)
- def __init__(self):
- self.dirty_handlers = [
- (r"/login", LoginHandler),
- (r"/", HomeHandler),
- ]
+ def configure_backends(self, backends):
+ """Configture data receive backends
- settings = dict(
+ :param backends: list of backends
+ """
+
+ for backend_path in backends:
+ module_path, name = backend_path.rsplit('.', 1)
+ try:
+ backend_module = importlib.import_module(module_path, name)
+
+ backend = getattr(backend_module, name)
+ except (ImportError, AttributeError), e:
+ raise Exception("Invalid backend: {0}".format(e))
+ backend.setup_backend(self)
+
+ return backend
+
+
+if __name__ == "__main__":
+ tornado.options.parse_command_line()
+
+ default_settings.update(
+ dict(
site_title=u"GottWall - statistics aggregator",
+ login_url="/login",
template_path=os.path.join(os.path.dirname(__file__), "templates"),
static_path=os.path.join(os.path.dirname(__file__), "static"),
xsrf_cookies=False,
- cookie_secret="0fkewrlhfwhrfweiurbweuybfrweoubfrowebfioubweoiufbwbeofbowebfbwup2XdTP1o/Vo=",
- autoescape=None,
- )
- tornado.web.Application.__init__(self, self.dirty_handlers, **settings)
-
+ cookie_secret="fkewrlhfwhrfweiurbweuybfrweoubfrowebfioubweoiufbwbeofbowebfbwup2XdTP1o/Vo=",
+ autoescape=None))
-application = HTTPApplication()
+ default_settings.from_file(options.config)
+ application = HTTPApplication(default_settings)
+ application.configure_app()
-if __name__ == "__main__":
- tornado.options.parse_command_line()
http_server = httpserver.HTTPServer(application)
http_server.listen(options.port)
ioloop = tornado.ioloop.IOLoop.instance()
@@ -0,0 +1,5 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
+from base import BaseBackend
+from http import HTTPBackend
View
@@ -0,0 +1,34 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
+"""
+gottwall.backends.base
+~~~~~~~~~~~~~~~~~~~~~~
+
+Base backends for metric calculation
+
+:copyright: (c) 2012 by Alexandr Lispython (alex@obout.ru).
+:license: BSD, see LICENSE for more details.
+:github: http://github.com/Lispython/gottwall
+"""
+
+class BaseBackend(object):
+ application = None
+
+ def __init__(self, *args, **kwargs):
+ pass
+
+ def process_data(self, project, data):
+ """Process `data`
+ """
+ if data.get('action', 'incr') == 'incr':
+ data.pop('action', None)
+ self.application.storage.incr(project, **data)
+ return True
+
+ def setup_backend(self, app):
+ """Setup backend for application
+
+ :param app: :class:`tornado.web.Application` instance
+ """
+ raise NotImplementedError("You need reimplement setup_backend method")
View
@@ -0,0 +1,81 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
+"""
+gottwall.backends.base
+~~~~~~~~~~~~~~~~~~~~~~
+
+Base backends for metric calculation
+
+:copyright: (c) 2012 by Alexandr Lispython (alex@obout.ru).
+:license: BSD, see LICENSE for more details.
+:github: http://github.com/Lispython/gottwall
+"""
+
+import simplejson as json
+from tornado.web import HTTPError
+
+from gottwall.backends.base import BaseBackend
+from gottwall.handlers import BaseHandler
+
+
+class HTTPBackend(BaseHandler, BaseBackend):
+
+
+ @staticmethod
+ def merge_handlers(app):
+ """Merge hosts handlers in application handler
+ """
+ hosts = {}
+ handlers = app.handlers
+ for host, patterns in handlers:
+ if host not in hosts.keys():
+ hosts[host] = []
+ hosts[host] += patterns
+ app.handlers = [(host, patterns) for host, patterns in hosts.items()]
+
+ @classmethod
+ def setup_backend(cls, app):
+ """Setup data handler to `app`
+
+ :param cls: :class:`BaseBackend` childrem class
+ :param app: :class:`tornado.web.Application` instance
+ """
+ handlers = [
+ (r"/(?P<project>.+)/api/store", cls), ]
+ app.add_handlers(r".*$", handlers)
+
+ cls.merge_handlers(app)
+
+ cls.application = app
+
+ def post(self, project, *args, **kwargs):
+ if not self.validate_project(project):
+ raise HTTPError(404, "Invalid project")
+
+ if not self.validate_content_type():
+ raise HTTPError(400, "Invalid content type")
+
+ if not self.check_auth():
+ raise HTTPError(403, "Forbidden")
+
+ data = json.loads(self.request.body)
+ self.process_data(project, data)
+
+ self.write("OK")
+ self.finish()
+
+ def validate_project(self, project):
+ """Validate projects name
+ """
+ return True
+
+ def validate_content_type(self):
+ if self.request.headers['content-type'] == 'application/json':
+ return True
+ return False
+
+ def check_auth(self):
+ """Check authorization headers
+ """
+ return True
View
@@ -0,0 +1,49 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
+"""
+gottwall.config
+~~~~~~~~~~~~~~~
+
+Gottwall configs wrapper
+
+:copyright: (c) 2012 by Alexandr Lispython (alex@obout.ru).
+:license: BSD, see LICENSE for more details.
+:github: http://github.com/Lispython/gottwall
+"""
+import imp
+import os.path
+from settings import PERIODS
+
+
+class Config(dict):
+ """Settings store
+ """
+ def __init__(self, **kwargs):
+ dict.__init__(self, kwargs or {})
+
+ def from_file(self, filename):
+ """Load settings from pyfile with `filename`
+ """
+ filename = os.path.abspath(filename)
+ d = imp.new_module('config')
+ d.__file__ = filename
+ try:
+ execfile(filename, d.__dict__)
+ except IOError, e:
+ raise Exception("Unable to load configuration file {0}: ".format(filename, e))
+
+ for key in dir(d):
+ if not key.startswith('__'):
+ self[key] = getattr(d, key)
+ return d
+
+ def __repr__(self):
+ return '<{0} {1}>'.format(self.__class__.__name__, dict.__repr__(self))
+
+
+
+default_settings = Config(**{
+ "BACKENDS": ['gottwall.backends.HTTPBackend'],
+ "STORAGE": "gottwall.storages.MemoryStorage",
+ "PERIODS": PERIODS})
View
@@ -0,0 +1,16 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
+"""
+gottwall.exceptions
+~~~~~~~~~~~~~~~~~~~
+
+GottWall core exceptions
+
+:copyright: (c) 2012 by Alexandr Lispython (alex@obout.ru).
+:license: BSD, see LICENSE for more details.
+:github: http://github.com/Lispython/gottwall
+"""
+
+class InvalidData(Exception):
+ pass
Oops, something went wrong.

0 comments on commit 471e3d6

Please sign in to comment.