45 |
46 |
47 |
BTC-USD
48 |
BCH-USD
49 |
XMR-USD
50 |
ZEC-USD
51 |
52 |
53 |
XRP-USD
54 |
XLM-USD
55 |
DASH-USD
56 |
BTG-USD
57 |
58 |
59 |
XRP-USD
60 |
TRX-USD
61 |
ETC-USD
62 |
MIOTA-USD
63 |
64 |
65 |
LTC-USD
66 |
ADA-USD
67 |
XEM-USD
68 |
BTCD-USD
69 |
70 |
71 |
72 |
73 |
74 |
75 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 | {% endblock %}
100 |
--------------------------------------------------------------------------------
/flaskblog/templates/user_posts.html:
--------------------------------------------------------------------------------
1 | {% extends "layout.html" %}
2 | {% block content %}
3 |
Posts by {{ user.username }} ({{ posts.total }})
4 | {% for post in posts.items %}
5 |
6 |
7 |
8 |
12 |
13 |
{{ post.content }}
14 |
15 |
16 | {% endfor %}
17 | {% for page_num in posts.iter_pages(left_edge=1, right_edge=1, left_current=1, right_current=2) %}
18 | {% if page_num %}
19 | {% if posts.page == page_num %}
20 |
{{ page_num }}
21 | {% else %}
22 |
{{ page_num }}
23 | {% endif %}
24 | {% else %}
25 | ...
26 | {% endif %}
27 | {% endfor %}
28 | {% endblock content %}
29 |
--------------------------------------------------------------------------------
/flaskblog/users/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/akmuthun/crypto_predictor_app/f437b85cfda719c9ef862b40447cb03e22690006/flaskblog/users/__init__.py
--------------------------------------------------------------------------------
/flaskblog/users/__pycache__/__init__.cpython-36.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/akmuthun/crypto_predictor_app/f437b85cfda719c9ef862b40447cb03e22690006/flaskblog/users/__pycache__/__init__.cpython-36.pyc
--------------------------------------------------------------------------------
/flaskblog/users/__pycache__/__init__.cpython-37.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/akmuthun/crypto_predictor_app/f437b85cfda719c9ef862b40447cb03e22690006/flaskblog/users/__pycache__/__init__.cpython-37.pyc
--------------------------------------------------------------------------------
/flaskblog/users/__pycache__/forms.cpython-36.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/akmuthun/crypto_predictor_app/f437b85cfda719c9ef862b40447cb03e22690006/flaskblog/users/__pycache__/forms.cpython-36.pyc
--------------------------------------------------------------------------------
/flaskblog/users/__pycache__/forms.cpython-37.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/akmuthun/crypto_predictor_app/f437b85cfda719c9ef862b40447cb03e22690006/flaskblog/users/__pycache__/forms.cpython-37.pyc
--------------------------------------------------------------------------------
/flaskblog/users/__pycache__/routes.cpython-36.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/akmuthun/crypto_predictor_app/f437b85cfda719c9ef862b40447cb03e22690006/flaskblog/users/__pycache__/routes.cpython-36.pyc
--------------------------------------------------------------------------------
/flaskblog/users/__pycache__/routes.cpython-37.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/akmuthun/crypto_predictor_app/f437b85cfda719c9ef862b40447cb03e22690006/flaskblog/users/__pycache__/routes.cpython-37.pyc
--------------------------------------------------------------------------------
/flaskblog/users/__pycache__/utils.cpython-36.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/akmuthun/crypto_predictor_app/f437b85cfda719c9ef862b40447cb03e22690006/flaskblog/users/__pycache__/utils.cpython-36.pyc
--------------------------------------------------------------------------------
/flaskblog/users/__pycache__/utils.cpython-37.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/akmuthun/crypto_predictor_app/f437b85cfda719c9ef862b40447cb03e22690006/flaskblog/users/__pycache__/utils.cpython-37.pyc
--------------------------------------------------------------------------------
/flaskblog/users/forms.py:
--------------------------------------------------------------------------------
1 | from flask_wtf import FlaskForm
2 | from flask_wtf.file import FileField, FileAllowed
3 | from wtforms import StringField, PasswordField, SubmitField, BooleanField
4 | from wtforms.validators import DataRequired, Length, Email, EqualTo, ValidationError
5 | from flask_login import current_user
6 | from flaskblog.models import User
7 |
8 |
9 | class RegistrationForm(FlaskForm):
10 | username = StringField('Username',
11 | validators=[DataRequired(), Length(min=2, max=20)])
12 | email = StringField('Email',
13 | validators=[DataRequired(), Email()])
14 | password = PasswordField('Password', validators=[DataRequired()])
15 | confirm_password = PasswordField('Confirm Password',
16 | validators=[DataRequired(), EqualTo('password')])
17 | submit = SubmitField('Sign Up')
18 |
19 | def validate_username(self, username):
20 | user = User.query.filter_by(username=username.data).first()
21 | if user:
22 | raise ValidationError('That username is taken. Please choose a different one.')
23 |
24 | def validate_email(self, email):
25 | user = User.query.filter_by(email=email.data).first()
26 | if user:
27 | raise ValidationError('That email is taken. Please choose a different one.')
28 |
29 |
30 | class LoginForm(FlaskForm):
31 | email = StringField('Email',
32 | validators=[DataRequired(), Email()])
33 | password = PasswordField('Password', validators=[DataRequired()])
34 | remember = BooleanField('Remember Me')
35 | submit = SubmitField('Login')
36 |
37 |
38 | class UpdateAccountForm(FlaskForm):
39 | username = StringField('Username',
40 | validators=[DataRequired(), Length(min=2, max=20)])
41 | email = StringField('Email',
42 | validators=[DataRequired(), Email()])
43 | picture = FileField('Update Profile Picture', validators=[FileAllowed(['jpg', 'png'])])
44 | submit = SubmitField('Update')
45 |
46 | def validate_username(self, username):
47 | if username.data != current_user.username:
48 | user = User.query.filter_by(username=username.data).first()
49 | if user:
50 | raise ValidationError('That username is taken. Please choose a different one.')
51 |
52 | def validate_email(self, email):
53 | if email.data != current_user.email:
54 | user = User.query.filter_by(email=email.data).first()
55 | if user:
56 | raise ValidationError('That email is taken. Please choose a different one.')
57 |
58 |
59 | class RequestResetForm(FlaskForm):
60 | email = StringField('Email',
61 | validators=[DataRequired(), Email()])
62 | submit = SubmitField('Request Password Reset')
63 |
64 | def validate_email(self, email):
65 | user = User.query.filter_by(email=email.data).first()
66 | if user is None:
67 | raise ValidationError('There is no account with that email. You must register first.')
68 |
69 |
70 | class ResetPasswordForm(FlaskForm):
71 | password = PasswordField('Password', validators=[DataRequired()])
72 | confirm_password = PasswordField('Confirm Password',
73 | validators=[DataRequired(), EqualTo('password')])
74 | submit = SubmitField('Reset Password')
75 |
--------------------------------------------------------------------------------
/flaskblog/users/routes.py:
--------------------------------------------------------------------------------
1 | from flask import render_template, url_for, flash, redirect, request, Blueprint
2 | from flask_login import login_user, current_user, logout_user, login_required
3 | from flaskblog import db, bcrypt
4 | from flaskblog.models import User, Post
5 | from flaskblog.users.forms import (RegistrationForm, LoginForm, UpdateAccountForm,
6 | RequestResetForm, ResetPasswordForm)
7 | from flaskblog.users.utils import save_picture, send_reset_email
8 |
9 | users = Blueprint('users', __name__)
10 |
11 |
12 | @users.route("/register", methods=['GET', 'POST'])
13 | def register():
14 | if current_user.is_authenticated:
15 | return redirect(url_for('main.home'))
16 | form = RegistrationForm()
17 | if form.validate_on_submit():
18 | hashed_password = bcrypt.generate_password_hash(form.password.data).decode('utf-8')
19 | user = User(username=form.username.data, email=form.email.data, password=hashed_password)
20 | db.session.add(user)
21 | db.session.commit()
22 | flash('Your account has been created! You are now able to log in', 'success')
23 | return redirect(url_for('users.login'))
24 | return render_template('register.html', title='Register', form=form)
25 |
26 |
27 | @users.route("/login", methods=['GET', 'POST'])
28 | def login():
29 | if current_user.is_authenticated:
30 | return redirect(url_for('main.home'))
31 | form = LoginForm()
32 | if form.validate_on_submit():
33 | user = User.query.filter_by(email=form.email.data).first()
34 | if user and bcrypt.check_password_hash(user.password, form.password.data):
35 | login_user(user, remember=form.remember.data)
36 | next_page = request.args.get('next')
37 | return redirect(next_page) if next_page else redirect(url_for('main.home'))
38 | else:
39 | flash('Login Unsuccessful. Please check email and password', 'danger')
40 | return render_template('login.html', title='Login', form=form)
41 |
42 |
43 | @users.route("/logout")
44 | def logout():
45 | logout_user()
46 | return redirect(url_for('main.home'))
47 |
48 |
49 | @users.route("/account", methods=['GET', 'POST'])
50 | @login_required
51 | def account():
52 | form = UpdateAccountForm()
53 | if form.validate_on_submit():
54 | if form.picture.data:
55 | picture_file = save_picture(form.picture.data)
56 | current_user.image_file = picture_file
57 | current_user.username = form.username.data
58 | current_user.email = form.email.data
59 | db.session.commit()
60 | flash('Your account has been updated!', 'success')
61 | return redirect(url_for('users.account'))
62 | elif request.method == 'GET':
63 | form.username.data = current_user.username
64 | form.email.data = current_user.email
65 | image_file = url_for('static', filename='profile_pics/' + current_user.image_file)
66 | return render_template('account.html', title='Account',image_file=image_file, form=form)
67 |
68 |
69 | @users.route("/user/
")
70 | def user_posts(username):
71 | page = request.args.get('page', 1, type=int)
72 | user = User.query.filter_by(username=username).first_or_404()
73 | posts = Post.query.filter_by(author=user)\
74 | .order_by(Post.date_posted.desc())\
75 | .paginate(page=page, per_page=5)
76 | return render_template('user_posts.html', posts=posts, user=user)
77 |
78 |
79 | @users.route("/reset_password", methods=['GET', 'POST'])
80 | def reset_request():
81 | if current_user.is_authenticated:
82 | return redirect(url_for('main.home'))
83 | form = RequestResetForm()
84 | if form.validate_on_submit():
85 | user = User.query.filter_by(email=form.email.data).first()
86 | send_reset_email(user)
87 | flash('An email has been sent with instructions to reset your password.', 'info')
88 | return redirect(url_for('users.login'))
89 | return render_template('reset_request.html', title='Reset Password', form=form)
90 |
91 |
92 | @users.route("/reset_password/", methods=['GET', 'POST'])
93 | def reset_token(token):
94 | if current_user.is_authenticated:
95 | return redirect(url_for('main.home'))
96 | user = User.verify_reset_token(token)
97 | if user is None:
98 | flash('That is an invalid or expired token', 'warning')
99 | return redirect(url_for('users.reset_request'))
100 | form = ResetPasswordForm()
101 | if form.validate_on_submit():
102 | hashed_password = bcrypt.generate_password_hash(form.password.data).decode('utf-8')
103 | user.password = hashed_password
104 | db.session.commit()
105 | flash('Your password has been updated! You are now able to log in', 'success')
106 | return redirect(url_for('users.login'))
107 | return render_template('reset_token.html', title='Reset Password', form=form)
108 |
--------------------------------------------------------------------------------
/flaskblog/users/utils.py:
--------------------------------------------------------------------------------
1 |
2 | import os
3 | import secrets
4 | from PIL import Image
5 | from flask import url_for, current_app
6 | from flask_mail import Message
7 | from flaskblog import mail
8 |
9 |
10 | def save_picture(form_picture):
11 | random_hex = secrets.token_hex(8)
12 | _, f_ext = os.path.splitext(form_picture.filename)
13 | picture_fn = random_hex + f_ext
14 | picture_path = os.path.join(current_app.root_path, 'static/profile_pics', picture_fn)
15 |
16 | output_size = (125, 125)
17 | i = Image.open(form_picture)
18 | i.thumbnail(output_size)
19 | i.save(picture_path)
20 |
21 | return picture_fn
22 |
23 |
24 | def send_reset_email(user):
25 | token = user.get_reset_token()
26 | msg = Message('Password Reset Request',
27 | sender='noreply@demo.com',
28 | recipients=[user.email])
29 | msg.body = f'''To reset your password, visit the following link:
30 | {url_for('users.reset_token', token=token, _external=True)}
31 |
32 | If you did not make this request then simply ignore this email and no changes will be made.
33 | '''
34 | mail.send(msg)
35 |
--------------------------------------------------------------------------------
/requirements.txt:
--------------------------------------------------------------------------------
1 | absl-py==0.8.1
2 | astor==0.8.0
3 | bcrypt==3.1.4
4 | blinker==1.4
5 | certifi==2019.9.11
6 | cffi==1.11.5
7 | chardet==3.0.4
8 | click==6.7
9 | DateTime==4.3
10 | Flask==1.0
11 | Flask-Bcrypt==0.7.1
12 | Flask-Login==0.4.1
13 | Flask-Mail==0.9.1
14 | Flask-SQLAlchemy==2.3.2
15 | Flask-WTF==0.14.2
16 | gast==0.2.2
17 | google-pasta==0.1.7
18 | grpcio==1.24.1
19 | h5py==2.10.0
20 | idna==2.8
21 | itsdangerous==0.24
22 | Jinja2==2.10
23 | Keras-Applications==1.0.8
24 | Keras-Preprocessing==1.1.0
25 | lxml==4.4.1
26 | Markdown==3.1.1
27 | MarkupSafe==1.0
28 | numpy==1.17.3
29 | opt-einsum==3.1.0
30 | pandas==0.25.2
31 | pandas-datareader==0.8.1
32 | Pillow==5.3.0
33 | protobuf==3.10.0
34 | pycparser==2.18
35 | pygal==2.4.0
36 | python-dateutil==2.8.0
37 | pytz==2019.3
38 | requests==2.22.0
39 | six==1.11.0
40 | SQLAlchemy==1.2.7
41 | stripe==2.37.2
42 | tensorboard==2.0.0
43 | tensorflow==2.0.0
44 | tensorflow-estimator==2.0.1
45 | termcolor==1.1.0
46 | urllib3==1.25.6
47 | Werkzeug==0.14.1
48 | wrapt==1.11.2
49 | WTForms==2.1
50 | zope.interface==4.6.0
--------------------------------------------------------------------------------
/run.py:
--------------------------------------------------------------------------------
1 | from flaskblog import create_app
2 |
3 | app = create_app()
4 |
5 | if __name__ == '__main__':
6 | app.run(debug= True)
7 |
--------------------------------------------------------------------------------