Initial commit
This commit is contained in:
commit
7004713412
19 changed files with 772 additions and 0 deletions
7
.gitignore
vendored
Normal file
7
.gitignore
vendored
Normal file
|
@ -0,0 +1,7 @@
|
|||
*.pyc
|
||||
*.pyo
|
||||
*~
|
||||
.*.swp
|
||||
/*.egg-info
|
||||
/cache/*
|
||||
/data/*
|
10
Makefile
Normal file
10
Makefile
Normal file
|
@ -0,0 +1,10 @@
|
|||
.PHONY: clean flake8
|
||||
|
||||
all: clean flake8
|
||||
|
||||
clean:
|
||||
find -name "*.pyc" | xargs rm -f
|
||||
rm -rf cache/*
|
||||
|
||||
flake8:
|
||||
flake8 --ignore=E123,E128 --max-line-length=120 mycoserver
|
24
README
Normal file
24
README
Normal file
|
@ -0,0 +1,24 @@
|
|||
Install
|
||||
=======
|
||||
|
||||
Debian dependencies:
|
||||
|
||||
$ aptitude install python python-mako python-markupsafe python-paste python-pastedeploy python-pastescript \
|
||||
python-weberror python-webhelpers python-webob
|
||||
|
||||
Non-Debian dependencies:
|
||||
|
||||
$ git clone git://gitorious.org/biryani/biryani.git biryani1
|
||||
$ cd biryani1
|
||||
$ git checkout biryani1
|
||||
$ python setup.py develop --no-deps --user
|
||||
|
||||
Install mycoserver Python egg (from mycoserver root directory):
|
||||
|
||||
$ python setup.py develop --no-deps --user
|
||||
|
||||
|
||||
Start server
|
||||
============
|
||||
|
||||
$ paster serve --reload development.ini
|
53
development.ini
Normal file
53
development.ini
Normal file
|
@ -0,0 +1,53 @@
|
|||
# EesyVPN Web - Development environment configuration
|
||||
#
|
||||
# The %(here)s variable will be replaced with the parent directory of this file.
|
||||
|
||||
[DEFAULT]
|
||||
debug = true
|
||||
# Uncomment and replace with the address which should receive any error reports
|
||||
#email_to = you@yourdomain.com
|
||||
smtp_server = localhost
|
||||
from_address = myco-server@localhost
|
||||
dbpass = myP@ssw0rd
|
||||
|
||||
[server:main]
|
||||
use = egg:Paste#http
|
||||
host = 0.0.0.0
|
||||
port = 8765
|
||||
|
||||
[app:main]
|
||||
use = egg:MyCoServer
|
||||
|
||||
|
||||
# Logging configuration
|
||||
[loggers]
|
||||
keys = root, mycoserver, mycoserver_router
|
||||
|
||||
[handlers]
|
||||
keys = console
|
||||
|
||||
[formatters]
|
||||
keys = generic
|
||||
|
||||
[logger_root]
|
||||
level = DEBUG
|
||||
handlers = console
|
||||
|
||||
[logger_mycoserver]
|
||||
level = DEBUG
|
||||
handlers =
|
||||
qualname = mycoserver
|
||||
|
||||
[logger_mycoserver_router]
|
||||
level = DEBUG
|
||||
handlers =
|
||||
qualname = mycoserver.router
|
||||
|
||||
[handler_console]
|
||||
class = StreamHandler
|
||||
args = (sys.stderr,)
|
||||
formatter = generic
|
||||
|
||||
[formatter_generic]
|
||||
format = %(asctime)s,%(msecs)03d %(levelname)-5.5s [%(name)s:%(funcName)s line %(lineno)d] %(message)s
|
||||
datefmt = %H:%M:%S
|
0
mycoserver/__init__.py
Normal file
0
mycoserver/__init__.py
Normal file
57
mycoserver/application.py
Normal file
57
mycoserver/application.py
Normal file
|
@ -0,0 +1,57 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
|
||||
|
||||
"""Middleware initialization"""
|
||||
|
||||
|
||||
import logging.config
|
||||
import os
|
||||
|
||||
from paste.cascade import Cascade
|
||||
from paste.urlparser import StaticURLParser
|
||||
from weberror.errormiddleware import ErrorMiddleware
|
||||
|
||||
from . import configuration, context, controllers, templates
|
||||
|
||||
import db
|
||||
|
||||
|
||||
def make_app(global_conf, **app_conf):
|
||||
"""Create a WSGI application and return it
|
||||
|
||||
``global_conf``
|
||||
The inherited configuration for this application. Normally from
|
||||
the [DEFAULT] section of the Paste ini file.
|
||||
|
||||
``app_conf``
|
||||
The application's local configuration. Normally specified in
|
||||
the [app:<name>] section of the Paste ini file (where <name>
|
||||
defaults to main).
|
||||
"""
|
||||
logging.config.fileConfig(global_conf['__file__'])
|
||||
app_ctx = context.Context()
|
||||
app_ctx.conf = configuration.load_configuration(global_conf, app_conf)
|
||||
app_ctx.templates = templates.load_templates(app_ctx)
|
||||
app_ctx.db = db.DB(
|
||||
app_ctx.conf.get('dbhost','localhost'),
|
||||
app_ctx.conf.get('dbuser','myco'),
|
||||
app_ctx.conf.get('dbpass','password'),
|
||||
app_ctx.conf.get('dbname','myco'),
|
||||
)
|
||||
if not app_ctx.db.connect():
|
||||
logging.error('Failed to connect DB')
|
||||
app = controllers.make_router()
|
||||
app = context.make_add_context_to_request(app, app_ctx)
|
||||
if not app_ctx.conf['debug']:
|
||||
app = ErrorMiddleware(
|
||||
app,
|
||||
error_email=app_ctx.conf['email_to'],
|
||||
error_log=app_ctx.conf.get('error_log', None),
|
||||
error_message=app_ctx.conf.get('error_message', 'An internal server error occurred'),
|
||||
error_subject_prefix=app_ctx.conf.get('error_subject_prefix', 'Web application error: '),
|
||||
from_address=app_ctx.conf['from_address'],
|
||||
smtp_server=app_ctx.conf.get('smtp_server', 'localhost'),
|
||||
)
|
||||
app = Cascade([StaticURLParser(os.path.join(app_ctx.conf['app_dir'], 'static')), app])
|
||||
app.ctx = app_ctx
|
||||
return app
|
30
mycoserver/configuration.py
Normal file
30
mycoserver/configuration.py
Normal file
|
@ -0,0 +1,30 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
|
||||
|
||||
"""Paste INI configuration"""
|
||||
|
||||
|
||||
import os
|
||||
|
||||
from biryani1 import strings
|
||||
from biryani1.baseconv import (check, default, guess_bool, pipe, struct)
|
||||
|
||||
|
||||
def load_configuration(global_conf, app_conf):
|
||||
"""Build the application configuration dict."""
|
||||
app_dir = os.path.dirname(os.path.abspath(__file__))
|
||||
conf = {}
|
||||
conf.update(strings.deep_decode(global_conf))
|
||||
conf.update(strings.deep_decode(app_conf))
|
||||
conf.update(check(struct(
|
||||
{
|
||||
'app_conf': default(app_conf),
|
||||
'app_dir': default(app_dir),
|
||||
'cache_dir': default(os.path.join(os.path.dirname(app_dir), 'cache')),
|
||||
'debug': pipe(guess_bool, default(False)),
|
||||
'global_conf': default(global_conf),
|
||||
},
|
||||
default='drop',
|
||||
drop_none_values=False,
|
||||
))(conf))
|
||||
return conf
|
24
mycoserver/context.py
Normal file
24
mycoserver/context.py
Normal file
|
@ -0,0 +1,24 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
|
||||
|
||||
"""Context loaded and saved in WSGI requests"""
|
||||
|
||||
|
||||
from webob.dec import wsgify
|
||||
|
||||
|
||||
def make_add_context_to_request(app, app_ctx):
|
||||
"""Return a WSGI middleware that adds context to requests."""
|
||||
@wsgify
|
||||
def add_context_to_request(req):
|
||||
req.ctx = app_ctx
|
||||
req.ctx.req = req
|
||||
return req.get_response(app)
|
||||
return add_context_to_request
|
||||
|
||||
|
||||
class Context(object):
|
||||
_ = lambda self, message: message
|
||||
conf = None
|
||||
templates = None
|
||||
db = None
|
70
mycoserver/controllers.py
Normal file
70
mycoserver/controllers.py
Normal file
|
@ -0,0 +1,70 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
|
||||
|
||||
import logging
|
||||
|
||||
from webob.dec import wsgify
|
||||
|
||||
from . import conv, router, templates, wsgi_helpers
|
||||
|
||||
import json
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
@wsgify
|
||||
def home(req):
|
||||
return templates.render(req.ctx, '/home.mako', data={})
|
||||
|
||||
@wsgify
|
||||
def login(req):
|
||||
params = req.params
|
||||
log.debug(u'params = {}'.format(params))
|
||||
inputs = {
|
||||
'email': params.get('email'),
|
||||
'password': params.get('password'),
|
||||
}
|
||||
log.debug(u'inputs = {}'.format(inputs))
|
||||
data, errors = conv.inputs_to_login_data(inputs)
|
||||
if errors is not None:
|
||||
return wsgi_helpers.bad_request(req.ctx, comment=errors)
|
||||
|
||||
log.debug(u'data = {}'.format(data))
|
||||
|
||||
login_data=req.ctx.db.login(data['email'],data['password'])
|
||||
return wsgi_helpers.respond_json(req.ctx,login_data,headers=[('Access-Control-Allow-Origin','*')])
|
||||
|
||||
@wsgify
|
||||
def sync(req):
|
||||
params = req.params
|
||||
log.debug(u'params = {}'.format(params))
|
||||
inputs = {
|
||||
'email': params.get('email'),
|
||||
'password': params.get('password'),
|
||||
'groups': params.get('groups')
|
||||
}
|
||||
log.debug(u'inputs = {}'.format(inputs))
|
||||
data, errors = conv.inputs_to_sync_data(inputs)
|
||||
if errors is not None or data['groups'] is None:
|
||||
return wsgi_helpers.bad_request(req.ctx, comment=errors)
|
||||
|
||||
data['groups']=json.loads(data['groups'])
|
||||
|
||||
log.debug(u'data = {}'.format(data))
|
||||
|
||||
login_data=req.ctx.db.login(data['email'],data['password'])
|
||||
if 'email' in login_data:
|
||||
ret=req.ctx.db.sync_group(data['email'],data['groups'])
|
||||
return wsgi_helpers.respond_json(req.ctx,ret,headers=[('Access-Control-Allow-Origin','*')])
|
||||
else:
|
||||
return wsgi_helpers.respond_json(
|
||||
req.ctx,
|
||||
login_data,
|
||||
headers=[('Access-Control-Allow-Origin','*')]
|
||||
)
|
||||
|
||||
def make_router():
|
||||
return router.make_router(
|
||||
('GET', '^/$', home),
|
||||
('GET', '^/login$', login),
|
||||
('GET', '^/sync$', sync),
|
||||
)
|
23
mycoserver/conv.py
Normal file
23
mycoserver/conv.py
Normal file
|
@ -0,0 +1,23 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
|
||||
|
||||
from biryani1.baseconv import cleanup_line, empty_to_none, not_none, pipe, struct
|
||||
|
||||
inputs_to_login_data = struct(
|
||||
{
|
||||
'email': pipe(cleanup_line, empty_to_none),
|
||||
'password': pipe(cleanup_line, empty_to_none),
|
||||
},
|
||||
default='drop',
|
||||
drop_none_values=False,
|
||||
)
|
||||
|
||||
inputs_to_sync_data = struct(
|
||||
{
|
||||
'email': pipe(cleanup_line, empty_to_none),
|
||||
'password': pipe(cleanup_line, empty_to_none),
|
||||
'groups': pipe(empty_to_none),
|
||||
},
|
||||
default='drop',
|
||||
drop_none_values=False,
|
||||
)
|
84
mycoserver/db/__init__.py
Normal file
84
mycoserver/db/__init__.py
Normal file
|
@ -0,0 +1,84 @@
|
|||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
import json
|
||||
import logging
|
||||
log = logging.getLogger(__name__)
|
||||
import MySQLdb
|
||||
|
||||
class DB(object):
|
||||
|
||||
def __init__(self,host,user,pwd,db):
|
||||
self.host = host
|
||||
self.user = user
|
||||
self.pwd = pwd
|
||||
self.db = db
|
||||
self.con = 0
|
||||
|
||||
def connect(self):
|
||||
if self.con == 0:
|
||||
try:
|
||||
con = MySQLdb.connect(self.host,self.user,self.pwd,self.db)
|
||||
self.con = con
|
||||
return True
|
||||
except Exception, e:
|
||||
log.fatal('Error connecting to database : %s' % e)
|
||||
return
|
||||
|
||||
def do_sql(self,sql):
|
||||
try:
|
||||
c=self.con.cursor()
|
||||
c.execute(sql)
|
||||
self.con.commit()
|
||||
return c
|
||||
except Exception,e:
|
||||
log.error('Error executing request %s : %s' % (sql,e))
|
||||
return False
|
||||
|
||||
def select(self,sql):
|
||||
ret=self.do_sql(sql)
|
||||
if ret!=False:
|
||||
return ret.fetchall()
|
||||
return ret
|
||||
|
||||
def login(self,email,password):
|
||||
ret=self.select("SELECT email,name,password FROM users WHERE email='%s' AND password='%s'" % (email,password))
|
||||
log.debug(ret)
|
||||
if ret:
|
||||
if len(ret)==1:
|
||||
return {
|
||||
'email': ret[0][0],
|
||||
'name': ret[0][1]
|
||||
}
|
||||
elif len(ret)>=1:
|
||||
log.warning('Duplicate user %s in database' % email)
|
||||
elif ret==():
|
||||
return { 'loginerror': 'Utilisateur inconnu' }
|
||||
return { 'loginerror': 'Erreur inconnu' }
|
||||
|
||||
def sync_group(self,email,groups):
|
||||
db_groups=self.get_group(email)
|
||||
json_group=json.dumps(groups)
|
||||
if db_groups!=False:
|
||||
if db_groups=={}:
|
||||
if groups=={}:
|
||||
return {'groups': {}}
|
||||
else:
|
||||
if self.do_sql("INSERT INTO groups (email,groups) VALUES ('%s','%s')" % (email,json_group)):
|
||||
return {'groups': groups}
|
||||
elif groups=={}:
|
||||
return {'groups': db_groups}
|
||||
else:
|
||||
if self.do_sql("UPDATE groups SET groups='%s' WHERE email='%s'" % (json_group,email)):
|
||||
return {'groups': groups}
|
||||
return {'syncerror': 'Erreur inconnu'}
|
||||
|
||||
def get_group(self,email):
|
||||
ret=self.select("SELECT groups FROM groups WHERE email='%s'" % email)
|
||||
if ret!=False:
|
||||
if len(ret)==1:
|
||||
return json.loads(ret[0][0])
|
||||
else:
|
||||
return {}
|
||||
else:
|
||||
return False
|
50
mycoserver/router.py
Normal file
50
mycoserver/router.py
Normal file
|
@ -0,0 +1,50 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
|
||||
|
||||
"""Helpers for URLs"""
|
||||
|
||||
|
||||
import logging
|
||||
import re
|
||||
|
||||
from webob.dec import wsgify
|
||||
|
||||
from . import wsgi_helpers
|
||||
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def make_router(*routings):
|
||||
"""Return a WSGI application that dispatches requests to controllers."""
|
||||
routes = []
|
||||
for routing in routings:
|
||||
methods, regex, app = routing[:3]
|
||||
if isinstance(methods, basestring):
|
||||
methods = (methods,)
|
||||
vars = routing[3] if len(routing) >= 4 else {}
|
||||
routes.append((methods, re.compile(regex), app, vars))
|
||||
|
||||
@wsgify
|
||||
def router(req):
|
||||
"""Dispatch request to controllers."""
|
||||
split_path_info = req.path_info.split('/')
|
||||
assert not split_path_info[0], split_path_info
|
||||
for methods, regex, app, vars in routes:
|
||||
if methods is None or req.method in methods:
|
||||
match = regex.match(req.path_info)
|
||||
if match is not None:
|
||||
log.debug(u'URL path = {path} matched controller {controller}'.format(
|
||||
controller=app, path=req.path_info))
|
||||
if getattr(req, 'urlvars', None) is None:
|
||||
req.urlvars = {}
|
||||
req.urlvars.update(dict(
|
||||
(name, value.decode('utf-8') if value is not None else None)
|
||||
for name, value in match.groupdict().iteritems()
|
||||
))
|
||||
req.urlvars.update(vars)
|
||||
req.script_name += req.path_info[:match.end()]
|
||||
req.path_info = req.path_info[match.end():]
|
||||
return req.get_response(app)
|
||||
return wsgi_helpers.not_found(req.ctx)
|
||||
return router
|
44
mycoserver/templates/__init__.py
Normal file
44
mycoserver/templates/__init__.py
Normal file
|
@ -0,0 +1,44 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
|
||||
|
||||
"""Mako templates rendering"""
|
||||
|
||||
|
||||
import json
|
||||
import mako.lookup
|
||||
import os
|
||||
|
||||
from . import helpers
|
||||
|
||||
|
||||
js = lambda x: json.dumps(x, encoding='utf-8', ensure_ascii=False)
|
||||
|
||||
|
||||
def load_templates(ctx):
|
||||
# Create the Mako TemplateLookup, with the default auto-escaping.
|
||||
return mako.lookup.TemplateLookup(
|
||||
default_filters=['h'],
|
||||
directories=[os.path.join(ctx.conf['app_dir'], 'templates')],
|
||||
input_encoding='utf-8',
|
||||
module_directory=os.path.join(ctx.conf['cache_dir'], 'templates'),
|
||||
)
|
||||
|
||||
|
||||
def render(ctx, template_path, **kw):
|
||||
return ctx.templates.get_template(template_path).render_unicode(
|
||||
ctx=ctx,
|
||||
helpers=helpers,
|
||||
js=js,
|
||||
N_=lambda message: message,
|
||||
req=ctx.req,
|
||||
**kw).strip()
|
||||
|
||||
|
||||
def render_def(ctx, template_path, def_name, **kw):
|
||||
return ctx.templates.get_template(template_path).get_def(def_name).render_unicode(
|
||||
_=ctx.translator.ugettext,
|
||||
ctx=ctx,
|
||||
js=js,
|
||||
N_=lambda message: message,
|
||||
req=ctx.req,
|
||||
**kw).strip()
|
14
mycoserver/templates/helpers.py
Normal file
14
mycoserver/templates/helpers.py
Normal file
|
@ -0,0 +1,14 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
|
||||
|
||||
import os
|
||||
import random
|
||||
import datetime
|
||||
|
||||
|
||||
def random_sequence(length):
|
||||
return [random.random() for idx in xrange(0, length)]
|
||||
|
||||
|
||||
def relative_path(ctx, abs_path):
|
||||
return os.path.relpath(abs_path, ctx.req.path)
|
13
mycoserver/templates/home.mako
Normal file
13
mycoserver/templates/home.mako
Normal file
|
@ -0,0 +1,13 @@
|
|||
## -*- coding: utf-8 -*-
|
||||
|
||||
|
||||
<%inherit file="/site.mako"/>
|
||||
|
||||
<%block name="title_content">MyCo</%block>
|
||||
|
||||
<%block name="body_content">
|
||||
<div class="hero-unit">
|
||||
<h1>MyCo <small>Gérer vos déponses communes</small></h1>
|
||||
<p class="muted">Application mobile de gestion de vos dépenses communes.</p>
|
||||
</div>
|
||||
</%block>
|
23
mycoserver/templates/http-error.mako
Normal file
23
mycoserver/templates/http-error.mako
Normal file
|
@ -0,0 +1,23 @@
|
|||
## -*- coding: utf-8 -*-
|
||||
|
||||
|
||||
<%inherit file="/site.mako"/>
|
||||
|
||||
|
||||
<%block name="body_content">
|
||||
<div class="alert alert-block alert-error">
|
||||
<h4 class="alert-heading">Error « ${title} »</h4>
|
||||
<p>${explanation}</p>
|
||||
% if comment:
|
||||
<p>${comment}</p>
|
||||
% endif
|
||||
% if message:
|
||||
<p>${message}</p>
|
||||
% endif
|
||||
</div>
|
||||
</%block>
|
||||
|
||||
|
||||
<%block name="title_content">
|
||||
${title} - ${parent.title_content()}
|
||||
</%block>
|
36
mycoserver/templates/site.mako
Normal file
36
mycoserver/templates/site.mako
Normal file
|
@ -0,0 +1,36 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="fr">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title><%block name="title_content">MyCo</%block></title>
|
||||
<link rel="stylesheet" href="https://netdna.bootstrapcdn.com/bootstrap/3.0.3/css/bootstrap.min.css">
|
||||
<link rel="stylesheet" href="https://netdna.bootstrapcdn.com/bootstrap/3.0.3/css/bootstrap-theme.min.css">
|
||||
<link rel="stylesheet" href="${helpers.relative_path(ctx, '/css/style.css')}">
|
||||
<!--[if lt IE 9]>
|
||||
<script src="https://oss.maxcdn.com/libs/html5shiv/3.7.0/html5shiv.js"></script>
|
||||
<script src="https://oss.maxcdn.com/libs/respond.js/1.3.0/respond.min.js"></script>
|
||||
<![endif]-->
|
||||
</head>
|
||||
<body>
|
||||
|
||||
|
||||
<div class="navbar navbar-inverse navbar-fixed-top" role="navigation">
|
||||
<div class="container">
|
||||
<div class="navbar-header">
|
||||
<a class="navbar-brand" href="index.html">MyCo</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="container">
|
||||
|
||||
<%block name="body_content"/>
|
||||
|
||||
</div>
|
||||
|
||||
<script src="https://code.jquery.com/jquery.js"></script>
|
||||
<script src="https://netdna.bootstrapcdn.com/bootstrap/3.0.3/js/bootstrap.min.js"></script>
|
||||
|
||||
</body>
|
||||
</html>
|
170
mycoserver/wsgi_helpers.py
Normal file
170
mycoserver/wsgi_helpers.py
Normal file
|
@ -0,0 +1,170 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
|
||||
|
||||
import collections
|
||||
import json
|
||||
|
||||
from markupsafe import Markup
|
||||
from webhelpers.html import tags
|
||||
import webob.dec
|
||||
import webob.exc
|
||||
|
||||
from . import templates
|
||||
|
||||
|
||||
N_ = lambda message: message
|
||||
|
||||
|
||||
errors_explanation = {
|
||||
400: N_("Request is faulty"),
|
||||
401: N_("Access is restricted to authorized persons."),
|
||||
403: N_("Access is forbidden."),
|
||||
404: N_("The requested page was not found."),
|
||||
}
|
||||
errors_message = {
|
||||
401: N_("You must login to access this page."),
|
||||
}
|
||||
errors_title = {
|
||||
400: N_("Unable to Access"),
|
||||
401: N_("Access Denied"),
|
||||
403: N_("Access Denied"),
|
||||
404: N_("Unable to Access"),
|
||||
}
|
||||
|
||||
|
||||
def bad_request(ctx, **kw):
|
||||
return error(ctx, 400, **kw)
|
||||
|
||||
|
||||
def discard_empty_items(data):
|
||||
if isinstance(data, collections.Mapping):
|
||||
# Use type(data) to keep OrderedDicts.
|
||||
data = type(data)(
|
||||
(name, discard_empty_items(value))
|
||||
for name, value in data.iteritems()
|
||||
if value is not None
|
||||
)
|
||||
return data
|
||||
|
||||
|
||||
def error(ctx, code, **kw):
|
||||
response = webob.exc.status_map[code](headers=kw.pop('headers', None))
|
||||
if code != 204: # No content
|
||||
body = kw.pop('body', None)
|
||||
if body is None:
|
||||
template_path = kw.pop('template_path', '/http-error.mako')
|
||||
explanation = kw.pop('explanation', None)
|
||||
if explanation is None:
|
||||
explanation = errors_explanation.get(code)
|
||||
explanation = ctx._(explanation) if explanation is not None else response.explanation
|
||||
message = kw.pop('message', None)
|
||||
if message is None:
|
||||
message = errors_message.get(code)
|
||||
if message is not None:
|
||||
message = ctx._(message)
|
||||
comment = kw.pop('comment', None)
|
||||
if isinstance(comment, dict):
|
||||
comment = tags.ul(u'{0} : {1}'.format(key, value) for key, value in comment.iteritems())
|
||||
elif isinstance(comment, list):
|
||||
comment = tags.ul(comment)
|
||||
title = kw.pop('title', None)
|
||||
if title is None:
|
||||
title = errors_title.get(code)
|
||||
title = ctx._(title) if title is not None else response.status
|
||||
body = templates.render(ctx, template_path,
|
||||
comment=comment,
|
||||
explanation=explanation,
|
||||
message=message,
|
||||
response=response,
|
||||
title=title,
|
||||
**kw)
|
||||
response.body = body.encode('utf-8') if isinstance(body, unicode) else body
|
||||
return response
|
||||
|
||||
|
||||
def forbidden(ctx, **kw):
|
||||
return error(ctx, 403, **kw)
|
||||
|
||||
|
||||
def method_not_allowed(ctx, **kw):
|
||||
return error(ctx, 405, **kw)
|
||||
|
||||
|
||||
def no_content(ctx, headers=None):
|
||||
return error(ctx, 204, headers=headers)
|
||||
|
||||
|
||||
def not_found(ctx, **kw):
|
||||
return error(ctx, 404, **kw)
|
||||
|
||||
|
||||
def redirect(ctx, code=302, location=None, **kw):
|
||||
assert location is not None
|
||||
location_str = location.encode('utf-8') if isinstance(location, unicode) else location
|
||||
response = webob.exc.status_map[code](headers=kw.pop('headers', None), location=location_str)
|
||||
body = kw.pop('body', None)
|
||||
if body is None:
|
||||
template_path = kw.pop('template_path', '/http-error.mako')
|
||||
explanation = kw.pop('explanation', None)
|
||||
if explanation is None:
|
||||
explanation = Markup(u'{0} <a href="{1}">{1}</a>.').format(ctx._(u"You'll be redirected to page"), location)
|
||||
message = kw.pop('message', None)
|
||||
if message is None:
|
||||
message = errors_message.get(code)
|
||||
if message is not None:
|
||||
message = ctx._(message)
|
||||
title = kw.pop('title', None)
|
||||
if title is None:
|
||||
title = ctx._("Redirection in progress...")
|
||||
body = templates.render(ctx, template_path,
|
||||
comment=kw.pop('comment', None),
|
||||
explanation=explanation,
|
||||
message=message,
|
||||
response=response,
|
||||
title=title,
|
||||
**kw)
|
||||
response.body = body.encode('utf-8') if isinstance(body, unicode) else body
|
||||
return response
|
||||
|
||||
|
||||
def respond_json(ctx, data, code=None, headers=None, jsonp=None):
|
||||
"""Return a JSON response.
|
||||
|
||||
This function is optimized for JSON following
|
||||
`Google JSON Style Guide <http://google-styleguide.googlecode.com/svn/trunk/jsoncstyleguide.xml>`_, but will handle
|
||||
any JSON except for HTTP errors.
|
||||
"""
|
||||
if isinstance(data, collections.Mapping):
|
||||
# Remove null properties as recommended by Google JSON Style Guide.
|
||||
data = discard_empty_items(data)
|
||||
error = data.get('error')
|
||||
else:
|
||||
error = None
|
||||
if headers is None:
|
||||
headers = []
|
||||
if jsonp:
|
||||
headers.append(('Content-Type', 'application/javascript; charset=utf-8'))
|
||||
else:
|
||||
headers.append(('Content-Type', 'application/json; charset=utf-8'))
|
||||
if error:
|
||||
code = code or error['code']
|
||||
assert isinstance(code, int)
|
||||
response = webob.exc.status_map[code](headers=headers)
|
||||
if error.get('code') is None:
|
||||
error['code'] = code
|
||||
if error.get('message') is None:
|
||||
error['message'] = response.title
|
||||
else:
|
||||
response = ctx.req.response
|
||||
if code is not None:
|
||||
response.status = code
|
||||
response.headers.update(headers)
|
||||
text = unicode(json.dumps(data, encoding='utf-8', ensure_ascii=False, indent=2, sort_keys=True))
|
||||
if jsonp:
|
||||
text = u'{0}({1})'.format(jsonp, text)
|
||||
response.text = text
|
||||
return response
|
||||
|
||||
|
||||
def unauthorized(ctx, **kw):
|
||||
return error(ctx, 401, **kw)
|
40
setup.py
Executable file
40
setup.py
Executable file
|
@ -0,0 +1,40 @@
|
|||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
|
||||
"""MyCO Server web application."""
|
||||
|
||||
|
||||
from setuptools import setup, find_packages
|
||||
|
||||
|
||||
doc_lines = __doc__.split('\n')
|
||||
|
||||
|
||||
setup(
|
||||
author=u'Benjamin Renard',
|
||||
author_email=u'brenard@zionetrix.net',
|
||||
description=doc_lines[0],
|
||||
entry_points="""
|
||||
[paste.app_factory]
|
||||
main = mycoserver.application:make_app
|
||||
""",
|
||||
include_package_data=True,
|
||||
install_requires=[
|
||||
'Biryani1 >= 0.9dev',
|
||||
'MarkupSafe >= 0.15',
|
||||
'WebError >= 0.10',
|
||||
'WebHelpers >= 1.3',
|
||||
'WebOb >= 1.1',
|
||||
],
|
||||
# keywords='',
|
||||
# license=u'http://www.fsf.org/licensing/licenses/agpl-3.0.html',
|
||||
long_description='\n'.join(doc_lines[2:]),
|
||||
name=u'MyCoServer',
|
||||
packages=find_packages(),
|
||||
paster_plugins=['PasteScript'],
|
||||
setup_requires=['PasteScript >= 1.6.3'],
|
||||
# url=u'',
|
||||
version='0.1',
|
||||
zip_safe=False,
|
||||
)
|
Loading…
Reference in a new issue