Make index view faster
This commit is contained in:
1
.vscode/settings.json
vendored
1
.vscode/settings.json
vendored
@ -17,6 +17,7 @@
|
|||||||
"sitewide",
|
"sitewide",
|
||||||
"socialaccount",
|
"socialaccount",
|
||||||
"Stresss",
|
"Stresss",
|
||||||
|
"tocs",
|
||||||
"ttvdrops",
|
"ttvdrops",
|
||||||
"ulimits",
|
"ulimits",
|
||||||
"Valair",
|
"Valair",
|
||||||
|
@ -1,45 +1,31 @@
|
|||||||
# Generated by Django 5.1 on 2024-08-12 03:47
|
# Generated by Django 5.1 on 2024-08-12 23:16
|
||||||
from __future__ import annotations
|
|
||||||
|
|
||||||
from typing import TYPE_CHECKING
|
|
||||||
|
|
||||||
import django.db.models.deletion
|
import django.db.models.deletion
|
||||||
import django.db.models.manager
|
|
||||||
from django.db import migrations, models
|
from django.db import migrations, models
|
||||||
|
from django.db.migrations.operations.base import Operation
|
||||||
if TYPE_CHECKING:
|
|
||||||
from django.db.migrations.operations.base import Operation
|
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
class Migration(migrations.Migration):
|
||||||
|
replaces: list[tuple[str, str]] = [
|
||||||
|
("core", "0001_initial"),
|
||||||
|
("core", "0002_alter_benefit_time_based_drop_and_more"),
|
||||||
|
("core", "0003_alter_benefit_options_alter_channel_options_and_more"),
|
||||||
|
]
|
||||||
|
|
||||||
initial = True
|
initial = True
|
||||||
|
|
||||||
dependencies: list[tuple[str, str]] = []
|
dependencies: list[tuple[str, str]] = []
|
||||||
|
|
||||||
operations: list[Operation] = [
|
operations: list[Operation] = [
|
||||||
migrations.CreateModel(
|
migrations.CreateModel(
|
||||||
name="DropCampaign",
|
name="Owner",
|
||||||
fields=[
|
fields=[
|
||||||
("created_at", models.DateTimeField(auto_created=True, null=True)),
|
|
||||||
("id", models.TextField(primary_key=True, serialize=False)),
|
("id", models.TextField(primary_key=True, serialize=False)),
|
||||||
("modified_at", models.DateTimeField(auto_now=True, null=True)),
|
|
||||||
("account_link_url", models.URLField(null=True)),
|
|
||||||
("description", models.TextField(null=True)),
|
|
||||||
("details_url", models.URLField(null=True)),
|
|
||||||
("ends_at", models.DateTimeField(null=True)),
|
|
||||||
("starts_at", models.DateTimeField(null=True)),
|
|
||||||
("image_url", models.URLField(null=True)),
|
|
||||||
("name", models.TextField(null=True)),
|
("name", models.TextField(null=True)),
|
||||||
("status", models.TextField(null=True)),
|
|
||||||
],
|
],
|
||||||
options={
|
options={
|
||||||
"abstract": False,
|
"abstract": False,
|
||||||
"base_manager_name": "prefetch_manager",
|
|
||||||
},
|
},
|
||||||
managers=[
|
|
||||||
("objects", django.db.models.manager.Manager()),
|
|
||||||
("prefetch_manager", django.db.models.manager.Manager()),
|
|
||||||
],
|
|
||||||
),
|
),
|
||||||
migrations.CreateModel(
|
migrations.CreateModel(
|
||||||
name="Game",
|
name="Game",
|
||||||
@ -49,69 +35,19 @@ class Migration(migrations.Migration):
|
|||||||
("name", models.TextField(null=True)),
|
("name", models.TextField(null=True)),
|
||||||
("box_art_url", models.URLField(null=True)),
|
("box_art_url", models.URLField(null=True)),
|
||||||
("slug", models.TextField(null=True)),
|
("slug", models.TextField(null=True)),
|
||||||
|
(
|
||||||
|
"org",
|
||||||
|
models.ForeignKey(
|
||||||
|
null=True,
|
||||||
|
on_delete=django.db.models.deletion.CASCADE,
|
||||||
|
related_name="games",
|
||||||
|
to="core.owner",
|
||||||
|
),
|
||||||
|
),
|
||||||
],
|
],
|
||||||
options={
|
options={
|
||||||
"abstract": False,
|
"abstract": False,
|
||||||
"base_manager_name": "prefetch_manager",
|
|
||||||
},
|
},
|
||||||
managers=[
|
|
||||||
("objects", django.db.models.manager.Manager()),
|
|
||||||
("prefetch_manager", django.db.models.manager.Manager()),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
migrations.CreateModel(
|
|
||||||
name="Owner",
|
|
||||||
fields=[
|
|
||||||
("id", models.TextField(primary_key=True, serialize=False)),
|
|
||||||
("name", models.TextField(null=True)),
|
|
||||||
],
|
|
||||||
options={
|
|
||||||
"abstract": False,
|
|
||||||
"base_manager_name": "prefetch_manager",
|
|
||||||
},
|
|
||||||
managers=[
|
|
||||||
("objects", django.db.models.manager.Manager()),
|
|
||||||
("prefetch_manager", django.db.models.manager.Manager()),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
migrations.CreateModel(
|
|
||||||
name="Channel",
|
|
||||||
fields=[
|
|
||||||
("twitch_id", models.TextField(primary_key=True, serialize=False)),
|
|
||||||
("display_name", models.TextField(null=True)),
|
|
||||||
("name", models.TextField(null=True)),
|
|
||||||
("twitch_url", models.URLField(null=True)),
|
|
||||||
("live", models.BooleanField(default=False)),
|
|
||||||
("drop_campaigns", models.ManyToManyField(related_name="channels", to="core.dropcampaign")),
|
|
||||||
],
|
|
||||||
options={
|
|
||||||
"abstract": False,
|
|
||||||
"base_manager_name": "prefetch_manager",
|
|
||||||
},
|
|
||||||
managers=[
|
|
||||||
("objects", django.db.models.manager.Manager()),
|
|
||||||
("prefetch_manager", django.db.models.manager.Manager()),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
migrations.AddField(
|
|
||||||
model_name="dropcampaign",
|
|
||||||
name="game",
|
|
||||||
field=models.ForeignKey(
|
|
||||||
null=True,
|
|
||||||
on_delete=django.db.models.deletion.CASCADE,
|
|
||||||
related_name="drop_campaigns",
|
|
||||||
to="core.game",
|
|
||||||
),
|
|
||||||
),
|
|
||||||
migrations.AddField(
|
|
||||||
model_name="game",
|
|
||||||
name="org",
|
|
||||||
field=models.ForeignKey(
|
|
||||||
null=True,
|
|
||||||
on_delete=django.db.models.deletion.CASCADE,
|
|
||||||
related_name="games",
|
|
||||||
to="core.owner",
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
migrations.CreateModel(
|
migrations.CreateModel(
|
||||||
name="RewardCampaign",
|
name="RewardCampaign",
|
||||||
@ -145,41 +81,35 @@ class Migration(migrations.Migration):
|
|||||||
],
|
],
|
||||||
options={
|
options={
|
||||||
"abstract": False,
|
"abstract": False,
|
||||||
"base_manager_name": "prefetch_manager",
|
|
||||||
},
|
},
|
||||||
managers=[
|
|
||||||
("objects", django.db.models.manager.Manager()),
|
|
||||||
("prefetch_manager", django.db.models.manager.Manager()),
|
|
||||||
],
|
|
||||||
),
|
),
|
||||||
migrations.CreateModel(
|
migrations.CreateModel(
|
||||||
name="Reward",
|
name="DropCampaign",
|
||||||
fields=[
|
fields=[
|
||||||
|
("created_at", models.DateTimeField(auto_created=True, null=True)),
|
||||||
("id", models.TextField(primary_key=True, serialize=False)),
|
("id", models.TextField(primary_key=True, serialize=False)),
|
||||||
|
("modified_at", models.DateTimeField(auto_now=True, null=True)),
|
||||||
|
("account_link_url", models.URLField(null=True)),
|
||||||
|
("description", models.TextField(null=True)),
|
||||||
|
("details_url", models.URLField(null=True)),
|
||||||
|
("ends_at", models.DateTimeField(null=True)),
|
||||||
|
("starts_at", models.DateTimeField(null=True)),
|
||||||
|
("image_url", models.URLField(null=True)),
|
||||||
("name", models.TextField(null=True)),
|
("name", models.TextField(null=True)),
|
||||||
("banner_image_url", models.URLField(null=True)),
|
("status", models.TextField(null=True)),
|
||||||
("thumbnail_image_url", models.URLField(null=True)),
|
|
||||||
("earnable_until", models.DateTimeField(null=True)),
|
|
||||||
("redemption_instructions", models.TextField(null=True)),
|
|
||||||
("redemption_url", models.URLField(null=True)),
|
|
||||||
(
|
(
|
||||||
"campaign",
|
"game",
|
||||||
models.ForeignKey(
|
models.ForeignKey(
|
||||||
null=True,
|
null=True,
|
||||||
on_delete=django.db.models.deletion.CASCADE,
|
on_delete=django.db.models.deletion.CASCADE,
|
||||||
related_name="rewards",
|
related_name="drop_campaigns",
|
||||||
to="core.rewardcampaign",
|
to="core.game",
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
options={
|
options={
|
||||||
"abstract": False,
|
"abstract": False,
|
||||||
"base_manager_name": "prefetch_manager",
|
|
||||||
},
|
},
|
||||||
managers=[
|
|
||||||
("objects", django.db.models.manager.Manager()),
|
|
||||||
("prefetch_manager", django.db.models.manager.Manager()),
|
|
||||||
],
|
|
||||||
),
|
),
|
||||||
migrations.CreateModel(
|
migrations.CreateModel(
|
||||||
name="TimeBasedDrop",
|
name="TimeBasedDrop",
|
||||||
@ -204,12 +134,21 @@ class Migration(migrations.Migration):
|
|||||||
],
|
],
|
||||||
options={
|
options={
|
||||||
"abstract": False,
|
"abstract": False,
|
||||||
"base_manager_name": "prefetch_manager",
|
|
||||||
},
|
},
|
||||||
managers=[
|
),
|
||||||
("objects", django.db.models.manager.Manager()),
|
migrations.CreateModel(
|
||||||
("prefetch_manager", django.db.models.manager.Manager()),
|
name="Channel",
|
||||||
|
fields=[
|
||||||
|
("twitch_id", models.TextField(primary_key=True, serialize=False)),
|
||||||
|
("display_name", models.TextField(null=True)),
|
||||||
|
("name", models.TextField(null=True)),
|
||||||
|
("twitch_url", models.URLField(null=True)),
|
||||||
|
("live", models.BooleanField(default=False)),
|
||||||
|
("drop_campaigns", models.ManyToManyField(related_name="channels", to="core.dropcampaign")),
|
||||||
],
|
],
|
||||||
|
options={
|
||||||
|
"abstract": False,
|
||||||
|
},
|
||||||
),
|
),
|
||||||
migrations.CreateModel(
|
migrations.CreateModel(
|
||||||
name="Benefit",
|
name="Benefit",
|
||||||
@ -234,11 +173,30 @@ class Migration(migrations.Migration):
|
|||||||
],
|
],
|
||||||
options={
|
options={
|
||||||
"abstract": False,
|
"abstract": False,
|
||||||
"base_manager_name": "prefetch_manager",
|
|
||||||
},
|
},
|
||||||
managers=[
|
),
|
||||||
("objects", django.db.models.manager.Manager()),
|
migrations.CreateModel(
|
||||||
("prefetch_manager", django.db.models.manager.Manager()),
|
name="Reward",
|
||||||
|
fields=[
|
||||||
|
("id", models.TextField(primary_key=True, serialize=False)),
|
||||||
|
("name", models.TextField(null=True)),
|
||||||
|
("banner_image_url", models.URLField(null=True)),
|
||||||
|
("thumbnail_image_url", models.URLField(null=True)),
|
||||||
|
("earnable_until", models.DateTimeField(null=True)),
|
||||||
|
("redemption_instructions", models.TextField(null=True)),
|
||||||
|
("redemption_url", models.URLField(null=True)),
|
||||||
|
(
|
||||||
|
"campaign",
|
||||||
|
models.ForeignKey(
|
||||||
|
null=True,
|
||||||
|
on_delete=django.db.models.deletion.CASCADE,
|
||||||
|
related_name="rewards",
|
||||||
|
to="core.rewardcampaign",
|
||||||
|
),
|
||||||
|
),
|
||||||
],
|
],
|
||||||
|
options={
|
||||||
|
"abstract": False,
|
||||||
|
},
|
||||||
),
|
),
|
||||||
]
|
]
|
@ -1,75 +0,0 @@
|
|||||||
# Generated by Django 5.1 on 2024-08-12 22: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]] = [
|
|
||||||
("core", "0001_initial"),
|
|
||||||
]
|
|
||||||
|
|
||||||
operations: list[Operation] = [
|
|
||||||
migrations.AlterField(
|
|
||||||
model_name="benefit",
|
|
||||||
name="time_based_drop",
|
|
||||||
field=auto_prefetch.ForeignKey(
|
|
||||||
null=True,
|
|
||||||
on_delete=django.db.models.deletion.CASCADE,
|
|
||||||
related_name="benefits",
|
|
||||||
to="core.timebaseddrop",
|
|
||||||
),
|
|
||||||
),
|
|
||||||
migrations.AlterField(
|
|
||||||
model_name="dropcampaign",
|
|
||||||
name="game",
|
|
||||||
field=auto_prefetch.ForeignKey(
|
|
||||||
null=True,
|
|
||||||
on_delete=django.db.models.deletion.CASCADE,
|
|
||||||
related_name="drop_campaigns",
|
|
||||||
to="core.game",
|
|
||||||
),
|
|
||||||
),
|
|
||||||
migrations.AlterField(
|
|
||||||
model_name="game",
|
|
||||||
name="org",
|
|
||||||
field=auto_prefetch.ForeignKey(
|
|
||||||
null=True,
|
|
||||||
on_delete=django.db.models.deletion.CASCADE,
|
|
||||||
related_name="games",
|
|
||||||
to="core.owner",
|
|
||||||
),
|
|
||||||
),
|
|
||||||
migrations.AlterField(
|
|
||||||
model_name="reward",
|
|
||||||
name="campaign",
|
|
||||||
field=auto_prefetch.ForeignKey(
|
|
||||||
null=True,
|
|
||||||
on_delete=django.db.models.deletion.CASCADE,
|
|
||||||
related_name="rewards",
|
|
||||||
to="core.rewardcampaign",
|
|
||||||
),
|
|
||||||
),
|
|
||||||
migrations.AlterField(
|
|
||||||
model_name="rewardcampaign",
|
|
||||||
name="game",
|
|
||||||
field=auto_prefetch.ForeignKey(
|
|
||||||
null=True,
|
|
||||||
on_delete=django.db.models.deletion.CASCADE,
|
|
||||||
related_name="reward_campaigns",
|
|
||||||
to="core.game",
|
|
||||||
),
|
|
||||||
),
|
|
||||||
migrations.AlterField(
|
|
||||||
model_name="timebaseddrop",
|
|
||||||
name="drop_campaign",
|
|
||||||
field=auto_prefetch.ForeignKey(
|
|
||||||
null=True,
|
|
||||||
on_delete=django.db.models.deletion.CASCADE,
|
|
||||||
related_name="drops",
|
|
||||||
to="core.dropcampaign",
|
|
||||||
),
|
|
||||||
),
|
|
||||||
]
|
|
@ -2,13 +2,12 @@ from __future__ import annotations
|
|||||||
|
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
import auto_prefetch
|
|
||||||
from django.db import models
|
from django.db import models
|
||||||
|
|
||||||
logger: logging.Logger = logging.getLogger(__name__)
|
logger: logging.Logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
class Owner(auto_prefetch.Model):
|
class Owner(models.Model):
|
||||||
"""The company or person that owns the game.
|
"""The company or person that owns the game.
|
||||||
|
|
||||||
Drops will be grouped by the owner. Users can also subscribe to owners.
|
Drops will be grouped by the owner. Users can also subscribe to owners.
|
||||||
@ -17,8 +16,11 @@ class Owner(auto_prefetch.Model):
|
|||||||
id = models.TextField(primary_key=True) # "ad299ac0-f1a5-417d-881d-952c9aed00e9"
|
id = models.TextField(primary_key=True) # "ad299ac0-f1a5-417d-881d-952c9aed00e9"
|
||||||
name = models.TextField(null=True) # "Microsoft"
|
name = models.TextField(null=True) # "Microsoft"
|
||||||
|
|
||||||
|
def __str__(self) -> str:
|
||||||
|
return self.name or "Owner name unknown"
|
||||||
|
|
||||||
class Game(auto_prefetch.Model):
|
|
||||||
|
class Game(models.Model):
|
||||||
"""This is the game we will see on the front end."""
|
"""This is the game we will see on the front end."""
|
||||||
|
|
||||||
twitch_id = models.TextField(primary_key=True) # "509658"
|
twitch_id = models.TextField(primary_key=True) # "509658"
|
||||||
@ -27,13 +29,13 @@ class Game(auto_prefetch.Model):
|
|||||||
box_art_url = models.URLField(null=True) # "https://static-cdn.jtvnw.net/ttv-boxart/Halo%20Infinite.jpg"
|
box_art_url = models.URLField(null=True) # "https://static-cdn.jtvnw.net/ttv-boxart/Halo%20Infinite.jpg"
|
||||||
slug = models.TextField(null=True) # "halo-infinite"
|
slug = models.TextField(null=True) # "halo-infinite"
|
||||||
|
|
||||||
org = auto_prefetch.ForeignKey(Owner, on_delete=models.CASCADE, related_name="games", null=True)
|
org = models.ForeignKey(Owner, on_delete=models.CASCADE, related_name="games", null=True)
|
||||||
|
|
||||||
def __str__(self) -> str:
|
def __str__(self) -> str:
|
||||||
return self.name or "Game name unknown"
|
return self.name or "Game name unknown"
|
||||||
|
|
||||||
|
|
||||||
class DropCampaign(auto_prefetch.Model):
|
class DropCampaign(models.Model):
|
||||||
"""This is the drop campaign we will see on the front end."""
|
"""This is the drop campaign we will see on the front end."""
|
||||||
|
|
||||||
id = models.TextField(primary_key=True) # "f257ce6e-502a-11ef-816e-0a58a9feac02"
|
id = models.TextField(primary_key=True) # "f257ce6e-502a-11ef-816e-0a58a9feac02"
|
||||||
@ -49,7 +51,7 @@ class DropCampaign(auto_prefetch.Model):
|
|||||||
ends_at = models.DateTimeField(null=True) # "2024-08-12T05:59:59.999Z"
|
ends_at = models.DateTimeField(null=True) # "2024-08-12T05:59:59.999Z"
|
||||||
starts_at = models.DateTimeField(null=True) # "2024-08-11T11:00:00Z""
|
starts_at = models.DateTimeField(null=True) # "2024-08-11T11:00:00Z""
|
||||||
|
|
||||||
game = auto_prefetch.ForeignKey(Game, on_delete=models.CASCADE, related_name="drop_campaigns", null=True)
|
game = models.ForeignKey(Game, on_delete=models.CASCADE, related_name="drop_campaigns", null=True)
|
||||||
|
|
||||||
# "https://static-cdn.jtvnw.net/twitch-quests-assets/CAMPAIGN/c8e02666-8b86-471f-bf38-7ece29a758e4.png"
|
# "https://static-cdn.jtvnw.net/twitch-quests-assets/CAMPAIGN/c8e02666-8b86-471f-bf38-7ece29a758e4.png"
|
||||||
image_url = models.URLField(null=True)
|
image_url = models.URLField(null=True)
|
||||||
@ -57,8 +59,11 @@ class DropCampaign(auto_prefetch.Model):
|
|||||||
name = models.TextField(null=True) # "HCS Open Series - Week 1 - DAY 2 - AUG11"
|
name = models.TextField(null=True) # "HCS Open Series - Week 1 - DAY 2 - AUG11"
|
||||||
status = models.TextField(null=True) # "ACTIVE"
|
status = models.TextField(null=True) # "ACTIVE"
|
||||||
|
|
||||||
|
def __str__(self) -> str:
|
||||||
|
return self.name or "Drop campaign name unknown"
|
||||||
|
|
||||||
class Channel(auto_prefetch.Model):
|
|
||||||
|
class Channel(models.Model):
|
||||||
"""This is the channel we will see on the front end."""
|
"""This is the channel we will see on the front end."""
|
||||||
|
|
||||||
twitch_id = models.TextField(primary_key=True) # "222719079"
|
twitch_id = models.TextField(primary_key=True) # "222719079"
|
||||||
@ -69,8 +74,11 @@ class Channel(auto_prefetch.Model):
|
|||||||
|
|
||||||
drop_campaigns = models.ManyToManyField(DropCampaign, related_name="channels")
|
drop_campaigns = models.ManyToManyField(DropCampaign, related_name="channels")
|
||||||
|
|
||||||
|
def __str__(self) -> str:
|
||||||
|
return self.display_name or "Channel name unknown"
|
||||||
|
|
||||||
class TimeBasedDrop(auto_prefetch.Model):
|
|
||||||
|
class TimeBasedDrop(models.Model):
|
||||||
"""This is the drop we will see on the front end."""
|
"""This is the drop we will see on the front end."""
|
||||||
|
|
||||||
id = models.TextField(primary_key=True) # "d5cdf372-502b-11ef-bafd-0a58a9feac02"
|
id = models.TextField(primary_key=True) # "d5cdf372-502b-11ef-bafd-0a58a9feac02"
|
||||||
@ -83,10 +91,13 @@ class TimeBasedDrop(auto_prefetch.Model):
|
|||||||
required_minutes_watched = models.PositiveBigIntegerField(null=True) # "120"
|
required_minutes_watched = models.PositiveBigIntegerField(null=True) # "120"
|
||||||
starts_at = models.DateTimeField(null=True) # "2024-08-11T11:00:00Z"
|
starts_at = models.DateTimeField(null=True) # "2024-08-11T11:00:00Z"
|
||||||
|
|
||||||
drop_campaign = auto_prefetch.ForeignKey(DropCampaign, on_delete=models.CASCADE, related_name="drops", null=True)
|
drop_campaign = models.ForeignKey(DropCampaign, on_delete=models.CASCADE, related_name="drops", null=True)
|
||||||
|
|
||||||
|
def __str__(self) -> str:
|
||||||
|
return self.name or "Drop name unknown"
|
||||||
|
|
||||||
|
|
||||||
class Benefit(auto_prefetch.Model):
|
class Benefit(models.Model):
|
||||||
"""This is the benefit we will see on the front end."""
|
"""This is the benefit we will see on the front end."""
|
||||||
|
|
||||||
id = models.TextField(primary_key=True) # "d5cdf372-502b-11ef-bafd-0a58a9feac02"
|
id = models.TextField(primary_key=True) # "d5cdf372-502b-11ef-bafd-0a58a9feac02"
|
||||||
@ -104,15 +115,18 @@ class Benefit(auto_prefetch.Model):
|
|||||||
|
|
||||||
name = models.TextField(null=True) # "Cosmic Nexus Chimera"
|
name = models.TextField(null=True) # "Cosmic Nexus Chimera"
|
||||||
|
|
||||||
time_based_drop = auto_prefetch.ForeignKey(
|
time_based_drop = models.ForeignKey(
|
||||||
TimeBasedDrop,
|
TimeBasedDrop,
|
||||||
on_delete=models.CASCADE,
|
on_delete=models.CASCADE,
|
||||||
related_name="benefits",
|
related_name="benefits",
|
||||||
null=True,
|
null=True,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def __str__(self) -> str:
|
||||||
|
return self.name or "Benefit name unknown"
|
||||||
|
|
||||||
class RewardCampaign(auto_prefetch.Model):
|
|
||||||
|
class RewardCampaign(models.Model):
|
||||||
"""Buy subscriptions to earn rewards."""
|
"""Buy subscriptions to earn rewards."""
|
||||||
|
|
||||||
id = models.TextField(primary_key=True) # "dc4ff0b4-4de0-11ef-9ec3-621fb0811846"
|
id = models.TextField(primary_key=True) # "dc4ff0b4-4de0-11ef-9ec3-621fb0811846"
|
||||||
@ -130,7 +144,7 @@ class RewardCampaign(auto_prefetch.Model):
|
|||||||
external_url = models.URLField(null=True) # "https://tv.apple.com/includes/commerce/redeem/code-entry"
|
external_url = models.URLField(null=True) # "https://tv.apple.com/includes/commerce/redeem/code-entry"
|
||||||
about_url = models.URLField(null=True) # "https://blog.twitch.tv/2024/07/26/sub-and-get-apple-tv/"
|
about_url = models.URLField(null=True) # "https://blog.twitch.tv/2024/07/26/sub-and-get-apple-tv/"
|
||||||
is_site_wide = models.BooleanField(null=True) # "True"
|
is_site_wide = models.BooleanField(null=True) # "True"
|
||||||
game = auto_prefetch.ForeignKey(Game, on_delete=models.CASCADE, related_name="reward_campaigns", null=True)
|
game = models.ForeignKey(Game, on_delete=models.CASCADE, related_name="reward_campaigns", null=True)
|
||||||
|
|
||||||
sub_goal = models.PositiveBigIntegerField(null=True) # "1"
|
sub_goal = models.PositiveBigIntegerField(null=True) # "1"
|
||||||
minute_watched_goal = models.PositiveBigIntegerField(null=True) # "0"
|
minute_watched_goal = models.PositiveBigIntegerField(null=True) # "0"
|
||||||
@ -138,8 +152,11 @@ class RewardCampaign(auto_prefetch.Model):
|
|||||||
# "https://static-cdn.jtvnw.net/twitch-quests-assets/CAMPAIGN/quests_appletv_q3_2024/apple_150x200.png"
|
# "https://static-cdn.jtvnw.net/twitch-quests-assets/CAMPAIGN/quests_appletv_q3_2024/apple_150x200.png"
|
||||||
image_url = models.URLField(null=True)
|
image_url = models.URLField(null=True)
|
||||||
|
|
||||||
|
def __str__(self) -> str:
|
||||||
|
return self.name or "Reward campaign name unknown"
|
||||||
|
|
||||||
class Reward(auto_prefetch.Model):
|
|
||||||
|
class Reward(models.Model):
|
||||||
"""This from the RewardCampaign."""
|
"""This from the RewardCampaign."""
|
||||||
|
|
||||||
id = models.TextField(primary_key=True) # "dc2e9810-4de0-11ef-9ec3-621fb0811846"
|
id = models.TextField(primary_key=True) # "dc2e9810-4de0-11ef-9ec3-621fb0811846"
|
||||||
@ -154,4 +171,7 @@ class Reward(auto_prefetch.Model):
|
|||||||
redemption_instructions = models.TextField(null=True) # ""
|
redemption_instructions = models.TextField(null=True) # ""
|
||||||
redemption_url = models.URLField(null=True) # "https://tv.apple.com/includes/commerce/redeem/code-entry"
|
redemption_url = models.URLField(null=True) # "https://tv.apple.com/includes/commerce/redeem/code-entry"
|
||||||
|
|
||||||
campaign = auto_prefetch.ForeignKey(RewardCampaign, on_delete=models.CASCADE, related_name="rewards", null=True)
|
campaign = models.ForeignKey(RewardCampaign, on_delete=models.CASCADE, related_name="rewards", null=True)
|
||||||
|
|
||||||
|
def __str__(self) -> str:
|
||||||
|
return self.name or "Reward name unknown"
|
||||||
|
@ -11,14 +11,15 @@
|
|||||||
{% for campaign in reward_campaigns %}
|
{% for campaign in reward_campaigns %}
|
||||||
{% include "partials/reward_campaign_card.html" %}
|
{% include "partials/reward_campaign_card.html" %}
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
<h2>Organizations</h2>
|
<h2>
|
||||||
{% for org in owners %}
|
Drop campaigns -
|
||||||
<h2 id="org-{{ org.id }}">
|
<div class="d-inline text-muted ">{{ games.count }} games</div>
|
||||||
<a href="#org-{{ org.id }}">{{ org.name }}</a>
|
</h2>
|
||||||
</h2>
|
{% for game in games %}
|
||||||
{% for game in org.games.all %}
|
{# Only show games with drop campaigns #}
|
||||||
|
{% if game.drop_campaigns.count > 0 %}
|
||||||
{% include "partials/game_card.html" %}
|
{% include "partials/game_card.html" %}
|
||||||
{% endfor %}
|
{% endif %}
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
<div class="card mb-4 shadow-sm" id="game-{{ game.twitch_id }}">
|
<div class="card mb-4 shadow-sm" id="#{{ game.twitch_id }}">
|
||||||
<div class="row g-0">
|
<div class="row g-0">
|
||||||
<div class="col-md-2">
|
<div class="col-md-2">
|
||||||
<img src="https://static-cdn.jtvnw.net/ttv-boxart/{{ game.twitch_id }}_IGDB.jpg"
|
<img src="https://static-cdn.jtvnw.net/ttv-boxart/{{ game.twitch_id }}_IGDB.jpg"
|
||||||
alt="{{ game.display_name }}"
|
alt="{{ game.name }} box art"
|
||||||
class="img-fluid rounded-start"
|
class="img-fluid rounded-start"
|
||||||
height="283"
|
height="283"
|
||||||
width="212"
|
width="212"
|
||||||
@ -21,11 +21,18 @@
|
|||||||
{% if not forloop.first %}<br>{% endif %}
|
{% if not forloop.first %}<br>{% endif %}
|
||||||
<div class="mt-3">
|
<div class="mt-3">
|
||||||
<h3 class="h6">{{ campaign.name }}</h3>
|
<h3 class="h6">{{ campaign.name }}</h3>
|
||||||
|
{% if campaign.details_url == campaign.account_link_url %}
|
||||||
|
<a href="{{ campaign.details_url }}" class="text-decoration-none">Details</a>
|
||||||
|
{% else %}
|
||||||
|
<a href="{{ campaign.details_url }}" class="text-decoration-none">Details</a>
|
||||||
|
|
|
||||||
|
<a href="{{ campaign.account_link_url }}" class="text-decoration-none">Link Account</a>
|
||||||
|
{% endif %}
|
||||||
<p class="mb-2 text-muted">
|
<p class="mb-2 text-muted">
|
||||||
Ends in: <abbr title="{{ campaign.starts_at|date:'l d F H:i' }} - {{ campaign.ends_at|date:'l d F H:i e' }}">{{ campaign.ends_at|timeuntil }}</abbr>
|
Ends in: <abbr title="{{ campaign.starts_at|date:'l d F H:i' }} - {{ campaign.ends_at|date:'l d F H:i e' }}">{{ campaign.ends_at|timeuntil }}</abbr>
|
||||||
</p>
|
</p>
|
||||||
{% if campaign.description != campaign.name %}
|
{% if campaign.description != campaign.name %}
|
||||||
{% if campaign.description|length > 200 %}
|
{% if campaign.description|length > 300 %}
|
||||||
<p>
|
<p>
|
||||||
<a class="btn btn-link p-0 text-muted"
|
<a class="btn btn-link p-0 text-muted"
|
||||||
data-bs-toggle="collapse"
|
data-bs-toggle="collapse"
|
||||||
|
@ -4,10 +4,12 @@ import logging
|
|||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
from typing import TYPE_CHECKING
|
from typing import TYPE_CHECKING
|
||||||
|
|
||||||
|
from django.db.models import Prefetch
|
||||||
from django.db.models.manager import BaseManager
|
from django.db.models.manager import BaseManager
|
||||||
from django.template.response import TemplateResponse
|
from django.template.response import TemplateResponse
|
||||||
|
from django.utils import timezone # type: ignore # noqa: PGH003
|
||||||
|
|
||||||
from core.models.twitch import Game, Owner, RewardCampaign
|
from core.models.twitch import DropCampaign, Game, RewardCampaign
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from django.db.models.manager import BaseManager
|
from django.db.models.manager import BaseManager
|
||||||
@ -57,18 +59,30 @@ def index(request: HttpRequest) -> HttpResponse:
|
|||||||
Returns:
|
Returns:
|
||||||
HttpResponse: The response object
|
HttpResponse: The response object
|
||||||
"""
|
"""
|
||||||
reward_campaigns: BaseManager[RewardCampaign] = RewardCampaign.objects.all()
|
reward_campaigns: BaseManager[RewardCampaign] = (
|
||||||
owners: BaseManager[Owner] = Owner.objects.all()
|
RewardCampaign.objects.all()
|
||||||
|
.prefetch_related("rewards")
|
||||||
|
.filter(ends_at__gt=timezone.now(), starts_at__lt=timezone.now())
|
||||||
|
)
|
||||||
|
future_campaigns: BaseManager[DropCampaign] = DropCampaign.objects.filter(
|
||||||
|
ends_at__gt=timezone.now(),
|
||||||
|
starts_at__lt=timezone.now(),
|
||||||
|
)
|
||||||
|
|
||||||
toc: str = build_toc([
|
games: BaseManager[Game] = Game.objects.all().prefetch_related(
|
||||||
TOCItem(name="Information", toc_id="#info-box"),
|
Prefetch("drop_campaigns", queryset=future_campaigns.prefetch_related("drops__benefits")),
|
||||||
TOCItem(name="Games", toc_id="#games"),
|
)
|
||||||
])
|
tocs: list[TOCItem] = []
|
||||||
|
for game in games.all():
|
||||||
|
game_name: str = game.name or "<div class='text-muted'>Game name unknown</div>"
|
||||||
|
tocs.append(TOCItem(name=game_name, toc_id=f"#{game.twitch_id}"))
|
||||||
|
|
||||||
context: dict[str, BaseManager[RewardCampaign] | str | BaseManager[Owner]] = {
|
toc: str = build_toc(tocs)
|
||||||
|
|
||||||
|
context: dict[str, BaseManager[RewardCampaign] | str | BaseManager[Game]] = {
|
||||||
"reward_campaigns": reward_campaigns,
|
"reward_campaigns": reward_campaigns,
|
||||||
|
"games": games,
|
||||||
"toc": toc,
|
"toc": toc,
|
||||||
"owners": owners,
|
|
||||||
}
|
}
|
||||||
return TemplateResponse(request=request, template="index.html", context=context)
|
return TemplateResponse(request=request, template="index.html", context=context)
|
||||||
|
|
||||||
@ -84,10 +98,7 @@ def game_view(request: HttpRequest) -> HttpResponse:
|
|||||||
"""
|
"""
|
||||||
games: BaseManager[Game] = Game.objects.all()
|
games: BaseManager[Game] = Game.objects.all()
|
||||||
|
|
||||||
tocs: list[TOCItem] = [TOCItem(name=game.name, toc_id=game.slug) for game in games if game.name and game.slug]
|
context: dict[str, BaseManager[Game] | str] = {"games": games}
|
||||||
toc: str = build_toc(tocs)
|
|
||||||
|
|
||||||
context: dict[str, BaseManager[Game] | str] = {"games": games, "toc": toc}
|
|
||||||
return TemplateResponse(request=request, template="games.html", context=context)
|
return TemplateResponse(request=request, template="games.html", context=context)
|
||||||
|
|
||||||
|
|
||||||
|
16
poetry.lock
generated
16
poetry.lock
generated
@ -315,20 +315,6 @@ tzdata = {version = "*", markers = "sys_platform == \"win32\""}
|
|||||||
argon2 = ["argon2-cffi (>=19.1.0)"]
|
argon2 = ["argon2-cffi (>=19.1.0)"]
|
||||||
bcrypt = ["bcrypt"]
|
bcrypt = ["bcrypt"]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "django-auto-prefetch"
|
|
||||||
version = "1.9.0"
|
|
||||||
description = "Automatically prefetch foreign key values as needed."
|
|
||||||
optional = false
|
|
||||||
python-versions = ">=3.8"
|
|
||||||
files = [
|
|
||||||
{file = "django_auto_prefetch-1.9.0-py3-none-any.whl", hash = "sha256:2bcc1d4551a952cc73124d339319570392aece390cedffbd9585d94f802eef47"},
|
|
||||||
{file = "django_auto_prefetch-1.9.0.tar.gz", hash = "sha256:b447a0342a734bd3c718f9721598042721edce1a95b81df5b5a167dd0997f347"},
|
|
||||||
]
|
|
||||||
|
|
||||||
[package.dependencies]
|
|
||||||
django = ">=3.2"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "django-debug-toolbar"
|
name = "django-debug-toolbar"
|
||||||
version = "4.4.6"
|
version = "4.4.6"
|
||||||
@ -1217,4 +1203,4 @@ brotli = ["brotli"]
|
|||||||
[metadata]
|
[metadata]
|
||||||
lock-version = "2.0"
|
lock-version = "2.0"
|
||||||
python-versions = "^3.12"
|
python-versions = "^3.12"
|
||||||
content-hash = "4efd9a959fea305be05a55cc0041474ab83ba202dc4c55479bb9d2b66387393b"
|
content-hash = "36def17b6380edc474dbfa9cd469124d4ae2d3b0c27ea4fc8cb4c6c54dddb032"
|
||||||
|
@ -10,7 +10,6 @@ package-mode = false
|
|||||||
python = "^3.12"
|
python = "^3.12"
|
||||||
discord-webhook = "^1.3.1"
|
discord-webhook = "^1.3.1"
|
||||||
django = { version = "^5.1", allow-prereleases = true }
|
django = { version = "^5.1", allow-prereleases = true }
|
||||||
django-auto-prefetch = "^1.9.0"
|
|
||||||
django-debug-toolbar = "^4.4.6"
|
django-debug-toolbar = "^4.4.6"
|
||||||
django-simple-history = "^3.7.0"
|
django-simple-history = "^3.7.0"
|
||||||
pillow = "^10.4.0"
|
pillow = "^10.4.0"
|
||||||
|
@ -1,6 +0,0 @@
|
|||||||
djlint
|
|
||||||
pip
|
|
||||||
pre-commit
|
|
||||||
pytest
|
|
||||||
pytest-django
|
|
||||||
ruff
|
|
@ -1,13 +0,0 @@
|
|||||||
discord-webhook
|
|
||||||
django-auto-prefetch
|
|
||||||
django-debug-toolbar
|
|
||||||
django-simple-history
|
|
||||||
django>=0.0.0.dev0
|
|
||||||
hishel
|
|
||||||
httpx
|
|
||||||
pillow
|
|
||||||
platformdirs
|
|
||||||
python-dotenv
|
|
||||||
sentry-sdk[django]
|
|
||||||
undetected-playwright-patch
|
|
||||||
whitenoise[brotli]
|
|
Reference in New Issue
Block a user