Skip to content

Commit

Permalink
commiting step 4 of tutorial
Browse files Browse the repository at this point in the history
  • Loading branch information
mattmakai committed Mar 27, 2015
1 parent acf4a3c commit e819ea3
Show file tree
Hide file tree
Showing 19 changed files with 250 additions and 3 deletions.
7 changes: 7 additions & 0 deletions cyoa/__init__.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
from flask import Flask
from flask.ext.socketio import SocketIO
from flask.ext.login import LoginManager
from flask.ext.sqlalchemy import SQLAlchemy
import redis

app = Flask(__name__, static_url_path='/static')
Expand All @@ -10,6 +12,11 @@
redis_db = redis.StrictRedis(host=REDIS_SERVER, port=REDIS_PORT, db=REDIS_DB)

socketio = SocketIO(app)
db = SQLAlchemy(app)

login_manager = LoginManager()
login_manager.login_view = 'sign_in'
login_manager.init_app(app)

from . import views
from . import websockets
1 change: 1 addition & 0 deletions cyoa/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
# General Flask app settings
DEBUG = os.environ.get('DEBUG', None)
SECRET_KEY = os.environ.get('SECRET_KEY', None)
SQLALCHEMY_DATABASE_URI = os.environ.get('DATABASE_URL', None)

# Redis connection
REDIS_SERVER = os.environ.get('REDIS_SERVER', None)
Expand Down
23 changes: 23 additions & 0 deletions cyoa/forms.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
from flask.ext.wtf import Form
from wtforms import StringField, PasswordField, BooleanField, SubmitField, \
DateField, IntegerField
from wtforms.validators import Required, Length, Regexp, EqualTo
from wtforms import ValidationError
from .models import Wizard


class LoginForm(Form):
wizard_name = StringField('Wizard Name',
validators=[Required(), Length(1, 32)])
password = PasswordField('Password', validators=[Required(),
Length(1, 32)])

def validate(self):
if not Form.validate(self):
return False
user = Wizard.query.filter_by(wizard_name=self.
wizard_name.data).first()
if user is not None and not user.verify_password(self.password.data):
self.password.errors.append('Incorrect password.')
return False
return True
33 changes: 33 additions & 0 deletions cyoa/models.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
from flask.ext.login import UserMixin
from werkzeug.security import generate_password_hash, check_password_hash

from . import db


class Wizard(UserMixin, db.Model):
"""
Represents a wizard who can access special parts of the application.
"""
__tablename__ = 'wizards'
id = db.Column(db.Integer, primary_key=True)
wizard_name = db.Column(db.String(64), unique=True, index=True)
password_hash = db.Column(db.String(128))

def __init__(self, wizard_name, password):
self.wizard_name = wizard_name
self.password = password

@property
def password(self):
raise AttributeError('password is not readable')

@password.setter
def password(self, password):
self.password_hash = generate_password_hash(password)

def verify_password(self, password):
return check_password_hash(self.password_hash, password)

def __repr__(self):
return '<Wizard %r>' % self.wizard_name

48 changes: 48 additions & 0 deletions cyoa/static/css/signin.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
/* Sticky footer styles
-------------------------------------------------- */
html {
position: relative;
min-height: 100%;
}
body {
/* Margin bottom by footer height */
margin-bottom: 60px;
}
.footer {
position: absolute;
bottom: 0;
width: 100%;
/* Set the fixed height of the footer here */
height: 60px;
background-color: rgba(0,0,0,0.3);
}


/* Custom page CSS
-------------------------------------------------- */
/* Not required for template or sticky footer method. */

.container {
width: auto;
max-width: 680px;
padding: 0 15px;
}
.container .text-muted {
margin: 20px 0;
}


.wizard-bg {
background: url("/static/img/magic.jpg") no-repeat center center fixed;
-webkit-background-size: cover;
background-size: cover;
}

.container.sign-in {
background: rgba(0,0,0,0.8);
border-radius: 5px;
margin-top: 20px;
padding-bottom: 20px;
color: #fff;
}

7 changes: 7 additions & 0 deletions cyoa/static/css/wizard.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
.btn-top-margin {
margin-top: 25px;
}
.admin-field {
text-align: left;
margin-top: 15px;
}
Binary file added cyoa/static/cyoa-tutorial-step-4.tar.gz
Binary file not shown.
Binary file removed cyoa/static/cyoa.tar.gz
Binary file not shown.
Binary file added cyoa/static/img/cyoa-choices.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added cyoa/static/img/cyoa-no-votes.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added cyoa/static/img/djangocon.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added cyoa/static/img/magic.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
25 changes: 25 additions & 0 deletions cyoa/templates/base.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="description" content="">
<meta name="author" content="Matt Makai">
<title>{% block title %}{% endblock %}CYOA Presentations</title>
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.2.0/css/bootstrap.min.css">
<link rel="stylesheet" href="/static/css/wizard.css">
{% block css_head %}{% endblock %}
<!--[if lt IE 9]>
<script src="https://oss.maxcdn.com/html5shiv/3.7.2/html5shiv.min.js">
</script>
<script src="https://oss.maxcdn.com/respond/1.4.2/respond.min.js">
</script>
<![endif]-->
</head>
<body{% block body_class %}{% endblock %}>
{% block nav %}{% endblock %}
{% block content %}{% endblock %}
{% block js_body %}{% endblock %}
</body>
</html>
12 changes: 12 additions & 0 deletions cyoa/templates/partials/_formhelpers.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{% macro render_field(field) %}
<dt style="text-align: left;" class="admin-field">{{ field.label }}
{% if field.errors %}
<ul class="errors">
{% for error in field.errors %}
<li style="color: red;">{{ error }}</li>
{% endfor %}
</ul>
{% endif %}
<dd>{{ field(class_="form-control", **kwargs)|safe }}
</dd>
{% endmacro %}
12 changes: 12 additions & 0 deletions cyoa/templates/wizard/presentations.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{% extends "base.html" %}

{% block content %}
<div class="container">
<div class="row">
<div class="col-md-10">
<h1>Wizards Only Stuff Here</h1>
<a href="{{ url_for('sign_out') }}">Sign out</a>
</div>
</div>
</div>
{% endblock %}
36 changes: 36 additions & 0 deletions cyoa/templates/wizard/sign_in.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
{% extends "base.html" %}

{% block css_head %}
<link href="/static/css/signin.css" rel="stylesheet">
{% endblock %}

{% block body_class %} class="wizard-bg" {% endblock %}

{% block content %}
<div class="container sign-in">
<div class="col-md-12">
<div id="login">
<h1>Wizards Only!</h1>
{% from "partials/_formhelpers.html" import render_field %}
<form method="post" action="{{ url_for('sign_in') }}"
id="login-form">
{{ form.csrf_token }}
{{ render_field(form.wizard_name, required=True) }}
<br>
{{ render_field(form.password, required=True) }}
<div style="margin-top: 10px; text-align: right;">
<input class="btn btn-primary"
type="submit" value="Sign in" />
</div>
</form>
</div>
</div>
</div>
<footer class="footer">
<div class="container">
<p class="text-muted pull-right">
Photo by <a href="http://photos.jdhancock.com/photo/2012-04-16-000233-a-spot-of-magic.html">JD Hancock</a>
</p>
</div>
</footer>
{% endblock %}
36 changes: 35 additions & 1 deletion cyoa/views.py
Original file line number Diff line number Diff line change
@@ -1,15 +1,23 @@
import cgi
from flask import render_template, abort, request
from flask import redirect, url_for
from flask.ext.login import login_user, logout_user, login_required, \
current_user
from jinja2 import TemplateNotFound
from twilio import twiml
from twilio.rest import TwilioRestClient

from .config import TWILIO_NUMBER
from .forms import LoginForm
from .models import Wizard

from . import app, redis_db, socketio
from . import app, redis_db, socketio, login_manager

client = TwilioRestClient()

@login_manager.user_loader
def load_user(userid):
return Wizard.query.get(int(userid))

@app.route('/<presentation_name>/', methods=['GET'])
def landing(presentation_name):
Expand All @@ -31,3 +39,29 @@ def twilio_callback():
resp = twiml.Response()
resp.message("Thanks for your vote!")
return str(resp)


@app.route('/wizard/', methods=['GET', 'POST'])
def sign_in():
form = LoginForm()
if form.validate_on_submit():
wizard = Wizard.query.filter_by(wizard_name=
form.wizard_name.data).first()
if wizard is not None and wizard.verify_password(form.password.data):
login_user(wizard)
return redirect(url_for('wizard_landing'))
return render_template('wizard/sign_in.html', form=form, no_nav=True)


@app.route('/sign-out/')
@login_required
def sign_out():
logout_user()
return redirect(url_for('sign_in'))


@app.route('/wizard/presentations/')
@login_required
def wizard_landing():
return render_template('wizard/presentations.html')

9 changes: 7 additions & 2 deletions manage.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,21 @@
import os
import redis

from cyoa import app, redis_db, socketio
from cyoa import app, redis_db, socketio, db
from cyoa.models import Wizard
from flask.ext.script import Manager, Shell

manager = Manager(app)

def make_shell_context():
return dict(app=app, redis_db=redis_db)
return dict(app=app, redis_db=redis_db, db=db, Wizard=Wizard)

manager.add_command("shell", Shell(make_context=make_shell_context))

@manager.command
def syncdb():
db.create_all()

@manager.command
def runserver():
socketio.run(app, "0.0.0.0", port=5001)
Expand Down
4 changes: 4 additions & 0 deletions requirements.txt
Original file line number Diff line number Diff line change
@@ -1,10 +1,14 @@
Flask==0.10.1
Flask-Script==2.0.5
Flask-SocketIO==0.4.1
Flask-Login==0.2.11
Flask-SQLAlchemy==2.0
Flask-WTF==0.10.3

gunicorn==19.1.1

redis==2.10.3

twilio==3.6.8

psycopg2==2.5.4

0 comments on commit e819ea3

Please sign in to comment.