Skip to content

Commit 07d905d

Browse files
committed
Automatic release of teaching material.
1 parent 0854970 commit 07d905d

16 files changed

+28955
-1
lines changed

README.adoc

+1-1
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ This page contains information on the schedule, material, exam, etc. for the ele
1414

1515
=== Schedule
1616

17-
Lectures are on Fridays from 10:00 to 12:00 in Auditorium 2 (0A35) and exercises are on Fridays from 12:00 to 14:00 in rooms 5A14-16.
17+
Lectures are on Fridays from 10:00 to 12:00 in Auditorium 2 (0A35) and exercises are on Fridays from 12:00 to 14:00 in rooms 2A52, 2A54.
1818

1919
*OBS*: We expect you to be present during the entire time, not only the lecture hours.
2020

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

0 commit comments

Comments
 (0)