import itertools import re from django import forms from django.core.exceptions import ValidationError from django.utils.translation import gettext_lazy as _ from AKModel.availability.forms import AvailabilitiesFormMixin from AKModel.availability.models import Availability from AKModel.models import AK, AKOwner, AKCategory, AKRequirement, AKSlot, AKOrgaMessage, Event class AKForm(AvailabilitiesFormMixin, forms.ModelForm): required_css_class = 'required' split_string = re.compile('[,;]') class Meta: model = AK fields = ['name', 'short_name', 'link', 'protocol_link', 'owners', 'description', 'category', 'reso', 'present', 'requirements', 'conflicts', 'prerequisites', 'notes', 'event' ] widgets = { 'requirements': forms.CheckboxSelectMultiple, 'event': forms.HiddenInput, } def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.initial = {**self.initial, **kwargs['initial']} # Use better multiple select input for owners, conflicts and prerequisites if "owners" in self.fields: self.fields["owners"].widget.attrs = {'class': 'chosen-select'} self.fields["conflicts"].widget.attrs = {'class': 'chosen-select'} self.fields["prerequisites"].widget.attrs = {'class': 'chosen-select'} help_tags_addition = _('Separate multiple tags with comma or semicolon') # Add text fields for tags self.fields["tags_raw"] = forms.CharField( required=False, label=AK.tags.field.verbose_name, help_text=f"{AK.tags.field.help_text} ({help_tags_addition})") self.fields['category'].queryset = AKCategory.objects.filter(event=self.initial.get('event')) self.fields['requirements'].queryset = AKRequirement.objects.filter(event=self.initial.get('event')) self.fields['prerequisites'].queryset = AK.objects.filter(event=self.initial.get('event')).exclude( pk=self.instance.pk) self.fields['conflicts'].queryset = AK.objects.filter(event=self.initial.get('event')).exclude( pk=self.instance.pk) if "owners" in self.fields: self.fields['owners'].queryset = AKOwner.objects.filter(event=self.initial.get('event')) @staticmethod def _clean_duration(duration): # Handle different duration formats (h:mm and decimal comma instead of point) if ":" in duration: h, m = duration.split(":") duration = int(h) + int(m) / 60 if "," in str(duration): duration = float(duration.replace(",", ".")) try: float(duration) except ValueError: raise ValidationError( _('"%(duration)s" is not a valid duration'), code='invalid', params={'duration': duration}, ) return duration def clean(self): cleaned_data = super().clean() # Generate short name if not given short_name = self.cleaned_data["short_name"] if len(short_name) == 0: short_name = self.cleaned_data['name'] short_name = short_name.partition(':')[0] short_name = short_name.partition(' - ')[0] short_name = short_name.partition(' (')[0] short_name = short_name[:AK._meta.get_field('short_name').max_length] for i in itertools.count(1): if not AK.objects.filter(short_name=short_name, event=self.cleaned_data["event"]).exists(): break digits = len(str(i)) short_name = '{}-{}'.format(short_name[:-(digits + 1)], i) cleaned_data["short_name"] = short_name # Generate wiki link if self.cleaned_data["event"].base_url: link = self.cleaned_data["event"].base_url + self.cleaned_data["name"].replace(" ", "_") # Truncate links longer than 200 characters (default length of URL fields in django) self.cleaned_data["link"] = link[:200] # Get tag names from raw tags cleaned_data["tag_names"] = [name.strip().lower() for name in self.split_string.split(cleaned_data["tags_raw"]) if name.strip() != ''] # Get durations from raw durations field if "durations" in cleaned_data: cleaned_data["durations"] = [self._clean_duration(d) for d in self.cleaned_data["durations"].split()] return cleaned_data class AKSubmissionForm(AKForm): class Meta(AKForm.Meta): exclude = ['link', 'protocol_link'] def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) # Add field for durations self.fields["durations"] = forms.CharField( widget=forms.Textarea, label=_("Duration(s)"), help_text=_( "Enter at least one planned duration (in hours). If your AK should have multiple slots, use multiple lines"), initial= self.initial.get('event').default_slot ) def clean_availabilities(self): availabilities = super().clean_availabilities() # If the user did not specify availabilities assume the full event duration is possible if len(availabilities) == 0: availabilities.append(Availability.with_event_length(event=self.cleaned_data["event"])) return availabilities class AKEditForm(AKForm): def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) # Add existing tags to tag raw field self.fields["tags_raw"].initial = "; ".join(str(tag) for tag in self.instance.tags.all()) class AKWishForm(AKForm): class Meta(AKForm.Meta): exclude = ['owners', 'link', 'protocol_link'] class AKOwnerForm(forms.ModelForm): required_css_class = 'required' class Meta: model = AKOwner fields = ['name', 'institution', 'link', 'event'] widgets = { 'event': forms.HiddenInput } class AKDurationForm(forms.ModelForm): class Meta: model = AKSlot fields = ['duration', 'ak', 'event'] widgets = { 'ak': forms.HiddenInput, 'event': forms.HiddenInput } class AKOrgaMessageForm(forms.ModelForm): class Meta: model = AKOrgaMessage fields = ['ak', 'text', 'event'] widgets = { 'ak': forms.HiddenInput, 'event': forms.HiddenInput, 'text': forms.Textarea, } class AKAddSlotForm(forms.Form): start = forms.CharField(label=_("Start"), disabled=True) end = forms.CharField(label=_("End"), disabled=True) duration = forms.CharField(label=_("Duration"), disabled=True) room = forms.IntegerField(label=_("Room"), disabled=True) def __init__(self, event): super().__init__() self.fields['ak'] = forms.ModelChoiceField(event.ak_set.all(), label=_("AK"))