1
0
forked from finn/site

mgt c6 checkpoint 2.2

This commit is contained in:
finn 2024-08-03 04:59:05 -07:00
parent cdebe081c3
commit 2b122f6ab2
11 changed files with 100 additions and 55 deletions

View File

@ -28,6 +28,13 @@ flask db upgrade
flask db downgrade [base] flask db downgrade [base]
flask db upgrade flask db upgrade
full reset?
rm app.db
rm -r migrations
flask db init
flask db migrate
flask db upgrade
``` ```
## build notes: ## build notes:

View File

@ -1,6 +1,6 @@
from flask_wtf import FlaskForm from flask_wtf import FlaskForm
from wtforms import StringField, PasswordField, BooleanField, SubmitField from wtforms import StringField, PasswordField, BooleanField, SubmitField, TextAreaField
from wtforms.validators import DataRequired, ValidationError, Email, EqualTo from wtforms.validators import DataRequired, ValidationError, Email, EqualTo, Length
import sqlalchemy as sa import sqlalchemy as sa
from app import db from app import db
from app.models import User from app.models import User
@ -27,3 +27,8 @@ class RegistrationForm(FlaskForm):
user = db.session.scalar(sa.select(User).where(User.email == email.data)) user = db.session.scalar(sa.select(User).where(User.email == email.data))
if user is not None: if user is not None:
raise ValidationError('Please use a different email address.') raise ValidationError('Please use a different email address.')
class EditProfileForm(FlaskForm):
username = StringField('Username', validators=[DataRequired()])
about_me = TextAreaField('About me', validators=[Length(min=0, max=140)])
submit = SubmitField('Update')

View File

@ -15,6 +15,8 @@ class User(UserMixin, db.Model):
email: so.Mapped[str] = so.mapped_column(sa.String(120), index=True, unique=True) email: so.Mapped[str] = so.mapped_column(sa.String(120), index=True, unique=True)
password_hash: so.Mapped[Optional[str]] = so.mapped_column(sa.String(256)) password_hash: so.Mapped[Optional[str]] = so.mapped_column(sa.String(256))
posts: so.WriteOnlyMapped['Post'] = so.relationship(back_populates='author') posts: so.WriteOnlyMapped['Post'] = so.relationship(back_populates='author')
about_me: so.Mapped[Optional[str]] = so.mapped_column(sa.String(140))
last_seen: so.Mapped[Optional[datetime]] = so.mapped_column(default=lambda: datetime.now(timezone.utc))
def set_password(self, password): def set_password(self, password):
self.password_hash = generate_password_hash(password) self.password_hash = generate_password_hash(password)
@ -41,7 +43,7 @@ class User(UserMixin, db.Model):
pngfile.close() pngfile.close()
else: else:
return str(base64.b64encode(pngicon))[2:-1] return str(base64.b64encode(pngicon))[2:-1]
def avatar(self): def avatar_path(self):
digest = hashlib.md5(self.email.lower().encode('utf-8')).hexdigest() digest = hashlib.md5(self.email.lower().encode('utf-8')).hexdigest()
basedir = os.path.abspath(os.path.dirname(__file__)) basedir = os.path.abspath(os.path.dirname(__file__))
pngloc = os.path.join(basedir, 'usercontent', 'identicon', digest + '.png') pngloc = os.path.join(basedir, 'usercontent', 'identicon', digest + '.png')

View File

@ -1,9 +1,11 @@
from flask import render_template, flash, redirect, url_for, request from flask import render_template, flash, redirect, url_for, request
from urllib.parse import urlsplit from urllib.parse import urlsplit
from datetime import datetime, timezone
from app import app, db from app import app, db
from app.forms import LoginForm, RegistrationForm from app.forms import LoginForm, RegistrationForm, EditProfileForm
from flask_login import current_user, login_user, logout_user, login_required
import sqlalchemy as sa import sqlalchemy as sa
from flask_login import current_user, login_user, logout_user, login_required
from app.models import User from app.models import User
@app.route('/') @app.route('/')
@ -25,6 +27,12 @@ def index():
#return posts; #return posts;
return render_template('index.html', title='Home', posts=posts) return render_template('index.html', title='Home', posts=posts)
@app.before_request
def before_request():
if current_user.is_authenticated:
current_user.last_seen = datetime.now(timezone.utc)
db.session.commit()
@app.route('/login', methods=['GET', 'POST']) @app.route('/login', methods=['GET', 'POST'])
def login(): def login():
if current_user.is_authenticated: if current_user.is_authenticated:
@ -57,7 +65,7 @@ def register():
user.set_password(form.password.data) user.set_password(form.password.data)
db.session.add(user) db.session.add(user)
db.session.commit() db.session.commit()
user.gen_avatar() #user.gen_avatar()
flash('User has been created.') flash('User has been created.')
return redirect(url_for('login')) return redirect(url_for('login'))
return render_template('register.html', title='Register', form=form) return render_template('register.html', title='Register', form=form)
@ -71,3 +79,19 @@ def user(username):
{'author': user, 'body': 'Test2?'} {'author': user, 'body': 'Test2?'}
] ]
return render_template('user.html', user=user, posts=posts) return render_template('user.html', user=user, posts=posts)
@app.route('/edit_profile', methods=['GET', 'POST'])
@login_required
def edit_profile():
form = EditProfileForm()
if form.validate_on_submit():
current_user.username = form.username.data
current_user.about_me = form.about_me.data
db.session.commit()
flash('Profile changes have been saved.')
return redirect(url_for('edit_profile'))
elif request.method == 'GET':
form.username.data = current_user.username
form.about_me.data = current_user.about_me()
return render_template('edit_profile.html', title='Edit Profile', form=form)

View File

@ -0,0 +1,24 @@
{% extends "base.html" %}
{% block content %}
<h1>Edit Profile</h1>
<form action="" method="post">
{{ form.hidden_tag() }}
<p>
{{ form.username.label }}
{{ form.username(size=32) }}
{% for error in form.username.errors %}
<span style="color: red;">[{{ error }}]</span>
{% endfor %}
</p>
<p>
{{ form.about_me.label }}
{{ form.about_me(cols=50, rows=4) }}
{% for error in form.about_me.errors %}
<span style="color: red;">[{{ error }}]</span>
{% endfor %}
</p>
<p>{{ form.submit }}</p>
</form>
{% endblock %}

View File

@ -4,9 +4,16 @@
<table> <table>
<tr valign="top"> <tr valign="top">
<td><img src="data:image/png;base64,{{ user.gen_avatar(write_png=False) }}"></td> <td><img src="data:image/png;base64,{{ user.gen_avatar(write_png=False) }}"></td>
<td><h1>User: {{ user.username }}</h1></td> <td>
<h1>User: {{ user.username }}</h1>
{% if user.about_me %}<p>{{ user.about_me }}</p>{% endif %}
{% if user.last_seen %}<p>Last activity:{{ user.last_seen }}</p>{% endif %}
</td>
</tr> </tr>
</table> </table>
{% if user == current_user() %}
<p><a href="{{ url_for('edit_profile') }}">Edit Profile</a></p>
{% endif %}
<hr> <hr>
{% for post in posts %} {% for post in posts %}
{% include '_post.html' %} {% include '_post.html' %}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 384 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 397 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 378 B

View File

@ -1,8 +1,8 @@
"""posts table """empty message
Revision ID: 938ae2fee021 Revision ID: 1a0e4f823e90
Revises: e42cf202d424 Revises:
Create Date: 2024-08-01 06:11:07.414657 Create Date: 2024-08-03 04:56:53.822820
""" """
from alembic import op from alembic import op
@ -10,14 +10,27 @@ import sqlalchemy as sa
# revision identifiers, used by Alembic. # revision identifiers, used by Alembic.
revision = '938ae2fee021' revision = '1a0e4f823e90'
down_revision = 'e42cf202d424' down_revision = None
branch_labels = None branch_labels = None
depends_on = None depends_on = None
def upgrade(): def upgrade():
# ### commands auto generated by Alembic - please adjust! ### # ### commands auto generated by Alembic - please adjust! ###
op.create_table('user',
sa.Column('id', sa.Integer(), nullable=False),
sa.Column('username', sa.String(length=64), nullable=False),
sa.Column('email', sa.String(length=120), nullable=False),
sa.Column('password_hash', sa.String(length=256), nullable=True),
sa.Column('about_me', sa.String(length=140), nullable=True),
sa.Column('last_seen', sa.DateTime(), nullable=True),
sa.PrimaryKeyConstraint('id')
)
with op.batch_alter_table('user', schema=None) as batch_op:
batch_op.create_index(batch_op.f('ix_user_email'), ['email'], unique=True)
batch_op.create_index(batch_op.f('ix_user_username'), ['username'], unique=True)
op.create_table('post', op.create_table('post',
sa.Column('id', sa.Integer(), nullable=False), sa.Column('id', sa.Integer(), nullable=False),
sa.Column('body', sa.String(length=140), nullable=False), sa.Column('body', sa.String(length=140), nullable=False),
@ -40,4 +53,9 @@ def downgrade():
batch_op.drop_index(batch_op.f('ix_post_timestamp')) batch_op.drop_index(batch_op.f('ix_post_timestamp'))
op.drop_table('post') op.drop_table('post')
with op.batch_alter_table('user', schema=None) as batch_op:
batch_op.drop_index(batch_op.f('ix_user_username'))
batch_op.drop_index(batch_op.f('ix_user_email'))
op.drop_table('user')
# ### end Alembic commands ### # ### end Alembic commands ###

View File

@ -1,42 +0,0 @@
"""users table
Revision ID: e42cf202d424
Revises:
Create Date: 2024-08-01 05:46:32.176166
"""
from alembic import op
import sqlalchemy as sa
# revision identifiers, used by Alembic.
revision = 'e42cf202d424'
down_revision = None
branch_labels = None
depends_on = None
def upgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.create_table('user',
sa.Column('id', sa.Integer(), nullable=False),
sa.Column('username', sa.String(length=64), nullable=False),
sa.Column('email', sa.String(length=120), nullable=False),
sa.Column('password_hash', sa.String(length=256), nullable=True),
sa.PrimaryKeyConstraint('id')
)
with op.batch_alter_table('user', schema=None) as batch_op:
batch_op.create_index(batch_op.f('ix_user_email'), ['email'], unique=True)
batch_op.create_index(batch_op.f('ix_user_username'), ['username'], unique=True)
# ### end Alembic commands ###
def downgrade():
# ### commands auto generated by Alembic - please adjust! ###
with op.batch_alter_table('user', schema=None) as batch_op:
batch_op.drop_index(batch_op.f('ix_user_username'))
batch_op.drop_index(batch_op.f('ix_user_email'))
op.drop_table('user')
# ### end Alembic commands ###