Compare commits
No commits in common. "e433a5d89d83070f99f1d3fc7c95f2972f4a307a" and "c9522f9d3dc1da6236af74261db630bae4a5b988" have entirely different histories.
e433a5d89d
...
c9522f9d3d
4 changed files with 46 additions and 259 deletions
|
|
@ -1,59 +0,0 @@
|
||||||
from typing import TYPE_CHECKING
|
|
||||||
|
|
||||||
from django.core.management import BaseCommand
|
|
||||||
from django.core.management import CommandError
|
|
||||||
from django.core.management import call_command
|
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
|
||||||
import argparse
|
|
||||||
|
|
||||||
|
|
||||||
class Command(BaseCommand):
|
|
||||||
"""Django management command to import a range of Chzzk campaigns by calling import_chzzk_campaign repeatedly."""
|
|
||||||
|
|
||||||
help = (
|
|
||||||
"Import a range of Chzzk campaigns by calling import_chzzk_campaign repeatedly."
|
|
||||||
)
|
|
||||||
|
|
||||||
def add_arguments(self, parser: argparse.ArgumentParser) -> None:
|
|
||||||
"""Add command-line arguments for the management command."""
|
|
||||||
parser.add_argument("start", type=int, help="Starting campaign number")
|
|
||||||
parser.add_argument("end", type=int, help="Ending campaign number (inclusive)")
|
|
||||||
parser.add_argument(
|
|
||||||
"--step",
|
|
||||||
type=int,
|
|
||||||
default=None,
|
|
||||||
help="Step to move between numbers (default -1 for descending, 1 for ascending)",
|
|
||||||
)
|
|
||||||
|
|
||||||
def handle(self, **options) -> None:
|
|
||||||
"""Main handler for the management command. Calls import_chzzk_campaign for each campaign number in the specified range.
|
|
||||||
|
|
||||||
Raises:
|
|
||||||
ValueError: If step is 0.
|
|
||||||
"""
|
|
||||||
start: int = options["start"]
|
|
||||||
end: int = options["end"]
|
|
||||||
step: int | None = options["step"]
|
|
||||||
|
|
||||||
if step is None:
|
|
||||||
step = -1 if start > end else 1
|
|
||||||
|
|
||||||
if step == 0:
|
|
||||||
msg = "Step cannot be 0"
|
|
||||||
raise ValueError(msg)
|
|
||||||
|
|
||||||
range_end: int = end + 1 if step > 0 else end - 1
|
|
||||||
|
|
||||||
msg: str = f"Importing campaigns from {start} to {end} with step {step}"
|
|
||||||
self.stdout.write(self.style.SUCCESS(msg))
|
|
||||||
|
|
||||||
for campaign_no in range(start, range_end, step):
|
|
||||||
self.stdout.write(f"Importing campaign {campaign_no}...")
|
|
||||||
try:
|
|
||||||
call_command("import_chzzk_campaign", str(campaign_no))
|
|
||||||
except CommandError as exc:
|
|
||||||
msg = f"Failed campaign {campaign_no}: {exc}"
|
|
||||||
self.stdout.write(self.style.ERROR(msg))
|
|
||||||
|
|
||||||
self.stdout.write(self.style.SUCCESS("Batch import complete."))
|
|
||||||
|
|
@ -60,46 +60,3 @@ class ChzzkDashboardViewTests(TestCase):
|
||||||
|
|
||||||
assert included in campaigns
|
assert included in campaigns
|
||||||
assert all(c.state != "TESTING" for c in campaigns)
|
assert all(c.state != "TESTING" for c in campaigns)
|
||||||
|
|
||||||
def test_campaign_detail_view_renders_chzzk_campaign_and_rewards(self) -> None:
|
|
||||||
"""Test that the campaign detail view correctly renders the details of a chzzk campaign and its rewards."""
|
|
||||||
now: datetime = timezone.now()
|
|
||||||
|
|
||||||
campaign: ChzzkCampaign = ChzzkCampaign.objects.create(
|
|
||||||
campaign_no=2001,
|
|
||||||
title="Campaign Detail Test",
|
|
||||||
description="Detailed campaign description",
|
|
||||||
category_type="game",
|
|
||||||
category_id="1",
|
|
||||||
category_value="TestGame",
|
|
||||||
service_id="chzzk",
|
|
||||||
state="ACTIVE",
|
|
||||||
start_date=now - timedelta(days=1),
|
|
||||||
end_date=now + timedelta(days=1),
|
|
||||||
has_ios_based_reward=False,
|
|
||||||
drops_campaign_not_started=False,
|
|
||||||
source_api="unit-test",
|
|
||||||
)
|
|
||||||
|
|
||||||
campaign.rewards.create( # pyright: ignore[reportAttributeAccessIssue]
|
|
||||||
reward_no=10,
|
|
||||||
title="Reward A",
|
|
||||||
reward_type="ITEM",
|
|
||||||
campaign_reward_type="Standard",
|
|
||||||
condition_type="watch",
|
|
||||||
condition_for_minutes=15,
|
|
||||||
ios_based_reward=False,
|
|
||||||
code_remaining_count=100,
|
|
||||||
)
|
|
||||||
|
|
||||||
response: _MonkeyPatchedWSGIResponse = self.client.get(
|
|
||||||
reverse("chzzk:campaign_detail", args=[campaign.campaign_no]),
|
|
||||||
)
|
|
||||||
assert response.status_code == 200
|
|
||||||
|
|
||||||
content: str = response.content.decode()
|
|
||||||
assert campaign.title in content
|
|
||||||
assert "Reward A" in content
|
|
||||||
assert "watch" in content
|
|
||||||
assert "100" in content
|
|
||||||
assert "No" in content # ios_based_reward=False
|
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,6 @@
|
||||||
from typing import TYPE_CHECKING
|
from typing import TYPE_CHECKING
|
||||||
|
|
||||||
from django.db.models.query import QuerySet
|
from django.db.models.query import QuerySet
|
||||||
from django.shortcuts import get_object_or_404
|
|
||||||
from django.shortcuts import render
|
from django.shortcuts import render
|
||||||
from django.urls import reverse
|
from django.urls import reverse
|
||||||
from django.utils import timezone
|
from django.utils import timezone
|
||||||
|
|
@ -63,8 +62,7 @@ def campaign_detail_view(request: HttpRequest, campaign_no: int) -> HttpResponse
|
||||||
Returns:
|
Returns:
|
||||||
HttpResponse: The HTTP response containing the rendered campaign detail page.
|
HttpResponse: The HTTP response containing the rendered campaign detail page.
|
||||||
"""
|
"""
|
||||||
campaign: models.ChzzkCampaign = get_object_or_404(
|
campaign: models.ChzzkCampaign = models.ChzzkCampaign.objects.get(
|
||||||
models.ChzzkCampaign,
|
|
||||||
campaign_no=campaign_no,
|
campaign_no=campaign_no,
|
||||||
)
|
)
|
||||||
rewards: QuerySet[models.ChzzkReward, models.ChzzkReward] = campaign.rewards.all() # pyright: ignore[reportAttributeAccessIssue]
|
rewards: QuerySet[models.ChzzkReward, models.ChzzkReward] = campaign.rewards.all() # pyright: ignore[reportAttributeAccessIssue]
|
||||||
|
|
|
||||||
|
|
@ -1,161 +1,52 @@
|
||||||
{% extends "base.html" %}
|
{% extends "base.html" %}
|
||||||
{% load static %}
|
|
||||||
{% load image_tags %}
|
|
||||||
{% block title %}
|
{% block title %}
|
||||||
{{ campaign.title }}
|
{{ campaign.title }}
|
||||||
{% endblock title %}
|
{% endblock title %}
|
||||||
{% block extra_head %}
|
|
||||||
<link rel="alternate"
|
|
||||||
type="application/rss+xml"
|
|
||||||
title="All chzzk campaigns (RSS)"
|
|
||||||
href="{% url 'chzzk:campaign_feed' %}" />
|
|
||||||
<link rel="alternate"
|
|
||||||
type="application/atom+xml"
|
|
||||||
title="All chzzk campaigns (Atom)"
|
|
||||||
href="{% url 'chzzk:campaign_feed_atom' %}" />
|
|
||||||
<link rel="alternate"
|
|
||||||
type="application/atom+xml"
|
|
||||||
title="All chzzk campaigns (Discord)"
|
|
||||||
href="{% url 'chzzk:campaign_feed_discord' %}" />
|
|
||||||
{% endblock extra_head %}
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<div style="display: flex; align-items: flex-start;">
|
<main>
|
||||||
<!-- Campaign image -->
|
<h1>{{ campaign.title }}</h1>
|
||||||
<div style="margin-right: 16px;">
|
<nav>
|
||||||
|
<a href="{% url 'chzzk:dashboard' %}">chzzk</a> > <a href="{% url 'chzzk:campaign_list' %}">Campaigns</a> > {{ campaign.title }}
|
||||||
|
</nav>
|
||||||
{% if campaign.image_url %}
|
{% if campaign.image_url %}
|
||||||
<img src="{{ campaign.image_url }}"
|
<img src="{{ campaign.image_url }}"
|
||||||
alt="{{ campaign.title }}"
|
alt="{{ campaign.title }}"
|
||||||
width="160"
|
|
||||||
height="auto" />
|
|
||||||
{% endif %}
|
|
||||||
</div>
|
|
||||||
<!-- Campaign Title -->
|
|
||||||
<div style="display: flex; flex-direction: column;">
|
|
||||||
<h1 style="margin-top: 0; margin-bottom: 0px;">{{ campaign.title }}</h1>
|
|
||||||
<!-- Add breadcrumbs -->
|
|
||||||
<div>
|
|
||||||
<a href="{% url 'chzzk:dashboard' %}">chzzk</a> > <a href="{% url 'chzzk:campaign_list' %}">Campaigns</a> > {{ campaign.title }}
|
|
||||||
</div>
|
|
||||||
<!-- Campaign description -->
|
|
||||||
<p>{{ campaign.description|linebreaksbr }}</p>
|
|
||||||
<div>
|
|
||||||
Published
|
|
||||||
{% if campaign.scraped_at %}
|
|
||||||
<time datetime="{{ campaign.scraped_at|date:'c' }}"
|
|
||||||
title="{{ campaign.scraped_at|date:'DATETIME_FORMAT' }}">{{ campaign.scraped_at|date:"M d, Y H:i" }}</time> ({{ campaign.scraped_at|timesince }} ago)
|
|
||||||
{% elif campaign.start_date %}
|
|
||||||
<time datetime="{{ campaign.start_date|date:'c' }}"
|
|
||||||
title="{{ campaign.start_date|date:'DATETIME_FORMAT' }}">{{ campaign.start_date|date:"M d, Y H:i" }}</time> ({{ campaign.start_date|timesince }} ago)
|
|
||||||
{% else %}
|
|
||||||
unknown
|
|
||||||
{% endif %}
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
Last updated
|
|
||||||
{% if campaign.scraped_at %}
|
|
||||||
<time datetime="{{ campaign.scraped_at|date:'c' }}"
|
|
||||||
title="{{ campaign.scraped_at|date:'DATETIME_FORMAT' }}">{{ campaign.scraped_at|date:"M d, Y H:i" }}</time> ({{ campaign.scraped_at|timesince }} ago)
|
|
||||||
{% elif campaign.updated_at %}
|
|
||||||
<time datetime="{{ campaign.updated_at|date:'c' }}"
|
|
||||||
title="{{ campaign.updated_at|date:'DATETIME_FORMAT' }}">{{ campaign.updated_at|date:"M d, Y H:i" }}</time> ({{ campaign.updated_at|timesince }} ago)
|
|
||||||
{% else %}
|
|
||||||
unknown
|
|
||||||
{% endif %}
|
|
||||||
</div>
|
|
||||||
<!-- Campaign end times -->
|
|
||||||
<div>
|
|
||||||
{% if campaign.end_date %}
|
|
||||||
{% if campaign.end_date < now %}
|
|
||||||
Ended
|
|
||||||
<time datetime="{{ campaign.end_date|date:'c' }}"
|
|
||||||
title="{{ campaign.end_date|date:'DATETIME_FORMAT' }}">{{ campaign.end_date|date:"M d, Y H:i" }}</time> ({{ campaign.end_date|timesince }} ago)
|
|
||||||
{% else %}
|
|
||||||
Ends in
|
|
||||||
<time datetime="{{ campaign.end_date|date:'c' }}"
|
|
||||||
title="{{ campaign.end_date|date:'DATETIME_FORMAT' }}">{{ campaign.end_date|date:"M d, Y H:i" }}</time> (in {{ campaign.end_date|timeuntil }})
|
|
||||||
{% endif %}
|
|
||||||
{% else %}
|
|
||||||
Ends unknown
|
|
||||||
{% endif %}
|
|
||||||
</div>
|
|
||||||
<!-- Campaign start times -->
|
|
||||||
<div>
|
|
||||||
{% if campaign.start_date %}
|
|
||||||
{% if campaign.start_date > now %}
|
|
||||||
Starts in
|
|
||||||
<time datetime="{{ campaign.start_date|date:'c' }}"
|
|
||||||
title="{{ campaign.start_date|date:'DATETIME_FORMAT' }}">{{ campaign.start_date|date:"M d, Y H:i" }}</time> (in {{ campaign.start_date|timeuntil }})
|
|
||||||
{% else %}
|
|
||||||
Started
|
|
||||||
<time datetime="{{ campaign.start_date|date:'c' }}"
|
|
||||||
title="{{ campaign.start_date|date:'DATETIME_FORMAT' }}">{{ campaign.start_date|date:"M d, Y H:i" }}</time> ({{ campaign.start_date|timesince }} ago)
|
|
||||||
{% endif %}
|
|
||||||
{% else %}
|
|
||||||
Starts unknown
|
|
||||||
{% endif %}
|
|
||||||
</div>
|
|
||||||
<!-- Campaign duration -->
|
|
||||||
<div>
|
|
||||||
Duration is
|
|
||||||
{% if campaign.start_date and campaign.end_date %}
|
|
||||||
<time datetime="P{{ campaign.end_date|date:'Y' }}-{{ campaign.end_date|date:'m' }}-{{ campaign.end_date|date:'d' }}T{{ campaign.end_date|date:'H' }}:00:00"
|
|
||||||
title="{{ campaign.start_date|date:'DATETIME_FORMAT' }} to {{ campaign.end_date|date:'DATETIME_FORMAT' }}">{{ campaign.end_date|timeuntil:campaign.start_date }}</time>
|
|
||||||
{% else %}
|
|
||||||
unknown
|
|
||||||
{% endif %}
|
|
||||||
</div>
|
|
||||||
<!-- Buttons -->
|
|
||||||
<div>
|
|
||||||
{% if campaign.pc_link_url %}<a href="{{ campaign.pc_link_url }}" target="_blank">[details]</a>{% endif %}
|
|
||||||
{% if campaign.account_link_url %}<a href="{{ campaign.account_link_url }}" target="_blank">[connect]</a>{% endif %}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<h5>Campaign Info</h5>
|
|
||||||
{% if rewards %}
|
|
||||||
<table style="border-collapse: collapse; width: 100%;">
|
|
||||||
<thead>
|
|
||||||
<tr>
|
|
||||||
<th style="text-align: left; padding: 4px;">Image</th>
|
|
||||||
<th style="text-align: left; padding: 4px;">Reward</th>
|
|
||||||
<th style="text-align: left; padding: 4px;">Type</th>
|
|
||||||
<th style="text-align: left; padding: 4px;">Condition</th>
|
|
||||||
<th style="text-align: left; padding: 4px;">iOS Reward</th>
|
|
||||||
<th style="text-align: left; padding: 4px;">Codes Left</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
{% for reward in rewards %}
|
|
||||||
<tr>
|
|
||||||
<td style="vertical-align: top; padding: 4px;">
|
|
||||||
{% if reward.image_url %}
|
|
||||||
<img src="{{ reward.image_url }}"
|
|
||||||
alt="{{ reward.title }}"
|
|
||||||
width="160"
|
|
||||||
height="auto"
|
height="auto"
|
||||||
style="object-fit: cover;
|
width="auto"
|
||||||
margin-right: 3px" />
|
style="max-width:200px;
|
||||||
|
height:auto;
|
||||||
|
border-radius:8px" />
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</td>
|
{% if campaign.description %}
|
||||||
<td style="vertical-align: top; padding: 4px;">
|
<section class="description">
|
||||||
{{ reward.title }}
|
{{ campaign.description|linebreaksbr }}
|
||||||
{% if reward.condition_for_minutes %}
|
</section>
|
||||||
<div style="font-size: 0.9em; color: #a9a9a9;">{{ reward.condition_for_minutes }} minutes watched</div>
|
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</td>
|
<section class="times">
|
||||||
<td style="vertical-align: top; padding: 4px;">{{ reward.reward_type }}</td>
|
{% if campaign.starts_at %}
|
||||||
<td style="vertical-align: top; padding: 4px;">{{ reward.condition_type }}</td>
|
<div>
|
||||||
<td style="vertical-align: top; padding: 4px;">{{ reward.ios_based_reward|yesno:"Yes,No" }}</td>
|
Starts: <time datetime="{{ campaign.starts_at|date:'c' }}">{{ campaign.starts_at|date:'M d, Y H:i' }}</time>
|
||||||
{% if reward.reward_type == "CODE_FIRST_COME" %}
|
</div>
|
||||||
<td style="vertical-align: top; padding: 4px;">{{ reward.code_remaining_count }}</td>
|
{% endif %}
|
||||||
|
{% if campaign.ends_at %}
|
||||||
|
<div>
|
||||||
|
Ends: <time datetime="{{ campaign.ends_at|date:'c' }}">{{ campaign.ends_at|date:'M d, Y H:i' }}</time>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
</section>
|
||||||
|
<hr />
|
||||||
|
<h2>Rewards</h2>
|
||||||
|
{% if rewards %}
|
||||||
|
<ul>
|
||||||
|
{% for r in rewards %}<li>{{ r.title }} — {{ r.condition_for_minutes }} minutes of watch time</li>{% endfor %}
|
||||||
|
</ul>
|
||||||
{% else %}
|
{% else %}
|
||||||
<td style="vertical-align: top; padding: 4px;">Unlimited</td>
|
<p>No rewards available.</p>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</tr>
|
{% if campaign.external_url %}
|
||||||
{% endfor %}
|
<p>
|
||||||
</tbody>
|
<a class="btn" href="{{ campaign.external_url }}">View on chzzk</a>
|
||||||
</table>
|
</p>
|
||||||
{% else %}
|
|
||||||
<p>No rewards available for this campaign.</p>
|
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
</main>
|
||||||
{% endblock content %}
|
{% endblock content %}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue