Skip to content

Commit

Permalink
Split __init__ into several modules and remove flask-peewee dependency.
Browse files Browse the repository at this point in the history
This should make imports more sensible and with less side effects. Also might reduce the memory footprint of the workers.
  • Loading branch information
arikfr committed May 18, 2014
1 parent f51df00 commit 4af979d
Show file tree
Hide file tree
Showing 13 changed files with 121 additions and 88 deletions.
7 changes: 5 additions & 2 deletions manage.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,12 @@
"""
CLI to manage redash.
"""
from redash import settings, app, db, models, __version__
from redash.import_export import import_manager
from flask.ext.script import Manager, prompt_pass

from redash import settings, models, __version__
from redash.wsgi import app
from redash.import_export import import_manager

manager = Manager(app)
database_manager = Manager(help="Manages the database (create/drop tables).")
users_manager = Manager(help="Users management commands.")
Expand All @@ -25,6 +27,7 @@ def runworkers():

@manager.shell
def make_shell_context():
from redash.models import db
return dict(app=app, db=db, models=models)

@manager.command
Expand Down
51 changes: 4 additions & 47 deletions redash/__init__.py
Original file line number Diff line number Diff line change
@@ -1,16 +1,9 @@
import json
import urlparse
import logging
from datetime import timedelta
from flask import Flask, make_response
from flask.ext.restful import Api
from flask_peewee.db import Database
import urlparse
import redis
from statsd import StatsClient
from celery import Celery

import events
from redash import settings, utils
from redash import settings, events

__version__ = '0.4.0'

Expand All @@ -26,48 +19,12 @@ def setup_logging():

setup_logging()

app = Flask(__name__,
template_folder=settings.STATIC_ASSETS_PATH,
static_folder=settings.STATIC_ASSETS_PATH,
static_path='/static')

celery = Celery('redash',
broker=settings.CELERY_BROKER,
include='redash.tasks')

celery.conf.update(CELERY_RESULT_BACKEND=settings.CELERY_BACKEND,
CELERYBEAT_SCHEDULE={
'refresh_queries': {
'task': 'redash.tasks.refresh_queries',
'schedule': timedelta(seconds=30)
},
},
CELERY_TIMEZONE='UTC')

api = Api(app)

# configure our database
settings.DATABASE_CONFIG.update({'threadlocals': True})
app.config['DATABASE'] = settings.DATABASE_CONFIG
db = Database(app)

from redash.authentication import setup_authentication
auth = setup_authentication(app)

@api.representation('application/json')
def json_representation(data, code, headers=None):
resp = make_response(json.dumps(data, cls=utils.JSONEncoder), code)
resp.headers.extend(headers or {})
return resp


redis_url = urlparse.urlparse(settings.REDIS_URL)
if redis_url.path:
redis_db = redis_url.path[1]
else:
redis_db = 0

# TODO: move this to function that create a connection?
redis_connection = redis.StrictRedis(host=redis_url.hostname, port=redis_url.port, db=redis_db, password=redis_url.password)
statsd_client = StatsClient(host=settings.STATSD_HOST, port=settings.STATSD_PORT, prefix=settings.STATSD_PREFIX)

from redash import controllers
statsd_client = StatsClient(host=settings.STATSD_HOST, port=settings.STATSD_PORT, prefix=settings.STATSD_PREFIX)
6 changes: 2 additions & 4 deletions redash/authentication.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,12 @@
import logging

from flask import request, make_response, redirect, url_for
from flask.ext.googleauth import GoogleAuth, login
from flask.ext.login import LoginManager, login_user, current_user
from flask.ext.googleauth import GoogleAuth, login
from werkzeug.contrib.fixers import ProxyFix

from models import AnonymousUser
from redash import models, settings


login_manager = LoginManager()
logger = logging.getLogger('authentication')

Expand Down Expand Up @@ -99,7 +97,7 @@ def setup_authentication(app):
openid_auth._OPENID_ENDPOINT = "https://www.google.com/a/%s/o8/ud?be=o8" % settings.GOOGLE_APPS_DOMAIN

login_manager.init_app(app)
login_manager.anonymous_user = AnonymousUser
login_manager.anonymous_user = models.AnonymousUser
app.wsgi_app = ProxyFix(app.wsgi_app)
app.secret_key = settings.COOKIE_SECRET

Expand Down
5 changes: 2 additions & 3 deletions redash/controllers.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,9 @@
import sqlparse
import events
from permissions import require_permission
from redash import settings, utils, __version__, statsd_client

from redash import app, auth, api, redis_connection
from redash import models
from redash import redis_connection, statsd_client, models, settings, utils, __version__
from redash.wsgi import app, auth, api

import logging
from tasks import QueryTask
Expand Down
44 changes: 37 additions & 7 deletions redash/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,46 @@
import logging
import time
import datetime
from flask.ext.peewee.utils import slugify
from flask.ext.login import UserMixin, AnonymousUserMixin
import itertools
from passlib.apps import custom_app_context as pwd_context

import peewee
from passlib.apps import custom_app_context as pwd_context
from playhouse.postgres_ext import ArrayField
from redash import db, utils
from flask.ext.login import UserMixin, AnonymousUserMixin

from redash import utils, settings


class Database(object):
def __init__(self):
self.database_config = dict(settings.DATABASE_CONFIG)
self.database_name = self.database_config.pop('name')
self.database = peewee.PostgresqlDatabase(self.database_name, **self.database_config)
self.app = None

def init_app(self, app):
self.app = app
self.register_handlers()

def connect_db(self):
self.database.connect()

def close_db(self, exc):
if not self.database.is_closed():
self.database.close()

def register_handlers(self):
self.app.before_request(self.connect_db)
self.app.teardown_request(self.close_db)


db = Database()


class BaseModel(peewee.Model):
class Meta:
database = db.database

class BaseModel(db.Model):
@classmethod
def get_by_id(cls, model_id):
return cls.get(cls.id == model_id)
Expand Down Expand Up @@ -383,11 +413,11 @@ def get_by_slug(cls, slug):

def save(self, *args, **kwargs):
if not self.slug:
self.slug = slugify(self.name)
self.slug = utils.slugify(self.name)

tries = 1
while self.select().where(Dashboard.slug == self.slug).first() is not None:
self.slug = slugify(self.name) + "_{0}".format(tries)
self.slug = utils.slugify(self.name) + "_{0}".format(tries)
tries += 1

super(Dashboard, self).save(*args, **kwargs)
Expand Down
4 changes: 1 addition & 3 deletions redash/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,7 @@

def parse_db_url(url):
url_parts = urlparse.urlparse(url)
connection = {
'engine': 'peewee.PostgresqlDatabase',
}
connection = {'threadlocals': True}

if url_parts.hostname and not url_parts.path:
connection['name'] = url_parts.hostname
Expand Down
8 changes: 4 additions & 4 deletions redash/tasks.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import time
import datetime
from celery.utils.log import get_task_logger
import peewee
import logging
from celery.result import AsyncResult
import redis
from redash.data.query_runner import get_query_runner
from redash import celery, redis_connection, models, statsd_client
from redash import models, redis_connection, statsd_client
from redash.worker import celery
from redash.utils import gen_query_hash

logger = get_task_logger(__name__)
Expand Down Expand Up @@ -77,7 +77,7 @@ def add_task(cls, query, data_source, scheduled=False):

def to_dict(self):
if self._async_result.status == 'STARTED':
updated_at = self._async_result.result['start_time']
updated_at = self._async_result.result.get('start_time', 0)
else:
updated_at = 0

Expand Down Expand Up @@ -169,7 +169,7 @@ def execute_query(self, query, data_source_id):
# TODO: it is possible that storing the data will fail, and we will need to retry
# while we already marked the job as done
# Delete query_hash
redis_connection.delete('query_hash_job:%s', query_hash)
redis_connection.delete('query_hash_job:%s' % query_hash)

if not error:
query_result = models.QueryResult.store_result(data_source.id, query_hash, query, data, run_time, datetime.datetime.utcnow())
Expand Down
4 changes: 4 additions & 0 deletions redash/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,10 @@ def _find_dml_statements(self):
return False


def slugify(s):
return re.sub('[^a-z0-9_\-]+', '-', s.lower())


def gen_query_hash(sql):
"""Returns hash of the given query after stripping all comments, line breaks and multiple
spaces, and lower casing all text.
Expand Down
21 changes: 21 additions & 0 deletions redash/worker.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
from celery import Celery
from datetime import timedelta
from redash import settings


celery = Celery('redash',
broker=settings.CELERY_BROKER,
include='redash.tasks')

celery.conf.update(CELERY_RESULT_BACKEND=settings.CELERY_BACKEND,
CELERYBEAT_SCHEDULE={
'refresh_queries': {
'task': 'redash.tasks.refresh_queries',
'schedule': timedelta(seconds=30)
},
},
CELERY_TIMEZONE='UTC')


if __name__ == '__main__':
celery.start()
32 changes: 32 additions & 0 deletions redash/wsgi.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import json
from flask import Flask, make_response
from flask.ext.restful import Api

from redash import settings, utils
from redash.models import db

__version__ = '0.4.0'

app = Flask(__name__,
template_folder=settings.STATIC_ASSETS_PATH,
static_folder=settings.STATIC_ASSETS_PATH,
static_path='/static')


api = Api(app)

# configure our database
settings.DATABASE_CONFIG.update({'threadlocals': True})
app.config['DATABASE'] = settings.DATABASE_CONFIG
db.init_app(app)

from redash.authentication import setup_authentication
auth = setup_authentication(app)

@api.representation('application/json')
def json_representation(data, code, headers=None):
resp = make_response(json.dumps(data, cls=utils.JSONEncoder), code)
resp.headers.extend(headers or {})
return resp

from redash import controllers
3 changes: 0 additions & 3 deletions requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,9 @@ Flask-Login==0.2.9
passlib==1.6.2
Jinja2==2.7.2
MarkupSafe==0.18
WTForms==1.0.5
Werkzeug==0.9.4
aniso8601==0.82
blinker==1.3
flask-peewee==0.6.5
itsdangerous==0.23
peewee==2.2.2
psycopg2==2.5.1
Expand All @@ -20,7 +18,6 @@ requests==2.2.0
six==1.5.2
sqlparse==0.1.8
wsgiref==0.1.2
wtf-peewee==0.2.2
Flask-Script==0.6.6
honcho==0.5.0
statsd==2.1.2
Expand Down
21 changes: 7 additions & 14 deletions tests/__init__.py
Original file line number Diff line number Diff line change
@@ -1,28 +1,21 @@
import logging
from unittest import TestCase
from redash import settings, db, app
import redash.models

# TODO: this isn't pretty...
from redash import settings
settings.DATABASE_CONFIG = {
'name': 'circle_test',
'engine': 'peewee.PostgresqlDatabase',
'threadlocals': True
}
app.config['DATABASE'] = settings.DATABASE_CONFIG
db.load_database()

logging.getLogger('peewee').setLevel(logging.INFO)
from redash import models

for model in redash.models.all_models:
model._meta.database = db.database
logging.getLogger('peewee').setLevel(logging.INFO)


class BaseTestCase(TestCase):
def setUp(self):
redash.models.create_db(True, True)
redash.models.init_db()
models.create_db(True, True)
models.init_db()

def tearDown(self):
db.close_db(None)
redash.models.create_db(False, True)
models.db.close_db(None)
models.create_db(False, True)
3 changes: 2 additions & 1 deletion tests/test_controllers.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@
from tests import BaseTestCase
from tests.factories import dashboard_factory, widget_factory, visualization_factory, query_factory, \
query_result_factory, user_factory, data_source_factory
from redash import app, models, settings
from redash import models, settings
from redash.wsgi import app
from redash.utils import json_dumps
from redash.authentication import sign

Expand Down

0 comments on commit 4af979d

Please sign in to comment.