├── .gitignore ├── Procfile ├── README.md ├── init_db.py ├── requirements.txt ├── run.py ├── static ├── css │ └── style.css └── images │ ├── Conor McGregor.jpg │ └── Floyd Mayweather.jpg └── templates └── index.html /.gitignore: -------------------------------------------------------------------------------- 1 | # Created by .ignore support plugin (hsz.mobi) 2 | ### JetBrains template 3 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and Webstorm 4 | # Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 5 | 6 | .idea 7 | # User-specific stuff: 8 | .idea/**/workspace.xml 9 | .idea/**/tasks.xml 10 | .idea/dictionaries 11 | 12 | # Sensitive or high-churn files: 13 | .idea/**/dataSources/ 14 | .idea/**/dataSources.ids 15 | .idea/**/dataSources.xml 16 | .idea/**/dataSources.local.xml 17 | .idea/**/sqlDataSources.xml 18 | .idea/**/dynamic.xml 19 | .idea/**/uiDesigner.xml 20 | 21 | # Gradle: 22 | .idea/**/gradle.xml 23 | .idea/**/libraries 24 | 25 | # Mongo Explorer plugin: 26 | .idea/**/mongoSettings.xml 27 | 28 | ## File-based project format: 29 | *.iws 30 | 31 | ## Plugin-specific files: 32 | 33 | # IntelliJ 34 | /out/ 35 | 36 | # mpeltonen/sbt-idea plugin 37 | .idea_modules/ 38 | 39 | # JIRA plugin 40 | atlassian-ide-plugin.xml 41 | 42 | # Crashlytics plugin (for Android Studio and IntelliJ) 43 | com_crashlytics_export_strings.xml 44 | crashlytics.properties 45 | crashlytics-build.properties 46 | fabric.properties 47 | ### Python template 48 | # Byte-compiled / optimized / DLL files 49 | __pycache__/ 50 | *.py[cod] 51 | *$py.class 52 | 53 | # C extensions 54 | *.so 55 | 56 | # Distribution / packaging 57 | .Python 58 | env/ 59 | build/ 60 | develop-eggs/ 61 | dist/ 62 | downloads/ 63 | eggs/ 64 | .eggs/ 65 | lib/ 66 | lib64/ 67 | parts/ 68 | sdist/ 69 | var/ 70 | wheels/ 71 | *.egg-info/ 72 | .installed.cfg 73 | *.egg 74 | 75 | # PyInstaller 76 | # Usually these files are written by a python script from a template 77 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 78 | *.manifest 79 | *.spec 80 | 81 | # Installer logs 82 | pip-log.txt 83 | pip-delete-this-directory.txt 84 | 85 | # Unit test / coverage reports 86 | htmlcov/ 87 | .tox/ 88 | .coverage 89 | .coverage.* 90 | .cache 91 | nosetests.xml 92 | coverage.xml 93 | *,cover 94 | .hypothesis/ 95 | 96 | # Translations 97 | *.mo 98 | *.pot 99 | 100 | # Django stuff: 101 | *.log 102 | local_settings.py 103 | 104 | # Flask stuff: 105 | instance/ 106 | .webassets-cache 107 | 108 | # Scrapy stuff: 109 | .scrapy 110 | 111 | # Sphinx documentation 112 | docs/_build/ 113 | 114 | # PyBuilder 115 | target/ 116 | 117 | # Jupyter Notebook 118 | .ipynb_checkpoints 119 | 120 | # pyenv 121 | .python-version 122 | 123 | # celery beat schedule file 124 | celerybeat-schedule 125 | 126 | # SageMath parsed files 127 | *.sage.py 128 | 129 | # dotenv 130 | .env 131 | 132 | # virtualenv 133 | .venv 134 | venv/ 135 | ENV/ 136 | 137 | # Spyder project settings 138 | .spyderproject 139 | 140 | # Rope project settings 141 | .ropeproject 142 | 143 | -------------------------------------------------------------------------------- /Procfile: -------------------------------------------------------------------------------- 1 | web: gunicorn run:app --log-file - -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Your First Python Web App: Conor McGregor vs. Floyd Mayweather Flask Voting App + Heroku 2 | 3 | ### Here are your instructions 4 | 1. download the latest version of python from https://www.python.org/downloads/ and then install it. Also, make sure to check the **ADD TO PATH** box when going through the installation wizard. 5 | 6 | 2. Create an account on Heroku.com 7 | 8 | 3. MAC USERS ONLY: Install `homebrew` if you don't have it already with `sudo /usr/bin/ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"` 9 | 10 | 4. MAC USERS ONLY: Open up your terminal with cmd + space then type in `terminal` and then Install the heroku CLI client with `brew install heroku` and press **enter** in the terminal 11 | 12 | 5. WINDOWS USERS ONLY: To install Heroku CLI Client on windows just go to `https://devcenter.heroku.com/articles/heroku-cli#windows` and click the `64-bit` or `32-bit` link to download and install 13 | 14 | 6. WINDOWS USERS ONLY: Open your command line by hitting the windows key and typing in `cmd` or `powershell`. 15 | 16 | 7. Type in `git` in your command line either on mac or windows and see if you get an error. If you do get an error, go online and type in `install git`. Then get `git` either for windows or mac by clicking one of the search results that pop up. 17 | 18 | 8. At this point, **Git and Heroku should be working**! 19 | 20 | 9. Now in your terminal or command prompt, enter the command `git clone https://github.com/CleverProgrammer/flask_vote_app` 21 | 22 | 10. Now navigate to the project folder using `cd flask_vote_app` 23 | 24 | 11. Enter the command: `heroku login` and provide your heroku login details. Note: When you type in password the cursor in the command line doesn't move. Don't freak out lol just type in your password for Heroku anyway. 25 | 26 | 12. Now after you are logged in, use `heroku create` to create a new heroku application for you. 27 | 28 | 13. push the code to heroku using `git push heroku master` 29 | 30 | 14. Now make your own database where the votes will be stored with `heroku addons:create heroku-postgresql` 31 | 32 | 15. Now launch the web app online: `heroku ps:scale web=1` 33 | 34 | 16. Here is a little tricky and advanced part but don't worry so much about understanding it yet... Then in your terminal, you’ll need to run migrations for your db schema, to do this simply type `heroku run bash` on your terminal 35 | 36 | 17. Once the terminal loads enter the command `python init_db.py` 37 | 38 | 18. Now press `ctrl + d` to exit out of the heroku bash thing. 39 | 40 | 19. BOOOOOOM! Your site is now on Heroku. Type in `heroku open` to go check it out online woohoo!! 41 | -------------------------------------------------------------------------------- /init_db.py: -------------------------------------------------------------------------------- 1 | from run import db, Fighter 2 | 3 | # Create all the tables 4 | db.create_all() 5 | 6 | # create fighters 7 | conor = Fighter(name='Conor McGregor') 8 | floyd = Fighter(name='Floyd Mayweather') 9 | 10 | # add fighters to session 11 | db.session.add(conor) 12 | db.session.add(floyd) 13 | 14 | # commit the fighters to database 15 | db.session.commit() 16 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | click==6.7 2 | Flask==0.12.2 3 | Flask-SQLAlchemy==2.2 4 | gunicorn==19.7.1 5 | itsdangerous==0.24 6 | Jinja2==2.9.6 7 | MarkupSafe==1.0 8 | psycopg2==2.7.3 9 | SQLAlchemy==1.1.13 10 | Werkzeug==0.12.2 11 | -------------------------------------------------------------------------------- /run.py: -------------------------------------------------------------------------------- 1 | import os 2 | from flask import Flask, render_template, request 3 | from flask_sqlalchemy import SQLAlchemy 4 | from sqlalchemy import func 5 | 6 | app = Flask(__name__) 7 | app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False 8 | 9 | # DATABASE_URL is by default 10 | app.config['SQLALCHEMY_DATABASE_URI'] = os.environ.get('DATABASE_URL', 'sqlite:////tmp/test.db') 11 | db = SQLAlchemy(app) 12 | 13 | 14 | class User(db.Model): 15 | id = db.Column(db.Integer, primary_key=True) 16 | email = db.Column(db.String(120), unique=True) 17 | 18 | def __init__(self, email): 19 | self.email = email 20 | 21 | def __repr__(self): 22 | return '' % self.email 23 | 24 | 25 | class Fighter(db.Model): 26 | id = db.Column(db.Integer, primary_key=True) 27 | name = db.Column(db.String(120), unique=True) 28 | 29 | 30 | class Vote(db.Model): 31 | id = db.Column(db.Integer, primary_key=True) 32 | user_id = db.Column(db.Integer, db.ForeignKey('user.id')) 33 | user = db.relationship('User', backref=db.backref('votes', lazy='dynamic')) 34 | fighter_id = db.Column(db.Integer, db.ForeignKey('fighter.id')) 35 | fighter = db.relationship('Fighter', backref=db.backref('votes', lazy='dynamic')) 36 | 37 | 38 | @app.route('/', methods=['GET', 'POST']) 39 | def homepage(): 40 | message = None 41 | message_level = '' 42 | if request.method == 'POST': 43 | email = request.form.get('email') 44 | fighter_id = request.form.get('fighter') 45 | if email and fighter_id: 46 | user = db.session.query(User).filter_by(email=email).first() 47 | if user: 48 | message_level = 'info' 49 | message = 'You have already voted!' 50 | else: 51 | user = User(email=email) 52 | db.session.add(user) 53 | fighter = db.session.query(Fighter).filter_by(id=fighter_id).first() 54 | vote = Vote(user=user, fighter=fighter) 55 | db.session.add(vote) 56 | db.session.commit() 57 | message_level = 'success' 58 | message = 'Your vote for {} has been submitted!'.format(fighter.name) 59 | else: 60 | message_level = 'danger' 61 | message = 'You must enter your email and select a fighter to cast a vote.' 62 | 63 | fighters = Fighter.query.order_by('id').all() 64 | total_votes = db.session.query(Vote).count() 65 | vote_query = db.session.query(Vote.fighter_id, func.count(Vote.fighter_id)) 66 | vote_counts = vote_query.group_by(Vote.fighter_id).order_by('fighter_id').all() 67 | 68 | return render_template('index.html', message=message, message_level=message_level, 69 | fighters=fighters, total_votes=total_votes, vote_counts=vote_counts) 70 | 71 | 72 | if __name__ == '__main__': 73 | app.run(debug=True) 74 | -------------------------------------------------------------------------------- /static/css/style.css: -------------------------------------------------------------------------------- 1 | body { 2 | font-family: 'Consolas', 'Deja Vu Sans Mono', 'Bitstream Vera Sans Mono', monospace; 3 | } 4 | 5 | .progress { 6 | background-color: #d9534f; 7 | } 8 | 9 | .radio img { 10 | width: 200px; 11 | margin-left: -20px; 12 | margin-top: 6px; 13 | border-radius: 4px; 14 | } 15 | 16 | .radio img:hover { 17 | border: 1px blue solid; 18 | } 19 | 20 | .radio label { 21 | font-weight: bold; 22 | } 23 | 24 | .fighter-choice { 25 | float: left; 26 | padding-right: 30px; 27 | } -------------------------------------------------------------------------------- /static/images/Conor McGregor.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CleverProgrammer/flask_vote_app/179aad1a08b9e6c9485fb27758b52343bfcb331d/static/images/Conor McGregor.jpg -------------------------------------------------------------------------------- /static/images/Floyd Mayweather.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CleverProgrammer/flask_vote_app/179aad1a08b9e6c9485fb27758b52343bfcb331d/static/images/Floyd Mayweather.jpg -------------------------------------------------------------------------------- /templates/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Voting App 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 |
15 |

Who Will Win!?

16 | 17 | {% if message %} 18 |
19 | {{ message }} 20 |
21 | {% endif %} 22 | 23 |
24 |
25 | 26 |
27 | 28 |
29 |
30 | 31 |
32 |
33 | {% for fighter in fighters %} 34 |
35 | 42 |
43 | {% endfor %} 44 |
45 |
46 | 47 |
48 |
49 | 50 |
51 |
52 |
53 | 54 |
55 | 56 | {% if vote_counts %} 57 |
58 |
59 |
Votes so far
60 |
61 |
62 |
65 | 70% Complete 66 |
67 |
68 |
69 | {{ fighters[0].name }} 70 | {{ fighters[1].name }} 71 |

Total votes: {{total_votes}}

72 |
73 |
74 |
75 |
76 | {% endif %} 77 | 78 |
79 | 80 | 81 | --------------------------------------------------------------------------------