180 lines
5.6 KiB
Python
180 lines
5.6 KiB
Python
from django import forms
|
|
from django.urls import reverse
|
|
|
|
from common.utils import safe_getattr
|
|
from games.models import Device, Edition, Game, Platform, Purchase, Session
|
|
|
|
custom_date_widget = forms.DateInput(attrs={"type": "date"})
|
|
custom_datetime_widget = forms.DateTimeInput(
|
|
attrs={"type": "datetime-local"}, format="%Y-%m-%d %H:%M"
|
|
)
|
|
autofocus_input_widget = forms.TextInput(attrs={"autofocus": "autofocus"})
|
|
|
|
|
|
class SessionForm(forms.ModelForm):
|
|
# purchase = forms.ModelChoiceField(
|
|
# queryset=Purchase.objects.filter(date_refunded=None).order_by("edition__name")
|
|
# )
|
|
purchase = forms.ModelChoiceField(
|
|
queryset=Purchase.objects.order_by("edition__sort_name"),
|
|
widget=forms.Select(attrs={"autofocus": "autofocus"}),
|
|
)
|
|
|
|
device = forms.ModelChoiceField(queryset=Device.objects.order_by("name"))
|
|
|
|
class Meta:
|
|
widgets = {
|
|
"timestamp_start": custom_datetime_widget,
|
|
"timestamp_end": custom_datetime_widget,
|
|
}
|
|
model = Session
|
|
fields = [
|
|
"purchase",
|
|
"timestamp_start",
|
|
"timestamp_end",
|
|
"duration_manual",
|
|
"device",
|
|
"note",
|
|
]
|
|
|
|
|
|
class EditionChoiceField(forms.ModelChoiceField):
|
|
def label_from_instance(self, obj) -> str:
|
|
return f"{obj.sort_name} ({obj.platform}, {obj.year_released})"
|
|
|
|
|
|
class IncludePlatformSelect(forms.Select):
|
|
def create_option(self, name, value, *args, **kwargs):
|
|
option = super().create_option(name, value, *args, **kwargs)
|
|
if platform_id := safe_getattr(value, "instance.platform.id"):
|
|
option["attrs"]["data-platform"] = platform_id
|
|
return option
|
|
|
|
|
|
class PurchaseForm(forms.ModelForm):
|
|
def __init__(self, *args, **kwargs):
|
|
super().__init__(*args, **kwargs)
|
|
|
|
# Automatically update related_purchase <select/>
|
|
# to only include purchases of the selected edition.
|
|
related_purchase_by_edition_url = reverse("related_purchase_by_edition")
|
|
self.fields["edition"].widget.attrs.update(
|
|
{
|
|
"hx-trigger": "load, click",
|
|
"hx-get": related_purchase_by_edition_url,
|
|
"hx-target": "#id_related_purchase",
|
|
"hx-swap": "outerHTML",
|
|
}
|
|
)
|
|
|
|
edition = EditionChoiceField(
|
|
queryset=Edition.objects.order_by("sort_name"),
|
|
widget=IncludePlatformSelect(attrs={"autoselect": "autoselect"}),
|
|
)
|
|
platform = forms.ModelChoiceField(queryset=Platform.objects.order_by("name"))
|
|
related_purchase = forms.ModelChoiceField(
|
|
queryset=Purchase.objects.filter(type=Purchase.GAME).order_by(
|
|
"edition__sort_name"
|
|
),
|
|
required=False,
|
|
)
|
|
|
|
class Meta:
|
|
widgets = {
|
|
"date_purchased": custom_date_widget,
|
|
"date_refunded": custom_date_widget,
|
|
"date_finished": custom_date_widget,
|
|
"date_dropped": custom_date_widget,
|
|
}
|
|
model = Purchase
|
|
fields = [
|
|
"edition",
|
|
"platform",
|
|
"date_purchased",
|
|
"date_refunded",
|
|
"date_finished",
|
|
"date_dropped",
|
|
"infinite",
|
|
"price",
|
|
"price_currency",
|
|
"ownership_type",
|
|
"type",
|
|
"related_purchase",
|
|
"name",
|
|
]
|
|
|
|
def clean(self):
|
|
cleaned_data = super().clean()
|
|
purchase_type = cleaned_data.get("type")
|
|
related_purchase = cleaned_data.get("related_purchase")
|
|
name = cleaned_data.get("name")
|
|
|
|
# Set the type on the instance to use get_type_display()
|
|
# This is safe because we're not saving the instance.
|
|
self.instance.type = purchase_type
|
|
|
|
if purchase_type != Purchase.GAME:
|
|
type_display = self.instance.get_type_display()
|
|
if not related_purchase:
|
|
self.add_error(
|
|
"related_purchase",
|
|
f"{type_display} must have a related purchase.",
|
|
)
|
|
if not name:
|
|
self.add_error("name", f"{type_display} must have a name.")
|
|
return cleaned_data
|
|
|
|
|
|
class IncludeNameSelect(forms.Select):
|
|
def create_option(self, name, value, *args, **kwargs):
|
|
option = super().create_option(name, value, *args, **kwargs)
|
|
if value:
|
|
option["attrs"]["data-name"] = value.instance.name
|
|
option["attrs"]["data-year"] = value.instance.year_released
|
|
return option
|
|
|
|
|
|
class GameModelChoiceField(forms.ModelChoiceField):
|
|
def label_from_instance(self, obj):
|
|
# Use sort_name as the label for the option
|
|
return obj.sort_name
|
|
|
|
|
|
class EditionForm(forms.ModelForm):
|
|
game = GameModelChoiceField(
|
|
queryset=Game.objects.order_by("sort_name"),
|
|
widget=IncludeNameSelect(attrs={"autofocus": "autofocus"}),
|
|
)
|
|
platform = forms.ModelChoiceField(
|
|
queryset=Platform.objects.order_by("name"), required=False
|
|
)
|
|
|
|
class Meta:
|
|
model = Edition
|
|
fields = ["game", "name", "sort_name", "platform", "year_released", "wikidata"]
|
|
|
|
|
|
class GameForm(forms.ModelForm):
|
|
class Meta:
|
|
model = Game
|
|
fields = ["name", "sort_name", "year_released", "wikidata"]
|
|
widgets = {"name": autofocus_input_widget}
|
|
|
|
|
|
class PlatformForm(forms.ModelForm):
|
|
class Meta:
|
|
model = Platform
|
|
fields = [
|
|
"name",
|
|
"icon",
|
|
"group",
|
|
]
|
|
widgets = {"name": autofocus_input_widget}
|
|
|
|
|
|
class DeviceForm(forms.ModelForm):
|
|
class Meta:
|
|
model = Device
|
|
fields = ["name", "type"]
|
|
widgets = {"name": autofocus_input_widget}
|