Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
45 commits
Select commit Hold shift + click to select a range
ba3be53
fixed card search, removed power & toughness forms, re-integrated pag…
Oct 27, 2020
3b7a501
finished basic home_routes tests
Oct 28, 2020
0ce258d
removed mtgsdk from all files because it was no longer working
Oct 29, 2020
85910a9
updated error messages
Oct 29, 2020
a059a66
cleaned up search code
Oct 29, 2020
5868e7a
user able to add posts
Oct 29, 2020
a712a22
about to make new branch to remove filter feature
Oct 29, 2020
ebcfd76
all tests working and running smoothly
Oct 30, 2020
08fdcef
ensured user is logged in for the proper routes, or else redirect to …
Oct 30, 2020
6644d1a
edit user password working correctly now
Oct 30, 2020
10d1466
cleaned up code for registering new user
Oct 30, 2020
31dfa43
added more tests to test for invalid user signup and updated login tests
Oct 30, 2020
e47ea0a
implemented AJAX for bookmark buttons
Oct 30, 2020
6243743
improved dropdowns for Add to Deck buttons and cleaned up card info p…
Oct 31, 2020
d972d7d
added dropdown icon to Show Info button and made bookmark button a ci…
Oct 31, 2020
b1f4e20
home.html, bookmarks.html and deck.html all have the same Show Info /…
Oct 31, 2020
3217f67
responsive grid of cards
Oct 31, 2020
187acc9
improved responsiveness
Oct 31, 2020
dde6159
fixed remove from deck button and add AJAX to its functionality
Nov 1, 2020
bf6b8a7
added title w/ username to decks page and AJAX to add/remove friend b…
Nov 1, 2020
d96c908
fixed last page on pagination to be dynamic
Nov 1, 2020
e2d2862
made pagination completely dynamic on home page / search results page
Nov 1, 2020
0354aa1
fixed index range for pagination
Nov 1, 2020
8dbc512
added all 54000+ cards to database
Nov 2, 2020
744e9bf
added AJAX functionality to adding card to deck so you stay on same p…
Nov 2, 2020
71d2641
changed seed.py
Nov 2, 2020
84160c5
added view profile link to deck page and updated tests
Nov 2, 2020
55869bb
minor finishing touches
Nov 2, 2020
1788391
fixed responsiveness of navbar
Nov 3, 2020
fe4e97e
fixed responsiveness of cards & their info tables
Nov 3, 2020
3ab3281
register & edit profile forms validate email address
Nov 3, 2020
bcf9768
minor change to css
Nov 3, 2020
8e00236
fixed minor issue in tests
Nov 3, 2020
022665e
added procfile & runtime.txt
Nov 3, 2020
d77ff89
fixed navbar appearance when logged out
Nov 3, 2020
b03ff19
updated seed.py for heroku
Nov 3, 2020
c056637
updated seed.py for heroku
Nov 3, 2020
dca34b0
fixed no results pagination
Nov 3, 2020
4e9191b
updated seed file to 40 pages
Nov 3, 2020
bef3913
updated divs in bookmarks.html
Nov 4, 2020
0bd60ca
updated divs in bookmarks.html
Nov 4, 2020
7debcfe
updated divs in bookmarks.html
Nov 4, 2020
f720bbf
updated divs in bookmarks.html
Nov 4, 2020
25176ed
centered content in new_deck.html
Nov 4, 2020
d4ffbbc
Create README.md
shapirobd Nov 6, 2020
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: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,4 @@
.DS_Store
venv/
.vscode/
__pycache__/
1 change: 1 addition & 0 deletions Procfile
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
web: gunicorn app:app
69 changes: 69 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
# capstone

Link to proposal:

https://docs.google.com/document/d/1vihomjFiPxAEcT1a_XN5z64aQJ3TPLc6ZatwUhQOaoU/edit?usp=sharing


Title of my site: MTG Deck Builder
Link to the URL where it is deployed: https://mtg-deck-builder-herokuapp.herokuapp.com/

MTG Deck Builder allows users to search for cards in the MTG library, see info on the cards, create their own decks, create posts, add friends, and view decks that other users have made.

Features implemented:
- The search bar at the top allows users to search for cards by card name, decks by deck name or type, and users/friends by username. This is what I believe would be the most common search criteria.
- There is AJAX involved in bookmarking a card, adding/removing a friend and adding a card to a deck - this way, a user does have to leave the page or refresh and can keep their momentum going through the site.
- You are able to see the owner of any given deck and click their username to immediately view their profile - this allows users to easily add a friend who has a deck they like, or simply view other decks from that user by visiting their profile.
- When you click "Add to Deck" under a card, at the end of the list of your decks that pops up under the card there is a button that says "Create Deck" - this allows you to add the selected card to a brand new deck. Upon making the new deck, the selected card will automatically be added to that deck.

Standard user flow:
1. Login/Register
2. View home page where all cards (paginated) are shown
3. Create new deck
4. Search for cards by name
5. Add cards to decks
6. You may also search for other decks for inspiration

Notes on MTG API:
- Requests for cards can take a very long time - if you want to retrieve every single card from the API, it can take around 2 hours. Also, the Python SDK suddenly stopped working one day, so there seems to be some issues that need fixing.

Technology stack used:
- astroid==2.4.2
- autopep8==1.5.4
- bcrypt==3.2.0
- blinker==1.4
- certifi==2020.6.20
- cffi==1.14.3
- chardet==3.0.4
- click==7.1.2
- dnspython==2.0.0
- email-validator==1.1.1
- Flask==1.1.2
- Flask-Bcrypt==0.7.1
- Flask-DebugToolbar==0.11.0
- flask-paginate==0.7.1
- Flask-SQLAlchemy==2.4.4
- Flask-WTF==0.14.3
- idna==2.10
- isort==5.6.4
- itsdangerous==1.1.0
- Jinja2==2.11.2
- lazy-object-proxy==1.4.3
- MarkupSafe==1.1.1
- mccabe==0.6.1
- mtgsdk==1.3.1
- psycopg2-binary==2.8.6
- pycodestyle==2.6.0
- pycparser==2.20
- pylint==2.6.0
- requests==2.24.0
- six==1.15.0
- SQLAlchemy==1.3.20
- toml==0.10.1
- typed-ast==1.4.1
- urllib3==1.25.11
- Werkzeug==1.0.1
- wrapt==1.12.1
- WTForms==2.3.3

There are still a lot of features that I want to add, but I need to move on with the curriculum so I am submitting my project with the features it currently has and will add more features as stretch goals.
5 changes: 0 additions & 5 deletions app.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,6 @@

CURR_USER_KEY = 'curr-user'

TYPES = mtgsdk.Type.all()


@app.before_request
def add_user_to_g():
Expand All @@ -44,6 +42,3 @@ def add_user_to_g():
g.user = User.query.get(session[CURR_USER_KEY])
else:
g.user = None


print('***********')
17 changes: 7 additions & 10 deletions bookmarks.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,20 +9,20 @@
from app import g
from flask import Flask, session, Blueprint, request, render_template, redirect, flash
from flask_debugtoolbar import DebugToolbarExtension
from models import db, connect_db, User, Friendship, Message, Card, Bookmark, Deck, CardDeck, Post
from forms import LoginForm, RegisterForm, TypeForm, PowerForm, ToughnessForm, DeckForm, EditUserForm
from models import db, connect_db, User, Friendship, Card, Bookmark, Deck, CardDeck, Post
from forms import LoginForm, RegisterForm, DeckForm, EditUserForm

bookmarks_blueprint = Blueprint('bookmarks_blueprint', __name__, static_folder='static',
template_folder='templates')

CURR_USER_KEY = 'curr-user'

TYPES = mtgsdk.Type.all()


@bookmarks_blueprint.route('/cards/<int:card_id>/bookmark', methods=['GET', 'POST'])
def add_bookmark(card_id):
"""Route for bookmarking a card"""
if g.user:
"""If this is a post request, create a new bookmark instance"""
bookmark = Bookmark(card_id=card_id, username=session[CURR_USER_KEY])
db.session.add(bookmark)
db.session.commit()
Expand All @@ -33,6 +33,7 @@ def add_bookmark(card_id):

@bookmarks_blueprint.route('/cards/<int:card_id>/unbookmark', methods=['GET', 'POST'])
def remove_bookmark(card_id):
"""Route for unbookmarking a card"""
if g.user:
bookmark = Bookmark.query.filter(Bookmark.card_id == card_id).first()
db.session.delete(bookmark)
Expand All @@ -44,17 +45,13 @@ def remove_bookmark(card_id):

@bookmarks_blueprint.route('/bookmarks')
def show_bookmarked_cards():
"""Route for showing your bookmarked cards"""
if g.user:
bookmarked_cards = g.user.bookmarked_cards
decks = Deck.query.all()

bookmarked_card_ids = [
bookmarked_card.id for bookmarked_card in bookmarked_cards]
type_form = TypeForm()
type_form.card_type.choices = TYPES

power_form = PowerForm()
toughness_form = ToughnessForm()

return render_template('bookmarks.html', bookmarked_cards=bookmarked_cards, decks=decks, type_form=type_form, power_form=power_form, toughness_form=toughness_form, bookmarked_card_ids=bookmarked_card_ids)
return render_template('bookmarks.html', bookmarked_cards=bookmarked_cards, decks=decks, bookmarked_card_ids=bookmarked_card_ids)
return redirect('/login')
53 changes: 31 additions & 22 deletions decks.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,55 +9,58 @@
from app import g
from flask import Flask, Blueprint, session, request, render_template, redirect, flash
from flask_debugtoolbar import DebugToolbarExtension
from models import db, connect_db, User, Friendship, Message, Card, Bookmark, Deck, CardDeck, Post
from forms import LoginForm, RegisterForm, TypeForm, PowerForm, ToughnessForm, DeckForm, EditUserForm
from models import db, connect_db, User, Card, Bookmark, Deck, CardDeck
from forms import DeckForm

decks_blueprint = Blueprint('decks_blueprint', __name__, static_folder='static',
template_folder='templates')

CURR_USER_KEY = 'curr-user'

TYPES = mtgsdk.Type.all()


@decks_blueprint.route('/decks', methods=['GET', 'POST'])
def view_decks():
"""Route for viewing your own decks"""
if g.user:
user = g.user
decks = g.user.decks
return render_template('decks.html', decks=decks)
search = False
return render_template('decks.html', decks=decks, user=user, search=search)
return redirect('/login')


@decks_blueprint.route('/decks/<int:deck_id>')
def show_deck(deck_id):
deck = Deck.query.get(deck_id)

bookmarks = Bookmark.query.all()
bookmarked_card_ids = [bookmark.card_id for bookmark in bookmarks]
type_form = TypeForm()
type_form.card_type.choices = TYPES
"""Route for viewing contents of deck"""
if g.user:
deck = Deck.query.get(deck_id)

power_form = PowerForm()
toughness_form = ToughnessForm()
return render_template('deck.html', deck=deck, type_form=type_form, power_form=power_form, toughness_form=toughness_form, bookmarked_card_ids=bookmarked_card_ids)
bookmarks = Bookmark.query.all()
bookmarked_card_ids = [bookmark.card_id for bookmark in bookmarks]
return render_template('deck.html', deck=deck, bookmarked_card_ids=bookmarked_card_ids)
return redirect('/login')


@decks_blueprint.route('/decks/<int:deck_id>/delete', methods=['POST'])
def delete_deck(deck_id):
deck = Deck.query.get(deck_id)
db.session.delete(deck)
db.session.commit()
return redirect('/decks')
"""Route for deleting a deck"""
if g.user:
deck = Deck.query.get(deck_id)
db.session.delete(deck)
db.session.commit()
return redirect('/decks')
return redirect('/login')


@decks_blueprint.route('/new', methods=['GET', 'POST'])
def create_deck():

"""Route for creating a deck"""
if g.user:
form = DeckForm()
form.deck_type.choices = ['Standard', 'Commander']

if form.validate_on_submit():

"""If this is a post request, created a new deck instance"""
deck = Deck(deck_name=form.deck_name.data,
deck_type=form.deck_type.data, username=session[CURR_USER_KEY])

Expand All @@ -71,11 +74,12 @@ def create_deck():

return redirect('/decks')
return render_template('new_deck.html', form=form)
return redirect('/')
return redirect('/login')


@decks_blueprint.route('/cards/<int:card_id>/decks/<int:deck_id>', methods=['POST'])
def add_to_deck(card_id, deck_id):
"""Route for adding a card to your deck"""
if g.user:
card = Card.query.get(card_id)
deck = Deck.query.get(deck_id)
Expand All @@ -84,10 +88,12 @@ def add_to_deck(card_id, deck_id):

db.session.commit()
return redirect('/home')
return redirect('/login')


@decks_blueprint.route('/cards/<int:card_id>/decks/<int:deck_id>/delete', methods=['POST'])
def delete_from_deck(card_id, deck_id):
"""Route for deleting a card from your deck"""
if g.user:
card_deck = CardDeck.query.filter(
CardDeck.card_id == card_id and CardDeck.deck_id == deck_id).first()
Expand All @@ -96,10 +102,13 @@ def delete_from_deck(card_id, deck_id):
db.session.commit()

return redirect(f'/decks/{deck_id}')
return redirect('/login')


@decks_blueprint.route('/users/<string:username>/decks')
def show_users_decks(username):
"""Route for viewing someone else's decks"""
user = User.query.get(username)
decks = user.decks
return render_template('decks.html', decks=decks)
search = False
return render_template('decks.html', decks=decks, user=user, search=search)
43 changes: 12 additions & 31 deletions forms.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
from flask_wtf import FlaskForm
from wtforms import StringField, PasswordField, IntegerField, RadioField, SelectField, FileField
from wtforms.validators import InputRequired, Length
from wtforms import StringField, PasswordField, IntegerField, RadioField, SelectField, FileField, TextAreaField
from wtforms.validators import InputRequired, Length, Email
from wtforms.widgets import CheckboxInput, ListWidget
import email_validator


class LoginForm(FlaskForm):
Expand All @@ -11,7 +12,8 @@ class LoginForm(FlaskForm):


class RegisterForm(FlaskForm):
email = StringField('Email', validators=[InputRequired()])
email = StringField('Email', validators=[
InputRequired(), Email("Invalid email address")])
username = StringField("Username", validators=[InputRequired()])
password = PasswordField("Password", validators=[
InputRequired(), Length(min=8)])
Expand All @@ -21,7 +23,8 @@ class RegisterForm(FlaskForm):


class EditUserForm(FlaskForm):
email = StringField('Email', validators=[InputRequired()])
email = StringField('Email', validators=[
InputRequired(), Email("Invalid email address")])
password = PasswordField("Password", validators=[
InputRequired(), Length(min=8)])
confirmed_password = PasswordField(
Expand All @@ -34,30 +37,8 @@ class DeckForm(FlaskForm):
deck_type = SelectField('Deck Type')


class TypeForm(FlaskForm):
card_type = RadioField('Type', option_widget=CheckboxInput())


class ColorForm(FlaskForm):
color = RadioField('Color', option_widget=CheckboxInput())


class RarityForm(FlaskForm):
rarity = RadioField('Rarity', option_widget=CheckboxInput())


class SetForm(FlaskForm):
set_name = RadioField('Set Name', option_widget=CheckboxInput())


class PowerForm(FlaskForm):
power_conditionals = RadioField('Power', option_widget=CheckboxInput(),
choices=["Less than", "Equal to", "Greater than"])
power = IntegerField('Power', default=0)


class ToughnessForm(FlaskForm):

toughness_conditionals = RadioField('Toughness', option_widget=CheckboxInput(),
choices=["Less than", "Equal to", "Greater than"])
toughness = IntegerField('Toughness', default=0)
class NewPostForm(FlaskForm):
title = StringField('Title', validators=[
InputRequired()])
content = TextAreaField('Content', validators=[
InputRequired()])
22 changes: 12 additions & 10 deletions friends.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,40 +9,42 @@
from app import g
from flask import Flask, Blueprint, session, request, render_template, redirect, flash
from flask_debugtoolbar import DebugToolbarExtension
from models import db, connect_db, User, Friendship, Message, Card, Bookmark, Deck, CardDeck, Post
from forms import LoginForm, RegisterForm, TypeForm, PowerForm, ToughnessForm, DeckForm, EditUserForm
from models import db, connect_db, User, Friendship, Card, Bookmark, Deck, CardDeck, Post
from forms import LoginForm, RegisterForm, DeckForm, EditUserForm

friends_blueprint = Blueprint('friends_blueprint', __name__, static_folder='static',
template_folder='templates')

CURR_USER_KEY = 'curr-user'

TYPES = mtgsdk.Type.all()


@friends_blueprint.route('/friends')
def show_friends():
@friends_blueprint.route('/users/<string:username>/friends')
def show_users_friends(username):
"""Route for showing your friends"""
if g.user:
friends = g.user.friends
return render_template('friends.html', friends=friends)
user = User.query.get(username)
friends = user.friends
return render_template('friends.html', friends=friends, user=user)
return redirect('/login')


@friends_blueprint.route('/add_friend/<string:friend_username>', methods=['POST'])
def add_friend(friend_username):
"""Route for adding a friend"""
if g.user:
friend = User.query.get(friend_username)
g.user.friends.append(friend)
db.session.commit()
return redirect('/friends')
return redirect(f'/users/{g.user.username}/friends')
return redirect('/login')


@friends_blueprint.route('/remove_friend/<string:friend_username>', methods=['POST'])
def remove_friend(friend_username):
"""Route for removing a friend"""
if g.user:
friend = User.query.get(friend_username)
g.user.friends.remove(friend)
db.session.commit()
return redirect('/friends')
return redirect(f'/users/{g.user.username}/friends')
return redirect('/login')
Loading