Skip to content

Commit

Permalink
first commit
Browse files Browse the repository at this point in the history
  • Loading branch information
Ciana Lim authored and Ciana Lim committed Nov 9, 2015
0 parents commit fd39e64
Show file tree
Hide file tree
Showing 27 changed files with 654 additions and 0 deletions.
Binary file added .DS_Store
Binary file not shown.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
__pycache__
33 changes: 33 additions & 0 deletions LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
Copyright (c) 2015 by Armin Ronacher and contributors. See AUTHORS
for more details.

Some rights reserved.

Redistribution and use in source and binary forms of the software as well
as documentation, with or without modification, are permitted provided
that the following conditions are met:

* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.

* Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the following
disclaimer in the documentation and/or other materials provided
with the distribution.

* The names of the contributors may not be used to endorse or
promote products derived from this software without specific
prior written permission.

THIS SOFTWARE AND DOCUMENTATION IS PROVIDED BY THE COPYRIGHT HOLDERS AND
CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT
NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE AND DOCUMENTATION, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
DAMAGE.
1 change: 1 addition & 0 deletions Procfile
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
web: gunicorn csitwit:app --log-file=-
6 changes: 6 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
export MINITWIT_SETTINGS env var or edit minitwit.py

python3 -m flask -a csitwit initdb
python3 csitwit.py

This is a modification of the MiniTwit example from Flask.
Empty file added __init__.py
Empty file.
Binary file added controllers/.DS_Store
Binary file not shown.
Empty file added controllers/__init__.py
Empty file.
33 changes: 33 additions & 0 deletions controllers/follow.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
from helpers import functions
from flask import Flask, request, session, url_for, redirect, \
render_template, abort, g, flash, _app_ctx_stack

def follow_user(username):
"""Adds the current user as follower of the given user."""
if not g.user:
abort(401)
whom_id = functions.get_user_id(username)
if whom_id is None:
abort(404)
db = functions.get_db()
db.execute('insert into follower (who_id, whom_id) values (?, ?)',
[session['user_id'], whom_id])
db.commit()
flash('You are now following "%s"' % username)
return redirect(functions.url_for('/%(username)s', {'username':username}))

def unfollow_user(username):
"""Removes the current user as follower of the given user."""
if not g.user:
abort(401)
whom_id = functions.get_user_id(username)
if whom_id is None:
abort(404)
db = functions.get_db()
db.execute('delete from follower where who_id=? and whom_id=?',
[session['user_id'], whom_id])
db.commit()
flash('You are no longer following "%s"' % username)
return redirect(functions.url_for('/%(username)s', {'username':username}))


48 changes: 48 additions & 0 deletions controllers/timeline.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
from helpers import functions
from flask import Flask, request, session, url_for, redirect, \
render_template, abort, g, flash, _app_ctx_stack

PER_PAGE = 30

def index():
"""Shows a users timeline or if no user is logged in it will
redirect to the public timeline. This timeline shows the user's
messages as well as all the messages of followed users.
"""
if not g.user:
return redirect(functions.url_for('/public'))
query_messages = functions.query_db('''
select message.*, user.* from message, user
where message.author_id = user.user_id and (
user.user_id = ? or
user.user_id in (select whom_id from follower
where who_id = ?))
order by message.pub_date desc limit ?''',
[session['user_id'], session['user_id'], PER_PAGE])
return render_template('timeline.html', messages=query_messages)

def public():
"""Displays the latest messages of all users."""
return render_template('timeline.html', messages=functions.query_db('''
select message.*, user.* from message, user
where message.author_id = user.user_id
order by message.pub_date desc limit ?''', [PER_PAGE]))

def user(username):
"""Display's a users tweets."""
profile_user = functions.query_db('select * from user where username = ?',
[username], one=True)
if profile_user is None:
abort(404)
followed = False
if g.user:
followed = functions.query_db('''select 1 from follower where
follower.who_id = ? and follower.whom_id = ?''',
[session['user_id'], profile_user['user_id']],
one=True) is not None
return render_template('timeline.html', messages=functions.query_db('''
select message.*, user.* from message, user where
user.user_id = message.author_id and user.user_id = ?
order by message.pub_date desc limit ?''',
[profile_user['user_id'], PER_PAGE]), followed=followed,
profile_user=profile_user)
17 changes: 17 additions & 0 deletions controllers/tweet.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
from helpers import functions
from flask import Flask, request, session, url_for, redirect, \
render_template, abort, g, flash, _app_ctx_stack
import time

def add_message():
"""Registers a new message for the user."""
if 'user_id' not in session:
abort(401)
if request.form['text']:
db = functions.get_db()
db.execute('''insert into message (author_id, text, pub_date)
values (?, ?, ?)''', (session['user_id'], request.form['text'],
int(time.time())))
db.commit()
flash('Your message was recorded')
return redirect(functions.url_for('/'))
57 changes: 57 additions & 0 deletions controllers/user.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
from helpers import functions
from flask import Flask, request, session, url_for, redirect, \
render_template, abort, g, flash, _app_ctx_stack
from werkzeug import check_password_hash, generate_password_hash

def login():
"""Logs the user in."""
if g.user:
return redirect(functions.url_for('/'))
error = None
if request.method == 'POST':
user = functions.query_db('''select * from user where
username = ?''', [request.form['username']], one=True)
if user is None:
error = 'Invalid username'
elif not check_password_hash(user['pw_hash'],
request.form['password']):
error = 'Invalid password'
else:
flash('You were logged in')
session['user_id'] = user['user_id']
return redirect(functions.url_for('/'))
return render_template('login.html', error=error)

def register():
"""Registers the user."""
if g.user:
return redirect(functions.url_for('/'))
error = None
if request.method == 'POST':
if not request.form['username']:
error = 'You have to enter a username'
elif not request.form['email'] or \
'@' not in request.form['email']:
error = 'You have to enter a valid email address'
elif not request.form['password']:
error = 'You have to enter a password'
elif request.form['password'] != request.form['password1']:
error = 'The two passwords do not match'
#request.form['password'] is not request.form['password1']
elif functions.get_user_id(request.form['username']) is not None:
error = 'The username is already taken'
else:
db = functions.get_db()
db.execute('''insert into user (username, email, pw_hash) values (?, ?, ?)''',
[request.form['username'], request.form['email'],
generate_password_hash(request.form['password'])])
db.commit()
flash('You were successfully registered and can login now')
return redirect(functions.url_for('login'))
return render_template('register.html', error=error)

def logout():
"""Logs the user out."""
flash('You were logged out')
session.pop('user_id', None)
return redirect(functions.url_for('/public'))
22 changes: 22 additions & 0 deletions csitwit.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
from flask import Flask
from helpers import functions, init
import router

# create our application
app = Flask(__name__)

# configuration
app.config.update(
DEBUG = True,
DATABASE = 'minitwit.db',
SECRET_KEY = 'a unique key'
)

# some initialization functions
init.start(app)

# set our router
router.set_router(app)

if __name__ == "__main__":
app.run(port=5001)
Binary file added helpers/.DS_Store
Binary file not shown.
Empty file added helpers/__init__.py
Empty file.
64 changes: 64 additions & 0 deletions helpers/functions.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
from flask import _app_ctx_stack
from flask import current_app as app
from flask import Flask, request, session, url_for, redirect, \
render_template, abort, g, flash, _app_ctx_stack
from sqlite3 import dbapi2 as sqlite3
from hashlib import md5
from datetime import datetime

def get_db():
"""Opens a new database connection if there is none yet for the
current application context.
"""
top = _app_ctx_stack.top
if not hasattr(top, 'sqlite_db'):
top.sqlite_db = sqlite3.connect(app.config['DATABASE'])
top.sqlite_db.row_factory = sqlite3.Row
return top.sqlite_db

def close_database(exception):
"""Closes the database again at the end of the request."""
top = _app_ctx_stack.top
if hasattr(top, 'sqlite_db'):
top.sqlite_db.close()

def init_db():
"""Initializes the database."""
db = get_db()
with app.open_resource('schema.sql', mode='r') as f:
db.cursor().executescript(f.read())
db.commit()

def initdb_command():
"""Creates the database tables."""
init_db()
print('Initialized the database.')

def query_db(query, args=(), one=False):
"""Queries the database and returns a list of dictionaries."""
cur = get_db().execute(query, args)
rv = cur.fetchall()
return (rv[0] if rv else None) if one else rv

def get_user_id(username):
"""Convenience method to look up the id for a username."""
rv = query_db('select user_id from user where username = ?',
[username], one=True)
return rv[0] if rv else None


def format_datetime(timestamp):
"""Format a timestamp for display."""
return datetime.utcfromtimestamp(timestamp).strftime('%Y-%m-%d @ %H:%M')


def gravatar_url(email, size=80):
"""Return the gravatar image for the given email address."""
return 'http://www.gravatar.com/avatar/%s?d=identicon&s=%d' % \
(md5(email.strip().lower().encode('utf-8')).hexdigest(), size)

def url_for(string, data=None):
if data is not None:
return (string) % data
else:
return string
14 changes: 14 additions & 0 deletions helpers/init.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
from helpers import functions

def start(app):
# add some filters to jinja, to enable us to use it on our template files
app.jinja_env.filters['datetimeformat'] = functions.format_datetime
app.jinja_env.filters['gravatar'] = functions.gravatar_url
app.jinja_env.filters['url_for'] = functions.url_for

# teardown_appcontext closes the database
app.teardown_appcontext(functions.close_database)

# we're adding the cli command to initialize the database
app.cli.command('initdb')(functions.initdb_command)

Binary file added minitwit.db
Binary file not shown.
8 changes: 8 additions & 0 deletions requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
click==5.1
-e git://github.com/mitsuhiko/flask.git#egg=Flask
gunicorn==19.3.0
itsdangerous==0.24
Jinja2==2.8
MarkupSafe==0.23
Werkzeug==0.10.4
wheel==0.24.0
33 changes: 33 additions & 0 deletions router.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
from flask import g, session
from helpers import functions, init
from controllers import follow, timeline, tweet, user

def set_router(app):
# we're setting g.user as the user in the current session
@app.before_request
def before_request():
g.user = None
if 'user_id' in session:
# if there is a user_id in session
# select it and set g.user to it
g.user = functions.query_db('select * from user where user_id = ?',
[session['user_id']], one=True)

### Our Routes ###

# timeline routes
app.route('/')(timeline.index)
app.route('/public')(timeline.public)
app.route('/<username>')(timeline.user)

# follow routes
app.route('/<username>/follow')(follow.follow_user)
app.route('/<username>/unfollow')(follow.unfollow_user)

# tweet routes
app.route('/add_message', methods=['POST'])(tweet.add_message)

# user routes
app.route('/login', methods=['GET', 'POST'])(user.login)
app.route('/register', methods=['GET', 'POST'])(user.register)
app.route('/logout')(user.logout)
23 changes: 23 additions & 0 deletions schema.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
drop table if exists user;
create table user (
user_id integer primary key autoincrement,
username text not null,
email text not null,
pw_hash text not null
);

drop table if exists follower;
create table follower (
who_id integer,
whom_id integer
);

drop table if exists message;
create table message (
message_id integer primary key autoincrement,
author_id integer not null,
text text not null,
pub_date integer
);

INSERT INTO user (username, email, pw_hash) VALUES ('person', '[email protected]', 'pbkdf2:sha1:1000$L1UfsRgW$d780bf74e8432301bbdd361ee5ade12e842820e3')
Loading

0 comments on commit fd39e64

Please sign in to comment.