Skip to content

Commit

Permalink
Merge branch 'enforce-pro-roulette'
Browse files Browse the repository at this point in the history
Closes #251 (on GitHub).
  • Loading branch information
colons committed Feb 24, 2024
2 parents 417dcdd + c0331b8 commit 689ea3d
Show file tree
Hide file tree
Showing 6 changed files with 124 additions and 33 deletions.
1 change: 1 addition & 0 deletions PRIVACY.md
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ This can include:
(like Twitter, for instance)
- a [hashed][django-password-storage] password
- a screen name and a display name
- any commitments you have made under pro roulette
- an avatar
- email addresses
- URLs of websites you choose to show on your profile page
Expand Down
59 changes: 59 additions & 0 deletions nkdsu/apps/vote/migrations/0024_proroulettecommitment.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import django.db.models.deletion
from django.conf import settings
from django.db import migrations, models
import nkdsu.apps.vote.models


class Migration(migrations.Migration):

dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
('vote', '0023_show_unique_showtime_dates'),
]

operations = [
migrations.CreateModel(
name='ProRouletteCommitment',
fields=[
(
'id',
models.AutoField(
auto_created=True,
primary_key=True,
serialize=False,
verbose_name='ID',
),
),
(
'show',
models.ForeignKey(
on_delete=django.db.models.deletion.PROTECT, to='vote.show'
),
),
(
'track',
models.ForeignKey(
on_delete=django.db.models.deletion.CASCADE, to='vote.track'
),
),
(
'user',
models.ForeignKey(
on_delete=django.db.models.deletion.CASCADE,
to=settings.AUTH_USER_MODEL,
),
),
('created_at', models.DateTimeField(auto_now_add=True)),
],
bases=(nkdsu.apps.vote.models.CleanOnSaveMixin, models.Model),
),
migrations.AddConstraint(
model_name='proroulettecommitment',
constraint=models.UniqueConstraint(
models.F('show'),
models.F('user'),
name='pro_roulette_commitment_unique',
violation_error_message='a user can only have one pro roulette commitment per show',
),
),
]
16 changes: 16 additions & 0 deletions nkdsu/apps/vote/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -1831,6 +1831,22 @@ def __str__(self) -> str:
return self.content


class ProRouletteCommitment(CleanOnSaveMixin, models.Model):
show = models.ForeignKey(Show, on_delete=models.PROTECT)
user = models.ForeignKey(User, on_delete=models.CASCADE)
track = models.ForeignKey(Track, on_delete=models.CASCADE)
created_at = models.DateTimeField(auto_now_add=True)

class Meta:
constraints = [
models.UniqueConstraint(
*('show', 'user'),
name='pro_roulette_commitment_unique',
violation_error_message='a user can only have one pro roulette commitment per show',
)
]


@dataclass
class Badge:
slug: str
Expand Down
67 changes: 36 additions & 31 deletions nkdsu/apps/vote/views/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,11 @@
from functools import cached_property
from itertools import chain
from random import sample
from typing import Any, Iterable, Optional, Sequence, cast
from typing import Any, Iterable, Optional, Sequence, cast, overload

from django.conf import settings
from django.contrib import messages
from django.contrib.auth.mixins import LoginRequiredMixin
from django.contrib.auth.mixins import AccessMixin, LoginRequiredMixin
from django.contrib.auth.models import AnonymousUser
from django.core.mail import send_mail
from django.core.paginator import InvalidPage, Paginator
Expand All @@ -36,6 +36,7 @@
from ..anime import get_anime, suggest_anime
from ..forms import BadMetadataForm, DarkModeForm, RequestForm, VoteForm
from ..models import (
ProRouletteCommitment,
Profile,
Request,
Role,
Expand Down Expand Up @@ -190,7 +191,7 @@ def get(self, *a, **k) -> HttpResponse:
return redirect(cast(Show, self.object).get_absolute_url())


class Roulette(ListView):
class Roulette(ListView, AccessMixin):
section = 'roulette'
model = Track
template_name = 'roulette.html'
Expand All @@ -208,11 +209,13 @@ class Roulette(ListView):
]

def get(self, request, *args, **kwargs) -> HttpResponse:
if kwargs.get('mode') != 'pro' and self.request.session.get(
self.pro_roulette_session_key()
):
if kwargs.get('mode') != 'pro' and self.commitment() is not None:
return redirect(reverse('vote:roulette', kwargs={'mode': 'pro'}))

elif kwargs.get('mode') == 'pro' and not request.user.is_authenticated:
messages.warning(self.request, 'pro roulette requires you to log in')
return self.handle_no_permission()

elif kwargs.get('mode') is None:
if request.user.is_staff:
mode = 'short'
Expand All @@ -223,39 +226,39 @@ def get(self, request, *args, **kwargs) -> HttpResponse:
else:
return super().get(request, *args, **kwargs)

def pro_roulette_session_key(self) -> str:
return PRO_ROULETTE.format(Show.current().pk)
@overload
def commitment(self, commit_from: TrackQuerySet) -> ProRouletteCommitment: ...

def pro_pk(self) -> int:
sk = self.pro_roulette_session_key()
pk = self.request.session.get(self.pro_roulette_session_key())
@overload
def commitment(self) -> Optional[ProRouletteCommitment]: ...

if pk is None:
for i in range(100):
track = self.get_base_queryset().order_by('?')[0]
if track.eligible():
break
def commitment(
self, commit_from: Optional[TrackQuerySet] = None
) -> Optional[ProRouletteCommitment]:
if not self.request.user.is_authenticated:
assert commit_from is None, 'cannot commit if not authenticated'
return None
try:
return ProRouletteCommitment.objects.get(
user=self.request.user, show=Show.current()
)
except ProRouletteCommitment.DoesNotExist:
if commit_from:
return ProRouletteCommitment.objects.create(
user=self.request.user,
show=Show.current(),
track=next(t for t in commit_from.order_by('?') if t.eligible()),
)
else:
raise RuntimeError('are you sure anything is eligible')
return None

pk = track.pk
session = self.request.session
session[sk] = pk
session.save()

return pk

def pro_queryset(self, qs):
return qs.filter(pk=self.pro_pk())

def get_base_queryset(self):
def get_base_queryset(self) -> TrackQuerySet:
return self.model.objects.public()

def get_tracks(self) -> tuple[Iterable[Track], int]:
qs = self.get_base_queryset()

if self.kwargs.get('mode') == 'pro':
qs = self.pro_queryset(qs)
return ([self.commitment(commit_from=qs).track], 1)
elif self.kwargs.get('mode') == 'hipster':
qs = qs.filter(play=None)
elif self.kwargs.get('mode') == 'almost-100':
Expand All @@ -280,7 +283,8 @@ def get_tracks(self) -> tuple[Iterable[Track], int]:
.filter(time_per_play__lt=parse_duration('365 days'))
)
# order_by('?') fails when annotate() has been used
return (sample(list(qs), 5), qs.count())
staples = list(qs)
return (sample(staples, min(len(staples), 5)), len(staples))
elif self.kwargs.get('mode') == 'short':
length_msec = (
int(self.kwargs.get('minutes', self.default_minutes_count)) * 60 * 1000
Expand All @@ -298,6 +302,7 @@ def get_context_data(self, **kwargs) -> dict[str, Any]:

context.update(
{
'pro_roulette_commitment': self.commitment(),
'decades': Track.all_decades(),
'decade': int(decade_str) if decade_str else None,
'minutes': int(minutes_str) if minutes_str else None,
Expand Down
12 changes: 11 additions & 1 deletion nkdsu/templates/roulette.html
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,14 @@ <h2>roulette</h2>
</p>
{% endif %}

{% if pro_roulette_commitment %}
<p class="subheading">
you committed to pro roulette
on {{ pro_roulette_commitment.created_at|date:"F jS"|lower }}
at {{ pro_roulette_commitment.created_at|date:"g:i A"|lower }}
</p>
{% endif %}

{% if mode == "short" %}
<p class="subheading roulette-mode-switch">
{% for minute_slug in allowed_minutes %}
Expand All @@ -56,8 +64,10 @@ <h2>roulette</h2>
these are all terrible
{% elif option_count > 1 %}
i would like to see these {{ option_count }} tracks in a new, random order
{% else %}
{% elif option_count == 1 %}
oh, just one, huh? can&hellip; can i try again anyway?
{% else %}
if there's nothing, it's probably not worth trying again. but i still want to
{% endif %}
</a></p>
<p class="aside">
Expand Down
2 changes: 1 addition & 1 deletion nkdsu/tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -78,11 +78,11 @@ class EverythingTest(
'/roulette/',
'/roulette/hipster/',
'/roulette/indiscriminate/',
'/roulette/pro/',
'/roulette/staple/',
'/roulette/decade/',
'/roulette/decade/1970/',
'/roulette/short/1/',
'/roulette/pro/',
'/archive/',
'/archive/2014/',
'/stats/',
Expand Down

0 comments on commit 689ea3d

Please sign in to comment.