Skip to content
This repository has been archived by the owner on Apr 12, 2018. It is now read-only.

Commit

Permalink
Merge pull request #6 from jrconlin/master
Browse files Browse the repository at this point in the history
Add admin panel for Campaign Manager
  • Loading branch information
mostlygeek committed Sep 27, 2013
2 parents 53855fa + 8b38deb commit 48aa0f7
Show file tree
Hide file tree
Showing 9 changed files with 255 additions and 64 deletions.
8 changes: 7 additions & 1 deletion campaign/decorators.py
Expand Up @@ -54,9 +54,15 @@ def authorized(self, email, request):
try:
domains = json.loads(settings.get('auth.valid.domains',
'["@mozilla.com", "@mozilla.org"]'))
result = False
for valid_domain in domains:
if email.lower().endswith(valid_domain):
return True
result = True
break
if not result:
return False
storage = request.registry.get('storage')
return storage.is_user(email)
except TypeError:
pass
except Exception:
Expand Down
2 changes: 2 additions & 0 deletions campaign/logger.py
Expand Up @@ -64,6 +64,8 @@ def log(self, msg=None, type='event', severity=7,
for k in fields.keys():
if fields[k] is None:
del(fields[k])
if k in fields and btype(fields[k]) is not str:
fields[k] = str(fields[k])
self.heka.heka(type=type,
logger=self.loggername,
severity=severity,
Expand Down
39 changes: 36 additions & 3 deletions campaign/storage/__init__.py
Expand Up @@ -3,21 +3,30 @@
# file, You can obtain one at http://mozilla.org/MPL/2.0/.

#import memcache
from campaign.logger import LOG
from campaign.utils import as_id
from dateutil import parser
from inspect import stack
from sqlalchemy import (create_engine)
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import scoped_session, sessionmaker
from time import time
from campaign.utils import as_id
import math
import json
import math
import string

Base = declarative_base()


class StorageException(Exception):
pass


class StorageBase(object):

engine = None
Session = None

def __init__(self, config, **kw):
self.config = config
self.settings = config.get_settings()
Expand All @@ -32,7 +41,31 @@ def _gen_key(self, data):
""" Create a semi-arbitrary unique key for this record """
nowbits = math.modf(time())
return '%s%s' % (self._encode_num(int(nowbits[1])),
self._encode_num(int(nowbits[0] * 100000)))
self._encode_num(int(nowbits[0] * 100000)))

def _connect(self):
try:
if self.engine is None:
userpass = ''
host = ''
if (self.settings.get('db.user')):
userpass = '%s:%s@' % (self.settings.get('db.user'),
self.settings.get('db.password'))
if (self.settings.get('db.host')):
host = '%s' % self.settings.get('db.host')
dsn = '%s://%s%s/%s' % (self.settings.get('db.type', 'mysql'),
userpass, host,
self.settings.get('db.db',
self.__database__))
self.engine = create_engine(dsn, pool_recycle=3600)
Base.metadata.create_all(self.engine)
if self.Session is None:
self.Session = scoped_session(sessionmaker(bind=self.engine,
expire_on_commit=True))
except Exception, e:
self.logger.log(msg='Could not connect to db "%s"' % repr(e),
type='error', severity=LOG.EMERGENCY)
raise e

def parse_date(self, datestr):
if not datestr:
Expand Down
25 changes: 1 addition & 24 deletions campaign/storage/metrics.py
Expand Up @@ -2,9 +2,8 @@
from campaign.storage import StorageBase
from datetime import datetime
from sqlalchemy import (Column, Integer, String,
create_engine, MetaData, text)
MetaData, text)
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import scoped_session, sessionmaker
import json
import re
import time
Expand Down Expand Up @@ -42,28 +41,6 @@ def __init__(self, config, logger, **kw):
type='error', severity=LOG.CRITICAL)
raise e

def _connect(self):
try:
userpass = ''
host = ''
if (self.settings.get('db.user')):
userpass = '%s:%s@' % (self.settings.get('db.user'),
self.settings.get('db.password'))
if (self.settings.get('db.host')):
host = '%s' % self.settings.get('db.host')
dsn = '%s://%s%s/%s' % (self.settings.get('db.type', 'mysql'),
userpass, host,
self.settings.get('db.db',
self.__database__))
self.engine = create_engine(dsn, pool_recycle=3600)
Base.metadata.create_all(self.engine)
self.session = scoped_session(sessionmaker(bind=self.engine))()
#self.metadata.create_all(self.engine)
except Exception, e:
self.logger.log(msg='Could not connect to db "%s"' % repr(e),
type='error', severity=LOG.EMERGENCY)
raise e

def bulk_increment(self, conn, id, action, time=time.time()):
action = re.sub(r'[^0-9A-Za-z]', '', action)
try:
Expand Down
110 changes: 78 additions & 32 deletions campaign/storage/sql.py
@@ -1,14 +1,20 @@
import json
import time
from . import StorageBase, StorageException
import uuid
from . import StorageBase, StorageException, Base
from .metrics import Counter
from campaign.logger import LOG
from campaign.views import api_version
from sqlalchemy import (Column, Integer, String, Text, create_engine, text)
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import scoped_session, sessionmaker
from sqlalchemy import (Column, Integer, String, Text, text)

Base = declarative_base()

class Users(Base):
__tablename__ = 'users'
email = Column('email', String(100), primary_key=True)
id = Column('id', String(32), index=True)
sponsor = Column('sponsor', String(100))
time = Column('date', Integer)
level = Column('level', Integer)


class Campaign(Base):
Expand All @@ -34,6 +40,7 @@ class Campaign(Base):
author = Column('author', String(255), index=True)
created = Column('created', Integer, index=True)
title = Column('title', String(50))
status = Column('status', Integer)


class Storage(StorageBase):
Expand All @@ -43,39 +50,18 @@ class Storage(StorageBase):
def __init__(self, config, logger, **kw):
try:
super(Storage, self).__init__(config, **kw)
self.logger = logger
self._connect()
# Store off a link to the main table.
self.campaigns = Base.metadata.tables.get(Campaign.__tablename__)
self.users = Base.metadata.tables.get(Users.__tablename__)
self.counter = Counter(config, logger, **kw)
self.logger = logger
#TODO: add the most common index.
except Exception, e:
logger.log(msg='Could not initialize Storage "%s"' % str(e),
type='error', severity=LOG.CRITICAL)
raise e

def _connect(self):
try:
userpass = ''
host = ''
if (self.settings.get('db.user')):
userpass = '%s:%s@' % (self.settings.get('db.user'),
self.settings.get('db.password'))
if (self.settings.get('db.host')):
host = '%s' % self.settings.get('db.host')
dsn = '%s://%s%s/%s' % (self.settings.get('db.type', 'mysql'),
userpass, host,
self.settings.get('db.db',
self.__database__))
self.engine = create_engine(dsn, pool_recycle=3600)
Base.metadata.create_all(self.engine)
self.Session = scoped_session(sessionmaker(bind=self.engine,
expire_on_commit=True))
# Store off a link to the main table.
self.campaigns = Base.metadata.tables.get(Campaign.__tablename__)
except Exception, e:
self.logger.log(msg='Could not connect to db "%s"' % repr(e),
type='error', severity=LOG.EMERGENCY)
raise e

def health_check(self):
try:
healthy = True
Expand Down Expand Up @@ -130,7 +116,7 @@ def put_announce(self, data, sessionHandle=None, now=None):
specificity = 0
for col in ['lang', 'loc', 'platform',
'channel', 'version']:
if len(str(data.get(col,''))):
if len(str(data.get(col, ''))):
specificity += 1
if data.get('idle_time') and int(data.get('idle_time')) != 0:
specificity += 1
Expand Down Expand Up @@ -169,7 +155,7 @@ def get_announce(self, data, now=None):
data['idle_time'] = data.get('idle', 0)
try:
if 'version' in data:
sql +=" and coalesce(version, :version) = :version"
sql += " and coalesce(version, :version) = :version"
params['version'] = str(data['version']).split('.')[0]
except Exception:
pass
Expand Down Expand Up @@ -245,3 +231,63 @@ def purge(self):
sql = 'delete from %s;' % self.__tablename__
self.engine.execute(text(sql))
session.commit()

def user_health_check(self):
#foo
try:
uid = self.add_user('test', '', 0)
self.rm_user(uid)
# with self.engine.begin() as conn:
except Exception, e:
import warnings
warnings.warn(str(e))
return False
return True

def is_user(self, email):
sel = self.users.select(self.users.c.email == email)
items = self.engine.execute(sel)
row = items.fetchone()
if row is None:
return False
return True

def user_list(self):
result = []
sql = "select * from users order by email"
items = self.engine.execute(text(sql))
for item in items:
result.append(dict(item))
return result

def add_user(self, email, sponsor, level=0):
try:
uid = uuid.uuid4().hex
with self.engine.begin() as conn:
ins = self.users.insert().values(
id=uid,
email=email,
sponsor=sponsor,
level=level,
date=int(time.time()))
conn.execute(ins)
return uid
except Exception, e:
self.logger.log(msg='Could not add user "%s", "%s"' %
(email, repr(e)),
type="error", severity=LOG.ERROR)
return None

def rm_user(self, id):
if len(id) == 0 or id == '0':
return False
try:
with self.engine.begin() as conn:
rm = self.users.delete().where(self.users.c.id == id)
conn.execute(rm)
return True
except Exception, e:
self.logger.log(msg='Could not delete user with id "%s", "%s"' %
(id, repr(e)),
type="error", severity=LOG.ERROR)
return False
53 changes: 53 additions & 0 deletions campaign/templates/accounts.mako
@@ -0,0 +1,53 @@
<!DOCTYPE html>
<%
from time import (strftime, localtime)
users = pageargs.get('users', [])
sponsor = pageargs.get('user', '')
error = pageargs.get('error', '')
land = pageargs.get('land', "/admin/")
time_format = '%a, %d %b %Y - %H:%M:%S GMT'
%>
<html>
<head>
<title>Account management</title>
<link rel="stylesheet" type="text/css" href="/static/style.css" />
<link rel="stylesheet" type="text/css" href="http://www.mozilla.org/tabzilla/media/css/tabzilla.css" />
</head>
<body>
<header>
<h1> User Management</h1>
<div class="control">
<a class="button" href="/author/1/">Campaigns</a>
</div>
</header>
<div class="new_user">
<h2>New User</h2>
<form id="new_user" action="${land}" method="POST">
<label for="user">User Email:<input name="user"></label>
% if error is not None and len(error) > 0:
<div class="error">${error}</div>
% endif
<button class="button" type="submit" alt="add">Add User</button>
<form>
<div>
<h2>Users:</h2>
<table class="existing_users">
<tr><th>User:</th><th>Added by:</th><th>On:</th><th><th></tr>
% for user in users:
<% duser = dict(user) %>
<tr>
<td class="user">${user['email']}</td>
<td class="sponsor">${user['sponsor']}</td>
<td class="date">${strftime(time_format, localtime(user['date']))}</td>
<td class="rm">
% if sponsor != user['email'] and user['sponsor'] is not None:
<button class="button remove" value="${user['id']}" name="remove">Remove</button>
% endif
</td>
</tr>
% endfor
</table>
</body>
</html>
9 changes: 5 additions & 4 deletions campaign/templates/main.mako
Expand Up @@ -45,6 +45,7 @@
<header>
<h1>Campaign Admin Panel</h1>
<div class="control">
<a class="button" href="/admin/">User Management</a>
<button class="button logout">Log out</button>
<!-- yep, this should be a REST get and display call. -->
</header>
Expand Down Expand Up @@ -125,15 +126,15 @@
<option value="ht">Haitian(Creole)</option>
<option value="ha">Hausa</option>
<option value="he">Hebrew</option>
<option value="iw">Hebrew</option>
<option value="iw">Hebrew Alternate "iw"</option>
<option value="hi">Hindi</option>
<option value="hu">Hungarian</option>
<option value="is">Icelandic</option>
<option value="io">Ido</option>
<option value="id">Indonesian</option>
<option value="in">Indonesian</option>
<option value="id">Indonesia</option>
<option value="in">Indonesian Alternate "in"</option>
<option value="ia">Interlingua</option>
<option value="ie">Interlingue</option>
<option value="ie">Interlingue Alternate "ie"</option>
<option value="iu">Inuktitut</option>
<option value="ik">Inupiak</option>
<option value="ga">Irish</option>
Expand Down

0 comments on commit 48aa0f7

Please sign in to comment.