Use template tags instead of partial HTML
This commit is contained in:
@ -43,7 +43,7 @@ repos:
|
|||||||
|
|
||||||
# An extremely fast Python linter and formatter.
|
# An extremely fast Python linter and formatter.
|
||||||
- repo: https://github.com/astral-sh/ruff-pre-commit
|
- repo: https://github.com/astral-sh/ruff-pre-commit
|
||||||
rev: v0.5.7
|
rev: v0.6.1
|
||||||
hooks:
|
hooks:
|
||||||
- id: ruff-format
|
- id: ruff-format
|
||||||
- id: ruff
|
- id: ruff
|
||||||
|
@ -76,11 +76,9 @@ async def add_reward_campaign(campaign: dict | None) -> None:
|
|||||||
"""
|
"""
|
||||||
if not campaign:
|
if not campaign:
|
||||||
return
|
return
|
||||||
|
|
||||||
logger.info("Adding reward campaign to database %s", campaign["id"])
|
|
||||||
if "data" in campaign and "rewardCampaignsAvailableToUser" in campaign["data"]:
|
if "data" in campaign and "rewardCampaignsAvailableToUser" in campaign["data"]:
|
||||||
for reward_campaign in campaign["data"]["rewardCampaignsAvailableToUser"]:
|
for reward_campaign in campaign["data"]["rewardCampaignsAvailableToUser"]:
|
||||||
our_reward_campaign = await handle_reward_campaign(reward_campaign)
|
our_reward_campaign: RewardCampaign = await handle_reward_campaign(reward_campaign)
|
||||||
|
|
||||||
if "rewards" in reward_campaign:
|
if "rewards" in reward_campaign:
|
||||||
for reward in reward_campaign["rewards"]:
|
for reward in reward_campaign["rewards"]:
|
||||||
@ -126,6 +124,7 @@ async def handle_reward_campaign(reward_campaign: dict) -> RewardCampaign:
|
|||||||
RewardCampaign: The reward campaign that was added or updated.
|
RewardCampaign: The reward campaign that was added or updated.
|
||||||
"""
|
"""
|
||||||
mappings: dict[str, str] = {
|
mappings: dict[str, str] = {
|
||||||
|
"name": "name",
|
||||||
"brand": "brand",
|
"brand": "brand",
|
||||||
"createdAt": "created_at",
|
"createdAt": "created_at",
|
||||||
"startsAt": "starts_at",
|
"startsAt": "starts_at",
|
||||||
@ -209,8 +208,7 @@ async def add_or_update_owner(owner_json: dict | None) -> Owner | None:
|
|||||||
if not owner_json:
|
if not owner_json:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
defaults = {}
|
defaults: dict[str, str] = {}
|
||||||
|
|
||||||
if owner_json.get("name"):
|
if owner_json.get("name"):
|
||||||
defaults["name"] = owner_json["name"]
|
defaults["name"] = owner_json["name"]
|
||||||
|
|
||||||
@ -235,7 +233,7 @@ async def add_or_update_channels(channels_json: list[dict]) -> list[Channel] | N
|
|||||||
|
|
||||||
channels: list[Channel] = []
|
channels: list[Channel] = []
|
||||||
for channel_json in channels_json:
|
for channel_json in channels_json:
|
||||||
defaults = {}
|
defaults: dict[str, str] = {}
|
||||||
if channel_json.get("displayName"):
|
if channel_json.get("displayName"):
|
||||||
defaults["display_name"] = channel_json["displayName"]
|
defaults["display_name"] = channel_json["displayName"]
|
||||||
|
|
||||||
@ -266,7 +264,7 @@ async def add_benefit(benefit: dict, time_based_drop: TimeBasedDrop) -> None:
|
|||||||
"isIosAvailable": "is_ios_available",
|
"isIosAvailable": "is_ios_available",
|
||||||
"name": "name",
|
"name": "name",
|
||||||
}
|
}
|
||||||
defaults = {new_key: benefit[key] for key, new_key in mappings.items() if benefit.get(key)}
|
defaults: dict[str, str] = {new_key: benefit[key] for key, new_key in mappings.items() if benefit.get(key)}
|
||||||
our_benefit, created = await Benefit.objects.aupdate_or_create(id=benefit["id"], defaults=defaults)
|
our_benefit, created = await Benefit.objects.aupdate_or_create(id=benefit["id"], defaults=defaults)
|
||||||
if created:
|
if created:
|
||||||
logger.info("Added benefit %s", our_benefit.id)
|
logger.info("Added benefit %s", our_benefit.id)
|
||||||
@ -284,9 +282,10 @@ async def add_drop_campaign(drop_campaign: dict | None) -> None:
|
|||||||
if not drop_campaign:
|
if not drop_campaign:
|
||||||
return
|
return
|
||||||
|
|
||||||
defaults = {}
|
defaults: dict[str, str | Game] = {}
|
||||||
if drop_campaign.get("game"):
|
if drop_campaign.get("game"):
|
||||||
game: Game | None = await add_or_update_game(drop_campaign["game"])
|
game: Game | None = await add_or_update_game(drop_campaign["game"])
|
||||||
|
if game:
|
||||||
defaults["game"] = game
|
defaults["game"] = game
|
||||||
|
|
||||||
mappings: dict[str, str] = {
|
mappings: dict[str, str] = {
|
||||||
@ -327,19 +326,22 @@ async def add_time_based_drops(drop_campaign: dict, our_drop_campaign: DropCampa
|
|||||||
our_drop_campaign (DropCampaign): The drop campaign object in the database.
|
our_drop_campaign (DropCampaign): The drop campaign object in the database.
|
||||||
"""
|
"""
|
||||||
for time_based_drop in drop_campaign.get("timeBasedDrops", []):
|
for time_based_drop in drop_campaign.get("timeBasedDrops", []):
|
||||||
|
time_based_drop: dict[str, typing.Any]
|
||||||
if time_based_drop.get("preconditionDrops"):
|
if time_based_drop.get("preconditionDrops"):
|
||||||
# TODO(TheLovinator): Add precondition drops to time-based drop # noqa: TD003
|
# TODO(TheLovinator): Add precondition drops to time-based drop # noqa: TD003
|
||||||
# TODO(TheLovinator): Send JSON to Discord # noqa: TD003
|
# TODO(TheLovinator): Send JSON to Discord # noqa: TD003
|
||||||
logger.error("Not implemented: Add precondition drops to time-based drop, JSON: %s", time_based_drop)
|
logger.error("Not implemented: Add precondition drops to time-based drop, JSON: %s", time_based_drop)
|
||||||
|
|
||||||
mappings = {
|
mappings: dict[str, str] = {
|
||||||
"requiredSubs": "required_subs",
|
"requiredSubs": "required_subs",
|
||||||
"endAt": "ends_at",
|
"endAt": "ends_at",
|
||||||
"name": "name",
|
"name": "name",
|
||||||
"requiredMinutesWatched": "required_minutes_watched",
|
"requiredMinutesWatched": "required_minutes_watched",
|
||||||
"startAt": "starts_at",
|
"startAt": "starts_at",
|
||||||
}
|
}
|
||||||
defaults = {new_key: time_based_drop[key] for key, new_key in mappings.items() if time_based_drop.get(key)}
|
defaults: dict[str, str | DropCampaign] = {
|
||||||
|
new_key: time_based_drop[key] for key, new_key in mappings.items() if time_based_drop.get(key)
|
||||||
|
}
|
||||||
if our_drop_campaign:
|
if our_drop_campaign:
|
||||||
defaults["drop_campaign"] = our_drop_campaign
|
defaults["drop_campaign"] = our_drop_campaign
|
||||||
|
|
||||||
@ -373,18 +375,18 @@ async def process_json_data(num: int, campaign: dict | None) -> None:
|
|||||||
|
|
||||||
# This is a Reward Campaign
|
# This is a Reward Campaign
|
||||||
if "rewardCampaignsAvailableToUser" in campaign.get("data", {}):
|
if "rewardCampaignsAvailableToUser" in campaign.get("data", {}):
|
||||||
save_json(campaign, "reward_campaigns")
|
save_json(campaign=campaign, dir_name="reward_campaigns")
|
||||||
await add_reward_campaign(campaign)
|
await add_reward_campaign(campaign=campaign)
|
||||||
|
|
||||||
if "dropCampaign" in campaign.get("data", {}).get("user", {}):
|
if "dropCampaign" in campaign.get("data", {}).get("user", {}):
|
||||||
save_json(campaign, "drop_campaign")
|
save_json(campaign=campaign, dir_name="drop_campaign")
|
||||||
if campaign.get("data", {}).get("user", {}).get("dropCampaign"):
|
if campaign.get("data", {}).get("user", {}).get("dropCampaign"):
|
||||||
await add_drop_campaign(campaign["data"]["user"]["dropCampaign"])
|
await add_drop_campaign(drop_campaign=campaign["data"]["user"]["dropCampaign"])
|
||||||
|
|
||||||
if "dropCampaigns" in campaign.get("data", {}).get("currentUser", {}):
|
if "dropCampaigns" in campaign.get("data", {}).get("currentUser", {}):
|
||||||
for drop_campaign in campaign["data"]["currentUser"]["dropCampaigns"]:
|
for drop_campaign in campaign["data"]["currentUser"]["dropCampaigns"]:
|
||||||
save_json(campaign, "drop_campaigns")
|
save_json(campaign=campaign, dir_name="drop_campaigns")
|
||||||
await add_drop_campaign(drop_campaign)
|
await add_drop_campaign(drop_campaign=drop_campaign)
|
||||||
|
|
||||||
|
|
||||||
class Command(BaseCommand):
|
class Command(BaseCommand):
|
||||||
@ -423,7 +425,7 @@ class Command(BaseCommand):
|
|||||||
while not logged_in:
|
while not logged_in:
|
||||||
try:
|
try:
|
||||||
await page.wait_for_selector(
|
await page.wait_for_selector(
|
||||||
'div[data-a-target="top-nav-avatar"]',
|
selector='div[data-a-target="top-nav-avatar"]',
|
||||||
timeout=300000,
|
timeout=300000,
|
||||||
)
|
)
|
||||||
logged_in = True
|
logged_in = True
|
||||||
@ -449,7 +451,7 @@ class Command(BaseCommand):
|
|||||||
|
|
||||||
async def run_with_playwright(self) -> None:
|
async def run_with_playwright(self) -> None:
|
||||||
async with async_playwright() as playwright:
|
async with async_playwright() as playwright:
|
||||||
await self.run(playwright)
|
await self.run(playwright=playwright)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
|
@ -171,13 +171,13 @@ MESSAGE_TAGS: dict[int, str] = {
|
|||||||
messages.ERROR: "alert-danger",
|
messages.ERROR: "alert-danger",
|
||||||
}
|
}
|
||||||
|
|
||||||
CACHE_MIDDLEWARE_SECONDS = 60 * 60 * 24 # 1 day
|
# CACHE_MIDDLEWARE_SECONDS = 60 * 60 * 24 # 1 day
|
||||||
CACHES = {
|
# CACHES = {
|
||||||
"default": {
|
# "default": {
|
||||||
"BACKEND": "django.core.cache.backends.filebased.FileBasedCache",
|
# "BACKEND": "django.core.cache.backends.filebased.FileBasedCache",
|
||||||
"LOCATION": DATA_DIR / "django_cache",
|
# "LOCATION": DATA_DIR / "django_cache",
|
||||||
},
|
# },
|
||||||
}
|
# }
|
||||||
SITE_ID = 1
|
SITE_ID = 1
|
||||||
|
|
||||||
|
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
{% extends "base.html" %}
|
{% extends "base.html" %}
|
||||||
{% load static %}
|
{% load static campaign_tags game_tags %}
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<div class="container mt-4">
|
<div class="container mt-4">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
@ -8,20 +8,21 @@
|
|||||||
{% include "partials/info_box.html" %}
|
{% include "partials/info_box.html" %}
|
||||||
{% include "partials/news.html" %}
|
{% include "partials/news.html" %}
|
||||||
<h2>
|
<h2>
|
||||||
Reward campaigns -
|
Reward campaign -
|
||||||
<div class="d-inline text-muted">{{ reward_campaigns.count }} campaigns</div>
|
<div class="d-inline text-muted">
|
||||||
|
{{ reward_campaigns.count }}
|
||||||
|
campaign{{ reward_campaigns.count|pluralize }}
|
||||||
|
</div>
|
||||||
</h2>
|
</h2>
|
||||||
{% for campaign in reward_campaigns %}
|
{% for campaign in reward_campaigns %}
|
||||||
{% include "partials/reward_campaign_card.html" %}
|
{% render_campaign campaign %}
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
<h2>
|
<h2>
|
||||||
Drop campaigns -
|
Drop campaigns -
|
||||||
<div class="d-inline text-muted ">{{ games.count }} games</div>
|
<div class="d-inline text-muted ">{{ games.count }} game{{ games.count|pluralize }}</div>
|
||||||
</h2>
|
</h2>
|
||||||
{% for game in games %}
|
{% for game in games %}
|
||||||
{% if game.drop_campaigns.count > 0 %}
|
{% render_game_card game %}
|
||||||
{% include "partials/game_card.html" %}
|
|
||||||
{% endif %}
|
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -1,52 +0,0 @@
|
|||||||
<div class="card mb-4 shadow-sm" id="#{{ game.twitch_id }}">
|
|
||||||
<div class="row g-0">
|
|
||||||
<div class="col-md-2">
|
|
||||||
<img src="{{ game.box_art_url }}"
|
|
||||||
alt="{{ game.name }} box art"
|
|
||||||
class="img-fluid rounded-start"
|
|
||||||
height="283"
|
|
||||||
width="212"
|
|
||||||
loading="lazy">
|
|
||||||
</div>
|
|
||||||
<div class="col-md-10">
|
|
||||||
<div class="card-body">
|
|
||||||
<h2 class="card-title h5">
|
|
||||||
<a href="https://www.twitch.tv/directory/category/{{ game.slug }}"
|
|
||||||
class="text-decoration-none">{{ game.name }}</a>
|
|
||||||
</h2>
|
|
||||||
<div class="mt-auto">
|
|
||||||
<!-- Insert nice buttons -->
|
|
||||||
</div>
|
|
||||||
{% for campaign in game.drop_campaigns.all %}
|
|
||||||
<div class="mt-3">
|
|
||||||
{% 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">
|
|
||||||
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>
|
|
||||||
<div class="row row-cols-1 row-cols-md-2 row-cols-lg-3">
|
|
||||||
{% for drop in campaign.drops.all %}
|
|
||||||
{% for benefit in drop.benefits.all %}
|
|
||||||
<div class="col d-flex align-items-center position-relative">
|
|
||||||
<img src="{{ benefit.image_url }}"
|
|
||||||
alt="{{ benefit.name }} drop image"
|
|
||||||
class="img-fluid rounded me-3"
|
|
||||||
height="50"
|
|
||||||
width="50"
|
|
||||||
loading="lazy">
|
|
||||||
{{ benefit.name }}
|
|
||||||
</div>
|
|
||||||
{% endfor %}
|
|
||||||
{% endfor %}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{% endfor %}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
@ -1,52 +0,0 @@
|
|||||||
<div class="card mb-4 shadow-sm" id="campaign-{{ campaign.id }}">
|
|
||||||
<div class="row g-0">
|
|
||||||
<div class="col-md-2">
|
|
||||||
<img src="{{ campaign.image_url }}"
|
|
||||||
alt="{{ campaign.name }}"
|
|
||||||
class="img-fluid rounded-start"
|
|
||||||
height="283"
|
|
||||||
width="212"
|
|
||||||
loading="lazy">
|
|
||||||
</div>
|
|
||||||
<div class="col-md-10">
|
|
||||||
<div class="card-body">
|
|
||||||
<h2 class="card-title h5" id="#reward-{{ campaign.id }}">
|
|
||||||
<a href="#campaign-{{ campaign.id }}" class="plain-text-item">{{ campaign.name }}</a>
|
|
||||||
</h2>
|
|
||||||
<p class="card-text text-muted">{{ campaign.summary }}</p>
|
|
||||||
<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>
|
|
||||||
</p>
|
|
||||||
<a href="{{ campaign.external_url }}"
|
|
||||||
class="btn btn-primary"
|
|
||||||
target="_blank">Learn More</a>
|
|
||||||
{% if campaign.instructions %}
|
|
||||||
<div class="mt-3">
|
|
||||||
<h3 class="h6">Instructions</h3>
|
|
||||||
<p>{{ campaign.instructions }}</p>
|
|
||||||
</div>
|
|
||||||
{% endif %}
|
|
||||||
{% if campaign.rewards.exists %}
|
|
||||||
<div class="mt-3">
|
|
||||||
<h3 class="h6">Rewards</h3>
|
|
||||||
<div class="row row-cols-1 row-cols-md-2 row-cols-lg-3 g-2">
|
|
||||||
{% for reward in campaign.rewards.all %}
|
|
||||||
<div class="col d-flex align-items-center position-relative">
|
|
||||||
<img src="{{ reward.thumbnail_image_url }}"
|
|
||||||
alt="{{ reward.name }} reward image"
|
|
||||||
class="img-fluid rounded me-3"
|
|
||||||
height="50"
|
|
||||||
width="50"
|
|
||||||
loading="lazy">
|
|
||||||
<div>
|
|
||||||
<strong>{{ reward.name }}</strong>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{% endfor %}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{% endif %}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
0
core/templatetags/__init__.py
Normal file
0
core/templatetags/__init__.py
Normal file
93
core/templatetags/campaign_tags.py
Normal file
93
core/templatetags/campaign_tags.py
Normal file
@ -0,0 +1,93 @@
|
|||||||
|
from django import template
|
||||||
|
from django.utils.html import format_html
|
||||||
|
from django.utils.safestring import SafeText
|
||||||
|
from django.utils.timesince import timesince
|
||||||
|
from django.utils.timezone import now
|
||||||
|
|
||||||
|
from core.models import Reward, RewardCampaign
|
||||||
|
|
||||||
|
register = template.Library()
|
||||||
|
|
||||||
|
|
||||||
|
@register.simple_tag
|
||||||
|
def render_campaign(campaign: RewardCampaign) -> SafeText:
|
||||||
|
"""Render the campaign HTML.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
campaign: The campaign object.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
The rendered HTML string.
|
||||||
|
"""
|
||||||
|
time_remaining = timesince(now(), campaign.ends_at)
|
||||||
|
ends_in: str = f'{campaign.ends_at.strftime("%A %d %B %H:%M %Z")}' if campaign.ends_at else ""
|
||||||
|
starts_in: str = f'{campaign.starts_at.strftime("%A %d %B %H:%M %Z")}' if campaign.starts_at else ""
|
||||||
|
|
||||||
|
# Start building the HTML
|
||||||
|
html: str = f"""
|
||||||
|
<div class="card mb-4 shadow-sm" id="campaign-{campaign.id}">
|
||||||
|
<div class="row g-0">
|
||||||
|
<div class="col-md-2">
|
||||||
|
<img src="{campaign.image_url}"
|
||||||
|
alt="{campaign.name}"
|
||||||
|
class="img-fluid rounded-start"
|
||||||
|
height="283"
|
||||||
|
width="212"
|
||||||
|
loading="lazy">
|
||||||
|
</div>
|
||||||
|
<div class="col-md-10">
|
||||||
|
<div class="card-body">
|
||||||
|
<h2 class="card-title h5" id="#reward-{campaign.id}">
|
||||||
|
<a href="#campaign-{campaign.id}" class="plain-text-item">{campaign.name}</a>
|
||||||
|
</h2>
|
||||||
|
<p class="card-text text-muted">{campaign.summary}</p>
|
||||||
|
<p class="mb-2 text-muted">
|
||||||
|
Ends in: <abbr title="{starts_in} - {ends_in}">{time_remaining}</abbr>
|
||||||
|
</p>
|
||||||
|
<a href="{campaign.external_url}"
|
||||||
|
class="btn btn-primary"
|
||||||
|
target="_blank">Learn More</a>
|
||||||
|
"""
|
||||||
|
|
||||||
|
# Add instructions if present
|
||||||
|
if campaign.instructions:
|
||||||
|
html += f"""
|
||||||
|
<div class="mt-3">
|
||||||
|
<h3 class="h6">Instructions</h3>
|
||||||
|
<p>{campaign.instructions}</p>
|
||||||
|
</div>
|
||||||
|
"""
|
||||||
|
|
||||||
|
# Add rewards if present
|
||||||
|
if campaign.rewards.exists(): # type: ignore # noqa: PGH003
|
||||||
|
html += """
|
||||||
|
<div class="mt-3">
|
||||||
|
<h3 class="h6">Rewards</h3>
|
||||||
|
<div class="row row-cols-1 row-cols-md-2 row-cols-lg-3 g-2">
|
||||||
|
"""
|
||||||
|
for reward in campaign.rewards.all(): # type: ignore # noqa: PGH003
|
||||||
|
reward: Reward
|
||||||
|
html += f"""
|
||||||
|
<div class="col d-flex align-items-center position-relative">
|
||||||
|
<img src="{reward.thumbnail_image_url}"
|
||||||
|
alt="{reward.name} reward image"
|
||||||
|
class="img-fluid rounded me-3"
|
||||||
|
height="50"
|
||||||
|
width="50"
|
||||||
|
loading="lazy">
|
||||||
|
<div>
|
||||||
|
<strong>{reward.name}</strong>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
"""
|
||||||
|
html += "</div></div>"
|
||||||
|
|
||||||
|
# Close the main divs
|
||||||
|
html += """
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
"""
|
||||||
|
|
||||||
|
return format_html(html)
|
130
core/templatetags/game_tags.py
Normal file
130
core/templatetags/game_tags.py
Normal file
@ -0,0 +1,130 @@
|
|||||||
|
from django import template
|
||||||
|
from django.utils.html import format_html
|
||||||
|
from django.utils.safestring import SafeText
|
||||||
|
from django.utils.timesince import timesince
|
||||||
|
from django.utils.timezone import now
|
||||||
|
|
||||||
|
from core.models import Benefit, DropCampaign, Game, TimeBasedDrop
|
||||||
|
|
||||||
|
register = template.Library()
|
||||||
|
|
||||||
|
|
||||||
|
@register.simple_tag
|
||||||
|
def render_game_card(game: Game) -> SafeText:
|
||||||
|
"""Render the game card HTML.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
game: The game object.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
The rendered HTML string.
|
||||||
|
"""
|
||||||
|
twitch_id: str = game.twitch_id
|
||||||
|
box_art_url: str = game.box_art_url or "https://static-cdn.jtvnw.net/ttv-static/404_boxart.jpg"
|
||||||
|
name: str = game.name or "Game name unknown"
|
||||||
|
slug: str = game.slug or "game-name-unknown"
|
||||||
|
drop_campaigns = game.drop_campaigns.all() # type: ignore # noqa: PGH003
|
||||||
|
return format_html(
|
||||||
|
"""
|
||||||
|
<div class="card mb-4 shadow-sm" id="#{}">
|
||||||
|
<div class="row g-0">
|
||||||
|
<div class="col-md-2">
|
||||||
|
<img src="{}" alt="{} box art" class="img-fluid rounded-start" height="283" width="212" loading="lazy">
|
||||||
|
</div>
|
||||||
|
<div class="col-md-10">
|
||||||
|
<div class="card-body">
|
||||||
|
<h2 class="card-title h5">
|
||||||
|
<a href="https://www.twitch.tv/directory/category/{}" class="text-decoration-none">{}</a>
|
||||||
|
</h2>
|
||||||
|
<div class="mt-auto">
|
||||||
|
<!-- Insert nice buttons -->
|
||||||
|
</div>
|
||||||
|
{}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
""",
|
||||||
|
twitch_id,
|
||||||
|
box_art_url,
|
||||||
|
name,
|
||||||
|
slug,
|
||||||
|
name,
|
||||||
|
render_campaigns(drop_campaigns),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def render_campaigns(campaigns: list[DropCampaign]) -> SafeText:
|
||||||
|
"""Render the campaigns HTML.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
campaigns: The list of campaigns.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
The rendered HTML string.
|
||||||
|
"""
|
||||||
|
campaign_html: str = ""
|
||||||
|
for campaign in campaigns:
|
||||||
|
if campaign.details_url == campaign.account_link_url:
|
||||||
|
link_html: SafeText = format_html(
|
||||||
|
'<a href="{}" class="text-decoration-none">Details</a>',
|
||||||
|
campaign.details_url,
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
link_html: SafeText = format_html(
|
||||||
|
'<a href="{}" class="text-decoration-none">Details</a> | <a href="{}" class="text-decoration-none">Link Account</a>', # noqa: E501
|
||||||
|
campaign.details_url,
|
||||||
|
campaign.account_link_url,
|
||||||
|
)
|
||||||
|
|
||||||
|
time_until: str = timesince(campaign.ends_at, now()) if campaign.ends_at else ""
|
||||||
|
starts_at: str = campaign.starts_at.strftime("%A %d %B %H:%M") if campaign.starts_at else ""
|
||||||
|
ends_at: str = campaign.ends_at.strftime("%A %d %B %H:%M") if campaign.ends_at else ""
|
||||||
|
drops: list[TimeBasedDrop] = campaign.drops.all() # type: ignore # noqa: PGH003
|
||||||
|
campaign_html += format_html(
|
||||||
|
"""
|
||||||
|
<div class="mt-3">
|
||||||
|
{}
|
||||||
|
<p class="mb-2 text-muted">Ends in: <abbr title="{} - {}">{}</abbr></p>
|
||||||
|
<div class="row row-cols-1 row-cols-md-2 row-cols-lg-3">
|
||||||
|
{}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
""",
|
||||||
|
link_html,
|
||||||
|
starts_at,
|
||||||
|
ends_at,
|
||||||
|
time_until,
|
||||||
|
render_drops(drops),
|
||||||
|
)
|
||||||
|
|
||||||
|
return format_html(campaign_html)
|
||||||
|
|
||||||
|
|
||||||
|
def render_drops(drops: list[TimeBasedDrop]) -> SafeText:
|
||||||
|
"""Render the drops HTML.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
drops: The list of drops.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
The rendered HTML string.
|
||||||
|
"""
|
||||||
|
drop_html: str = ""
|
||||||
|
for drop in drops:
|
||||||
|
benefits: list[Benefit] = drop.benefits.all() # type: ignore # noqa: PGH003
|
||||||
|
for benefit in benefits:
|
||||||
|
image_url: str = benefit.image_url or "https://static-cdn.jtvnw.net/ttv-static/404_boxart.jpg"
|
||||||
|
name = benefit.name or "Drop name unknown"
|
||||||
|
drop_html += format_html(
|
||||||
|
"""
|
||||||
|
<div class="col d-flex align-items-center position-relative">
|
||||||
|
<img src="{}" alt="{} drop image" class="img-fluid rounded me-3" height="50" width="50" loading="lazy">
|
||||||
|
{}
|
||||||
|
</div>
|
||||||
|
""",
|
||||||
|
image_url,
|
||||||
|
name,
|
||||||
|
name,
|
||||||
|
)
|
||||||
|
return format_html(drop_html)
|
Reference in New Issue
Block a user