Duration: {{ campaign_data.campaign.start_at|timesince:campaign_data.campaign.end_at }}
-
+
{% if owners %}
- Owned by
@@ -179,7 +178,8 @@
{% endif %}
- Ends in {{ campaign.end_at|timeuntil }}
+
|
{% endfor %}
@@ -208,7 +208,8 @@
{% endif %}
- Starts in {{ campaign.start_at|timeuntil }}
+
|
{% endfor %}
@@ -238,7 +239,8 @@
{% endif %}
- {{ campaign.end_at|timesince }} ago
+
|
{% endfor %}
@@ -273,7 +275,8 @@
{% endif %}
- Ends in {{ reward.ends_at|timeuntil }}
+
|
{% if reward.about_url and reward.external_url and reward.about_url == reward.external_url %}
@@ -311,7 +314,8 @@
{% endif %}
|
- Starts in {{ reward.starts_at|timeuntil }}
+
|
{% if reward.about_url and reward.external_url and reward.about_url == reward.external_url %}
@@ -349,7 +353,8 @@
{% endif %}
|
- {{ reward.ends_at|timesince }} ago
+
|
{% if reward.about_url and reward.external_url and reward.about_url == reward.external_url %}
diff --git a/templates/twitch/games_grid.html b/templates/twitch/games_grid.html
index ff35d8c..f02d338 100644
--- a/templates/twitch/games_grid.html
+++ b/templates/twitch/games_grid.html
@@ -93,7 +93,9 @@
text-align: center">
{% if item.game.box_art_best_url %}
- {% picture item.game.box_art_best_url alt="Box art for "|add:item.game.display_name width=180 height=240 style="border-radius: 8px" %}
+
+ {% picture item.game.box_art_best_url alt="Box art for "|add:item.game.display_name width=180 height=240 style="border-radius: 8px" %}
+
{% else %}
- {% picture campaign.image_best_url alt="Image for "|add:campaign.name width=120 %}
-
-
- {% if campaign.starts_at <= now and campaign.ends_at >= now %}
-
- Ends:
-
-
- {% elif campaign.starts_at > now %}
-
- Starts:
-
-
- {% else %}
-
- Ended:
-
-
- {% endif %}
- {% if campaign.game %}
-
- Game:
- {{ campaign.game.display_name }}
-
- {% elif campaign.is_sitewide %}
-
- Type: Site-wide reward campaign
-
- {% else %}
-
- Type:
- No game assigned
-
- {% endif %}
- {% if campaign.summary %}
- {{ campaign.summary }}
- {% endif %}
-
+
+ {% picture campaign.image_best_url alt="Image for "|add:campaign.name width=120 %}
+
+
+
+ {% if campaign.starts_at <= now and campaign.ends_at >= now %}
+ - Ends
+ -
+
+
+ {% elif campaign.starts_at > now %}
+ - Starts
+ -
+
+
+ {% else %}
+ - Ended
+ -
+
+
+ {% endif %}
+ {% if campaign.game %}
+ - Game
+ - {{ campaign.game.display_name }}
+ {% elif campaign.is_sitewide %}
+ - Type
+ - Site-wide reward campaign
+ {% else %}
+ - Type
+ - Brand reward
+ {% endif %}
+ - Data source
+ - {{ campaign.data_source }}
+
+ {% if campaign.summary %}
+ {{ campaign.summary }}
+ {% endif %}
+
diff --git a/templates/twitch/organization_detail.html b/templates/twitch/organization_detail.html
index 6432d78..368761a 100644
--- a/templates/twitch/organization_detail.html
+++ b/templates/twitch/organization_detail.html
@@ -33,7 +33,6 @@
{{ games_count }} game{{ games_count|pluralize }}
{% if total_campaigns %}ยท {{ total_campaigns }} campaign{{ total_campaigns|pluralize }}{% endif %}
-
{% if reward_campaign.brand %}
- {{ reward_campaign.brand }}: {{ reward_campaign.name }}
+ {{ reward_campaign.brand }}: {{ reward_campaign.name }}
{% else %}
- {{ reward_campaign.name }}
+ {{ reward_campaign.name }}
{% endif %}
@@ -50,7 +50,7 @@
{% if reward_campaign.summary %}{{ reward_campaign.summary|linebreaksbr }} {% endif %}
-
+
- Status
-
@@ -64,11 +64,13 @@
- Added
-
- {{ reward_campaign.added_at|date:"M d, Y H:i" }} ({{ reward_campaign.added_at|timesince }} ago)
+ ({{ reward_campaign.added_at|timesince }} ago)
- Last updated
-
- {{ reward_campaign.updated_at|date:"M d, Y H:i" }} ({{ reward_campaign.updated_at|timesince }} ago)
+ ({{ reward_campaign.updated_at|timesince }} ago)
-
diff --git a/twitch/models.py b/twitch/models.py
index f497e43..1d9a0a3 100644
--- a/twitch/models.py
+++ b/twitch/models.py
@@ -1126,13 +1126,32 @@ class DropCampaign(auto_prefetch.Model): # ruff:ignore[too-many-public-methods]
drops: list[TimeBasedDrop] = list(self.time_based_drops.all()) # pyright: ignore[reportAttributeAccessIssue]
awarded_badges: dict[str, ChatBadge] = self.awarded_badges_by_drop_twitch_id()
+ current_tz: str = str(timezone.get_current_timezone()) or "UTC"
+
+ def _duration_text(drop: TimeBasedDrop) -> str:
+ """Compact duration between start and end, e.g. "23d 8h 3m".
+
+ Returns:
+ str: Compact duration string, or empty string if either date is missing.
+ """
+ start = drop.start_at
+ end = drop.end_at
+ if not start or not end:
+ return ""
+ total = end - start
+ days: int = total.days
+ hours, remainder = divmod(total.seconds, 3600)
+ minutes, _seconds = divmod(remainder, 60)
+ return f"{days}d {hours}h {minutes}m"
+
return [
{
"drop": drop,
"local_start": drop.start_at,
"local_end": drop.end_at,
- "timezone_name": "UTC",
+ "timezone_name": current_tz,
"countdown_text": self._countdown_text_for_drop(drop, now),
+ "duration_text": _duration_text(drop),
"awarded_badge": awarded_badges.get(drop.twitch_id),
}
for drop in drops
--
2.51.2
|