Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

AAH2-19 Initialise Video Names, AAH2-24, AAH2-8, AAH2-4, AAH2-14 #12

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion loginapp/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,4 +56,4 @@ def uploadme():
@main.route('/profile')
@login_required
def profile():
return render_template('profile.html', name=current_user.name)
return render_template('profile.html', name=current_user.name)
3 changes: 3 additions & 0 deletions loginapp/templates/upload.html
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,9 @@ <h2>Select file to upload</h2>
<p>
<input type="file" name="file" autocomplete="off" required>
</p>
<p>
<input type="text" name="filename" autocomplete="off" required> <!--Create a variable for the name of the file so that we can flash this in Python-->
</p>
</dl>
<p>
<input type="submit" value="Submit">
Expand Down
File renamed without changes.
File renamed without changes.
File renamed without changes.
3 changes: 3 additions & 0 deletions microblog-project/flask1/.vscode/settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"jira-plugin.workingProject": ""
}
1 change: 1 addition & 0 deletions microblog-project/flask1/CNAME.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
flask.skinetics.tech
File renamed without changes.
20 changes: 20 additions & 0 deletions microblog-project/flask1/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# flask1
Flask web app testing - with user authentication and front end posting

[ParentRepo](https://github.com/acord-robotics/datascience)

List of python libraries:
* Flask
* Flask-wtf
* Flask-migrate
* Flask-sqlalchemy
* Flask-login

E.g.
```
pip install flask
```

# Commit reference guide
* GHC = Github Codespaces
* Venv = `root/microblog` (in root, or main/parent directory) - virtual environment with the above libraries installed
44 changes: 44 additions & 0 deletions microblog-project/flask1/app/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import logging
from logging.handlers import SMTPHandler, RotatingFileHandler
import os
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
from flask_migrate import Migrate
from flask_login import LoginManager
from config import Config
#from app import routes, models, errors

app = Flask(__name__)
app.config.from_object(Config)
db = SQLAlchemy(app)
migrate = Migrate(app, db)
login = LoginManager(app)
login.login_view = 'login'

if not app.debug: # Only enable the email logger when the app is running without debug mode
if app.config['MAIL_SERVER']:
auth = None
if app.config['MAIL_USERNAME'] or app.config['MAIL_PASSWORD']:
auth = (app.config['MAIL_USERNAME'], app/config['MAIL_PASSWORD'])
secure = None
if app.config['MAIL_USE_TLS']:
secure = ()
mail_handler = SMTPHandler(
mailhost=(app.config['MAIL_SERVER'], app.config['MAIL_PORT']),
fromaddr='no-reply@' + app.config['MAIL_SERVER'],
toaddrs=app.config['ADMINS'], subject='Microblog Failure',
credentials=auth, secure=secure)
mail_handler.setLevel(logging.ERROR)
app.logger.addHandler(mail_handler)

if not os.path.exists('logs'):
os.mkdir('logs') # create logs directory if it doesn't exist using os library
file_handler = RotatingFileHandler('logs/microblog.log', maxBytes=10240,
backupCount=10)
file_handler.setFormatter(logging.Formatter(
'%(asctime)s %(levelname)s: %(message)s [in %(pathname)s:%(lineno)d]'))
file_handler.setLevel(logging.INFO)
app.logger.addHandler(file_handler)

app.logger.setLevel(logging.INFO)
app.logger.info('Microblog startup')
11 changes: 11 additions & 0 deletions microblog-project/flask1/app/errors.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
from flask import render_template
from app import app, db

@app.errorhandler(404)
def not_found_error(error):
return render_template('404.html'), 404

@app.errorhandler(500)
def internal_error(error):
db.session.rollback()
return render_template('500.html'), 500
36 changes: 36 additions & 0 deletions microblog-project/flask1/app/forms.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
from flask_wtf import FlaskForm
from wtforms import StringField, PasswordField, BooleanField, SubmitField
from wtforms.validators import DataRequired, Length
from wtforms import StringField, TextAreaField, SubmitField


class LoginForm(FlaskForm):
username = StringField('Username', validators=[DataRequired()])
password = PasswordField('Password', validators=[DataRequired()])
remember_me = BooleanField('Remember Me')
submit = SubmitField('Sign In')

class EditProfileForm(FlaskForm):
username = StringField('Username', validators=[DataRequired()])
about_me = TextAreaField('About me', validators=[Length(min=0, max=140)])
submit = SubmitField('Submit')

def __init__(self, original_username, *args, **kwargs):
super(EditProfileForm, self).__init__(*args, **kwargs)
self.original_username = original_username

def validate_username(self, username):
if username.data != self.original_username:
user = User.query.filter_by(username=self.username.data).first()
if user is not None:
raise ValidationError('Please use a different username')

# Integrating Followers with the Application - Part 8
class EmptyForm(FlaskForm):
submit = SubmitField('Submit')

# allow users to make their own posts
class PostForm(FlaskForm):
post = TextAreaField('Say something', validators=[
DataRequired(), Length(min=1, max=140]) # The form must be completed with content and the length has to be within 1-140 characters
submit = SubmitField('Submit') # button labelled submit that links to db.commit()
76 changes: 76 additions & 0 deletions microblog-project/flask1/app/models.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
from datetime import datetime
from hashlib import md5
from app import db, login
from flask_login import UserMixin
from werkzeug.security import generate_password_hash, check_password_hash


followers = db.Table(
'followers',
db.Column('follower_id', db.Integer, db.ForeignKey('user.id')),
db.Column('followed_id', db.Integer, db.ForeignKey('user.id'))
)


class User(UserMixin, db.Model):
id = db.Column(db.Integer, primary_key=True)
username = db.Column(db.String(64), index=True, unique=True)
email = db.Column(db.String(120), index=True, unique=True)
password_hash = db.Column(db.String(128))
posts = db.relationship('Post', backref='author', lazy='dynamic')
about_me = db.Column(db.String(140))
last_seen = db.Column(db.DateTime, default=datetime.utcnow)
followed = db.relationship(
'User', secondary=followers,
primaryjoin=(followers.c.follower_id == id),
secondaryjoin=(followers.c.followed_id == id),
backref=db.backref('followers', lazy='dynamic'), lazy='dynamic')

def __repr__(self):
return '<User {}>'.format(self.username)

def set_password(self, password):
self.password_hash = generate_password_hash(password)

def check_password(self, password):
return check_password_hash(self.password_hash, password)

def avatar(self, size): # new function (class method) of the user class
digest = md5(self.email.lower().encode('utf-8')).hexdigest()
return 'https://www.gravatar.com/avatar/{}?d=identicon&s={}'.format( # return the url of the user's Gravatar
digest, size # generates an identicon for users without a gravatar
)

def follow(self, user):
if not self.is_following(user):
self.followed.append(user)

def unfollow(self, user):
if self.is_following(user):
self.followed.remove(user)

def is_following(self, user):
return self.followed.filter(
followers.c.followed_id == user.id).count() > 0

def followed_posts(self):
return Post.query.join(
followers, (followers.c.followed_id == Post.user_id)).filter(
followers.c.follower_id == self.id).order_by(
Post.timestamp.desc())
own = Post.query.filter_by(user_id=self.id) # show the user's posts in their feed
return followed.union(own).order_by(Post.timestamp.desc())


class Post(db.Model):
id = db.Column(db.Integer, primary_key=True)
body = db.Column(db.String(140))
timestamp = db.Column(db.DateTime, index=True, default=datetime.utcnow)
user_id = db.Column(db.Integer, db.ForeignKey('user.id'))

def __repr__(self):
return '<Post {}>'.format(self.body)

@login.user_loader
def load_user(id):
return User.query.get(int(id))
147 changes: 147 additions & 0 deletions microblog-project/flask1/app/routes.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
from flask import render_template, flash, redirect, url_for
from app import app
from app.forms import LoginForm
from flask_login import login_user, logout_user, current_user, login_required
from flask_login import logout_user
from app.models import User
from datetime import datetime
from app.forms import EditProfileForm
from app.forms import EmptyForm # note that since the tutorial is open-source we can always just copy the code from that if we ever have any really big questions. Base/core understanding is still there
from app.forms import PostForm
from app.models import Post

@app.before_request
def before_request():
if current_user.is_authenticated:
current_user.last_seen = datetime.utcnow()
db.session.commit()

@app.route('/', methods=['GET', 'POST'])
@app.route('/index', methods=['GET', 'POST'])
@login_required
def index():
form = PostForm()
if form.validate_on_submit():
post = Post(body=form.post.data, author=current_user) # create a post variable if the form has been submitted, and then add it to the db:
db.session.add(post)
db.session.commit()
flash('Your post is now live!')
return redirect(url_for('index'))
page = request.args.get('page', 1, type=int)
posts = current_user.followed_posts().paginate(
page, app.config['POSTS_PER_PAGE'], False)
next_url = url_for('index', page=posts.next_num) \
if posts.has_next else None
prev_url = url_for('index', page=posts.prev_num) \
if posts.has_prev else None
return render_template('index.html', title='Home', form=form,
posts=posts.items, next_url=next_url,
prev_url=prev_url)


@app.route('/login', methods=['GET', 'POST'])
def login():
if current_user.is_authenticated:
return redirect(url_for('index'))
if form.validate_on_submit():
user = User.query.filter_by(username=form.username.data).first()
if user is None or not user.check_password(form.password.data):
flash('Invalid username or password')
return redirect(url_for('login'))
login_user(user, remember=form.remember_me.data)
next_page = request.args.get('next')
if not next_page or url_parse(next_page).netloc != '':
next_page = url_for('index')
return redirect(next_page)
return render_template('login.html', title='Sign In', form=form)

@app.route('/logout')
def logout():
logout_user()
return redirect(url_for('index'))

@app.route('/user/<username>')
@login_required
def user(username):
user = User.query.filter_by(username=username).first_or_404()
page = request.args.get('page', 1, type=int)
posts = user.posts.order_by(Post.timestamp.desc()).paginate(
page, app.config['POSTS_PER_PAGE'], False)
next_url = url_for('user', username=user.username, page=posts.next_num) \
if posts.has_next else None
prev_url = url_for('user', username=user.username, page=posts.prev_num) \
if posts.has_prev else None
form = EmptyForm()
return render_template('user.html', user=user, posts=posts.items,
next_url=next_url, prev_url=prev_url, form=form)

@app.route('/edit_profile')
@login_required
def edit_profile():
form = EditProfileForm(current_user.username)
if form.validate_on_submit():
current_user.username = form.username.data
current_user.about_me = form.about_me.data
db.session.commit()
flash('Your 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)

# Integrate followers and create a follow page
# ALlow a user to follow another user
@app.route('/follow/<username>', methods=['POST']) # Post result of a form which is to follow a user from another user
@login_required
def follow(username):
form = EmptyForm()
if form.validate_on_submit():
user = User.query.filter_by(username=username).first() # the user that the account page belongs to, not the logged in user
if user is None: # If the user that the logged in user is trying to follow doesn't exist
flash('user {} not found.'.format(username))
return redirect(url_for('index'))
if user == current_user: # if the account page's user is the same as the logged in user (l.i.u = current_user)
flash('You cannot follow yourself!')
return redirect(url_for('user', username=username))
current_user.follow(user) # use the user's "follow" function to follow the user who owns the current page
db.session.commit() # add this latest development/update to the database
flash('You are following {}!'.format(username))
return redirect(url_for('user', username=username))
else:
return redirect(url_for('index'))
return render_template('user.html', user=user, posts=posts, form=form)
# Allow a user to unfollow another user
@app.route('/unfollow/<username>', methods=['POST'])
@login_required
def unfollow(username):
form = EmptyForm()
if form.validate_on_submit():
user = User.query.filter_by(username=username).first()
if user is None:
flash('User {} not found.'.format(username))
return redirect(url_for('index'))
if user == current_user:
flash('You cannot unfollow yourself!')
return redirect(url_for('user', username=username))
current_user.unfollow(user)
db.session.commit()
flash('You are not following {}.'.format(username))
return redirect(url_for('user', username=username))
else:
return redirect(url_for('index'))

# Explore page, shows a global post stream from all users to make it easier to find other accounts
@app.route('/explore')
@login_required
def explore():
page = request.args.get('page', 1, type=int)
posts = Post.query.order_by(Post.timestamp.desc()).paginate(
page, app.config['POSTS_PER_PAGE'], False)
next_url = url_for('explore', page=posts.next_num) \
if posts.has_next else None
prev_url = url_for('explore', page=posts.prev_num) \
if posts.has_prev else None
return render_template("index.html", title='Explore', posts=posts.items,
next_url=next_url, prev_url=prev_url)
6 changes: 6 additions & 0 deletions microblog-project/flask1/app/templates/404.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{% extends "base.html" %}

{% block content %}
<h1>File Not Found</h1>
<p><a href="{{ url_for('index') }}">Back</a></p>
{% endblock %}
7 changes: 7 additions & 0 deletions microblog-project/flask1/app/templates/500.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{% extends "base.html" %}

{% block content %}
<h1>An unexpected error has occured</h1>
<p>The administrator has been notified. Sorry for the inconvenience!</p>
<p><a href="{{ url_for('index') }}">Back</a></p>
{% endblock %}
11 changes: 11 additions & 0 deletions microblog-project/flask1/app/templates/_post.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<table>
<tr valign="top">
<td><img src="{{ post.author.avatar(36) }}"></td>
<td>
<a href="{{ url_for('user', username=post.author.username) }}">
{{ post.author.username }}
</a>
says:<br>{{ post.body }}
</td>
</tr>
</table>
Loading