From 5ebcb665c2998b6a07732d245bf8f03a15741bb0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Joakim=20Hells=C3=A9n?= Date: Wed, 10 Jul 2024 01:44:33 +0200 Subject: [PATCH] Add WIP --- core/admin.py | 7 - core/migrations/0001_initial.py | 62 ---- ...scordsetting_notification_type_and_more.py | 26 -- core/migrations/0003_subscription.py | 53 --- ...aldiscordsetting_historicalsubscription.py | 146 --------- ...0005_discordsetting_created_at_and_more.py | 33 -- core/models.py | 26 -- core/templates/index.html | 4 + core/views/index.py | 113 +------ twitch_app/admin.py | 5 +- twitch_app/api.py | 18 +- .../management/commands/scrape_twitch.py | 105 +++--- twitch_app/migrations/0001_initial.py | 177 +++++----- twitch_app/migrations/0002_game_image_url.py | 27 -- ...benefit_historicaldropcampaign_and_more.py | 308 ------------------ ...ampaigns_delete_historicaluser_and_more.py | 23 -- ...ons_alter_dropcampaign_options_and_more.py | 92 ------ ...ons_alter_dropcampaign_options_and_more.py | 172 ---------- ..._dropcampaign_time_based_drops_and_more.py | 23 -- .../0008_alter_dropbenefit_game_and_more.py | 33 -- twitch_app/models.py | 58 +--- 21 files changed, 183 insertions(+), 1328 deletions(-) delete mode 100644 core/admin.py delete mode 100644 core/migrations/0001_initial.py delete mode 100644 core/migrations/0002_remove_discordsetting_notification_type_and_more.py delete mode 100644 core/migrations/0003_subscription.py delete mode 100644 core/migrations/0004_historicaldiscordsetting_historicalsubscription.py delete mode 100644 core/migrations/0005_discordsetting_created_at_and_more.py delete mode 100644 core/models.py delete mode 100644 twitch_app/migrations/0002_game_image_url.py delete mode 100644 twitch_app/migrations/0003_historicaldropbenefit_historicaldropcampaign_and_more.py delete mode 100644 twitch_app/migrations/0004_remove_user_drop_campaigns_delete_historicaluser_and_more.py delete mode 100644 twitch_app/migrations/0005_alter_dropbenefit_options_alter_dropcampaign_options_and_more.py delete mode 100644 twitch_app/migrations/0006_alter_dropbenefit_options_alter_dropcampaign_options_and_more.py delete mode 100644 twitch_app/migrations/0007_alter_dropcampaign_time_based_drops_and_more.py delete mode 100644 twitch_app/migrations/0008_alter_dropbenefit_game_and_more.py diff --git a/core/admin.py b/core/admin.py deleted file mode 100644 index de528d8..0000000 --- a/core/admin.py +++ /dev/null @@ -1,7 +0,0 @@ -from django.contrib import admin -from simple_history.admin import SimpleHistoryAdmin - -from .models import Webhook - -# https://django-simple-history.readthedocs.io/en/latest/admin.html -admin.site.register(Webhook, SimpleHistoryAdmin) diff --git a/core/migrations/0001_initial.py b/core/migrations/0001_initial.py deleted file mode 100644 index bdf7d1c..0000000 --- a/core/migrations/0001_initial.py +++ /dev/null @@ -1,62 +0,0 @@ -# Generated by Django 5.0.6 on 2024-06-30 23:42 - -import django.db.models.deletion -from django.conf import settings -from django.db import migrations, models -from django.db.migrations.operations.base import Operation - - -class Migration(migrations.Migration): - initial = True - - dependencies: list[tuple[str, str]] = [ - migrations.swappable_dependency(settings.AUTH_USER_MODEL), - ] - - operations: list[Operation] = [ - migrations.CreateModel( - name="NotificationType", - fields=[ - ( - "id", - models.BigAutoField( - auto_created=True, - primary_key=True, - serialize=False, - verbose_name="ID", - ), - ), - ("name", models.CharField(max_length=255)), - ], - ), - migrations.CreateModel( - name="DiscordSetting", - fields=[ - ( - "id", - models.BigAutoField( - auto_created=True, - primary_key=True, - serialize=False, - verbose_name="ID", - ), - ), - ("webhook_url", models.URLField()), - ("disabled", models.BooleanField(default=False)), - ( - "user", - models.ForeignKey( - on_delete=django.db.models.deletion.CASCADE, - to=settings.AUTH_USER_MODEL, - ), - ), - ( - "notification_type", - models.ForeignKey( - on_delete=django.db.models.deletion.CASCADE, - to="core.notificationtype", - ), - ), - ], - ), - ] diff --git a/core/migrations/0002_remove_discordsetting_notification_type_and_more.py b/core/migrations/0002_remove_discordsetting_notification_type_and_more.py deleted file mode 100644 index 3204d7b..0000000 --- a/core/migrations/0002_remove_discordsetting_notification_type_and_more.py +++ /dev/null @@ -1,26 +0,0 @@ -# Generated by Django 5.0.6 on 2024-07-01 01:10 - -from django.db import migrations, models -from django.db.migrations.operations.base import Operation - - -class Migration(migrations.Migration): - dependencies: list[tuple[str, str]] = [ - ("core", "0001_initial"), - ] - - operations: list[Operation] = [ - migrations.RemoveField( - model_name="discordsetting", - name="notification_type", - ), - migrations.AddField( - model_name="discordsetting", - name="name", - field=models.CharField(default="No name", max_length=255), - preserve_default=False, - ), - migrations.DeleteModel( - name="NotificationType", - ), - ] diff --git a/core/migrations/0003_subscription.py b/core/migrations/0003_subscription.py deleted file mode 100644 index 19956e4..0000000 --- a/core/migrations/0003_subscription.py +++ /dev/null @@ -1,53 +0,0 @@ -# Generated by Django 5.0.6 on 2024-07-01 03:28 - -import django.db.models.deletion -from django.conf import settings -from django.db import migrations, models -from django.db.migrations.operations.base import Operation - - -class Migration(migrations.Migration): - dependencies: list[tuple[str, str]] = [ - ("core", "0002_remove_discordsetting_notification_type_and_more"), - ("twitch_app", "0001_initial"), - migrations.swappable_dependency(settings.AUTH_USER_MODEL), - ] - - operations: list[Operation] = [ - migrations.CreateModel( - name="Subscription", - fields=[ - ( - "id", - models.BigAutoField( - auto_created=True, - primary_key=True, - serialize=False, - verbose_name="ID", - ), - ), - ("created_at", models.DateTimeField(auto_now_add=True)), - ( - "discord_webhook", - models.ForeignKey( - on_delete=django.db.models.deletion.CASCADE, - to="core.discordsetting", - ), - ), - ( - "game", - models.ForeignKey( - on_delete=django.db.models.deletion.CASCADE, - to="twitch_app.game", - ), - ), - ( - "user", - models.ForeignKey( - on_delete=django.db.models.deletion.CASCADE, - to=settings.AUTH_USER_MODEL, - ), - ), - ], - ), - ] diff --git a/core/migrations/0004_historicaldiscordsetting_historicalsubscription.py b/core/migrations/0004_historicaldiscordsetting_historicalsubscription.py deleted file mode 100644 index 3996907..0000000 --- a/core/migrations/0004_historicaldiscordsetting_historicalsubscription.py +++ /dev/null @@ -1,146 +0,0 @@ -# Generated by Django 5.0.6 on 2024-07-01 15:17 - -import django.db.models.deletion -import simple_history.models -from django.conf import settings -from django.db import migrations, models -from django.db.migrations.operations.base import Operation - - -class Migration(migrations.Migration): - dependencies: list[tuple[str, str]] = [ - ("core", "0003_subscription"), - ("twitch_app", "0003_historicaldropbenefit_historicaldropcampaign_and_more"), - migrations.swappable_dependency(settings.AUTH_USER_MODEL), - ] - - operations: list[Operation] = [ - migrations.CreateModel( - name="HistoricalDiscordSetting", - fields=[ - ( - "id", - models.BigIntegerField( - auto_created=True, - blank=True, - db_index=True, - verbose_name="ID", - ), - ), - ("name", models.CharField(max_length=255)), - ("webhook_url", models.URLField()), - ("disabled", models.BooleanField(default=False)), - ("history_id", models.AutoField(primary_key=True, serialize=False)), - ("history_date", models.DateTimeField(db_index=True)), - ("history_change_reason", models.CharField(max_length=100, null=True)), - ( - "history_type", - models.CharField( - choices=[("+", "Created"), ("~", "Changed"), ("-", "Deleted")], - max_length=1, - ), - ), - ( - "history_user", - models.ForeignKey( - null=True, - on_delete=django.db.models.deletion.SET_NULL, - related_name="+", - to=settings.AUTH_USER_MODEL, - ), - ), - ( - "user", - models.ForeignKey( - blank=True, - db_constraint=False, - null=True, - on_delete=django.db.models.deletion.DO_NOTHING, - related_name="+", - to=settings.AUTH_USER_MODEL, - ), - ), - ], - options={ - "verbose_name": "historical discord setting", - "verbose_name_plural": "historical discord settings", - "ordering": ("-history_date", "-history_id"), - "get_latest_by": ("history_date", "history_id"), - }, - bases=(simple_history.models.HistoricalChanges, models.Model), - ), - migrations.CreateModel( - name="HistoricalSubscription", - fields=[ - ( - "id", - models.BigIntegerField( - auto_created=True, - blank=True, - db_index=True, - verbose_name="ID", - ), - ), - ("created_at", models.DateTimeField(blank=True, editable=False)), - ("history_id", models.AutoField(primary_key=True, serialize=False)), - ("history_date", models.DateTimeField(db_index=True)), - ("history_change_reason", models.CharField(max_length=100, null=True)), - ( - "history_type", - models.CharField( - choices=[("+", "Created"), ("~", "Changed"), ("-", "Deleted")], - max_length=1, - ), - ), - ( - "discord_webhook", - models.ForeignKey( - blank=True, - db_constraint=False, - null=True, - on_delete=django.db.models.deletion.DO_NOTHING, - related_name="+", - to="core.discordsetting", - ), - ), - ( - "game", - models.ForeignKey( - blank=True, - db_constraint=False, - null=True, - on_delete=django.db.models.deletion.DO_NOTHING, - related_name="+", - to="twitch_app.game", - ), - ), - ( - "history_user", - models.ForeignKey( - null=True, - on_delete=django.db.models.deletion.SET_NULL, - related_name="+", - to=settings.AUTH_USER_MODEL, - ), - ), - ( - "user", - models.ForeignKey( - blank=True, - db_constraint=False, - null=True, - on_delete=django.db.models.deletion.DO_NOTHING, - related_name="+", - to=settings.AUTH_USER_MODEL, - ), - ), - ], - options={ - "verbose_name": "historical subscription", - "verbose_name_plural": "historical subscriptions", - "ordering": ("-history_date", "-history_id"), - "get_latest_by": ("history_date", "history_id"), - }, - bases=(simple_history.models.HistoricalChanges, models.Model), - ), - ] diff --git a/core/migrations/0005_discordsetting_created_at_and_more.py b/core/migrations/0005_discordsetting_created_at_and_more.py deleted file mode 100644 index 9dfb7df..0000000 --- a/core/migrations/0005_discordsetting_created_at_and_more.py +++ /dev/null @@ -1,33 +0,0 @@ -# Generated by Django 5.0.6 on 2024-07-01 21:47 - -import django.utils.timezone -from django.db import migrations, models -from django.db.migrations.operations.base import Operation - - -class Migration(migrations.Migration): - dependencies: list[tuple[str, str]] = [ - ("core", "0004_historicaldiscordsetting_historicalsubscription"), - ] - - operations: list[Operation] = [ - migrations.AddField( - model_name="discordsetting", - name="created_at", - field=models.DateTimeField( - auto_now_add=True, - default=django.utils.timezone.now, - ), - preserve_default=False, - ), - migrations.AddField( - model_name="historicaldiscordsetting", - name="created_at", - field=models.DateTimeField( - blank=True, - default=django.utils.timezone.now, - editable=False, - ), - preserve_default=False, - ), - ] diff --git a/core/models.py b/core/models.py deleted file mode 100644 index baaa292..0000000 --- a/core/models.py +++ /dev/null @@ -1,26 +0,0 @@ -from typing import Literal - -import auto_prefetch -from django.db import models -from simple_history.models import HistoricalRecords - -from twitch_app.models import Game - - -class Webhook(auto_prefetch.Model): - """Webhooks to send notifications to.""" - - url = models.URLField(unique=True) - game = models.ForeignKey(Game, on_delete=models.CASCADE) - disabled = models.BooleanField(default=False) - added_at = models.DateTimeField(blank=True, null=True, auto_now_add=True) - modified_at = models.DateTimeField(blank=True, null=True, auto_now=True) - history = HistoricalRecords() - - class Meta(auto_prefetch.Model.Meta): - verbose_name: str = "Webhook" - verbose_name_plural: str = "Webhooks" - ordering: tuple[Literal["url"]] = ("url",) - - def __str__(self) -> str: - return self.url diff --git a/core/templates/index.html b/core/templates/index.html index a24b5f6..dbf5e6a 100644 --- a/core/templates/index.html +++ b/core/templates/index.html @@ -5,6 +5,10 @@
{% include "partials/toc.html" %}
+ {% for org in orgs %} + {{ org }} + fsafa + {% endfor %} {% include "partials/info_box.html" %} {% include "partials/news.html" %} {% for game in games %} diff --git a/core/views/index.py b/core/views/index.py index 8bc9b57..afcd51b 100644 --- a/core/views/index.py +++ b/core/views/index.py @@ -1,6 +1,5 @@ from __future__ import annotations -import datetime import logging from typing import TYPE_CHECKING @@ -9,17 +8,15 @@ from django.conf import settings from django.http import HttpRequest, HttpResponse from django.template.response import TemplateResponse -from core.data import CampaignContext, DropContext, GameContext, WebhookData +from core.data import WebhookData from twitch_app.models import ( - DropBenefit, - DropCampaign, - Game, - TimeBasedDrop, + Organization, ) if TYPE_CHECKING: from pathlib import Path + from django.db.models.manager import BaseManager from django.http import ( HttpRequest, HttpResponse, @@ -44,111 +41,12 @@ controller = hishel.Controller( ) -def fetch_campaigns(game: Game) -> list[CampaignContext]: - """Fetch active campaigns for a given game.""" - campaigns: list[CampaignContext] = [] - for campaign in DropCampaign.objects.filter( - game=game, - status="ACTIVE", - end_at__gt=datetime.datetime.now(tz=datetime.UTC), - ).only( - "id", - "name", - "image_url", - "status", - "account_link_url", - "description", - "details_url", - "start_at", - "end_at", - ): - drops = fetch_drops(campaign) - if not drops: - logger.info("No drops found for %s", campaign.name) - continue - - campaigns.append( - CampaignContext( - drop_id=campaign.id, - name=campaign.name, - image_url=campaign.image_url, - status=campaign.status, - account_link_url=campaign.account_link_url, - description=campaign.description, - details_url=campaign.details_url, - start_at=campaign.start_at, - end_at=campaign.end_at, - drops=drops, - ), - ) - return campaigns - - -def fetch_drops(campaign: DropCampaign) -> list[DropContext]: - """Fetch drops for a given campaign.""" - drops: list[DropContext] = [] - drop: TimeBasedDrop - for drop in campaign.time_based_drops.all().only( - "id", - "name", - "required_minutes_watched", - "required_subs", - ): - benefit: DropBenefit | None = drop.benefits.first() - - image_asset_url: str = "https://static-cdn.jtvnw.net/twitch-quests-assets/CAMPAIGN/default.png" - if benefit and benefit.image_asset_url: - image_asset_url = benefit.image_asset_url - - drops.append( - DropContext( - drops_id=drop.id, - image_url=image_asset_url, - name=drop.name, - required_minutes_watched=drop.required_minutes_watched, - required_subs=drop.required_subs, - ), - ) - return drops - - -def sort_games_by_campaign_start(list_of_games: list[GameContext]) -> list[GameContext]: - """Sort games by the start date of the first campaign and reverse the list so the latest games are first.""" - if list_of_games and list_of_games[0].campaigns: - list_of_games.sort( - key=lambda x: x.campaigns[0].start_at - if x.campaigns and x.campaigns[0].start_at is not None - else datetime.datetime.min, - ) - list_of_games.reverse() - return list_of_games - - def get_webhooks(request: HttpRequest) -> list[str]: """Get the webhooks from the cookie.""" cookie: str = request.COOKIES.get("webhooks", "") return list(filter(None, cookie.split(","))) -def prepare_game_contexts() -> list[GameContext]: - """Prepare game contexts with their respective campaigns and drops.""" - list_of_games: list[GameContext] = [] - for game in list(Game.objects.all().only("id", "image_url", "display_name", "slug")): - campaigns: list[CampaignContext] = fetch_campaigns(game) - if not campaigns: - continue - list_of_games.append( - GameContext( - game_id=game.id, - campaigns=campaigns, - image_url=game.image_url, - display_name=game.display_name, - twitch_url=game.twitch_url, - ), - ) - return list_of_games - - def get_avatar(webhook_response: Response) -> str: """Get the avatar URL from the webhook response.""" avatar: str = "https://cdn.discordapp.com/embed/avatars/0.png" @@ -173,12 +71,11 @@ def get_webhook_data(webhook: str) -> WebhookData: def index(request: HttpRequest) -> HttpResponse: """Render the index page.""" - list_of_games: list[GameContext] = prepare_game_contexts() - sorted_list_of_games: list[GameContext] = sort_games_by_campaign_start(list_of_games) + orgs: BaseManager[Organization] = Organization.objects.all() webhooks: list[WebhookData] = [get_webhook_data(webhook) for webhook in get_webhooks(request)] return TemplateResponse( request=request, template="index.html", - context={"games": sorted_list_of_games, "webhooks": webhooks}, + context={"orgs": orgs, "webhooks": webhooks}, ) diff --git a/twitch_app/admin.py b/twitch_app/admin.py index c62f8b0..cb25eb3 100644 --- a/twitch_app/admin.py +++ b/twitch_app/admin.py @@ -1,11 +1,10 @@ from django.contrib import admin from simple_history.admin import SimpleHistoryAdmin -from .models import DropBenefit, DropCampaign, Game, Organization, TimeBasedDrop +from .models import Drop, DropCampaign, Game, Organization # https://django-simple-history.readthedocs.io/en/latest/admin.html -admin.site.register(DropBenefit, SimpleHistoryAdmin) +admin.site.register(Drop, SimpleHistoryAdmin) admin.site.register(DropCampaign, SimpleHistoryAdmin) admin.site.register(Game, SimpleHistoryAdmin) admin.site.register(Organization, SimpleHistoryAdmin) -admin.site.register(TimeBasedDrop, SimpleHistoryAdmin) diff --git a/twitch_app/api.py b/twitch_app/api.py index 553f70c..d8cb1cf 100644 --- a/twitch_app/api.py +++ b/twitch_app/api.py @@ -5,11 +5,10 @@ from django.http import HttpRequest from ninja import Router, Schema from .models import ( - DropBenefit, + Drop, DropCampaign, Game, Organization, - TimeBasedDrop, ) router = Router( @@ -98,13 +97,6 @@ def get_games(request: HttpRequest) -> BaseManager[Game]: # noqa: ARG001 return Game.objects.all() -# http://localhost:8000/api/twitch/drop_benefits -@router.get("/drop_benefits", response=list[DropBenefitSchema]) -def get_drop_benefits(request: HttpRequest) -> BaseManager[DropBenefit]: # noqa: ARG001 - """Get all drop benefits.""" - return DropBenefit.objects.all() - - # http://localhost:8000/api/twitch/drop_campaigns @router.get("/drop_campaigns", response=list[DropCampaignSchema]) def get_drop_campaigns(request: HttpRequest) -> BaseManager[DropCampaign]: # noqa: ARG001 @@ -112,8 +104,8 @@ def get_drop_campaigns(request: HttpRequest) -> BaseManager[DropCampaign]: # no return DropCampaign.objects.all() -# http://localhost:8000/api/twitch/time_based_drops -@router.get("/time_based_drops", response=list[TimeBasedDropSchema]) -def get_time_based_drops(request: HttpRequest) -> BaseManager[TimeBasedDrop]: # noqa: ARG001 +# http://localhost:8000/api/twitch/drops +@router.get("/drops", response=list[TimeBasedDropSchema]) +def get_drops(request: HttpRequest) -> BaseManager[Drop]: # noqa: ARG001 """Get all time-based drops.""" - return TimeBasedDrop.objects.all() + return Drop.objects.all() diff --git a/twitch_app/management/commands/scrape_twitch.py b/twitch_app/management/commands/scrape_twitch.py index 660834f..4909ee9 100644 --- a/twitch_app/management/commands/scrape_twitch.py +++ b/twitch_app/management/commands/scrape_twitch.py @@ -11,11 +11,10 @@ from playwright.async_api import Playwright, async_playwright from playwright.async_api._generated import Response from twitch_app.models import ( - DropBenefit, + Drop, DropCampaign, Game, Organization, - TimeBasedDrop, ) if TYPE_CHECKING: @@ -35,10 +34,10 @@ if not data_dir: msg = "DATA_DIR is not set in settings.py" raise ValueError(msg) -logger: logging.Logger = logging.getLogger("twitch.management.commands.scrape_twitch") +logger: logging.Logger = logging.getLogger(__name__) -async def insert_data(data: dict) -> None: # noqa: PLR0914, C901 +async def insert_data(data: dict) -> None: # noqa: C901, PLR0914 """Insert data into the database. Args: @@ -52,6 +51,7 @@ async def insert_data(data: dict) -> None: # noqa: PLR0914, C901 user_data["id"] drop_campaign_data = user_data["dropCampaign"] if not drop_campaign_data: + logger.debug("No drop campaign data found") return # Create or get the organization @@ -62,6 +62,8 @@ async def insert_data(data: dict) -> None: # noqa: PLR0914, C901 ) if created: logger.debug("Organization created: %s", owner) + else: + logger.debug("Organization found: %s", owner) # Create or get the game game_data = drop_campaign_data["game"] @@ -70,6 +72,7 @@ async def insert_data(data: dict) -> None: # noqa: PLR0914, C901 defaults={ "slug": game_data["slug"], "display_name": game_data["displayName"], + "organization": owner, }, ) if created: @@ -79,16 +82,15 @@ async def insert_data(data: dict) -> None: # noqa: PLR0914, C901 drop_campaign, created = await sync_to_async(DropCampaign.objects.get_or_create)( id=drop_campaign_data["id"], defaults={ - "account_link_url": drop_campaign_data["accountLinkURL"], - "description": drop_campaign_data["description"], - "details_url": drop_campaign_data["detailsURL"], - "end_at": drop_campaign_data["endAt"], - "image_url": drop_campaign_data["imageURL"], - "name": drop_campaign_data["name"], - "start_at": drop_campaign_data["startAt"], - "status": drop_campaign_data["status"], + "account_link_url": drop_campaign_data.get("accountLinkURL"), + "description": drop_campaign_data.get("description"), + "details_url": drop_campaign_data.get("detailsURL"), + "end_at": drop_campaign_data.get("endAt"), + "image_url": drop_campaign_data.get("imageURL"), + "name": drop_campaign_data.get("name"), + "start_at": drop_campaign_data.get("startAt"), + "status": drop_campaign_data.get("status"), "game": game, - "owner": owner, }, ) if created: @@ -97,62 +99,58 @@ async def insert_data(data: dict) -> None: # noqa: PLR0914, C901 # Create time-based drops for drop_data in drop_campaign_data["timeBasedDrops"]: drop_benefit_edges = drop_data["benefitEdges"] - drop_benefits = [] + + time_based_drop, created = await sync_to_async(Drop.objects.get_or_create)( + id=drop_data["id"], + defaults={ + "required_subs": drop_data.get("requiredSubs"), + "end_at": drop_data.get("endAt"), + "name": drop_data.get("name"), + "required_minutes_watched": drop_data.get("requiredMinutesWatched"), + "start_at": drop_data.get("startAt"), + "drop_campaign": drop_campaign, + }, + ) + if created: + logger.debug("Time-based drop created: %s", time_based_drop) for edge in drop_benefit_edges: benefit_data = edge["benefit"] benefit_owner_data = benefit_data["ownerOrganization"] - benefit_owner, created = await sync_to_async( + org, created = await sync_to_async( Organization.objects.get_or_create, )( id=benefit_owner_data["id"], defaults={"name": benefit_owner_data["name"]}, ) if created: - logger.debug("Benefit owner created: %s", benefit_owner) + logger.debug("Organization created: %s", org) benefit_game_data = benefit_data["game"] benefit_game, created = await sync_to_async(Game.objects.get_or_create)( id=benefit_game_data["id"], - defaults={"name": benefit_game_data["name"]}, + defaults={"display_name": benefit_game_data["name"]}, ) if created: logger.debug("Benefit game created: %s", benefit_game) - benefit, created = await sync_to_async(DropBenefit.objects.get_or_create)( - id=benefit_data["id"], + # Get the drop to add the data to + drop, created = await sync_to_async(Drop.objects.get_or_create)( + id=drop_data["id"], defaults={ - "created_at": benefit_data["createdAt"], - "entitlement_limit": benefit_data["entitlementLimit"], - "image_asset_url": benefit_data["imageAssetURL"], - "is_ios_available": benefit_data["isIosAvailable"], - "name": benefit_data["name"], - "owner_organization": benefit_owner, + "created_at": benefit_data.get("createdAt"), + "entitlement_limit": benefit_data.get("entitlementLimit"), + "image_asset_url": benefit_data.get("imageAssetURL"), + "is_ios_available": benefit_data.get("isIosAvailable"), + "name": benefit_data.get("name"), + "owner_organization": org, "game": benefit_game, }, ) - drop_benefits.append(benefit) + if created: - logger.debug("Benefit created: %s", benefit) - - time_based_drop, created = await sync_to_async( - TimeBasedDrop.objects.get_or_create, - )( - id=drop_data["id"], - defaults={ - "required_subs": drop_data["requiredSubs"], - "end_at": drop_data["endAt"], - "name": drop_data["name"], - "required_minutes_watched": drop_data["requiredMinutesWatched"], - "start_at": drop_data["startAt"], - }, - ) - await sync_to_async(time_based_drop.benefits.set)(drop_benefits) - await sync_to_async(drop_campaign.time_based_drops.add)(time_based_drop) - - if created: - logger.debug("Time-based drop created: %s", time_based_drop) + logger.debug("Drop created: %s", drop) class Command(BaseCommand): @@ -162,18 +160,20 @@ class Command(BaseCommand): self, playwright: Playwright, ) -> list[dict[str, typing.Any]]: - profile_dir: Path = Path(data_dir / "firefox-profile") + profile_dir: Path = Path(data_dir / "chrome-profile") profile_dir.mkdir(parents=True, exist_ok=True) logger.debug( - "Launching Firefox browser with user data directory: %s", + "Launching Chrome browser with user data directory: %s", profile_dir, ) - browser: BrowserContext = await playwright.firefox.launch_persistent_context( + browser: BrowserContext = await playwright.chromium.launch_persistent_context( user_data_dir=profile_dir, - headless=True, + headless=False, + user_agent="Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.0.0 Safari/537.36", # noqa: E501 + viewport={"width": 1920, "height": 1080}, ) - logger.debug("Launched Firefox browser") + logger.debug("Launched Chrome browser") page: Page = await browser.new_page() json_data: list[dict] = [] @@ -198,7 +198,7 @@ class Command(BaseCommand): try: await page.wait_for_selector( 'div[data-a-target="top-nav-avatar"]', - timeout=30000, + timeout=300000, ) logged_in = True logger.info("Logged in to Twitch") @@ -211,6 +211,9 @@ class Command(BaseCommand): await page.wait_for_load_state("networkidle") logger.debug("Page loaded. Scraping data...") + # Wait 5 seconds for the page to load + await asyncio.sleep(5) + await browser.close() for num, campaign in enumerate(json_data, start=1): diff --git a/twitch_app/migrations/0001_initial.py b/twitch_app/migrations/0001_initial.py index 034a1b0..f6b697a 100644 --- a/twitch_app/migrations/0001_initial.py +++ b/twitch_app/migrations/0001_initial.py @@ -1,17 +1,45 @@ -# Generated by Django 5.0.6 on 2024-07-01 00:08 +# Generated by Django 5.1b1 on 2024-07-09 22:26 +import auto_prefetch import django.db.models.deletion import django.db.models.functions.text +import django.db.models.manager from django.db import migrations, models -from django.db.migrations.operations.base import Operation class Migration(migrations.Migration): initial = True - dependencies: list[tuple[str, str]] = [] + dependencies = [] - operations: list[Operation] = [ + operations = [ + migrations.CreateModel( + name="DropCampaign", + fields=[ + ("id", models.TextField(primary_key=True, serialize=False)), + ("account_link_url", models.URLField(blank=True, null=True)), + ("description", models.TextField(blank=True, null=True)), + ("details_url", models.URLField(blank=True, null=True)), + ("end_at", models.DateTimeField(blank=True, null=True)), + ("image_url", models.URLField(blank=True, null=True)), + ("name", models.TextField(blank=True, null=True)), + ("start_at", models.DateTimeField(blank=True, null=True)), + ("status", models.TextField(blank=True, null=True)), + ("added_at", models.DateTimeField(auto_now_add=True, null=True)), + ("modified_at", models.DateTimeField(auto_now=True, null=True)), + ], + options={ + "verbose_name": "Drop Campaign", + "verbose_name_plural": "Drop Campaigns", + "ordering": ("name",), + "abstract": False, + "base_manager_name": "prefetch_manager", + }, + managers=[ + ("objects", django.db.models.manager.Manager()), + ("prefetch_manager", django.db.models.manager.Manager()), + ], + ), migrations.CreateModel( name="Game", fields=[ @@ -28,10 +56,33 @@ class Migration(migrations.Migration): output_field=models.TextField(), ), ), + ( + "image_url", + models.GeneratedField( # type: ignore # noqa: PGH003 + db_persist=True, + expression=django.db.models.functions.text.Concat( + models.Value("https://static-cdn.jtvnw.net/ttv-boxart/"), + "id", + models.Value("_IGDB.jpg"), + ), + output_field=models.URLField(), + ), + ), ("display_name", models.TextField(blank=True, null=True)), ("added_at", models.DateTimeField(auto_now_add=True, null=True)), ("modified_at", models.DateTimeField(auto_now=True, null=True)), ], + options={ + "verbose_name": "Game", + "verbose_name_plural": "Games", + "ordering": ("display_name",), + "abstract": False, + "base_manager_name": "prefetch_manager", + }, + managers=[ + ("objects", django.db.models.manager.Manager()), + ("prefetch_manager", django.db.models.manager.Manager()), + ], ), migrations.CreateModel( name="Organization", @@ -41,97 +92,69 @@ class Migration(migrations.Migration): ("added_at", models.DateTimeField(auto_now_add=True, null=True)), ("modified_at", models.DateTimeField(auto_now=True, null=True)), ], + options={ + "verbose_name": "Organization", + "verbose_name_plural": "Organizations", + "ordering": ("name",), + "abstract": False, + "base_manager_name": "prefetch_manager", + }, + managers=[ + ("objects", django.db.models.manager.Manager()), + ("prefetch_manager", django.db.models.manager.Manager()), + ], ), migrations.CreateModel( - name="DropBenefit", + name="Drop", fields=[ ("id", models.TextField(primary_key=True, serialize=False)), ("created_at", models.DateTimeField(blank=True, null=True)), ("entitlement_limit", models.IntegerField(blank=True, null=True)), ("image_asset_url", models.URLField(blank=True, null=True)), - ("is_ios_available", models.BooleanField(blank=True, null=True)), ("name", models.TextField(blank=True, null=True)), ("added_at", models.DateTimeField(auto_now_add=True, null=True)), ("modified_at", models.DateTimeField(auto_now=True, null=True)), - ( - "game", - models.ForeignKey( - on_delete=django.db.models.deletion.CASCADE, - to="twitch_app.game", - ), - ), - ( - "owner_organization", - models.ForeignKey( - on_delete=django.db.models.deletion.CASCADE, - to="twitch_app.organization", - ), - ), - ], - ), - migrations.CreateModel( - name="TimeBasedDrop", - fields=[ - ("id", models.TextField(primary_key=True, serialize=False)), ("required_subs", models.IntegerField(blank=True, null=True)), ("end_at", models.DateTimeField(blank=True, null=True)), - ("name", models.TextField(blank=True, null=True)), - ( - "required_minutes_watched", - models.IntegerField(blank=True, null=True), - ), + ("required_minutes_watched", models.IntegerField(blank=True, null=True)), ("start_at", models.DateTimeField(blank=True, null=True)), - ("added_at", models.DateTimeField(auto_now_add=True, null=True)), - ("modified_at", models.DateTimeField(auto_now=True, null=True)), - ("benefits", models.ManyToManyField(to="twitch_app.dropbenefit")), - ], - ), - migrations.CreateModel( - name="DropCampaign", - fields=[ - ("id", models.TextField(primary_key=True, serialize=False)), - ("account_link_url", models.URLField(blank=True, null=True)), - ("description", models.TextField(blank=True, null=True)), - ("details_url", models.URLField(blank=True, null=True)), - ("end_at", models.DateTimeField(blank=True, null=True)), - ("image_url", models.URLField(blank=True, null=True)), - ("name", models.TextField(blank=True, null=True)), - ("start_at", models.DateTimeField(blank=True, null=True)), - ("status", models.TextField(blank=True, null=True)), - ("added_at", models.DateTimeField(auto_now_add=True, null=True)), - ("modified_at", models.DateTimeField(auto_now=True, null=True)), ( - "game", - models.ForeignKey( + "drop_campaign", + auto_prefetch.ForeignKey( on_delete=django.db.models.deletion.CASCADE, - related_name="drop_campaigns", - to="twitch_app.game", + related_name="drops", + to="twitch_app.dropcampaign", ), ), - ( - "owner", - models.ForeignKey( - on_delete=django.db.models.deletion.CASCADE, - related_name="drop_campaigns", - to="twitch_app.organization", - ), - ), - ( - "time_based_drops", - models.ManyToManyField(to="twitch_app.timebaseddrop"), - ), + ], + options={ + "verbose_name": "Drop", + "verbose_name_plural": "Drops", + "ordering": ("name",), + "abstract": False, + "base_manager_name": "prefetch_manager", + }, + managers=[ + ("objects", django.db.models.manager.Manager()), + ("prefetch_manager", django.db.models.manager.Manager()), ], ), - migrations.CreateModel( - name="User", - fields=[ - ("id", models.TextField(primary_key=True, serialize=False)), - ("added_at", models.DateTimeField(auto_now_add=True, null=True)), - ("modified_at", models.DateTimeField(auto_now=True, null=True)), - ( - "drop_campaigns", - models.ManyToManyField(to="twitch_app.dropcampaign"), - ), - ], + migrations.AddField( + model_name="dropcampaign", + name="game", + field=auto_prefetch.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="drop_campaigns", + to="twitch_app.game", + ), + ), + migrations.AddField( + model_name="game", + name="organization", + field=auto_prefetch.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="games", + to="twitch_app.organization", + ), ), ] diff --git a/twitch_app/migrations/0002_game_image_url.py b/twitch_app/migrations/0002_game_image_url.py deleted file mode 100644 index 3cba036..0000000 --- a/twitch_app/migrations/0002_game_image_url.py +++ /dev/null @@ -1,27 +0,0 @@ -# Generated by Django 5.0.6 on 2024-07-01 03:49 - -import django.db.models.functions.text -from django.db import migrations, models -from django.db.migrations.operations.base import Operation - - -class Migration(migrations.Migration): - dependencies: list[tuple[str, str]] = [ - ("twitch_app", "0001_initial"), - ] - - operations: list[Operation] = [ - migrations.AddField( - model_name="game", - name="image_url", - field=models.GeneratedField( # type: ignore # noqa: PGH003 - db_persist=True, - expression=django.db.models.functions.text.Concat( - models.Value("https://static-cdn.jtvnw.net/ttv-boxart/"), - "id", - models.Value("_IGDB.jpg"), - ), - output_field=models.URLField(), - ), - ), - ] diff --git a/twitch_app/migrations/0003_historicaldropbenefit_historicaldropcampaign_and_more.py b/twitch_app/migrations/0003_historicaldropbenefit_historicaldropcampaign_and_more.py deleted file mode 100644 index 2d332f0..0000000 --- a/twitch_app/migrations/0003_historicaldropbenefit_historicaldropcampaign_and_more.py +++ /dev/null @@ -1,308 +0,0 @@ -# Generated by Django 5.0.6 on 2024-07-01 15:17 - -import django.db.models.deletion -import django.db.models.functions.text -import simple_history.models -from django.conf import settings -from django.db import migrations, models -from django.db.migrations.operations.base import Operation - - -class Migration(migrations.Migration): - dependencies: list[tuple[str, str]] = [ - ("twitch_app", "0002_game_image_url"), - migrations.swappable_dependency(settings.AUTH_USER_MODEL), - ] - - operations: list[Operation] = [ - migrations.CreateModel( - name="HistoricalDropBenefit", - fields=[ - ("id", models.TextField(db_index=True)), - ("created_at", models.DateTimeField(blank=True, null=True)), - ("entitlement_limit", models.IntegerField(blank=True, null=True)), - ("image_asset_url", models.URLField(blank=True, null=True)), - ("is_ios_available", models.BooleanField(blank=True, null=True)), - ("name", models.TextField(blank=True, null=True)), - ( - "added_at", - models.DateTimeField(blank=True, editable=False, null=True), - ), - ( - "modified_at", - models.DateTimeField(blank=True, editable=False, null=True), - ), - ("history_id", models.AutoField(primary_key=True, serialize=False)), - ("history_date", models.DateTimeField(db_index=True)), - ("history_change_reason", models.CharField(max_length=100, null=True)), - ( - "history_type", - models.CharField( - choices=[("+", "Created"), ("~", "Changed"), ("-", "Deleted")], - max_length=1, - ), - ), - ( - "game", - models.ForeignKey( - blank=True, - db_constraint=False, - null=True, - on_delete=django.db.models.deletion.DO_NOTHING, - related_name="+", - to="twitch_app.game", - ), - ), - ( - "history_user", - models.ForeignKey( - null=True, - on_delete=django.db.models.deletion.SET_NULL, - related_name="+", - to=settings.AUTH_USER_MODEL, - ), - ), - ( - "owner_organization", - models.ForeignKey( - blank=True, - db_constraint=False, - null=True, - on_delete=django.db.models.deletion.DO_NOTHING, - related_name="+", - to="twitch_app.organization", - ), - ), - ], - options={ - "verbose_name": "historical drop benefit", - "verbose_name_plural": "historical drop benefits", - "ordering": ("-history_date", "-history_id"), - "get_latest_by": ("history_date", "history_id"), - }, - bases=(simple_history.models.HistoricalChanges, models.Model), - ), - migrations.CreateModel( - name="HistoricalDropCampaign", - fields=[ - ("id", models.TextField(db_index=True)), - ("account_link_url", models.URLField(blank=True, null=True)), - ("description", models.TextField(blank=True, null=True)), - ("details_url", models.URLField(blank=True, null=True)), - ("end_at", models.DateTimeField(blank=True, null=True)), - ("image_url", models.URLField(blank=True, null=True)), - ("name", models.TextField(blank=True, null=True)), - ("start_at", models.DateTimeField(blank=True, null=True)), - ("status", models.TextField(blank=True, null=True)), - ( - "added_at", - models.DateTimeField(blank=True, editable=False, null=True), - ), - ( - "modified_at", - models.DateTimeField(blank=True, editable=False, null=True), - ), - ("history_id", models.AutoField(primary_key=True, serialize=False)), - ("history_date", models.DateTimeField(db_index=True)), - ("history_change_reason", models.CharField(max_length=100, null=True)), - ( - "history_type", - models.CharField( - choices=[("+", "Created"), ("~", "Changed"), ("-", "Deleted")], - max_length=1, - ), - ), - ( - "game", - models.ForeignKey( - blank=True, - db_constraint=False, - null=True, - on_delete=django.db.models.deletion.DO_NOTHING, - related_name="+", - to="twitch_app.game", - ), - ), - ( - "history_user", - models.ForeignKey( - null=True, - on_delete=django.db.models.deletion.SET_NULL, - related_name="+", - to=settings.AUTH_USER_MODEL, - ), - ), - ( - "owner", - models.ForeignKey( - blank=True, - db_constraint=False, - null=True, - on_delete=django.db.models.deletion.DO_NOTHING, - related_name="+", - to="twitch_app.organization", - ), - ), - ], - options={ - "verbose_name": "historical drop campaign", - "verbose_name_plural": "historical drop campaigns", - "ordering": ("-history_date", "-history_id"), - "get_latest_by": ("history_date", "history_id"), - }, - bases=(simple_history.models.HistoricalChanges, models.Model), - ), - migrations.CreateModel( - name="HistoricalGame", - fields=[ - ("id", models.TextField(db_index=True)), - ("slug", models.TextField(blank=True, null=True)), - ( - "twitch_url", - models.GeneratedField( # type: ignore # noqa: PGH003 - db_persist=True, - expression=django.db.models.functions.text.Concat( - models.Value("https://www.twitch.tv/directory/category/"), - "slug", - ), - output_field=models.TextField(), - ), - ), - ( - "image_url", - models.GeneratedField( # type: ignore # noqa: PGH003 - db_persist=True, - expression=django.db.models.functions.text.Concat( - models.Value("https://static-cdn.jtvnw.net/ttv-boxart/"), - "id", - models.Value("_IGDB.jpg"), - ), - output_field=models.URLField(), - ), - ), - ("display_name", models.TextField(blank=True, null=True)), - ( - "added_at", - models.DateTimeField(blank=True, editable=False, null=True), - ), - ( - "modified_at", - models.DateTimeField(blank=True, editable=False, null=True), - ), - ("history_id", models.AutoField(primary_key=True, serialize=False)), - ("history_date", models.DateTimeField(db_index=True)), - ("history_change_reason", models.CharField(max_length=100, null=True)), - ( - "history_type", - models.CharField( - choices=[("+", "Created"), ("~", "Changed"), ("-", "Deleted")], - max_length=1, - ), - ), - ( - "history_user", - models.ForeignKey( - null=True, - on_delete=django.db.models.deletion.SET_NULL, - related_name="+", - to=settings.AUTH_USER_MODEL, - ), - ), - ], - options={ - "verbose_name": "historical game", - "verbose_name_plural": "historical games", - "ordering": ("-history_date", "-history_id"), - "get_latest_by": ("history_date", "history_id"), - }, - bases=(simple_history.models.HistoricalChanges, models.Model), - ), - migrations.CreateModel( - name="HistoricalTimeBasedDrop", - fields=[ - ("id", models.TextField(db_index=True)), - ("required_subs", models.IntegerField(blank=True, null=True)), - ("end_at", models.DateTimeField(blank=True, null=True)), - ("name", models.TextField(blank=True, null=True)), - ( - "required_minutes_watched", - models.IntegerField(blank=True, null=True), - ), - ("start_at", models.DateTimeField(blank=True, null=True)), - ( - "added_at", - models.DateTimeField(blank=True, editable=False, null=True), - ), - ( - "modified_at", - models.DateTimeField(blank=True, editable=False, null=True), - ), - ("history_id", models.AutoField(primary_key=True, serialize=False)), - ("history_date", models.DateTimeField(db_index=True)), - ("history_change_reason", models.CharField(max_length=100, null=True)), - ( - "history_type", - models.CharField( - choices=[("+", "Created"), ("~", "Changed"), ("-", "Deleted")], - max_length=1, - ), - ), - ( - "history_user", - models.ForeignKey( - null=True, - on_delete=django.db.models.deletion.SET_NULL, - related_name="+", - to=settings.AUTH_USER_MODEL, - ), - ), - ], - options={ - "verbose_name": "historical time based drop", - "verbose_name_plural": "historical time based drops", - "ordering": ("-history_date", "-history_id"), - "get_latest_by": ("history_date", "history_id"), - }, - bases=(simple_history.models.HistoricalChanges, models.Model), - ), - migrations.CreateModel( - name="HistoricalUser", - fields=[ - ("id", models.TextField(db_index=True)), - ( - "added_at", - models.DateTimeField(blank=True, editable=False, null=True), - ), - ( - "modified_at", - models.DateTimeField(blank=True, editable=False, null=True), - ), - ("history_id", models.AutoField(primary_key=True, serialize=False)), - ("history_date", models.DateTimeField(db_index=True)), - ("history_change_reason", models.CharField(max_length=100, null=True)), - ( - "history_type", - models.CharField( - choices=[("+", "Created"), ("~", "Changed"), ("-", "Deleted")], - max_length=1, - ), - ), - ( - "history_user", - models.ForeignKey( - null=True, - on_delete=django.db.models.deletion.SET_NULL, - related_name="+", - to=settings.AUTH_USER_MODEL, - ), - ), - ], - options={ - "verbose_name": "historical user", - "verbose_name_plural": "historical users", - "ordering": ("-history_date", "-history_id"), - "get_latest_by": ("history_date", "history_id"), - }, - bases=(simple_history.models.HistoricalChanges, models.Model), - ), - ] diff --git a/twitch_app/migrations/0004_remove_user_drop_campaigns_delete_historicaluser_and_more.py b/twitch_app/migrations/0004_remove_user_drop_campaigns_delete_historicaluser_and_more.py deleted file mode 100644 index 02a30a6..0000000 --- a/twitch_app/migrations/0004_remove_user_drop_campaigns_delete_historicaluser_and_more.py +++ /dev/null @@ -1,23 +0,0 @@ -# Generated by Django 5.0.6 on 2024-07-01 15:23 - -from django.db import migrations -from django.db.migrations.operations.base import Operation - - -class Migration(migrations.Migration): - dependencies: list[tuple[str, str]] = [ - ("twitch_app", "0003_historicaldropbenefit_historicaldropcampaign_and_more"), - ] - - operations: list[Operation] = [ - migrations.RemoveField( - model_name="user", - name="drop_campaigns", - ), - migrations.DeleteModel( - name="HistoricalUser", - ), - migrations.DeleteModel( - name="User", - ), - ] diff --git a/twitch_app/migrations/0005_alter_dropbenefit_options_alter_dropcampaign_options_and_more.py b/twitch_app/migrations/0005_alter_dropbenefit_options_alter_dropcampaign_options_and_more.py deleted file mode 100644 index 0ed97db..0000000 --- a/twitch_app/migrations/0005_alter_dropbenefit_options_alter_dropcampaign_options_and_more.py +++ /dev/null @@ -1,92 +0,0 @@ -# Generated by Django 5.0.6 on 2024-07-01 15:40 - -from django.db import migrations -from django.db.migrations.operations.base import Operation - - -class Migration(migrations.Migration): - dependencies: list[tuple[str, str]] = [ - ( - "twitch_app", - "0004_remove_user_drop_campaigns_delete_historicaluser_and_more", - ), - ] - - operations: list[Operation] = [ - migrations.AlterModelOptions( - name="dropbenefit", - options={ - "ordering": ("name",), - "verbose_name": "Drop Benefit", - "verbose_name_plural": "Drop Benefits", - }, - ), - migrations.AlterModelOptions( - name="dropcampaign", - options={ - "ordering": ("name",), - "verbose_name": "Drop Campaign", - "verbose_name_plural": "Drop Campaigns", - }, - ), - migrations.AlterModelOptions( - name="game", - options={ - "ordering": ("display_name",), - "verbose_name": "Game", - "verbose_name_plural": "Games", - }, - ), - migrations.AlterModelOptions( - name="historicaldropbenefit", - options={ - "get_latest_by": ("history_date", "history_id"), - "ordering": ("-history_date", "-history_id"), - "verbose_name": "historical Drop Benefit", - "verbose_name_plural": "historical Drop Benefits", - }, - ), - migrations.AlterModelOptions( - name="historicaldropcampaign", - options={ - "get_latest_by": ("history_date", "history_id"), - "ordering": ("-history_date", "-history_id"), - "verbose_name": "historical Drop Campaign", - "verbose_name_plural": "historical Drop Campaigns", - }, - ), - migrations.AlterModelOptions( - name="historicalgame", - options={ - "get_latest_by": ("history_date", "history_id"), - "ordering": ("-history_date", "-history_id"), - "verbose_name": "historical Game", - "verbose_name_plural": "historical Games", - }, - ), - migrations.AlterModelOptions( - name="historicaltimebaseddrop", - options={ - "get_latest_by": ("history_date", "history_id"), - "ordering": ("-history_date", "-history_id"), - "verbose_name": "historical Time-Based Drop", - "verbose_name_plural": "historical Time-Based Drops", - }, - ), - migrations.AlterModelOptions( - name="organization", - options={ - "ordering": ("name",), - "verbose_name": "Organization", - "verbose_name_plural": "Organizations", - }, - ), - migrations.AlterModelOptions( - name="timebaseddrop", - options={ - "ordering": ("name",), - "verbose_name": "Time-Based Drop", - "verbose_name_plural": "Time-Based Drops", - }, - ), - ] diff --git a/twitch_app/migrations/0006_alter_dropbenefit_options_alter_dropcampaign_options_and_more.py b/twitch_app/migrations/0006_alter_dropbenefit_options_alter_dropcampaign_options_and_more.py deleted file mode 100644 index 63bd598..0000000 --- a/twitch_app/migrations/0006_alter_dropbenefit_options_alter_dropcampaign_options_and_more.py +++ /dev/null @@ -1,172 +0,0 @@ -# Generated by Django 5.0.6 on 2024-07-01 15:44 - -import auto_prefetch -import django.db.models.deletion -import django.db.models.manager -from django.db import migrations -from django.db.migrations.operations.base import Operation - - -class Migration(migrations.Migration): - dependencies: list[tuple[str, str]] = [ - ("twitch_app", "0005_alter_dropbenefit_options_alter_dropcampaign_options_and_more"), - ] - - operations: list[Operation] = [ - migrations.AlterModelOptions( - name="dropbenefit", - options={ - "base_manager_name": "prefetch_manager", - "ordering": ("name",), - "verbose_name": "Drop Benefit", - "verbose_name_plural": "Drop Benefits", - }, - ), - migrations.AlterModelOptions( - name="dropcampaign", - options={ - "base_manager_name": "prefetch_manager", - "ordering": ("name",), - "verbose_name": "Drop Campaign", - "verbose_name_plural": "Drop Campaigns", - }, - ), - migrations.AlterModelOptions( - name="game", - options={ - "base_manager_name": "prefetch_manager", - "ordering": ("display_name",), - "verbose_name": "Game", - "verbose_name_plural": "Games", - }, - ), - migrations.AlterModelOptions( - name="organization", - options={ - "base_manager_name": "prefetch_manager", - "ordering": ("name",), - "verbose_name": "Organization", - "verbose_name_plural": "Organizations", - }, - ), - migrations.AlterModelOptions( - name="timebaseddrop", - options={ - "base_manager_name": "prefetch_manager", - "ordering": ("name",), - "verbose_name": "Time-Based Drop", - "verbose_name_plural": "Time-Based Drops", - }, - ), - migrations.AlterModelManagers( - name="dropbenefit", - managers=[ - ("objects", django.db.models.manager.Manager()), - ("prefetch_manager", django.db.models.manager.Manager()), - ], - ), - migrations.AlterModelManagers( - name="dropcampaign", - managers=[ - ("objects", django.db.models.manager.Manager()), - ("prefetch_manager", django.db.models.manager.Manager()), - ], - ), - migrations.AlterModelManagers( - name="game", - managers=[ - ("objects", django.db.models.manager.Manager()), - ("prefetch_manager", django.db.models.manager.Manager()), - ], - ), - migrations.AlterModelManagers( - name="organization", - managers=[ - ("objects", django.db.models.manager.Manager()), - ("prefetch_manager", django.db.models.manager.Manager()), - ], - ), - migrations.AlterModelManagers( - name="timebaseddrop", - managers=[ - ("objects", django.db.models.manager.Manager()), - ("prefetch_manager", django.db.models.manager.Manager()), - ], - ), - migrations.AlterField( - model_name="dropbenefit", - name="game", - field=auto_prefetch.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to="twitch_app.game"), - ), - migrations.AlterField( - model_name="dropbenefit", - name="owner_organization", - field=auto_prefetch.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to="twitch_app.organization"), - ), - migrations.AlterField( - model_name="dropcampaign", - name="game", - field=auto_prefetch.ForeignKey( - on_delete=django.db.models.deletion.CASCADE, - related_name="drop_campaigns", - to="twitch_app.game", - ), - ), - migrations.AlterField( - model_name="dropcampaign", - name="owner", - field=auto_prefetch.ForeignKey( - on_delete=django.db.models.deletion.CASCADE, - related_name="drop_campaigns", - to="twitch_app.organization", - ), - ), - migrations.AlterField( - model_name="historicaldropbenefit", - name="game", - field=auto_prefetch.ForeignKey( - blank=True, - db_constraint=False, - null=True, - on_delete=django.db.models.deletion.DO_NOTHING, - related_name="+", - to="twitch_app.game", - ), - ), - migrations.AlterField( - model_name="historicaldropbenefit", - name="owner_organization", - field=auto_prefetch.ForeignKey( - blank=True, - db_constraint=False, - null=True, - on_delete=django.db.models.deletion.DO_NOTHING, - related_name="+", - to="twitch_app.organization", - ), - ), - migrations.AlterField( - model_name="historicaldropcampaign", - name="game", - field=auto_prefetch.ForeignKey( - blank=True, - db_constraint=False, - null=True, - on_delete=django.db.models.deletion.DO_NOTHING, - related_name="+", - to="twitch_app.game", - ), - ), - migrations.AlterField( - model_name="historicaldropcampaign", - name="owner", - field=auto_prefetch.ForeignKey( - blank=True, - db_constraint=False, - null=True, - on_delete=django.db.models.deletion.DO_NOTHING, - related_name="+", - to="twitch_app.organization", - ), - ), - ] diff --git a/twitch_app/migrations/0007_alter_dropcampaign_time_based_drops_and_more.py b/twitch_app/migrations/0007_alter_dropcampaign_time_based_drops_and_more.py deleted file mode 100644 index 365c558..0000000 --- a/twitch_app/migrations/0007_alter_dropcampaign_time_based_drops_and_more.py +++ /dev/null @@ -1,23 +0,0 @@ -# Generated by Django 5.0.6 on 2024-07-02 17:11 - -from django.db import migrations, models -from django.db.migrations.operations.base import Operation - - -class Migration(migrations.Migration): - dependencies: list[tuple[str, str]] = [ - ("twitch_app", "0006_alter_dropbenefit_options_alter_dropcampaign_options_and_more"), - ] - - operations: list[Operation] = [ - migrations.AlterField( - model_name="dropcampaign", - name="time_based_drops", - field=models.ManyToManyField(related_name="drop_campaigns", to="twitch_app.timebaseddrop"), - ), - migrations.AlterField( - model_name="timebaseddrop", - name="benefits", - field=models.ManyToManyField(related_name="time_based_drops", to="twitch_app.dropbenefit"), - ), - ] diff --git a/twitch_app/migrations/0008_alter_dropbenefit_game_and_more.py b/twitch_app/migrations/0008_alter_dropbenefit_game_and_more.py deleted file mode 100644 index 4d04bba..0000000 --- a/twitch_app/migrations/0008_alter_dropbenefit_game_and_more.py +++ /dev/null @@ -1,33 +0,0 @@ -# Generated by Django 5.0.6 on 2024-07-03 21:19 - -import auto_prefetch -import django.db.models.deletion -from django.db import migrations -from django.db.migrations.operations.base import Operation - - -class Migration(migrations.Migration): - dependencies: list[tuple[str, str]] = [ - ("twitch_app", "0007_alter_dropcampaign_time_based_drops_and_more"), - ] - - operations: list[Operation] = [ - migrations.AlterField( - model_name="dropbenefit", - name="game", - field=auto_prefetch.ForeignKey( - on_delete=django.db.models.deletion.CASCADE, - related_name="drop_benefits", - to="twitch_app.game", - ), - ), - migrations.AlterField( - model_name="dropbenefit", - name="owner_organization", - field=auto_prefetch.ForeignKey( - on_delete=django.db.models.deletion.CASCADE, - related_name="drop_benefits", - to="twitch_app.organization", - ), - ), - ] diff --git a/twitch_app/models.py b/twitch_app/models.py index b30f5a0..0cd3bed 100644 --- a/twitch_app/models.py +++ b/twitch_app/models.py @@ -3,10 +3,7 @@ from typing import Literal import auto_prefetch from django.db import models from django.db.models import Value -from django.db.models.functions import ( - Concat, -) -from simple_history.models import HistoricalRecords +from django.db.models.functions import Concat class Organization(auto_prefetch.Model): @@ -35,6 +32,11 @@ class Game(auto_prefetch.Model): For example, MultiVersus. """ + organization = auto_prefetch.ForeignKey( + Organization, + on_delete=models.CASCADE, + related_name="games", + ) id = models.TextField(primary_key=True) slug = models.TextField(blank=True, null=True) twitch_url = models.GeneratedField( # type: ignore # noqa: PGH003 @@ -54,7 +56,6 @@ class Game(auto_prefetch.Model): display_name = models.TextField(blank=True, null=True) added_at = models.DateTimeField(blank=True, null=True, auto_now_add=True) modified_at = models.DateTimeField(blank=True, null=True, auto_now=True) - history = HistoricalRecords() class Meta(auto_prefetch.Model.Meta): verbose_name: str = "Game" @@ -65,55 +66,29 @@ class Game(auto_prefetch.Model): return self.display_name or self.slug or self.id -class DropBenefit(auto_prefetch.Model): - """Information about the drop.""" +class Drop(auto_prefetch.Model): + """The actual drop that is being given out.""" id = models.TextField(primary_key=True) created_at = models.DateTimeField(blank=True, null=True) entitlement_limit = models.IntegerField(blank=True, null=True) image_asset_url = models.URLField(blank=True, null=True) - is_ios_available = models.BooleanField(blank=True, null=True) name = models.TextField(blank=True, null=True) - owner_organization = auto_prefetch.ForeignKey( - Organization, - on_delete=models.CASCADE, - related_name="drop_benefits", - ) - game = auto_prefetch.ForeignKey(Game, on_delete=models.CASCADE, related_name="drop_benefits") added_at = models.DateTimeField(blank=True, null=True, auto_now_add=True) modified_at = models.DateTimeField(blank=True, null=True, auto_now=True) - history = HistoricalRecords() - - class Meta(auto_prefetch.Model.Meta): - verbose_name: str = "Drop Benefit" - verbose_name_plural: str = "Drop Benefits" - ordering: tuple[Literal["name"]] = ("name",) - - def __str__(self) -> str: - return f"{self.owner_organization.name} - {self.game.display_name} - {self.name}" - - -class TimeBasedDrop(auto_prefetch.Model): - """The actual drop that is being given out.""" - - id = models.TextField(primary_key=True) required_subs = models.IntegerField(blank=True, null=True) end_at = models.DateTimeField(blank=True, null=True) - name = models.TextField(blank=True, null=True) required_minutes_watched = models.IntegerField(blank=True, null=True) start_at = models.DateTimeField(blank=True, null=True) - benefits = models.ManyToManyField(DropBenefit, related_name="time_based_drops") - added_at = models.DateTimeField(blank=True, null=True, auto_now_add=True) - modified_at = models.DateTimeField(blank=True, null=True, auto_now=True) - history = HistoricalRecords() + drop_campaign = auto_prefetch.ForeignKey("DropCampaign", on_delete=models.CASCADE, related_name="drops") class Meta(auto_prefetch.Model.Meta): - verbose_name: str = "Time-Based Drop" - verbose_name_plural: str = "Time-Based Drops" + verbose_name: str = "Drop" + verbose_name_plural: str = "Drops" ordering: tuple[Literal["name"]] = ("name",) def __str__(self) -> str: - return f"{self.benefits.first()} - {self.name}" + return f"{self.name}" class DropCampaign(auto_prefetch.Model): @@ -136,15 +111,8 @@ class DropCampaign(auto_prefetch.Model): on_delete=models.CASCADE, related_name="drop_campaigns", ) - owner = auto_prefetch.ForeignKey( - Organization, - on_delete=models.CASCADE, - related_name="drop_campaigns", - ) - time_based_drops = models.ManyToManyField(TimeBasedDrop, related_name="drop_campaigns") added_at = models.DateTimeField(blank=True, null=True, auto_now_add=True) modified_at = models.DateTimeField(blank=True, null=True, auto_now=True) - history = HistoricalRecords() class Meta(auto_prefetch.Model.Meta): verbose_name: str = "Drop Campaign" @@ -152,4 +120,4 @@ class DropCampaign(auto_prefetch.Model): ordering: tuple[Literal["name"]] = ("name",) def __str__(self) -> str: - return f"{self.owner.name} - {self.game.display_name} - {self.name}" + return f"{self.game.display_name} - {self.name}"