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}"