Coverage for website/newsletters/forms.py: 30.11%

65 statements  

« prev     ^ index     » next       coverage.py v7.6.7, created at 2025-08-14 10:31 +0000

1"""The forms defined by the newsletters package.""" 

2 

3from django import forms 

4from django.contrib.admin.widgets import AdminDateWidget 

5from django.db.models import Q 

6from django.utils import timezone 

7from django.utils.translation import gettext_lazy as _ 

8 

9from events.models import Event 

10from thaliawebsite import settings 

11 

12from .models import Newsletter, NewsletterEvent 

13 

14 

15class NewsletterEventForm(forms.ModelForm): 

16 """Custom ModelForm for the NewsletterEvent model to add the order field and javascript for automatic field filling.""" 

17 

18 event = forms.ChoiceField(label=_("Event")) 

19 

20 def __init__(self, *args, **kwargs): 

21 super().__init__(*args, **kwargs) 

22 

23 self.fields["event"].choices = [(None, "-----")] + [ 

24 (e.pk, e.title) 

25 for e in Event.objects.filter(published=True, start__gt=timezone.now()) 

26 ] 

27 self.fields["event"].required = False 

28 

29 class Meta: 

30 fields = ( 

31 "order", 

32 "event", 

33 "title", 

34 "url", 

35 "description", 

36 "where", 

37 "start_datetime", 

38 "end_datetime", 

39 "show_costs_warning", 

40 "price", 

41 "penalty_costs", 

42 ) 

43 model = NewsletterEvent 

44 

45 class Media: 

46 js = ( 

47 "js/js.cookie.min.js", 

48 "admin/newsletters/js/forms.js", 

49 ) 

50 

51 

52class NewsletterImportEventForm(forms.Form): 

53 event_start = forms.DateField(required=False, widget=AdminDateWidget()) 

54 event_end = forms.DateField(required=False, widget=AdminDateWidget()) 

55 

56 registration_start = forms.DateField(required=False, widget=AdminDateWidget()) 

57 registration_end = forms.DateField(required=False, widget=AdminDateWidget()) 

58 

59 remove_existing = forms.BooleanField(required=False, initial=False) 

60 

61 sort_by = forms.ChoiceField( 

62 choices=( 

63 ("start", _("Event start date")), 

64 ("registration", _("Registrations open date")), 

65 ), 

66 required=True, 

67 ) 

68 

69 def import_events(self, newsletter: Newsletter): 

70 """Import events from the database into the newsletter.""" 

71 if self.cleaned_data["remove_existing"]: 

72 NewsletterEvent.objects.filter(newsletter=newsletter).delete() 

73 

74 (event_start, event_end, registration_start, registration_end) = ( 

75 self.cleaned_data["event_start"], 

76 self.cleaned_data["event_end"], 

77 self.cleaned_data["registration_start"], 

78 self.cleaned_data["registration_end"], 

79 ) 

80 

81 events = Event.objects.filter(published=True) 

82 

83 if event_start and registration_start: 

84 events = events.filter( 

85 Q( 

86 registration_start__date__gte=registration_start, 

87 registration_start__date__lte=registration_end, 

88 ) 

89 | Q( 

90 start__date__lte=event_end, 

91 start__date__gte=event_start, 

92 ) 

93 ) 

94 elif event_start: 

95 events = events.filter( 

96 start__date__gte=event_start, 

97 start__date__lte=event_end, 

98 ) 

99 elif registration_start: 

100 events = events.filter( 

101 registration_start__date__gte=registration_start, 

102 registration_start__date__lte=registration_end, 

103 ) 

104 else: 

105 raise forms.ValidationError( 

106 _("Please specify which events you want to add to the newsletter.") 

107 ) 

108 

109 if self.cleaned_data["sort_by"] == "start": 

110 events = events.order_by("start") 

111 elif self.cleaned_data["sort_by"] == "registration": 

112 events = events.order_by("registration_start") 

113 

114 max_order = ( 

115 NewsletterEvent.objects.filter(newsletter=newsletter) 

116 .order_by("-order") 

117 .first() 

118 ) 

119 max_order = max_order.order if max_order else 0 

120 

121 for i, event in enumerate(events, start=max_order + 1): 

122 NewsletterEvent.objects.create( 

123 newsletter=newsletter, 

124 order=i, 

125 title=event.title, 

126 url=settings.BASE_URL + event.get_absolute_url(), 

127 description=event.description, 

128 where=event.location, 

129 start_datetime=event.start, 

130 end_datetime=event.end, 

131 show_costs_warning=event.fine > 0, 

132 price=event.price, 

133 penalty_costs=event.fine, 

134 ) 

135 

136 def clean(self): 

137 data = super().clean() 

138 (event_start, event_end, registration_start, registration_end) = ( 

139 data.get("event_start"), 

140 data.get("event_end"), 

141 data.get("registration_start"), 

142 data.get("registration_end"), 

143 ) 

144 if event_start is not None and event_end is None: 

145 self.add_error( 

146 "event_end", _("This field is required if you specify a start date.") 

147 ) 

148 if event_end is not None and event_start is None: 

149 self.add_error( 

150 "event_start", _("This field is required if you specify an end date.") 

151 ) 

152 if registration_start is not None and registration_end is None: 

153 self.add_error( 

154 "registration_end", 

155 _("This field is required if you specify a start date."), 

156 ) 

157 if registration_end is not None and registration_start is None: 

158 self.add_error( 

159 "registration_start", 

160 _("This field is required if you specify an end date."), 

161 ) 

162 

163 if ( 

164 event_start is not None 

165 and event_end is not None 

166 and (event_start > event_end) 

167 ): 

168 self.add_error("event_end", _("Range must end after it starts.")) 

169 if ( 

170 registration_start is not None 

171 and registration_end is not None 

172 and (registration_start > registration_end) 

173 ): 

174 self.add_error("registration_end", _("Range must end after it starts.")) 

175 

176 if event_start is None and registration_start is None: 

177 self.add_error( 

178 "event_start", 

179 _("Please specify which events you want to add to the newsletter."), 

180 ) 

181 self.add_error( 

182 "registration_start", 

183 _("Please specify which events you want to add to the newsletter."), 

184 ) 

185 

186 return data