diff --git a/app/forms.py b/app/forms.py index 072873a..3f4e4f0 100644 --- a/app/forms.py +++ b/app/forms.py @@ -1,7 +1,10 @@ -from flask.ext.wtf import Form, TextField, BooleanField -from flask.ext.wtf import Required +from flask.ext.wtf import Form, TextField, BooleanField, TextAreaField +from flask.ext.wtf import Required, Length class LoginForm(Form): openid = TextField('openid', validators = [Required()]) remember_me = BooleanField('remember_me', default = False) - \ No newline at end of file + +class EditForm(Form): + nickname = TextField('nickname', validators = [Required()]) + about_me = TextAreaField('about_me', validators = [Length(min = 0, max = 140)]) diff --git a/app/models.py b/app/models.py index bcf2b7d..cbd73d5 100644 --- a/app/models.py +++ b/app/models.py @@ -1,3 +1,4 @@ +from hashlib import md5 from app import db ROLE_USER = 0 @@ -9,6 +10,8 @@ class User(db.Model): email = db.Column(db.String(120), index = True, unique = True) role = db.Column(db.SmallInteger, default = ROLE_USER) posts = db.relationship('Post', backref = 'author', lazy = 'dynamic') + about_me = db.Column(db.String(140)) + last_seen = db.Column(db.DateTime) def is_authenticated(self): return True @@ -22,6 +25,9 @@ class User(db.Model): def get_id(self): return unicode(self.id) + def avatar(self, size): + return 'http://www.gravatar.com/avatar/' + md5(self.email).hexdigest() + '?d=mm&s=' + str(size) + def __repr__(self): return '' % (self.nickname) diff --git a/app/templates/base.html b/app/templates/base.html index 1208920..c0f2e7b 100644 --- a/app/templates/base.html +++ b/app/templates/base.html @@ -10,6 +10,7 @@
Microblog: Home {% if g.user.is_authenticated() %} + | Your Profile | Logout {% endif %}
diff --git a/app/templates/edit.html b/app/templates/edit.html new file mode 100644 index 0000000..2f4e252 --- /dev/null +++ b/app/templates/edit.html @@ -0,0 +1,23 @@ + +{% extends "base.html" %} + +{% block content %} +

Edit Your Profile

+
+ {{form.hidden_tag()}} + + + + + + + + + + + + + +
Your nickname:{{form.nickname(size = 24)}}
About yourself:{{form.about_me(cols = 32, rows = 4)}}
+
+{% endblock %} diff --git a/app/templates/post.html b/app/templates/post.html new file mode 100644 index 0000000..84c11ad --- /dev/null +++ b/app/templates/post.html @@ -0,0 +1,5 @@ + + + + +
{{post.author.nickname}} says:
{{post.body}}
diff --git a/app/templates/user.html b/app/templates/user.html new file mode 100644 index 0000000..817afe1 --- /dev/null +++ b/app/templates/user.html @@ -0,0 +1,20 @@ + +{% extends "base.html" %} + +{% block content %} + + + + + +
+

User: {{user.nickname}}

+ {% if user.about_me %}

{{user.about_me}}

{% endif %} + {% if user.last_seen %}

Last seen on: {{user.last_seen}}

{% endif %} + {% if user.id == g.user.id %}

Edit

{% endif %} +
+
+{% for post in posts %} + {% include 'post.html' %} +{% endfor %} +{% endblock %} \ No newline at end of file diff --git a/app/views.py b/app/views.py index 84e9943..d3f1772 100644 --- a/app/views.py +++ b/app/views.py @@ -1,8 +1,9 @@ from flask import render_template, flash, redirect, session, url_for, request, g from flask.ext.login import login_user, logout_user, current_user, login_required from app import app, db, lm, oid -from forms import LoginForm +from forms import LoginForm, EditForm from models import User, ROLE_USER, ROLE_ADMIN +from datetime import datetime @lm.user_loader def load_user(id): @@ -11,6 +12,10 @@ def load_user(id): @app.before_request def before_request(): g.user = current_user + if g.user.is_authenticated(): + g.user.last_seen = datetime.utcnow() + db.session.add(g.user) + db.session.commit() @app.route('/') @app.route('/index') @@ -69,4 +74,37 @@ def after_login(resp): @app.route('/logout') def logout(): logout_user() - return redirect(url_for('index')) \ No newline at end of file + return redirect(url_for('index')) + +@app.route('/user/') +@login_required +def user(nickname): + user = User.query.filter_by(nickname = nickname).first() + if user == None: + flash('User ' + nickname + ' not found.') + return redirect(url_for('index')) + posts = [ + { 'author': user, 'body': 'Test post #1' }, + { 'author': user, 'body': 'Test post #2' } + ] + return render_template('user.html', + user = user, + posts = posts) + +@app.route('/edit', methods = ['GET', 'POST']) +@login_required +def edit(): + form = EditForm() + if form.validate_on_submit(): + g.user.nickname = form.nickname.data + g.user.about_me = form.about_me.data + db.session.add(g.user) + db.session.commit() + flash('Your changes have been saved.') + return redirect(url_for('edit')) + else: + form.nickname.data = g.user.nickname + form.about_me.data = g.user.about_me + return render_template('edit.html', + form = form) + \ No newline at end of file diff --git a/db_repository/versions/003_migration.py b/db_repository/versions/003_migration.py new file mode 100644 index 0000000..6d7b961 --- /dev/null +++ b/db_repository/versions/003_migration.py @@ -0,0 +1,32 @@ +from sqlalchemy import * +from migrate import * + + +from migrate.changeset import schema +pre_meta = MetaData() +post_meta = MetaData() +user = Table('user', post_meta, + Column('id', Integer, primary_key=True, nullable=False), + Column('nickname', String(length=64)), + Column('email', String(length=120)), + Column('role', SmallInteger, default=ColumnDefault(0)), + Column('about_me', String(length=140)), + Column('last_seen', DateTime), +) + + +def upgrade(migrate_engine): + # Upgrade operations go here. Don't create your own engine; bind + # migrate_engine to your metadata + pre_meta.bind = migrate_engine + post_meta.bind = migrate_engine + post_meta.tables['user'].columns['about_me'].create() + post_meta.tables['user'].columns['last_seen'].create() + + +def downgrade(migrate_engine): + # Operations to reverse the above upgrade go here. + pre_meta.bind = migrate_engine + post_meta.bind = migrate_engine + post_meta.tables['user'].columns['about_me'].drop() + post_meta.tables['user'].columns['last_seen'].drop()