[moods] Add a nice form for checking in
This commit is contained in:
@ -1,5 +1,6 @@
|
|||||||
import logging
|
import logging
|
||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
|
from enum import IntEnum
|
||||||
from typing import Optional
|
from typing import Optional
|
||||||
from uuid import uuid4
|
from uuid import uuid4
|
||||||
|
|
||||||
@ -21,10 +22,21 @@ class MoodType(models.TextChoices):
|
|||||||
NEGATIVE = "negative", "Negative"
|
NEGATIVE = "negative", "Negative"
|
||||||
|
|
||||||
|
|
||||||
|
class MoodQuality(IntEnum):
|
||||||
|
VERY_UNPLEASANT = 1
|
||||||
|
UNPLEASANT = 2
|
||||||
|
SLIGHTLY_UNPLEASANT = 3
|
||||||
|
NEUTRAL = 4
|
||||||
|
SLIGHTLY_PLEASANT = 5
|
||||||
|
PLEASANT = 6
|
||||||
|
VERY_PLEASANT = 7
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class MoodLogData(BaseLogData):
|
class MoodLogData(BaseLogData):
|
||||||
mood_type: Optional[str] = None
|
mood_type: Optional[str] = None
|
||||||
mood_reason_ids: Optional[list[int]] = None
|
mood_reason_ids: Optional[list[int]] = None
|
||||||
|
mood_quality: Optional[int] = None
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def override_fields(cls) -> dict:
|
def override_fields(cls) -> dict:
|
||||||
@ -42,6 +54,14 @@ class MoodLogData(BaseLogData):
|
|||||||
required=False,
|
required=False,
|
||||||
widget=forms.SelectMultiple(attrs={"size": 10}),
|
widget=forms.SelectMultiple(attrs={"size": 10}),
|
||||||
),
|
),
|
||||||
|
"mood_quality": forms.ChoiceField(
|
||||||
|
choices=[
|
||||||
|
(mq.value, mq.name.replace("_", " ").title())
|
||||||
|
for mq in MoodQuality
|
||||||
|
],
|
||||||
|
required=False,
|
||||||
|
widget=forms.Select(attrs={"class": "form-control"}),
|
||||||
|
),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
28
vrobbler/apps/moods/templates/moods/checkin_mood.html
Normal file
28
vrobbler/apps/moods/templates/moods/checkin_mood.html
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
{% extends "base.html" %}
|
||||||
|
|
||||||
|
{% block page_title %}Check-in - Select your mood{% endblock %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
<div class="container mt-5">
|
||||||
|
<div class="row justify-content-center">
|
||||||
|
<div class="col-md-8">
|
||||||
|
<h2 class="mb-4">Select your mood</h2>
|
||||||
|
<form method="post">
|
||||||
|
{% csrf_token %}
|
||||||
|
<div class="d-grid gap-3">
|
||||||
|
{% for mood in moods %}
|
||||||
|
<button type="submit" name="mood" value="{{ mood.id }}"
|
||||||
|
class="btn btn-outline-secondary btn-lg">
|
||||||
|
{% if mood.title %}{{ mood.title }}{% else %}{{ mood.uuid }}{% endif %}
|
||||||
|
{% if mood.description %}<br><small class="text-muted">{{ mood.description }}</small>{% endif %}
|
||||||
|
</button>
|
||||||
|
{% endfor %}
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
<div class="mt-3">
|
||||||
|
<a href="{% url 'moods:checkin' %}" class="btn btn-link">Back to quality selection</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endblock %}
|
||||||
24
vrobbler/apps/moods/templates/moods/checkin_quality.html
Normal file
24
vrobbler/apps/moods/templates/moods/checkin_quality.html
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
{% extends "base.html" %}
|
||||||
|
|
||||||
|
{% block page_title %}Check-in - How are you feeling?{% endblock %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
<div class="container mt-5">
|
||||||
|
<div class="row justify-content-center">
|
||||||
|
<div class="col-md-8">
|
||||||
|
<h2 class="mb-4">How are you feeling?</h2>
|
||||||
|
<form method="post">
|
||||||
|
{% csrf_token %}
|
||||||
|
<div class="d-grid gap-3">
|
||||||
|
{% for quality in qualities %}
|
||||||
|
<button type="submit" name="mood_quality" value="{{ quality.value }}"
|
||||||
|
class="btn btn-outline-primary btn-lg">
|
||||||
|
{{ quality.name }}
|
||||||
|
</button>
|
||||||
|
{% endfor %}
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endblock %}
|
||||||
15
vrobbler/apps/moods/templates/moods/checkin_success.html
Normal file
15
vrobbler/apps/moods/templates/moods/checkin_success.html
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
{% extends "base.html" %}
|
||||||
|
|
||||||
|
{% block page_title %}Check-in Complete{% endblock %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
<div class="container mt-5">
|
||||||
|
<div class="row justify-content-center">
|
||||||
|
<div class="col-md-8 text-center">
|
||||||
|
<h2 class="mb-4">Check-in Complete!</h2>
|
||||||
|
<p class="lead">Your mood has been recorded.</p>
|
||||||
|
<a href="{% url 'vrobbler-home' %}" class="btn btn-primary">Back to Home</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endblock %}
|
||||||
@ -11,4 +11,6 @@ urlpatterns = [
|
|||||||
views.MoodDetailView.as_view(),
|
views.MoodDetailView.as_view(),
|
||||||
name="mood_detail",
|
name="mood_detail",
|
||||||
),
|
),
|
||||||
|
path("checkin/", views.checkin, name="checkin"),
|
||||||
|
path("checkin/success/", views.checkin_success, name="checkin_success"),
|
||||||
]
|
]
|
||||||
|
|||||||
@ -1,4 +1,83 @@
|
|||||||
from moods.models import Mood
|
import logging
|
||||||
|
from django.contrib.auth.decorators import login_required
|
||||||
|
from django.shortcuts import redirect, render
|
||||||
|
from django.urls import reverse
|
||||||
|
from django.utils import timezone
|
||||||
|
from moods.models import Mood, MoodLogData, MoodQuality
|
||||||
|
from scrobbles.models import Scrobble
|
||||||
|
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
@login_required
|
||||||
|
def checkin(request):
|
||||||
|
"""Two-step mood check-in wizard"""
|
||||||
|
step = request.session.get("checkin_step", 1)
|
||||||
|
|
||||||
|
if request.method == "POST":
|
||||||
|
if step == 1:
|
||||||
|
quality = request.POST.get("mood_quality")
|
||||||
|
if quality:
|
||||||
|
request.session["checkin_quality"] = int(quality)
|
||||||
|
request.session["checkin_step"] = 2
|
||||||
|
return redirect("moods:checkin")
|
||||||
|
qualities = [
|
||||||
|
{"value": q.value, "name": q.name.replace("_", " ").title()}
|
||||||
|
for q in MoodQuality
|
||||||
|
]
|
||||||
|
return render(
|
||||||
|
request, "moods/checkin_quality.html", {"qualities": qualities}
|
||||||
|
)
|
||||||
|
|
||||||
|
elif step == 2:
|
||||||
|
mood_id = request.POST.get("mood")
|
||||||
|
quality = request.session.get("checkin_quality")
|
||||||
|
|
||||||
|
if mood_id and quality:
|
||||||
|
try:
|
||||||
|
mood = Mood.objects.get(id=mood_id)
|
||||||
|
log_data = MoodLogData(
|
||||||
|
mood_quality=quality,
|
||||||
|
mood_type=mood.mood_type,
|
||||||
|
)
|
||||||
|
|
||||||
|
scrobble = Scrobble.objects.create(
|
||||||
|
user=request.user,
|
||||||
|
mood=mood,
|
||||||
|
media_type=Scrobble.MediaType.MOOD,
|
||||||
|
timestamp=timezone.now(),
|
||||||
|
source="web_checkin",
|
||||||
|
log=log_data.asdict,
|
||||||
|
)
|
||||||
|
|
||||||
|
# Clear session data
|
||||||
|
request.session.pop("checkin_step", None)
|
||||||
|
request.session.pop("checkin_quality", None)
|
||||||
|
|
||||||
|
return redirect("moods:checkin_success")
|
||||||
|
except Mood.DoesNotExist:
|
||||||
|
logger.error(f"Mood with id {mood_id} not found")
|
||||||
|
|
||||||
|
return redirect("moods:checkin")
|
||||||
|
|
||||||
|
if step == 1:
|
||||||
|
qualities = [
|
||||||
|
{"value": q.value, "name": q.name.replace("_", " ").title()}
|
||||||
|
for q in MoodQuality
|
||||||
|
]
|
||||||
|
return render(
|
||||||
|
request, "moods/checkin_quality.html", {"qualities": qualities}
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
moods = Mood.objects.all()
|
||||||
|
return render(request, "moods/checkin_mood.html", {"moods": moods})
|
||||||
|
|
||||||
|
|
||||||
|
@login_required
|
||||||
|
def checkin_success(request):
|
||||||
|
return render(request, "moods/checkin_success.html")
|
||||||
|
|
||||||
|
|
||||||
from scrobbles.views import ScrobbleableListView, ScrobbleableDetailView
|
from scrobbles.views import ScrobbleableListView, ScrobbleableDetailView
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -15,6 +15,7 @@ AUTO_FINISH_MEDIA = {
|
|||||||
"webpages": "WebPage",
|
"webpages": "WebPage",
|
||||||
"tracks": "Track",
|
"tracks": "Track",
|
||||||
"videos": "Video",
|
"videos": "Video",
|
||||||
|
"moods": "Mood",
|
||||||
}
|
}
|
||||||
|
|
||||||
PLAY_AGAIN_MEDIA = {
|
PLAY_AGAIN_MEDIA = {
|
||||||
|
|||||||
@ -218,6 +218,9 @@
|
|||||||
{% endif %}
|
{% endif %}
|
||||||
{% if user.is_authenticated %}
|
{% if user.is_authenticated %}
|
||||||
<div class="navbar-nav">
|
<div class="navbar-nav">
|
||||||
|
<div class="nav-item text-nowrap">
|
||||||
|
<a class="nav-link px-3" href="{% url "moods:checkin" %}">Check-in</a>
|
||||||
|
</div>
|
||||||
<div class="nav-item text-nowrap">
|
<div class="nav-item text-nowrap">
|
||||||
<a class="nav-link px-3" href="{% url "profiles:profile_settings" %}">Settings</a>
|
<a class="nav-link px-3" href="{% url "profiles:profile_settings" %}">Settings</a>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Reference in New Issue
Block a user