Skip to content

Commit e72b755

Browse files
committed
Added Dockerfile
1 parent a6650b8 commit e72b755

15 files changed

+803
-2
lines changed

Dockerfile

+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
FROM python:3.6.4-alpine3.6
2+
3+
ENV FLASK_APP=minitwit
4+
5+
COPY . /app
6+
7+
WORKDIR /app
8+
9+
RUN pip install --editable .
10+
11+
RUN flask initdb
12+
13+
# Unit tests
14+
# python setup.py test
15+
16+
EXPOSE 5000
17+
18+
CMD [ "flask", "run", "--host=0.0.0.0" ]
19+
20+
21+

MANIFEST.in

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
graft minitwit/templates
2+
graft minitwit/static
3+
include minitwit/schema.sql

README

+39
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
2+
/ MiniTwit /
3+
4+
because writing todo lists is not fun
5+
6+
7+
~ What is MiniTwit?
8+
9+
A SQLite and Flask powered twitter clone
10+
11+
~ How do I use it?
12+
13+
1. edit the configuration in the minitwit.py file or
14+
export an MINITWIT_SETTINGS environment variable
15+
pointing to a configuration file.
16+
17+
2. install the app from the root of the project directory
18+
19+
pip install --editable .
20+
21+
3. tell flask about the right application:
22+
23+
export FLASK_APP=minitwit
24+
25+
4. fire up a shell and run this:
26+
27+
flask initdb
28+
29+
5. now you can run minitwit:
30+
31+
flask run
32+
33+
the application will greet you on
34+
http://localhost:5000/
35+
36+
~ Is it tested?
37+
38+
You betcha. Run the `python setup.py test` file to
39+
see the tests pass.

README.md

-2
This file was deleted.

minitwit/__init__.py

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
from .minitwit import app

minitwit/minitwit.py

+256
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,256 @@
1+
# -*- coding: utf-8 -*-
2+
"""
3+
MiniTwit
4+
~~~~~~~~
5+
6+
A microblogging application written with Flask and sqlite3.
7+
8+
:copyright: © 2010 by the Pallets team.
9+
:license: BSD, see LICENSE for more details.
10+
"""
11+
12+
import time
13+
from sqlite3 import dbapi2 as sqlite3
14+
from hashlib import md5
15+
from datetime import datetime
16+
from flask import Flask, request, session, url_for, redirect, \
17+
render_template, abort, g, flash, _app_ctx_stack
18+
from werkzeug import check_password_hash, generate_password_hash
19+
20+
21+
# configuration
22+
DATABASE = '/tmp/minitwit.db'
23+
PER_PAGE = 30
24+
DEBUG = True
25+
SECRET_KEY = b'_5#y2L"F4Q8z\n\xec]/'
26+
27+
# create our little application :)
28+
app = Flask('minitwit')
29+
app.config.from_object(__name__)
30+
app.config.from_envvar('MINITWIT_SETTINGS', silent=True)
31+
32+
33+
def get_db():
34+
"""Opens a new database connection if there is none yet for the
35+
current application context.
36+
"""
37+
top = _app_ctx_stack.top
38+
if not hasattr(top, 'sqlite_db'):
39+
top.sqlite_db = sqlite3.connect(app.config['DATABASE'])
40+
top.sqlite_db.row_factory = sqlite3.Row
41+
return top.sqlite_db
42+
43+
44+
@app.teardown_appcontext
45+
def close_database(exception):
46+
"""Closes the database again at the end of the request."""
47+
top = _app_ctx_stack.top
48+
if hasattr(top, 'sqlite_db'):
49+
top.sqlite_db.close()
50+
51+
52+
def init_db():
53+
"""Initializes the database."""
54+
db = get_db()
55+
with app.open_resource('schema.sql', mode='r') as f:
56+
db.cursor().executescript(f.read())
57+
db.commit()
58+
59+
60+
@app.cli.command('initdb')
61+
def initdb_command():
62+
"""Creates the database tables."""
63+
init_db()
64+
print('Initialized the database.')
65+
66+
67+
def query_db(query, args=(), one=False):
68+
"""Queries the database and returns a list of dictionaries."""
69+
cur = get_db().execute(query, args)
70+
rv = cur.fetchall()
71+
return (rv[0] if rv else None) if one else rv
72+
73+
74+
def get_user_id(username):
75+
"""Convenience method to look up the id for a username."""
76+
rv = query_db('select user_id from user where username = ?',
77+
[username], one=True)
78+
return rv[0] if rv else None
79+
80+
81+
def format_datetime(timestamp):
82+
"""Format a timestamp for display."""
83+
return datetime.utcfromtimestamp(timestamp).strftime('%Y-%m-%d @ %H:%M')
84+
85+
86+
def gravatar_url(email, size=80):
87+
"""Return the gravatar image for the given email address."""
88+
return 'https://www.gravatar.com/avatar/%s?d=identicon&s=%d' % \
89+
(md5(email.strip().lower().encode('utf-8')).hexdigest(), size)
90+
91+
92+
@app.before_request
93+
def before_request():
94+
g.user = None
95+
if 'user_id' in session:
96+
g.user = query_db('select * from user where user_id = ?',
97+
[session['user_id']], one=True)
98+
99+
100+
@app.route('/')
101+
def timeline():
102+
"""Shows a users timeline or if no user is logged in it will
103+
redirect to the public timeline. This timeline shows the user's
104+
messages as well as all the messages of followed users.
105+
"""
106+
if not g.user:
107+
return redirect(url_for('public_timeline'))
108+
return render_template('timeline.html', messages=query_db('''
109+
select message.*, user.* from message, user
110+
where message.author_id = user.user_id and (
111+
user.user_id = ? or
112+
user.user_id in (select whom_id from follower
113+
where who_id = ?))
114+
order by message.pub_date desc limit ?''',
115+
[session['user_id'], session['user_id'], PER_PAGE]))
116+
117+
118+
@app.route('/public')
119+
def public_timeline():
120+
"""Displays the latest messages of all users."""
121+
return render_template('timeline.html', messages=query_db('''
122+
select message.*, user.* from message, user
123+
where message.author_id = user.user_id
124+
order by message.pub_date desc limit ?''', [PER_PAGE]))
125+
126+
127+
@app.route('/<username>')
128+
def user_timeline(username):
129+
"""Display's a users tweets."""
130+
profile_user = query_db('select * from user where username = ?',
131+
[username], one=True)
132+
if profile_user is None:
133+
abort(404)
134+
followed = False
135+
if g.user:
136+
followed = query_db('''select 1 from follower where
137+
follower.who_id = ? and follower.whom_id = ?''',
138+
[session['user_id'], profile_user['user_id']],
139+
one=True) is not None
140+
return render_template('timeline.html', messages=query_db('''
141+
select message.*, user.* from message, user where
142+
user.user_id = message.author_id and user.user_id = ?
143+
order by message.pub_date desc limit ?''',
144+
[profile_user['user_id'], PER_PAGE]), followed=followed,
145+
profile_user=profile_user)
146+
147+
148+
@app.route('/<username>/follow')
149+
def follow_user(username):
150+
"""Adds the current user as follower of the given user."""
151+
if not g.user:
152+
abort(401)
153+
whom_id = get_user_id(username)
154+
if whom_id is None:
155+
abort(404)
156+
db = get_db()
157+
db.execute('insert into follower (who_id, whom_id) values (?, ?)',
158+
[session['user_id'], whom_id])
159+
db.commit()
160+
flash('You are now following "%s"' % username)
161+
return redirect(url_for('user_timeline', username=username))
162+
163+
164+
@app.route('/<username>/unfollow')
165+
def unfollow_user(username):
166+
"""Removes the current user as follower of the given user."""
167+
if not g.user:
168+
abort(401)
169+
whom_id = get_user_id(username)
170+
if whom_id is None:
171+
abort(404)
172+
db = get_db()
173+
db.execute('delete from follower where who_id=? and whom_id=?',
174+
[session['user_id'], whom_id])
175+
db.commit()
176+
flash('You are no longer following "%s"' % username)
177+
return redirect(url_for('user_timeline', username=username))
178+
179+
180+
@app.route('/add_message', methods=['POST'])
181+
def add_message():
182+
"""Registers a new message for the user."""
183+
if 'user_id' not in session:
184+
abort(401)
185+
if request.form['text']:
186+
db = get_db()
187+
db.execute('''insert into message (author_id, text, pub_date)
188+
values (?, ?, ?)''', (session['user_id'], request.form['text'],
189+
int(time.time())))
190+
db.commit()
191+
flash('Your message was recorded')
192+
return redirect(url_for('timeline'))
193+
194+
195+
@app.route('/login', methods=['GET', 'POST'])
196+
def login():
197+
"""Logs the user in."""
198+
if g.user:
199+
return redirect(url_for('timeline'))
200+
error = None
201+
if request.method == 'POST':
202+
user = query_db('''select * from user where
203+
username = ?''', [request.form['username']], one=True)
204+
if user is None:
205+
error = 'Invalid username'
206+
elif not check_password_hash(user['pw_hash'],
207+
request.form['password']):
208+
error = 'Invalid password'
209+
else:
210+
flash('You were logged in')
211+
session['user_id'] = user['user_id']
212+
return redirect(url_for('timeline'))
213+
return render_template('login.html', error=error)
214+
215+
216+
@app.route('/register', methods=['GET', 'POST'])
217+
def register():
218+
"""Registers the user."""
219+
if g.user:
220+
return redirect(url_for('timeline'))
221+
error = None
222+
if request.method == 'POST':
223+
if not request.form['username']:
224+
error = 'You have to enter a username'
225+
elif not request.form['email'] or \
226+
'@' not in request.form['email']:
227+
error = 'You have to enter a valid email address'
228+
elif not request.form['password']:
229+
error = 'You have to enter a password'
230+
elif request.form['password'] != request.form['password2']:
231+
error = 'The two passwords do not match'
232+
elif get_user_id(request.form['username']) is not None:
233+
error = 'The username is already taken'
234+
else:
235+
db = get_db()
236+
db.execute('''insert into user (
237+
username, email, pw_hash) values (?, ?, ?)''',
238+
[request.form['username'], request.form['email'],
239+
generate_password_hash(request.form['password'])])
240+
db.commit()
241+
flash('You were successfully registered and can login now')
242+
return redirect(url_for('login'))
243+
return render_template('register.html', error=error)
244+
245+
246+
@app.route('/logout')
247+
def logout():
248+
"""Logs the user out."""
249+
flash('You were logged out')
250+
session.pop('user_id', None)
251+
return redirect(url_for('public_timeline'))
252+
253+
254+
# add some filters to jinja
255+
app.jinja_env.filters['datetimeformat'] = format_datetime
256+
app.jinja_env.filters['gravatar'] = gravatar_url

minitwit/schema.sql

+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
drop table if exists user;
2+
create table user (
3+
user_id integer primary key autoincrement,
4+
username text not null,
5+
email text not null,
6+
pw_hash text not null
7+
);
8+
9+
drop table if exists follower;
10+
create table follower (
11+
who_id integer,
12+
whom_id integer
13+
);
14+
15+
drop table if exists message;
16+
create table message (
17+
message_id integer primary key autoincrement,
18+
author_id integer not null,
19+
text text not null,
20+
pub_date integer
21+
);

0 commit comments

Comments
 (0)