Пример #1
0
class Event(db.Model):
    __tablename__ = TableNames.Event
    # this event_id is the e argument in
    # http://www.mtgtop8.com/event?e=19120&d=320734&f=LE
    event_id = db.Column(db.Integer, primary_key=True, unique=True)
    decks = db.relationship(TableNames.Deck,
                            backref="event",
                            lazy=True,
                            primaryjoin="Event.event_id == Deck.event_id",
                            cascade="all,delete")

    event_format = db.Column(db.String)
    name = db.Column(db.String)
    date = db.Column(db.String)
    num_players = db.Column(db.Integer)
    date_created = db.Column(db.DateTime, default=db.func.current_timestamp())
    date_modified = db.Column(db.DateTime,
                              default=db.func.current_timestamp(),
                              onupdate=db.func.current_timestamp())

    @staticmethod
    def create_event(**kwargs):
        event = Event(**kwargs)
        db.session.add(event)
        db.session.commit()
        return event
Пример #2
0
class CollectionCard(db.Model):
	__tablename__ = TableNames.CollectionCard

	collection_card_id = db.Column(db.Integer, primary_key=True, unique = True, autoincrement = True)
	user_id = db.Column(db.Integer, db.ForeignKey(TableNames.User + '.user_id'), nullable = False)
	card_id = db.Column(db.Integer, db.ForeignKey(TableNames.Card + '.card_id'), nullable = False)
	quantity = db.Column(db.Integer, nullable = False)
	
	card = db.relationship(TableNames.Card , lazy = True,
		primaryjoin="CollectionCard.card_id == Card.card_id")

	date_created  = db.Column(db.DateTime,  default=db.func.current_timestamp())
	date_modified = db.Column(db.DateTime,  default=db.func.current_timestamp(),
										   onupdate=db.func.current_timestamp())

	def serialize(self):
		output = {}
		output['collection_card_id'] = self.collection_card_id
		output['card_id'] = self.card_id
		output['user_id'] = self.user_id
		output['quantity'] = self.quantity
		output['card'] = self.card.serialize()
		return output
Пример #3
0
class DeckCard(db.Model):
    __tablename__ = TableNames.DeckCard

    deck_card_id = db.Column(db.Integer,
                             primary_key=True,
                             unique=True,
                             autoincrement=True)
    deck_id = db.Column(db.Integer,
                        db.ForeignKey(TableNames.Deck + '.deck_id'),
                        nullable=False)
    card_id = db.Column(db.Integer,
                        db.ForeignKey(TableNames.Card + '.card_id'),
                        nullable=False)
    quantity = db.Column(db.Integer, nullable=False)
    is_main_deck = db.Column(db.Boolean, nullable=False)
    card = db.relationship(TableNames.Card,
                           lazy=True,
                           primaryjoin="Card.card_id == DeckCard.card_id")

    date_created = db.Column(db.DateTime, default=db.func.current_timestamp())
    date_modified = db.Column(db.DateTime,
                              default=db.func.current_timestamp(),
                              onupdate=db.func.current_timestamp())
Пример #4
0
class User(db.Model):
	__tablename__ = TableNames.User
	user_id = db.Column(db.Integer, primary_key=True, unique = True, autoincrement = True)
	collection = db.relationship(TableNames.CollectionCard, backref = "owner", lazy = True,
		primaryjoin="User.user_id == CollectionCard.user_id")

	username = db.Column(db.String, nullable = False)
	password_hash = db.Column(db.String, nullable = False)
	date_created  = db.Column(db.DateTime,  default=db.func.current_timestamp())
	date_modified = db.Column(db.DateTime,  default=db.func.current_timestamp(),
										   onupdate=db.func.current_timestamp())


	def __init__(self, username, password):
		"""
		: Initializes a user with an empty collection
		"""
		self.username = username
		self.password_hash = User.argonHash(password)
		self.jwt = self.create_jwt()
		

	def __repr__(self):
		return '<User %r >' % (self.username)

	def __str__(self):
		"""
		: returns a string with the user's information 
		: including their collection
		: Make the string however you see fit
		"""
		pass

	

	def serialize(self, include_jwt = True):
		"""
		: Returns the user and their collection in dictionary form
		: This function needs to be implemented
		"""
		output = {}
		output['user_id'] = self.user_id
		output['username'] = self.username
		if include_jwt:
			output['jwt'] = self.create_jwt()

		output['collection'] = [card.serialize() for card in self.collection]
		return output


	def collection_size(self):
		"""
		: returns an integer with the number of cards in the collection
		"""
		size = 0
		for card in self.collection:
			size += card.quantity
		return size

	def query_cards(self, search_query, query_limit = 100):
		"""
		: search_query: string input for what the user typed
		: return type: List of cards that match the search query.
		: A match is defined if the formatted search_query is a substring of the 
		: search_card name property.
		: Make sure to include the quantity of each card for this user 
		"""
		search_name = Card.format_card_name_for_search(search_query)
		matching_cards = Card.query.filter(Card.search_name.contains(search_name)).limit(query_limit)
		
		output = []
		for card in matching_cards:
			check_if_in_collection = CollectionCard.query.filter_by(user_id = self.user_id, card_id = card.card_id).first()
			if check_if_in_collection:
				card_dict = {"quantity": check_if_in_collection.quantity, "card": card.serialize()}
				output.append(card_dict)
			else:
				card_dict = {"quantity": 0, "card": card.serialize()}
				output.append(card_dict)
		return output

	def add_card_to_collection(self, card, quantity):
		"""
		: card: is a Card object from card.py
		: quantity : non-negative integer
		: adds this card and quantity to the user's collection
		: if the card already exists, we increment it's amount
		: return type: None
		"""
		if card is None:
			return None
		
		if quantity < 1:
			return None

		check_if_in_collection = CollectionCard.query.filter_by(user_id = self.user_id, card_id = card.card_id).first()

		if check_if_in_collection:
			check_if_in_collection.quantity += quantity
			db.session.commit()
			return None

		else:
			new_card = CollectionCard(user_id = self.user_id, card_id = card.card_id, quantity = quantity)
			db.session.add(new_card)
			db.session.commit()
			return None

	def remove_card_from_collection(self, card, quantity):
		"""
		: card: is a Card object from card.py
		: quantity: non-negative integer
		: removes this card quantity pair from the collection. 
		: If the collection doesn't have this amount of the 
		: card, then remove all copies of the card 
		: Remember when you set a card's quantity to zero
		: to actually delete the row from the database
		: return type: None
		"""
		if card is None:
			return None
		if quantity < 1:
			return None
		check_if_in_collection = CollectionCard.query.filter_by(user_id = self.user_id, card_id = card.card_id).first()
		if check_if_in_collection is None:
			return None
		if check_if_in_collection.quantity > quantity:
			check_if_in_collection.quantity -= quantity
			db.session.commit()
		else: 
			db.session.delete(check_if_in_collection)
			db.session.commit()
		
	def missing_cards_from_main_deck(self, deck):
		"""
		: deck : Deck object from deck.py
		: Given a deck returns a list of tuples
		: indicating which cards are missing from the main deck
		: return type: list of tuples with Card object and quantity 
		: example output: [(name, quantity),....]
		"""
		output = []
		for deck_card in deck.main_deck:
			check_if_in_collection = CollectionCard.query.filter_by(user_id = self.user_id, card_id = deck_card.card_id).first()
			if check_if_in_collection:
				if check_if_in_collection.quantity < deck_card.quantity:
					card_dict = {"name": deck_card.card.name, "quantity": deck_card.quantity - check_if_in_collection.quantity, "card": deck_card.card}
					output.append(card_dict)
			else:
				if deck_card.quantity == 0:
					continue
				card_dict = {"name": deck_card.card.name, "quantity": deck_card.quantity, "card": deck_card.card}
				output.append(card_dict)
		return output

	def missing_cards_from_sideboard(self, deck):
		"""
		: deck : Deck object from deck.py
		: Given a deck returns a list of tuples of indicating 
		: which cards are missing  from the sideboard
		: return type: list of tuples with Card object and quantity 
		: example output: [(name, quantity),....]
		: Hint: Get missing cards from deck and subtract missing cards from main_deck
		: Do this method last
		"""
		missing_from_whole_deck = self.missing_cards_from_deck(deck)
		missing_from_main_deck = self.missing_cards_from_main_deck(deck)
		output = []
		for triple in missing_from_whole_deck:
			for also_triple in missing_from_main_deck:
				if also_triple["name"] == triple["name"]:
					if triple["quantity"] > also_triple["quantity"]:
						new_triple = {"name": triple["name"], "quantity": triple["quantity"] - also_triple["quantity"], "card": triple["card"]}
						output.append(new_triple)
						missing_from_whole_deck.remove(triple)
					else:
						missing_from_whole_deck.remove(triple)

		output = output + missing_from_whole_deck
		return output

	def missing_cards_from_deck(self, deck):
		"""
		: deck : Deck object from deck.py
		: Given a deck returns a tuple with 2 lists of 
		: card objects indicating which cards are missing 
		: from the main deck and sideboard
		: return type: list of tuples with Card object and quantity 
		: example output: [(name, quantity),....]
		"""
		md_list = deck.main_deck
		sb_list = deck.sideboard
		needed_list = {}
		for deck_card in (md_list + sb_list):
			card_id = deck_card.card_id
			if needed_list.get(card_id):
				needed_list[card_id] += deck_card.quantity
			else:
				needed_list[card_id] = deck_card.quantity
		merged_list = md_list + sb_list
		output = []
		for card_id, quantity in needed_list.items():
			collection_card = CollectionCard.query.filter_by(user_id = self.user_id, card_id = card_id).first()
			this_card  = Card.query.filter_by(card_id = card_id).first()
			if collection_card:
				if collection_card.quantity < quantity:
					card_dict = {"name": this_card.name, "quantity": quantity - collection_card.quantity, "card": this_card}
					output.append(card_dict)
			else:
				card_dict = {"name": this_card.name, "quantity": quantity, "card": this_card}
				output.append(card_dict)
		return output

	def has_deck(self, deck, missing_cards = 0):
		"""
		: deck : Deck object from deck.py
		: given a deck returns True if this collection can make the given deck 
 		: with this collection. 
 		: Optinal missing_cards parameter allows that many number of 
 		: cards from the collection that cannot be in the deck.
 		: 
 		: Example: if a user had all of Jeskai except 4 Scalding Tarns
 		: this function would return True for arguemnts (Jeskai, misisng_cards) where
 		: missing_cards >= 4 and false for missing_cards < 4

 		: return type: boolean
		"""
		if deck is None:
			return None
		missing_from_whole_deck = self.missing_cards_from_deck(deck)
		if (missing_from_whole_deck is None) and missing_cards == 0:
			return True
		count = 0
		for triple in missing_from_whole_deck:
			count += triple["quantity"]
		return count <= missing_cards

	def remove_deck(self, deck):
		"""
		: for every card in the deck, remove each card and it's quantity
		: from the collection. If the collection doesn't have every card in the deck
		: then do nothing to the collection and return None. Otherwise return the deck object
		: return type: deck object
		"""
		if not self.has_deck(deck):
			return None
		for deck_card in deck.main_deck:
			card = Card.query.filter_by(card_id = deck_card.card_id).first()
			self.remove_card_from_collection(card, deck_card.quantity)
		for deck_card in deck.sideboard:
			card = Card.query.filter_by(card_id = deck_card.card_id).first()
			self.remove_card_from_collection(card, deck_card.quantity)
		return deck

	def add_deck(self, deck):
		"""
		: for every card in the deck, add each card and it's quantity
		: to this collection
		: No return value
		"""
		for deck_card in deck.main_deck:
			card = Card.query.filter_by(card_id = deck_card.card_id).first()
			self.add_card_to_collection(card, deck_card.quantity)
		for deck_card in deck.sideboard:
			card = Card.query.filter_by(card_id = deck_card.card_id).first()
			self.add_card_to_collection(card, deck_card.quantity)

	@staticmethod
	def login(username, password):
		"""
		: Given a login attempt, returns the corresponding user if credentials are valid
		: If invalid, then returns None
		"""
		user_matches = User.query.filter_by(username = username).all()
		if len(user_matches) == 0:
			return None
		user = user_matches[0]
		if User.argonCheck(password, user.password_hash) == False:
			return None
		return user

	@staticmethod
	def check_password(password, password_confirm):
		if (password == "") or (password is None) or (password != password_confirm):
			return False
		return True

	@staticmethod
	def create_user(username, password, password_confirm):
		check_if_username_is_in_database = User.query.filter_by(username = username).first()
		check_if_legal_password = User.check_password(password, password_confirm)
		if (check_if_username_is_in_database is not None) or (not check_if_legal_password):
			return None
		else:
			new_user = User(username = username, password = password)
			db.session.add(new_user)
			db.session.commit()
			return new_user

	def create_jwt(self):
		payload = self.serialize(include_jwt = False)
		secret_key = os.environ.get(SECRET_KEY)
		this_jwt = jwt.encode(payload, secret_key, algorithm = algorithm)
		return this_jwt.decode(UTF8)

	@staticmethod
	def decode_jwt(jwt_str):
		if jwt_str is None:
			return None
		try:
			encoded = jwt_str.encode(UTF8)
			decoded = jwt.decode(encoded, os.environ.get(SECRET_KEY), algorithms=[algorithm])
			user_id = decoded.get("user_id")
			if user_id is None:
				return None
			jwt_user = User.query.filter_by(user_id = user_id).first()
			return jwt_user
		except:
			return None

	@staticmethod
	def argonHash(pre_hash):
		"""
		: Uses argon algorithm to hash a string 
		"""
		return argon2.using(rounds=4).hash(pre_hash)

	@staticmethod
	def argonCheck(pre_hash, post_hash):
		"""
		: Uses argon hash to check if the pre_hash and post_hash are a match
		: Returns True if so and False otherwise
		"""
		if pre_hash is None:
			return False
		return argon2.verify(pre_hash, post_hash)
Пример #5
0
class Deck(db.Model):
    __tablename__ = TableNames.Deck
    # this deck id is the d argument in
    # http://www.mtgtop8.com/event?e=19120&d=320734&f=LE

    deck_id = db.Column(db.Integer, primary_key=True, unique=True)

    main_deck = db.relationship(
        TableNames.DeckCard,
        lazy=True,
        cascade="all,delete",
        primaryjoin=
        "and_(Deck.deck_id == DeckCard.deck_id, DeckCard.is_main_deck == True)"
    )
    sideboard = db.relationship(
        TableNames.DeckCard,
        lazy=True,
        cascade="all,delete",
        primaryjoin=
        "and_(Deck.deck_id == DeckCard.deck_id, DeckCard.is_main_deck == False)"
    )

    event_id = db.Column(db.Integer,
                         db.ForeignKey(TableNames.Event + '.event_id'),
                         nullable=False)
    name = db.Column(db.String)
    event_placing = db.Column(db.String)
    player = db.Column(db.String)
    url = db.Column(db.String)
    archetype = db.Column(db.String)

    date_created = db.Column(db.DateTime, default=db.func.current_timestamp())
    date_modified = db.Column(db.DateTime,
                              default=db.func.current_timestamp(),
                              onupdate=db.func.current_timestamp())

    def main_deck_size(self):
        """
		: Returns the number of cards in the main_deck
		: Return type: integer
		"""

    def sideboard_size(self):
        """
		: We have no limit on number of cards in the sideboard for now
		: Returns the number of cards in the sideboard
		: Return type: integer
		"""

    def missing_cards_from_deck(deck, tmp_collection):
        """
		: deck : Deck object from deck.py
		: Given a deck returns a tuple with 2 lists of 
		: card objects indicating which cards are missing 
		: from the main deck and sideboard
		: return type: list of tuples with Card object and quantity 
		: example output: [(name, quantity),....]
		"""
        md_list = deck.main_deck
        sb_list = deck.sideboard
        needed_list = {}
        for deck_card in (md_list + sb_list):
            card_id = deck_card.card_id
            if needed_list.get(card_id):
                needed_list[card_id] += deck_card.quantity
            else:
                needed_list[card_id] = deck_card.quantity

        output = []
        for card_id, quantity in needed_list.items():
            #collection_card = CollectionCard.query.filter_by(user_id = self.user_id, card_id = card_id).first()
            in_tmp_collection_flag = 0
            cur_quantity = -1
            for dic in tmp_collection:
                cur_card_id = dic['card_id']
                if cur_card_id == card_id:
                    in_tmp_collection_flag = 1
                    cur_quantity = dic['quantity']

            this_card = Card.query.filter_by(card_id=card_id).first()
            if in_tmp_collection_flag:
                if cur_quantity < quantity:
                    card_dict = {
                        "card_id": this_card.card_id,
                        "quantity": quantity - cur_quantity,
                        "card": this_card.serialize()
                    }
                    output.append(card_dict)
            else:
                card_dict = {
                    "card_id": this_card.card_id,
                    "quantity": quantity,
                    "card": this_card.serialize()
                }
                output.append(card_dict)

        return output

    def has_deck(deck, tmp_collection, missing_cards=0):
        """
		: deck : Deck object from deck.py
		: given a deck returns True if this collection can make the given deck 
 		: with this collection. 
 		: Optinal missing_cards parameter allows that many number of 
 		: cards from the collection that cannot be in the deck.
 		: 
 		: Example: if a user had all of Jeskai except 4 Scalding Tarns
 		: this function would return True for arguemnts (Jeskai, misisng_cards) where
 		: missing_cards >= 4 and false for missing_cards < 4

 		: return type: boolean
		"""
        if deck is None:
            return None
        missing_from_whole_deck = Deck.missing_cards_from_deck(
            deck, tmp_collection)
        if (missing_from_whole_deck is None) and missing_cards == 0:
            return True
        count = 0
        for triple in missing_from_whole_deck:
            count += triple["quantity"]
        has_deck_flag = (count <= missing_cards)
        return (has_deck_flag, missing_from_whole_deck)

    @staticmethod
    def remove_deck_from_tmp(deck, tmp_collection):
        """
		: for every card in the deck, remove each card and it's quantity
		: from the collection. If the collection doesn't have every card in the deck
		: then do nothing to the collection and return None. Otherwise return the deck object
		: return type: deck object
		"""
        #{'card': {'card_id': 1, 'name': 'Adorable Kitten'}, 'card_id': 1, 'collection_card_id': 14, 'quantity': 1, 'user_id': 2}

        for deck_card in deck.main_deck:
            card = Card.query.filter_by(card_id=deck_card.card_id).first()
            for dic in tmp_collection:
                card_id = dic['card_id']
                if card_id == card.card_id:
                    if dic['quantity'] > deck_card.quantity:
                        dic['quantity'] -= deck_card.quantity
                    else:
                        dic['quantity'] = 0

        for deck_card in deck.sideboard:
            card = Card.query.filter_by(card_id=deck_card.card_id).first()
            for dic in tmp_collection:
                card_id = dic['card_id']
                if card_id == card.card_id:
                    if dic['quantity'] > deck_card.quantity:
                        dic['quantity'] -= deck_card.quantity
                    else:
                        dic['quantity'] = 0

        return tmp_collection

    # @staticmethod
    # def user_collection_to_tmp_collection(collection):
    # 	new_collection = {}
    # 	for dictionary in collection:
    # 		card_id = dictionary['card_id']
    # 		quantity = dictionary['quantity']
    # 		name = dictionary['card']['name']
    # 		new_collection[card_id] = (name,quantity)
    # 	return new_collection

    @staticmethod
    def add_deck_to_tmp(deck, tmp_collection):
        """
		: for every card in the deck, add each card and it's quantity
		: to this collection
		: No return value
		"""
        #{'card': {'card_id': 1, 'name': 'Adorable Kitten'}, 'card_id': 1, 'collection_card_id': 14, 'quantity': 1, 'user_id': 2}
        for deck_card in deck.main_deck:
            card = Card.query.filter_by(card_id=deck_card.card_id).first()
            added_flag = 0
            for dic in tmp_collection:
                card_id = dic['card_id']
                if card_id == card.card_id:
                    dic['quantity'] += deck_card.quantity
                    added_flag = 1
            if not added_flag:
                dic_to_add = {
                    'card': {
                        'card_id': card.card_id,
                        'name': card.name
                    },
                    'card_id': card.card_id,
                    'quantity': deck_card.quantity
                }
                tmp_collection.append(dic_to_add)

        for deck_card in deck.sideboard:
            card = Card.query.filter_by(card_id=deck_card.card_id).first()
            added_flag = 0
            for dic in tmp_collection:
                card_id = dic['card_id']
                if card_id == card.card_id:
                    dic['quantity'] += deck_card.quantity
                    added_flag = 1
            if not added_flag:
                dic_to_add = {
                    'card': {
                        'card_id': card.card_id,
                        'name': card.name
                    },
                    'card_id': card.card_id,
                    'quantity': deck_card.quantity
                }
                tmp_collection.append(dic_to_add)

        return tmp_collection

    @staticmethod
    def create_deck(**kwargs):
        """
		: if either maindeck or sideboard exists, then add the corresponding 
		: DeckCard objects to match them.
		: Both will be input as lists of dictionaries 
		: For example 
		: main_deck = [
		: 	{"name", "Scalding Tarn", "quantity" : 4},
		:	....
		:	{"name", "Steam Vents", "quantity" : 2}
		: 	]
		: is a sample input
		: 
		: We have this in the constructor since we don't plan on editing decks once
		: they are made. Hence there is no add/remove card form deck method
		"""
        name = kwargs.get("name")
        event_placing = kwargs.get("event_placing")
        event = kwargs.get("event")
        main_deck = kwargs.get("main_deck")
        sideboard = kwargs.get("sideboard")
        player = kwargs.get("player")
        deck_id = kwargs.get("deck_id")
        archetype = kwargs.get("archetype")
        url = kwargs.get("url")

        # check if the deck is already in the database
        prev_deck = Deck.query.filter_by(deck_id=deck_id).first()
        if prev_deck:
            return prev_deck

        new_deck = Deck(url=url,
                        name=name,
                        event_placing=event_placing,
                        event_id=event.event_id,
                        player=player,
                        deck_id=deck_id,
                        archetype=archetype)

        db.session.add(new_deck)
        db.session.commit()
        if main_deck:
            for card in main_deck:
                if card["quantity"] == 0:
                    continue
                search_card = Card.search_card_by_name(card['name'])
                if search_card:
                    deck_id = new_deck.deck_id
                    quantity = card['quantity']
                    is_main_deck = True
                    new_deck_card = DeckCard(card_id=search_card.card_id,
                                             deck_id=deck_id,
                                             quantity=quantity,
                                             is_main_deck=is_main_deck)
                    db.session.add(new_deck_card)

        if sideboard:
            for card in sideboard:
                if card["quantity"] == 0:
                    continue
                search_card = Card.search_card_by_name(card['name'])
                if search_card:
                    deck_id = new_deck.deck_id
                    quantity = card['quantity']
                    is_main_deck = False
                    new_deck_card = DeckCard(card_id=search_card.card_id,
                                             deck_id=deck_id,
                                             quantity=quantity,
                                             is_main_deck=is_main_deck)
                    db.session.add(new_deck_card)
        db.session.commit()
        return new_deck

    @staticmethod
    def get_decks(page):
        DECKS_PER_PAGE = 10
        if page is not None:
            matching_decks = Deck.query.limit(page * DECKS_PER_PAGE).all()
        else:
            matching_decks = Deck.query.all()
        archetypes = set()
        length = Deck.query.count()
        output = []
        for deck in matching_decks:
            deck_dic = {
                'deck_id': deck.deck_id,
                'name': deck.name,
                'url': deck.url,
                'archetype': deck.archetype,
                'deck': deck.serialize(),
            }
            output.append(deck_dic)
            archetypes.add(deck.archetype)
        offset = 0
        if length % 10 != 0:
            offset = 1

        return output, length // DECKS_PER_PAGE + offset, sorted(
            list(archetypes))

    def __repr__(self):
        return '<Deck %r %r %r>' % (self.name, self.event_placing,
                                    self.event_id)

    def serialize(self):
        """
		: Returns the deck object in dictionary form, including the main_deck and sideboard
		:
		"""
        output = {
            'maindeck': [],
            'sideboard': [],
            'deck_id': self.deck_id,
            'url': self.url,
            'name': self.name
        }

        for card in self.main_deck:
            output['maindeck'].append(card.card.serialize())

        for card in self.sideboard:
            output['sideboard'].append(card.card.serialize())

        return output