Initial code commit

This commit is contained in:
2014-06-13 14:56:37 +02:00
parent ba42b4725d
commit 8be3dcf804
20 changed files with 831 additions and 0 deletions

6
.gitignore vendored Normal file
View File

@@ -0,0 +1,6 @@
*.pyc
venv
*.sublime-project
*.sublime-workspace
blender-bfct/config.py
blender-bfct/runserver.wsgi

View File

@@ -0,0 +1,16 @@
from flask import Flask
from flask.ext.sqlalchemy import SQLAlchemy
# Create app
app = Flask(__name__)
import config
app.config.from_object(config.Development)
# Create database connection object
db = SQLAlchemy(app)
from controllers import main
from controllers import admin
from controllers.applications import applications
app.register_blueprint(applications, url_prefix='/applications')

View File

@@ -0,0 +1,64 @@
from application import app, db
from application.models.users import user_datastore
from application.models.applications import Application
from flask import render_template, redirect, url_for
from flask.ext.sqlalchemy import SQLAlchemy
from flask.ext import admin, login
from flask.ext.admin import Admin, expose, form
from flask.ext.admin.contrib import sqla
from flask.ext.admin.contrib.sqla import ModelView
from flask.ext.admin.base import BaseView
from flask.ext.security import login_required, current_user, roles_accepted
from werkzeug import secure_filename
from jinja2 import Markup
import os, hashlib, time
import os.path as op
# Create customized views with access restriction
class CustomModelView(ModelView):
def is_accessible(self):
default_role = user_datastore.find_role("admin")
#return login.current_user.is_authenticated()
return login.current_user.has_role(default_role)
class CustomBaseView(BaseView):
def is_accessible(self):
return True
# Create customized index view class that handles login & registration
class MyAdminIndexView(admin.AdminIndexView):
@expose('/')
def index(self):
default_role = user_datastore.find_role("admin")
if login.current_user.is_authenticated() and login.current_user.has_role(default_role):
return super(MyAdminIndexView, self).index()
else:
return redirect(url_for('homepage'))
@expose('/logout/')
def logout_view(self):
login.logout_user()
return redirect(url_for('homepage'))
class ApplicationView(CustomModelView):
column_list = ('user.first_name', 'user.last_name', 'city_country', 'submission_date', 'status')
column_labels = {'user.first_name' : 'First Name', 'user.last_name' : 'Last Name'}
column_searchable_list = ('website', 'status')
can_create = False
# Create admin
backend = admin.Admin(
app,
'BFCT management',
index_view=MyAdminIndexView(),
base_template='admin/layout_admin.html'
)
backend.add_view(ApplicationView(Application, db.session, name='Applications', url='applications'))

View File

@@ -0,0 +1,94 @@
import datetime
from application import db
from application.models.users import *
from application.models.applications import Application, Skill, ReviewersApplications
from flask import render_template, redirect, url_for, request, Blueprint
from flask.ext.security import login_required, roles_accepted
from flask.ext.security.core import current_user
from flask_wtf import Form
from wtforms import TextField, TextAreaField, BooleanField, SelectMultipleField
from wtforms.validators import DataRequired
from wtforms.fields.html5 import URLField
from wtforms.validators import url
applications = Blueprint('applications', __name__)
@applications.route('/')
@roles_accepted('bfct_manager', 'admin')
def index():
return render_template('applications/index.html',
title='applications',
applications=Application.query.all())
@applications.route('/view/<int:id>')
@roles_accepted('bfct_manager', 'admin')
def view(id):
review = ReviewersApplications.query.\
filter_by(application_id=id).\
filter_by(reviewer_blender_id=current_user.id).\
first()
reviews = ReviewersApplications.query.\
filter_by(application_id=id).\
all()
return render_template('applications/view.html',
title='applications',
application=Application.query.get_or_404(id),
review=review,
reviews=reviews)
@applications.route('/vote/<int:approved>/<int:id>')
@roles_accepted('bfct_manager', 'admin')
def vote(approved, id):
application = Application.query.get_or_404(id)
review = ReviewersApplications.query.\
filter_by(application_id=id).\
filter_by(reviewer_blender_id=current_user.id).\
first()
if approved:
if review:
application.reject -= 1
application.approve += 1
else:
if review:
application.approve -= 1
application.reject += 1
if application.status == 'submitted':
application.status = 'under_review'
application.review_start_date = datetime.datetime.now()
db.session.add(application)
if not review:
review = ReviewersApplications(
application_id=id,
reviewer_blender_id=current_user.id,
approved=approved)
else:
review.approved = approved
db.session.add(review)
db.session.commit()
return redirect(url_for('.view', id=id))
@applications.route('/final-review/<int:approved>/<int:id>')
@roles_accepted('admin')
def final_review(approved, id):
application = Application.query.get_or_404(id)
if application.status != 'under_review':
return 'error'
else:
if approved:
application.status = 'approved'
else:
application.status = 'rejected'
application.review_end_date = datetime.datetime.now()
db.session.add(application)
db.session.commit()
return redirect(url_for('.view', id=id))

View File

@@ -0,0 +1,79 @@
from application import app, db
from application.models.users import *
from application.models.applications import Application, Skill
from flask import render_template, redirect, url_for, request, flash
from flask.ext.security import login_required
from flask.ext.security.core import current_user
from sqlalchemy.orm.exc import MultipleResultsFound
from flask_wtf import Form
from wtforms import TextField, TextAreaField, BooleanField, SelectMultipleField
from wtforms.validators import DataRequired
from wtforms.fields.html5 import URLField
from wtforms.validators import url
class ApplicationForm(Form):
network_profile = TextField('Blender Network Profile', validators=[DataRequired()])
website = URLField(validators=[url()])
city_country = TextField('City and Country', validators=[DataRequired()])
teaching = BooleanField('Teaching')
skills = SelectMultipleField('Areas of expertise', coerce=int)
video_example = URLField(validators=[url()])
written_example = URLField(validators=[url()])
portfolio_cv = URLField(validators=[url()])
bio_message = TextAreaField('Your message for the board')
# Views
@app.route('/')
def homepage():
return render_template('index.html', title='home')
@app.route('/apply', methods=['GET', 'POST'])
@login_required
def apply():
application = Application.query.filter_by(blender_id=current_user.id).first()
if application:
return redirect(url_for('my_application'))
else:
form = ApplicationForm()
form.skills.choices = [(s.id, s.name) for s in Skill.query.all()]
if form.validate_on_submit():
print 'validating'
application = Application(
blender_id=current_user.id,
website=form.website.data,
city_country=form.city_country.data,
teaching=form.teaching.data,
video_example=form.video_example.data,
written_example=form.written_example.data,
portfolio_cv=form.portfolio_cv.data,
bio_message=form.bio_message.data,
status='submitted')
for skill in form.skills.data:
s = Skill.query.get(skill)
application.skills.append(s)
db.session.add(application)
db.session.commit()
return redirect(url_for('my_application'))
# print form.errors
return render_template('apply.html',
title='apply',
form=form)
@app.route('/my-application')
@login_required
def my_application():
try:
application = Application.query.filter_by(blender_id=current_user.id).one()
except MultipleResultsFound:
flash('You have submitted more than one application. Get in touch with support@blendernetwork.org.')
return render_template('index.html')
if not application:
return redirect(url_for('apply'))
else:
return render_template('my_application.html',
application=application)

View File

@@ -0,0 +1,42 @@
def pretty_date(time=False):
"""
Get a datetime object or a int() Epoch timestamp and return a
pretty string like 'an hour ago', 'Yesterday', '3 months ago',
'just now', etc
"""
from datetime import datetime
now = datetime.now()
if type(time) is int:
diff = now - datetime.fromtimestamp(time)
elif isinstance(time,datetime):
diff = now - time
elif not time:
diff = now - now
second_diff = diff.seconds
day_diff = diff.days
if day_diff < 0:
return ''
if day_diff == 0:
if second_diff < 10:
return "just now"
if second_diff < 60:
return str(second_diff) + " seconds ago"
if second_diff < 120:
return "a minute ago"
if second_diff < 3600:
return str( second_diff / 60 ) + " minutes ago"
if second_diff < 7200:
return "an hour ago"
if second_diff < 86400:
return str( second_diff / 3600 ) + " hours ago"
if day_diff == 1:
return "Yesterday"
if day_diff <= 7:
return str(day_diff) + " days ago"
if day_diff <= 31:
return str(day_diff/7) + " weeks ago"
if day_diff <= 365:
return str(day_diff/30) + " months ago"
return str(day_diff/365) + " years ago"

View File

@@ -0,0 +1,61 @@
import datetime
from application import app
from application import db
from application.helpers import pretty_date
from users import User
from sqlalchemy.ext.associationproxy import association_proxy
class Application(db.Model):
__table_args__ = {'schema': 'blender-bfct'}
id = db.Column(db.Integer(), primary_key=True)
blender_id = db.Column(db.Integer(), db.ForeignKey(User.id), nullable=False)
user = db.relationship('User')
network_profile = db.Column(db.String(255))
website = db.Column(db.String(255))
city_country = db.Column(db.String(255))
teaching = db.Column(db.Boolean())
skills = db.relationship('Skill', secondary='skills_applications',
backref=db.backref('applications', lazy='dynamic'))
video_example = db.Column(db.String(255))
written_example = db.Column(db.String(255))
portfolio_cv = db.Column(db.String(255))
bio_message = db.Column(db.Text())
submission_date = db.Column(db.DateTime(), default=datetime.datetime.now)
status = db.Column(db.String(255))
approve = db.Column(db.Integer(), default=0)
reject = db.Column(db.Integer(), default=0)
review_start_date = db.Column(db.DateTime())
review_end_date = db.Column(db.DateTime())
renewal_date = db.Column(db.DateTime())
def show_pretty_date(self, stage_date):
if stage_date == 'submission':
return pretty_date(self.submission_date)
elif stage_date == 'review_start':
return pretty_date(self.review_start_date)
elif stage_date == 'review_end':
return pretty_date(self.review_end_date)
else:
return '--'
class Skill(db.Model):
id = db.Column(db.Integer(), primary_key=True)
name = db.Column(db.String(80), unique=True)
description = db.Column(db.String(255))
skills_applications = db.Table('skills_applications',
db.Column('application_id', db.Integer(), db.ForeignKey(Application.id)),
db.Column('skill_id', db.Integer(), db.ForeignKey('skill.id')))
class ReviewersApplications(db.Model):
id = db.Column(db.Integer(), primary_key=True)
application_id = db.Column(db.Integer(), db.ForeignKey(Application.id), nullable=False)
application = db.relationship('Application')
reviewer_blender_id = db.Column(db.Integer(), db.ForeignKey(User.id), nullable=False)
reviewer = db.relationship('User')
approved = db.Column(db.Boolean(), nullable=False)

View File

@@ -0,0 +1,35 @@
from flask.ext.security import Security, SQLAlchemyUserDatastore, \
UserMixin, RoleMixin
from application import app
from application import db
class User(db.Model, UserMixin):
__bind_key__ = 'users'
__table_args__ = {'schema': 'blender-id'}
id = db.Column(db.Integer(), primary_key=True)
first_name = db.Column(db.String(255))
last_name = db.Column(db.String(255))
email = db.Column(db.String(255), unique=True)
password = db.Column(db.String(255))
active = db.Column(db.Boolean())
confirmed_at = db.Column(db.DateTime())
roles = db.relationship('Role', secondary='roles_users',
backref=db.backref('users', lazy='dynamic'))
class Role(db.Model, RoleMixin):
__bind_key__ = 'users'
id = db.Column(db.Integer(), primary_key=True)
name = db.Column(db.String(80), unique=True)
description = db.Column(db.String(255))
# Define models
roles_users = db.Table('roles_users',
db.Column('user_id', db.Integer(), db.ForeignKey(User.id)),
db.Column('role_id', db.Integer(), db.ForeignKey('role.id')),
info={'bind_key': 'users'})
# Setup Flask-Security
user_datastore = SQLAlchemyUserDatastore(db, User, Role)
security = Security(app, user_datastore)

View File

@@ -0,0 +1,14 @@
{% extends 'admin/base.html' %}
{% block access_control %}
{% if current_user.is_authenticated() %}
<div class="btn-group pull-right">
<a class="btn dropdown-toggle" data-toggle="dropdown" href="#">
<i class="icon-user"></i> {{ current_user.login }} <span class="caret"></span>
</a>
<ul class="dropdown-menu">
<li><a href="{{ url_for('admin.logout_view') }}">Log out</a></li>
</ul>
</div>
{% endif %}
{% endblock %}

View File

@@ -0,0 +1,33 @@
{% extends 'layout.html' %}
{% block body %}
<div class="row">
<div class="col-md-12">
<table class="table">
<thead>
<tr>
<th>Applicant</th>
<th>Country</th>
<th>Date</th>
<th>Status</th>
<th>Approvals</th>
<th></th>
</tr>
</thead>
<tbody>
{% for application in applications %}
<tr>
<td>{{application.user.first_name}} {{application.user.last_name}}</td>
<td>{{application.city_country}}</td>
<td>{{application.pretty_submission_date}}</td>
<td>{{application.status}}</td>
<td>{{application.approve}} / {{application.approve + application.reject}}</td>
<td><a href="{{url_for('applications.view', id=application.id)}}" class="btn btn-default btn-xs">View</a></td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
{% endblock %}

View File

@@ -0,0 +1,77 @@
{% extends 'layout.html' %}
{% block body %}
<div class="row">
<div class="col-md-6">
<h2>{{application.user.first_name}} {{application.user.last_name}} <small>{{application.city_country}}</small></h2>
<p>{{application.bio_message}}</p>
<ul>
<li><a href="{{application.website}}">{{application.website}}</a></li>
<li><a href="{{application.video_example}}">{{application.video_example}}</a></li>
<li><a href="{{application.written_example}}">{{application.written_example}}</a></li>
<li><a href="{{application.portfolio_cv}}">{{application.portfolio_cv}}</a></li>
</ul>
</div>
<div class="col-md-6">
<h2>Status: {{application.status}}</h2>
<div class="row">
<div class="col-md-12">
<table class="table">
<thead>
<tr>
<th>Reviewer</th>
<th>Vote</th>
</tr>
</thead>
<tbody>
{% for review in reviews%}
<tr>
<td>
{% if review.reviewer.id == current_user.id %}
<strong>You</strong>
{% endif %}
{{review.reviewer.first_name}} {{review.reviewer.last_name}}
</td>
<td>
{% if review.approved %}
{% endif %}
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
<div class="row">
{% if not review %}
<div class="col-md-6">
<a href="{{url_for('applications.vote', approved=1, id=application.id)}}" class="btn btn-default">Approve</a>
</div>
<div class="col-md-6">
<a href="{{url_for('applications.vote', approved=0, id=application.id)}}" class="btn btn-default">Reject</a>
</div>
{% else %}
<div class="col-md-6">
<p>You {% if review.approved %} approved {% else %} rejected {% endif %} this candidate.</p>
</div>
{% endif %}
</div>
<div class="row">
{% if current_user.has_role('admin') %}
{% if not application.review_end_date %}
<div class="col-md-6">
<a href="{{url_for('applications.final_review', approved=1, id=application.id)}}" class="btn btn-default">Final Approve</a>
</div>
<div class="col-md-6">
<a href="{{url_for('applications.final_review', approved=0, id=application.id)}}" class="btn btn-default">Final Reject</a>
</div>
{% endif %}
{% endif %}
</div>
</div>
</div>
{% endblock %}

View File

@@ -0,0 +1,77 @@
{% extends 'layout.html' %}
{% block body %}
<div class="row">
<div class="col-md-6">
<form class="form-horizontal" method="POST" action="{{url_for('apply')}}">
<fieldset>
{{ form.hidden_tag() }}
<label class="control-label">
<div class="input-group input-group-lg">
<span class="input-group-addon">Blender Network Profile link</span>
{{ form.network_profile(class='form-control', placeholder='First Name') }}
</div>
</label>
<label class="control-label">
<div class="input-group input-group-lg">
<span class="input-group-addon">Website</span>
{{ form.website(class='form-control', placeholder='Your website') }}
</div>
</label>
<label class="control-label">
<div class="input-group input-group-lg">
<span class="input-group-addon">City and Country</span>
{{ form.city_country(class='form-control', placeholder='Your city and country') }}
</div>
</label>
<label class="control-label">
<div class="input-group input-group-lg">
<span class="input-group-addon">Teaching at university</span>
{{ form.teaching(class='form-control') }}
</div>
</label>
<label class="control-label">
<div class="input-group input-group-lg">
<span class="input-group-addon">Areas of expertise</span>
{{ form.skills(class='form-control') }}
</div>
</label>
<label class="control-label">
<div class="input-group input-group-lg">
<span class="input-group-addon">Video Example</span>
{{ form.video_example(class='form-control', placeholder='Video Example') }}
</div>
</label>
<label class="control-label">
<div class="input-group input-group-lg">
<span class="input-group-addon">Written Example</span>
{{ form.written_example(class='form-control', placeholder='Written Example') }}
</div>
</label>
<label class="control-label">
<div class="input-group input-group-lg">
<span class="input-group-addon">Portfolio and CV</span>
{{ form.portfolio_cv(class='form-control', placeholder='Porftolio and CV') }}
</div>
</label>
<label class="control-label">
<div class="input-group input-group-lg">
<span class="input-group-addon">Bio and message</span>
{{ form.bio_message(class='form-control', placeholder='Bio and message') }}
</div>
</label>
<input class="btn btn-default btn-block" type="submit" value="Go">
</fieldset>
</form>
</div>
</div>
{% endblock %}

View File

@@ -0,0 +1,18 @@
{% extends 'layout.html' %}
{% block body %}
<div class="row">
<div class="col-md-12">
<h2>BFCT program</h2>
<p>BFCT is the Blender Foundations official certified trainers program.This membership is renewed annually, along with updated knowledge on new topics Blender might cover.</p>
<p>The goals of the BFCT program are:</p>
<ul>
<li>Provide a standard for Certification for everyone who is interested in teaching Blender professionally</li>
<li>Help experienced Blender artists and developers to get into training business</li>
<li>Increase the quality and quantity of Blender training worldwide</li>
</ul>
</div>
</div>
{% endblock %}

View File

@@ -0,0 +1,104 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Blender Cloud</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="description" content="">
<meta name="author" content="">
<!-- <link href='http://fonts.googleapis.com/css?family=Open+Sans:400,700,300' rel='stylesheet' type='text/css'> -->
<link href="//maxcdn.bootstrapcdn.com/bootstrap/3.1.1/css/bootstrap.min.css" rel="stylesheet">
<!-- <link rel="stylesheet" type="text/css" href="http://ajax.aspnetcdn.com/ajax/jquery.dataTables/1.9.4/css/jquery.dataTables.css"> -->
<!-- IE6-8 support of HTML5 elements -->
<!--[if lt IE 9]>
<script src="http://html5shim.googlecode.com/svn/trunk/html5.js"></script>
<![endif]-->
<!-- Le fav and touch icons -->
<link rel="shortcut icon" href="{{ url_for('static', filename='ico/favicon.ico') }}">
<link rel="apple-touch-icon-precomposed" sizes="144x144" href="/apple-touch-icon-144-precomposed.png">
<link rel="apple-touch-icon-precomposed" sizes="114x114" href="/apple-touch-icon-114-precomposed.png">
<link rel="apple-touch-icon-precomposed" sizes="72x72" href="/apple-touch-icon-72-precomposed.png">
<link rel="apple-touch-icon-precomposed" href="apple-touch-icon-57-precomposed.png">
</head>
<body>
{% block modal %}
{% endblock %}
<header class="navbar navbar-default navbar-static-top" role="navigation">
<div class="container">
<!-- Brand and toggle get grouped for better mobile display -->
<div class="navbar-header">
<button type="button" class="navbar-toggle" data-toggle="collapse" data-target="#bs-example-navbar-collapse-1">
<span class="sr-only">Toggle navigation</span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
<a class="navbar-brand" href="{{ url_for('homepage') }}">blender-BFCT</a>
<ul class="nav navbar-nav">
<li><a href="http://blendernetwork.org/BFCT">Certified Trainers</a></li>
<li><a href="http://www.blender.org/certification/become-a-trainer/">Become a Trainer</a></li>
{% if current_user.has_role('admin') %}
<li><a href="{{url_for('applications.index')}}">Review Applications</a></li>
{% endif %}
</ul>
</div>
<!-- Collect the nav links, forms, and other content for toggling -->
<nav class="collapse navbar-collapse" id="bs-example-navbar-collapse-1">
<ul class="nav navbar-nav navbar-right">
{% if current_user.is_authenticated() %}
<li class="dropdown">
<a href="#" class="dropdown-toggle" data-toggle="dropdown">{{current_user.email}} <b class="caret"></b></a>
<ul class="dropdown-menu">
<li {% if title == 'home': %} class="active"{% endif %}>
<a href="{{ url_for('homepage') }}">Home</a>
</li>
{% if not current_user.has_role('bfct') %}
<li><a href="{{url_for('apply')}}">Apply/View application</a></li>
{% endif %}
<li class="divider"></li>
<li><a href="{{url_for_security('logout')}}">Log out</a></li>
</ul>
</li>
{% else %}
<li><a href="{{url_for_security('login')}}">Log in</a></li>
{% endif %}
</ul>
</nav><!-- /.navbar-collapse -->
</div>
</header>
<div class="container">
<div class="row">
<div class="col-md-12">
{% for message in get_flashed_messages() %}
<div class="alert alert-warning">
<button type="button" class="close" data-dismiss="alert">&times;</button>
{{ message }}
</div>
{% endfor %}
{% block body %}{% endblock %}
</div>
</div>
<hr>
<footer>
<p>Blender-BFCT is part of the blender.org project</p>
</footer>
</div> <!-- /container -->
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.11.0/jquery.min.js"></script>
<script src="//maxcdn.bootstrapcdn.com/bootstrap/3.1.1/js/bootstrap.min.js"></script>
{% block footer_scripts %}{% endblock %}
</body>
</html>

View File

@@ -0,0 +1,36 @@
{% extends 'layout.html' %}
{% block body %}
<div class="row">
<div class="col-md-12">
<h2>Your BFCT status dashboard</h2>
<table class="table">
<thead>
<tr>
<th>Date</th>
<th>Stage</th>
</tr>
</thead>
<tbody>
<tr>
<td>{{application.show_pretty_date('submission')}}</td>
<td>You submitted your application</td>
</tr>
{% if application.show_pretty_date('review_start') %}
<tr>
<td>{{application.show_pretty_date('review_start')}}</td>
<td>Application review started</td>
</tr>
{% endif%}
{% if application.show_pretty_date('review_end') %}
<tr>
<td>{{application.show_pretty_date('review_end')}}</td>
<td>Application review ended</td>
</tr>
{% endif%}
</tbody>
</table>
</div>
</div>
{% endblock %}

View File

@@ -0,0 +1,46 @@
class Config(object):
DEBUG=False
# Configured for GMAIL
MAIL_SERVER = 'smtp.gmail.com'
MAIL_PORT = 465
MAIL_USE_SSL = True
MAIL_USERNAME = ''
MAIL_PASSWORD = ''
DEFAULT_MAIL_SENDER = ''
# Flask-Security setup
SECURITY_LOGIN_WITHOUT_CONFIRMATION = True
SECURITY_REGISTERABLE = True
SECURITY_RECOVERABLE = True
SECURITY_CHANGEABLE = True
SECUIRTY_POST_LOGIN = '/'
SECURITY_PASSWORD_HASH = 'bcrypt'
SECURITY_PASSWORD_SALT = 'YOURSALT'
SECURITY_EMAIL_SENDER = ''
SECURITY_POST_REGISTER_VIEW = '/welcome'
GOOGLE_ANALYTICS_TRACKING_ID = ''
GOOGLE_ANALYTICS_DOMAIN = ''
ADMIN_EMAIL = ['']
CDN_USE_URL_SIGNING = False
CDN_SERVICE_DOMAIN_PROTOCOL = 'http'
CDN_SERVICE_DOMAIN = ''
CDN_CONTENT_SUBFOLDER = ''
CDN_URL_SIGNING_KEY = ''
class Development(Config):
SECRET_KEY='YOURSECRET'
SERVER_NAME='cloud.blender.local'
DEBUG=True
SQLALCHEMY_DATABASE_URI='mysql://root:root@localhost/blender-bfct'
SQLALCHEMY_BINDS = {
'users': 'mysql://root:root@localhost/blender-id',
}
SECURITY_REGISTERABLE=True
SECURITY_LOGIN_USER_TEMPLATE = 'security/login_user.html'
ASSETS_DEBUG = False
CACHE_TYPE = ''
CACHE_DEFAULT_TIMEOUT = 60
CACHE_DIR = ''

View File

@@ -0,0 +1,5 @@
from application import app
from application import db
db.create_all(bind=['users'])
db.create_all(bind=None)
app.run()

24
requirements.txt Normal file
View File

@@ -0,0 +1,24 @@
Flask==0.10.1
Flask-Login==0.2.9
Flask-Mail==0.9.0
Flask-OAuth==0.12
Flask-Principal==0.4.0
Flask-SQLAlchemy==1.0
Flask-Security==1.7.1
Flask-Social==1.6.2
Flask-WTF==0.9.4
Jinja2==2.7.2
MarkupSafe==0.18
MySQL-python==1.2.5
SQLAlchemy==0.9.1
WTForms==1.0.5
Werkzeug==0.9.4
blinker==1.3
braintree==2.29.1
httplib2==0.8
itsdangerous==0.23
oauth2==1.5.211
passlib==1.6.2
py-bcrypt==0.4
requests==2.3.0
wsgiref==0.1.2