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 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 django.db.models.query import QuerySet
|
||||
from django.shortcuts import get_object_or_404
|
||||
from django.shortcuts import render
|
||||
from django.urls import reverse
|
||||
from django.utils import timezone
|
||||
|
|
@ -63,8 +62,7 @@ def campaign_detail_view(request: HttpRequest, campaign_no: int) -> HttpResponse
|
|||
Returns:
|
||||
HttpResponse: The HTTP response containing the rendered campaign detail page.
|
||||
"""
|
||||
campaign: models.ChzzkCampaign = get_object_or_404(
|
||||
models.ChzzkCampaign,
|
||||
campaign: models.ChzzkCampaign = models.ChzzkCampaign.objects.get(
|
||||
campaign_no=campaign_no,
|
||||
)
|
||||
rewards: QuerySet[models.ChzzkReward, models.ChzzkReward] = campaign.rewards.all() # pyright: ignore[reportAttributeAccessIssue]
|
||||
|
|
|
|||
|
|
@ -1,161 +1,52 @@
|
|||
{% extends "base.html" %}
|
||||
{% load static %}
|
||||
{% load image_tags %}
|
||||
{% block title %}
|
||||
{{ campaign.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 %}
|
||||
<div style="display: flex; align-items: flex-start;">
|
||||
<!-- Campaign image -->
|
||||
<div style="margin-right: 16px;">
|
||||
{% if campaign.image_url %}
|
||||
<img src="{{ campaign.image_url }}"
|
||||
alt="{{ campaign.title }}"
|
||||
width="160"
|
||||
height="auto" />
|
||||
<main>
|
||||
<h1>{{ campaign.title }}</h1>
|
||||
<nav>
|
||||
<a href="{% url 'chzzk:dashboard' %}">chzzk</a> > <a href="{% url 'chzzk:campaign_list' %}">Campaigns</a> > {{ campaign.title }}
|
||||
</nav>
|
||||
{% if campaign.image_url %}
|
||||
<img src="{{ campaign.image_url }}"
|
||||
alt="{{ campaign.title }}"
|
||||
height="auto"
|
||||
width="auto"
|
||||
style="max-width:200px;
|
||||
height:auto;
|
||||
border-radius:8px" />
|
||||
{% endif %}
|
||||
{% if campaign.description %}
|
||||
<section class="description">
|
||||
{{ campaign.description|linebreaksbr }}
|
||||
</section>
|
||||
{% endif %}
|
||||
<section class="times">
|
||||
{% if campaign.starts_at %}
|
||||
<div>
|
||||
Starts: <time datetime="{{ campaign.starts_at|date:'c' }}">{{ campaign.starts_at|date:'M d, Y H:i' }}</time>
|
||||
</div>
|
||||
{% 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"
|
||||
style="object-fit: cover;
|
||||
margin-right: 3px" />
|
||||
{% endif %}
|
||||
</td>
|
||||
<td style="vertical-align: top; padding: 4px;">
|
||||
{{ reward.title }}
|
||||
{% if reward.condition_for_minutes %}
|
||||
<div style="font-size: 0.9em; color: #a9a9a9;">{{ reward.condition_for_minutes }} minutes watched</div>
|
||||
{% endif %}
|
||||
</td>
|
||||
<td style="vertical-align: top; padding: 4px;">{{ reward.reward_type }}</td>
|
||||
<td style="vertical-align: top; padding: 4px;">{{ reward.condition_type }}</td>
|
||||
<td style="vertical-align: top; padding: 4px;">{{ reward.ios_based_reward|yesno:"Yes,No" }}</td>
|
||||
{% if reward.reward_type == "CODE_FIRST_COME" %}
|
||||
<td style="vertical-align: top; padding: 4px;">{{ reward.code_remaining_count }}</td>
|
||||
{% else %}
|
||||
<td style="vertical-align: top; padding: 4px;">Unlimited</td>
|
||||
{% endif %}
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
{% else %}
|
||||
<p>No rewards available for this campaign.</p>
|
||||
{% 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 %}
|
||||
<p>No rewards available.</p>
|
||||
{% endif %}
|
||||
{% if campaign.external_url %}
|
||||
<p>
|
||||
<a class="btn" href="{{ campaign.external_url }}">View on chzzk</a>
|
||||
</p>
|
||||
{% endif %}
|
||||
</main>
|
||||
{% endblock content %}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue