import os import random from datetime import datetime import click import pytz from faker import Faker from flask import Flask, render_template, request from flask_wtf.csrf import CSRFProtect, CSRFError from flask_login import LoginManager, current_user from flask_sqlalchemy import SQLAlchemy from werkzeug.security import generate_password_hash from database import db csrf = CSRFProtect() def create_app(): app = Flask(__name__) app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False if app.config['ENV'] == 'development': app.config['SECRET_KEY'] = 'secret key goes here' app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///db.sqlite' # Heroku deployment if app.config['ENV'] == 'production': app.config['SECRET_KEY'] = os.getenv('SECRET_KEY') app.config['SQLALCHEMY_DATABASE_URI'] = os.getenv('DATABASE_URL', '').replace('postgres://', 'postgresql://', 1) db.init_app(app) csrf.init_app(app) login_manager = LoginManager() login_manager.login_view = 'auth.login' login_manager.init_app(app) from models import User, Post, Comment @login_manager.user_loader def load_user(user_id): return User.query.get(int(user_id)) from auth import blueprint as auth_blueprint app.register_blueprint(auth_blueprint) from routes import blueprint as routes_blueprint app.register_blueprint(routes_blueprint) from route_forms import blueprint as forms_blueprint app.register_blueprint(forms_blueprint) @app.errorhandler(404) def page_not_found(e): # note that we set the 404 status explicitly return render_template('errors/404.html'), 404 @app.errorhandler(CSRFError) def handle_csrf_error(e): return render_template('errors/csrf.html', reason=e.description), 400 @app.before_request def update_last_seen(): if current_user.is_authenticated: current_user.last_seen = datetime.now(tz=pytz.UTC) # datetime.utcnow doesn't actually attach a timezone current_user.last_ip = str(request.remote_addr) db.session.add(current_user) db.session.commit() @app.template_filter('pluralize') def pluralize(number, singular='', plural='s'): if number == 1: return singular else: return plural @app.context_processor def inject(): return dict(now=datetime.utcnow) # CLI commands setup @app.shell_context_processor def shell_context(): """Provides specific Flask components to the shell.""" return {'app': app, 'db': db} @app.cli.command("fake") @click.argument("count") def create_fake_users(count: int): fake = Faker() count = int(count) users = {} for _ in range(count): profile: dict = fake.simple_profile() users[profile['username']] = profile for profile in users.values(): new_user = User(username=profile['username'], name=profile['name'], password=generate_password_hash('password', method='sha256'), about_me=fake.paragraph(nb_sentences=5)) db.session.add(new_user) print(f'Committing {len(users)} users into DB.') db.session.commit() all_users: List[User] = User.query.all() post_count: int = 0 for author in random.choices(all_users, k=count // 2): new_post = Post(author=author, text=fake.paragraph(nb_sentences=2)) db.session.add(new_post) post_count += 1 print(f'Committing {post_count} posts into the DB.') db.session.commit() comment_count: int = 0 for post in Post.query.all(): for _ in range(random.randint(3, len(all_users) // 4)): new_comment = Comment(text=fake.paragraph(nb_sentences=1), author=random.choice(all_users), post=post) db.session.add(new_comment) comment_count += 1 print(f'Committing {comment_count} comments into the DB.') db.session.commit() @app.cli.command("create_all") def db_create_all() -> None: with app.app_context(): db.create_all(app=app) with app.app_context(): db.create_all(app=app) return app # Only used for Heroku; use 'flask run' or internal IDE configurations otherwise app = create_app() if __name__ == '__main__': app.run()