redditraffler/redditraffler

View on GitHub
app/db/models/raffle.py

Summary

Maintainability
A
0 mins
Test Coverage
A
94%
from typing import List
from sqlalchemy import inspect, func
from datetime import datetime, timedelta, timezone

from app.extensions import db


class Raffle(db.Model):
    MAX_WINNER_COUNT = 100

    id = db.Column(db.Integer, primary_key=True)
    created_at = db.Column(db.DateTime, default=datetime.utcnow)
    updated_at = db.Column(
        db.DateTime, default=datetime.utcnow, onupdate=datetime.utcnow
    )

    submission_id = db.Column(db.Text, index=True, unique=True, nullable=False)
    submission_title = db.Column(db.Text, nullable=False)
    submission_author = db.Column(db.Text, nullable=False)
    subreddit = db.Column(db.Text, nullable=False)
    winner_count = db.Column(db.Integer, nullable=False)
    min_account_age = db.Column(db.Integer, nullable=False)
    min_comment_karma = db.Column(db.Integer, nullable=True)
    min_link_karma = db.Column(db.Integer, nullable=True)
    min_combined_karma = db.Column(db.Integer, nullable=True)
    ignored_users = db.Column(db.Text, nullable=True)

    winners = db.relationship(
        "Winner", backref="raffle", lazy=True, cascade="all, delete-orphan"
    )

    user_id = db.Column(db.Integer, db.ForeignKey("user.id"), nullable=True, index=True)

    def __repr__(self):
        return "<Raffle {}>".format(self.submission_id)

    @classmethod
    def get_verified_raffles(cls):
        return cls.query.filter(cls.user_id.isnot(None)).all()

    @classmethod
    def get_vanity_metrics(cls) -> dict:
        result = (
            cls.query.with_entities(cls.subreddit, cls.winner_count, cls.created_at)
            .filter(cls.user_id.isnot(None))
            .order_by(cls.created_at.desc())
        ).all()

        # Basic Raffle and Winner stats
        num_total_verified_raffles = len(result)
        num_total_winners = sum([raffle.winner_count for raffle in result])
        num_total_subreddits = len(set([raffle.subreddit for raffle in result]))

        # Get the top 3 subreddits with the most raffles in the last 30 days
        thirty_days_ago = datetime.now() - timedelta(days=30)
        num_raffles_by_subreddit = (
            cls.query.with_entities(cls.subreddit, func.count())
            .filter(cls.created_at > thirty_days_ago)
            .group_by(cls.subreddit)
            .order_by(func.count().desc())
            .all()
        )
        top_recent_subreddits = [
            {"subreddit": subreddit, "num_raffles": num_raffles}
            for subreddit, num_raffles in num_raffles_by_subreddit
        ]  # Reshape the tuples from the db call into a named dict
        num_recent_subreddits_to_show = min(
            len(top_recent_subreddits), 3
        )  # Possible that we have raffles for fewer than 3 subreddits
        top_recent_subreddits = top_recent_subreddits[:num_recent_subreddits_to_show]

        return {
            "num_total_verified_raffles": num_total_verified_raffles,
            "num_total_winners": num_total_winners,
            "num_total_subreddits": num_total_subreddits,
            "top_recent_subreddits": top_recent_subreddits,
        }

    @classmethod
    def get_recent_raffles(
        cls, include_unverified=False, oldest_raffle_age_days=30
    ) -> List["Raffle"]:
        """Returns a list of raffles that were created no earlier than
        oldest_raffle_age_days ago. Use include_unverified to filter
        unverified raffles from the result set.

        Returns:
            List[Raffle]: The raffles meeting the given criteria
        """
        oldest_raffle_age_days_ago = datetime.now() - timedelta(
            days=oldest_raffle_age_days
        )

        return (
            cls.query.filter(
                cls.created_at > oldest_raffle_age_days_ago,
                True if include_unverified else cls.user_id.isnot(None),
            )
            .order_by(cls.created_at.desc())
            .all()
        )

    def is_verified(self):
        return self.creator and (self.submission_author == self.creator.username)

    def created_at_readable(self):
        return (
            self.created_at.strftime("%B %-d %Y, %-I:%M%p")
            .replace("AM", "am")
            .replace("PM", "pm")
        )

    def as_dict(self):
        exclude = set(["id", "updated_at"])
        res = {}
        for col in inspect(self).mapper.column_attrs:
            if col.key == "created_at":
                res[col.key] = (
                    getattr(self, col.key).replace(tzinfo=timezone.utc).timestamp()
                )
            elif col.key not in exclude:
                res[col.key] = getattr(self, col.key)
        res["created_at_readable"] = self.created_at_readable()
        return res

    def ignored_users_list(self):
        return self.ignored_users.split(",")