sandwitches 1.2.0__py3-none-any.whl → 1.3.1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
sandwitches/api.py CHANGED
@@ -37,6 +37,16 @@ class Error(Schema):
37
37
  message: str
38
38
 
39
39
 
40
+ class RatingResponseSchema(Schema):
41
+ average: float
42
+ count: int
43
+
44
+
45
+ @api.get("ping")
46
+ def ping(request):
47
+ return {"status": "ok", "message": "pong"}
48
+
49
+
40
50
  @api.get("v1/me", response={200: UserSchema, 403: Error})
41
51
  def me(request):
42
52
  if not request.user.is_authenticated:
@@ -71,6 +81,15 @@ def get_recipe_of_the_day(request):
71
81
  return recipe
72
82
 
73
83
 
84
+ @api.get("v1/recipes/{recipe_id}/rating", response=RatingResponseSchema)
85
+ def get_recipe_rating(request, recipe_id: int):
86
+ recipe = get_object_or_404(Recipe, id=recipe_id)
87
+ return {
88
+ "average": recipe.average_rating(),
89
+ "count": recipe.rating_count(),
90
+ }
91
+
92
+
74
93
  @api.get("v1/tags", response=list[TagSchema])
75
94
  def get_tags(request):
76
95
  return Tag.objects.all() # ty:ignore[unresolved-attribute]
sandwitches/forms.py CHANGED
@@ -1,6 +1,7 @@
1
1
  from django import forms
2
2
  from django.contrib.auth import get_user_model
3
3
  from django.contrib.auth.forms import UserCreationForm
4
+ from django.utils.translation import gettext_lazy as _
4
5
  from .models import Recipe
5
6
 
6
7
  User = get_user_model()
@@ -13,7 +14,7 @@ class BaseUserFormMixin:
13
14
  p1 = cleaned_data.get("password1")
14
15
  p2 = cleaned_data.get("password2")
15
16
  if p1 and p2 and p1 != p2:
16
- raise forms.ValidationError("Passwords do not match.")
17
+ raise forms.ValidationError(_("Passwords do not match."))
17
18
  return cleaned_data
18
19
 
19
20
  def _set_user_attributes(self, user, data):
@@ -25,8 +26,8 @@ class BaseUserFormMixin:
25
26
 
26
27
 
27
28
  class AdminSetupForm(forms.ModelForm, BaseUserFormMixin):
28
- password1 = forms.CharField(widget=forms.PasswordInput, label="Password")
29
- password2 = forms.CharField(widget=forms.PasswordInput, label="Confirm Password")
29
+ password1 = forms.CharField(widget=forms.PasswordInput, label=_("Password"))
30
+ password2 = forms.CharField(widget=forms.PasswordInput, label=_("Confirm Password"))
30
31
 
31
32
  class Meta:
32
33
  model = User
@@ -80,10 +81,13 @@ class RecipeForm(forms.ModelForm):
80
81
 
81
82
 
82
83
  class RatingForm(forms.Form):
83
- """Simple form for rating recipes (1-5)."""
84
-
85
- score = forms.ChoiceField(
86
- choices=[(str(i), str(i)) for i in range(1, 6)],
87
- widget=forms.RadioSelect,
88
- label="Your rating",
84
+ """Form for rating recipes (0-10)."""
85
+
86
+ score = forms.FloatField(
87
+ min_value=0.0,
88
+ max_value=10.0,
89
+ widget=forms.NumberInput(
90
+ attrs={"step": "0.1", "min": "0", "max": "10", "class": "slider"}
91
+ ),
92
+ label=_("Your rating"),
89
93
  )
Binary file
@@ -2,138 +2,359 @@
2
2
  msgid ""
3
3
  msgstr ""
4
4
  "Project-Id-Version: sandwitches 1.0\n"
5
- "POT-Creation-Date: 2025-01-01 00:00+0000\n"
5
+ "Report-Msgid-Bugs-To: \n"
6
+ "POT-Creation-Date: 2025-12-31 13:39+0000\n"
6
7
  "PO-Revision-Date: 2025-01-01 00:00+0000\n"
7
8
  "Language: nl\n"
8
9
  "MIME-Version: 1.0\n"
9
10
  "Content-Type: text/plain; charset=UTF-8\n"
10
11
  "Content-Transfer-Encoding: 8bit\n"
11
12
 
12
- msgid "Sign up"
13
- msgstr "Aanmelden"
13
+ #: src/sandwitches/forms.py:17
14
+ msgid "Passwords do not match."
15
+ msgstr "Wachtwoorden komen niet overeen."
14
16
 
15
- msgid "Sign Up"
16
- msgstr "Aanmelden"
17
+ #: src/sandwitches/forms.py:29 src/sandwitches/templates/setup.html:41
18
+ #: src/sandwitches/templates/signup.html:35
19
+ msgid "Password"
20
+ msgstr "Wachtwoord"
17
21
 
18
- msgid "Cancel"
19
- msgstr "Annuleren"
22
+ #: src/sandwitches/forms.py:30
23
+ msgid "Confirm Password"
24
+ msgstr "Bevestig wachtwoord"
20
25
 
21
- msgid "Username"
22
- msgstr "Gebruikersnaam"
26
+ #: src/sandwitches/forms.py:92
27
+ msgid "Your rating"
28
+ msgstr "Jouw beoordeling"
23
29
 
24
- msgid "Email (optional)"
25
- msgstr "E-mail (optioneel)"
30
+ #: src/sandwitches/tasks.py:52
31
+ #, python-format
32
+ msgid ""
33
+ "\n"
34
+ " Hungry? We just added <strong>%(title)s</strong> to our collection.\n"
35
+ " \n"
36
+ " It's a delicious recipe that you won't want to miss!\n"
37
+ " %(description)s\n"
38
+ "\n"
39
+ " Check out the full recipe, ingredients, and steps here:\n"
40
+ " %(url)s\n"
41
+ "\n"
42
+ " Happy Cooking!\n"
43
+ "\n"
44
+ " The Sandwitches Team\n"
45
+ " "
46
+ msgstr ""
47
+ "\n"
48
+ " Trek? We hebben zojuist <strong>%(title)s</strong> toegevoegd aan onze collectie.\n"
49
+ " \n"
50
+ " Het is een heerlijk recept dat je niet wilt missen!\n"
51
+ " %(description)s\n"
52
+ "\n"
53
+ " Bekijk het volledige recept, ingrediënten en stappen hier:\n"
54
+ " %(url)s\n"
55
+ "\n"
56
+ " Veel kookplezier!\n"
57
+ "\n"
58
+ " Het Sandwitches Team\n"
59
+ " "
60
+
61
+ #: src/sandwitches/tasks.py:76
62
+ #, python-format
63
+ msgid ""
64
+ "\n"
65
+ " <div style=\"font-family: 'Helvetica', sans-serif; max-width: 600px; "
66
+ "margin: auto; border: 1px solid #eee; padding: 20px;\">\n"
67
+ " <h2 style=\"color: #d35400; text-align: center;\">New Recipe: "
68
+ "%(title)s by %(uploaded_by)s</h2>\n"
69
+ " <div style=\"text-align: center; margin: 20px 0;\">\n"
70
+ " <img src=\"%(image_url)s\" alt=\"%(title)s\" style=\"width: "
71
+ "100%%; border-radius: 8px;\">\n"
72
+ " </div>\n"
73
+ " <p style=\"font-size: 16px; line-height: 1.5; color: #333;\">\n"
74
+ " Hungry? We just added <strong>%(title)s</strong> to our "
75
+ "collection.\n"
76
+ " <br>\n"
77
+ " It's a delicious recipe that you won't want to miss!\n"
78
+ " <br>\n"
79
+ " %(description)s\n"
80
+ " <br>\n"
81
+ " Check out the full recipe, ingredients, and steps here:\n"
82
+ " Click the button below to see how to make it!\n"
83
+ " <br>\n"
84
+ " Happy Cooking!\n"
85
+ " <br>\n"
86
+ " The Sandwitches Team\n"
87
+ " </p>\n"
88
+ " <div style=\"text-align: center; margin-top: 30px;\">\n"
89
+ " <a href=\"%(url)s\" style=\"background-color: #e67e22; color: "
90
+ "white; padding: 12px 25px; text-decoration: none; border-radius: 5px; font-"
91
+ "weight: bold;\">VIEW RECIPE</a>\n"
92
+ " </div>\n"
93
+ " </div>\n"
94
+ " "
95
+ msgstr ""
96
+ "\n"
97
+ " <div style=\"font-family: 'Helvetica', sans-serif; max-width: 600px; "
98
+ "margin: auto; border: 1px solid #eee; padding: 20px;\">\n"
99
+ " <h2 style=\"color: #d35400; text-align: center;\">Nieuw recept: "
100
+ "%(title)s door %(uploaded_by)s</h2>\n"
101
+ " <div style=\"text-align: center; margin: 20px 0;\">\n"
102
+ " <img src=\"%(image_url)s\" alt=\"%(title)s\" style=\"width: "
103
+ "100%%; border-radius: 8px;\">\n"
104
+ " </div>\n"
105
+ " <p style=\"font-size: 16px; line-height: 1.5; color: #333;\">\n"
106
+ " Trek? We hebben zojuist <strong>%(title)s</strong> toegevoegd aan onze "
107
+ "collectie.\n"
108
+ " <br>\n"
109
+ " Het is een heerlijk recept dat je niet wilt missen!\n"
110
+ " <br>\n"
111
+ " %(description)s\n"
112
+ " <br>\n"
113
+ " Bekijk het volledige recept, ingrediënten en stappen hier:\n"
114
+ " Klik op de knop hieronder om te zien hoe je het maakt!\n"
115
+ " <br>\n"
116
+ " Veel kookplezier!\n"
117
+ " <br>\n"
118
+ " Het Sandwitches Team\n"
119
+ " </p>\n"
120
+ " <div style=\"text-align: center; margin-top: 30px;\">\n"
121
+ " <a href=\"%(url)s\" style=\"background-color: #e67e22; color: "
122
+ "white; padding: 12px 25px; text-decoration: none; border-radius: 5px; font-"
123
+ "weight: bold;\">BEKIJK RECEPT</a>\n"
124
+ " </div>\n"
125
+ " </div>\n"
126
+ " "
127
+
128
+ #: src/sandwitches/tasks.py:104
129
+ #, python-format
130
+ msgid "Sandwitches - New Recipe: %(title)s by %(uploaded_by)s"
131
+ msgstr "Sandwitches - Nieuw recept: %(title)s door %(uploaded_by)s"
132
+
133
+ #: src/sandwitches/templates/base_pico.html:175
134
+ msgid "Language"
135
+ msgstr "Taal"
136
+
137
+ #: src/sandwitches/templates/base_pico.html:185
138
+ msgid "Docs"
139
+ msgstr "Documentatie"
26
140
 
27
- msgid "First name"
28
- msgstr "Voornaam"
141
+ #: src/sandwitches/templates/base_pico.html:187
142
+ msgid "Admin"
143
+ msgstr "Beheer"
29
144
 
30
- msgid "Last name"
31
- msgstr "Achternaam"
145
+ #: src/sandwitches/templates/base_pico.html:188
146
+ msgid "Logout"
147
+ msgstr "Uitloggen"
32
148
 
33
- msgid "Password"
34
- msgstr "Wachtwoord"
149
+ #: src/sandwitches/templates/base_pico.html:190
150
+ msgid "Login"
151
+ msgstr "Inloggen"
35
152
 
36
- msgid "Confirm password"
37
- msgstr "Bevestig wachtwoord"
153
+ #: src/sandwitches/templates/base_pico.html:191
154
+ #: src/sandwitches/templates/signup.html:9
155
+ msgid "Sign up"
156
+ msgstr "Aanmelden"
38
157
 
158
+ #: src/sandwitches/templates/detail.html:7
39
159
  msgid "Back to all"
40
160
  msgstr "Terug naar alles"
41
161
 
162
+ #: src/sandwitches/templates/detail.html:21
163
+ #: src/sandwitches/templates/detail.html:28
42
164
  msgid "Description"
43
165
  msgstr "Beschrijving"
44
166
 
45
- msgid "Ingredients"
46
- msgstr "Ingrediënten"
47
-
48
- msgid "Instructions"
49
- msgstr "Instructies"
167
+ #: src/sandwitches/templates/detail.html:25
168
+ msgid "Uploaded by"
169
+ msgstr "Geüpload door"
50
170
 
171
+ #: src/sandwitches/templates/detail.html:32
51
172
  msgid "No description yet."
52
173
  msgstr "Nog geen beschrijving."
53
174
 
175
+ #: src/sandwitches/templates/detail.html:35
176
+ msgid "Ingredients"
177
+ msgstr "Ingrediënten"
178
+
179
+ #: src/sandwitches/templates/detail.html:39
54
180
  msgid "No ingredients listed."
55
181
  msgstr "Geen ingrediënten vermeld."
56
182
 
183
+ #: src/sandwitches/templates/detail.html:42
184
+ msgid "Instructions"
185
+ msgstr "Instructies"
186
+
187
+ #: src/sandwitches/templates/detail.html:46
57
188
  msgid "No instructions yet."
58
189
  msgstr "Nog geen instructies."
59
190
 
191
+ #: src/sandwitches/templates/detail.html:58
60
192
  msgid "Rating"
61
193
  msgstr "Beoordeling"
62
194
 
195
+ #: src/sandwitches/templates/detail.html:60
63
196
  msgid "Average:"
64
197
  msgstr "Gemiddelde:"
65
198
 
199
+ #: src/sandwitches/templates/detail.html:60
200
+ msgid "vote"
201
+ msgstr "stem"
202
+
203
+ #: src/sandwitches/templates/detail.html:62
66
204
  msgid "No ratings yet."
67
205
  msgstr "Nog geen beoordelingen."
68
206
 
69
- msgid "Your rating:"
207
+ #: src/sandwitches/templates/detail.html:67
208
+ msgid "Your rating:"
70
209
  msgstr "Jouw beoordeling:"
71
210
 
211
+ #: src/sandwitches/templates/detail.html:73
72
212
  msgid "What would you rate this sandwich?"
73
213
  msgstr "Wat zou je dit broodje geven?"
74
214
 
215
+ #: src/sandwitches/templates/detail.html:88
216
+ msgid "Score (0-10):"
217
+ msgstr "Score (0-10):"
218
+
219
+ #: src/sandwitches/templates/detail.html:105
75
220
  msgid "Update"
76
221
  msgstr "Bijwerken"
77
222
 
223
+ #: src/sandwitches/templates/detail.html:105
78
224
  msgid "Rate"
79
225
  msgstr "Beoordeel"
80
226
 
81
- msgid "Login"
227
+ #: src/sandwitches/templates/detail.html:110
228
+ msgid "Log in"
82
229
  msgstr "Inloggen"
83
230
 
231
+ #: src/sandwitches/templates/detail.html:110
84
232
  msgid "to rate this recipe."
85
233
  msgstr "om dit recept te beoordelen."
86
234
 
87
- msgid "Docs"
88
- msgstr "Docs"
235
+ #: src/sandwitches/templates/detail.html:116
236
+ #: src/sandwitches/templates/form.html:5
237
+ #: src/sandwitches/templates/index.html:42
238
+ msgid "Edit"
239
+ msgstr "Bewerk"
89
240
 
90
- msgid "Admin"
91
- msgstr "Beheer"
241
+ #: src/sandwitches/templates/form.html:9
242
+ msgid "Edit Recipe:"
243
+ msgstr "Recept bewerken:"
92
244
 
93
- msgid "Logout"
94
- msgstr "Uitloggen"
245
+ #: src/sandwitches/templates/form.html:12
246
+ msgid "Save"
247
+ msgstr "Opslaan"
95
248
 
249
+ #: src/sandwitches/templates/form.html:13
250
+ #: src/sandwitches/templates/setup.html:49
251
+ #: src/sandwitches/templates/signup.html:43
252
+ msgid "Cancel"
253
+ msgstr "Annuleren"
254
+
255
+ #: src/sandwitches/templates/index.html:8
256
+ msgid "Sandwitches: sandwiches so good, they haunt you!"
257
+ msgstr "Sandwitches: broodjes zo lekker, dat ze je achtervolgen!"
258
+
259
+ #: src/sandwitches/templates/index.html:12
260
+ msgid "Search by title or tag"
261
+ msgstr "Zoek op titel of tag"
262
+
263
+ #: src/sandwitches/templates/index.html:12
264
+ msgid "Search"
265
+ msgstr "Zoeken"
266
+
267
+ #: src/sandwitches/templates/index.html:50
268
+ msgid "No sandwitches yet, please stay tuned."
269
+ msgstr "Nog geen broodjes, kom later terug."
270
+
271
+ #: src/sandwitches/templates/setup.html:3
96
272
  msgid "Initial setup — Create admin"
97
273
  msgstr "Eerste installatie — Beheerder aanmaken"
98
274
 
275
+ #: src/sandwitches/templates/setup.html:9
99
276
  msgid "Create initial administrator"
100
277
  msgstr "Maak de eerste beheerder"
101
278
 
102
- msgid "This page is only available when there are no admin users in the database."
103
- msgstr "Deze pagina is alleen beschikbaar als er nog geen beheerders in de database zijn."
279
+ #: src/sandwitches/templates/setup.html:11
280
+ msgid ""
281
+ "This page is only available when there are no admin users in the database."
282
+ msgstr ""
283
+ "Deze pagina is alleen beschikbaar als er nog geen beheerders in de database "
284
+ "zijn."
285
+
286
+ #: src/sandwitches/templates/setup.html:14
287
+ msgid ""
288
+ "After creating the account you will be logged in and redirected to the admin."
289
+ msgstr ""
290
+ "Na aanmaken wordt u ingelogd en doorgestuurd naar het beheerpaneel."
291
+
292
+ #: src/sandwitches/templates/setup.html:29
293
+ #: src/sandwitches/templates/signup.html:23
294
+ msgid "Username"
295
+ msgstr "Gebruikersnaam"
296
+
297
+ #: src/sandwitches/templates/setup.html:32
298
+ #: src/sandwitches/templates/signup.html:26
299
+ msgid "Email (optional)"
300
+ msgstr "E-mail (optioneel)"
301
+
302
+ #: src/sandwitches/templates/setup.html:35
303
+ #: src/sandwitches/templates/signup.html:29
304
+ msgid "First name"
305
+ msgstr "Voornaam"
306
+
307
+ #: src/sandwitches/templates/setup.html:38
308
+ #: src/sandwitches/templates/signup.html:32
309
+ msgid "Last name"
310
+ msgstr "Achternaam"
104
311
 
105
- msgid "After creating the account you will be logged in and redirected to the admin."
106
- msgstr "Na aanmaken wordt u ingelogd en doorgestuurd naar het beheerpaneel."
312
+ #: src/sandwitches/templates/setup.html:44
313
+ #: src/sandwitches/templates/signup.html:38
314
+ msgid "Confirm password"
315
+ msgstr "Bevestig wachtwoord"
107
316
 
317
+ #: src/sandwitches/templates/setup.html:48
108
318
  msgid "Create admin"
109
319
  msgstr "Maak beheerder"
110
320
 
111
- msgid "Account created and signed in."
112
- msgstr "Account aangemaakt en ingelogd."
113
-
114
- msgid "Admin account created and signed in."
115
- msgstr "Beheerderaccount aangemaakt en ingelogd."
321
+ #: src/sandwitches/templates/signup.html:3
322
+ #: src/sandwitches/templates/signup.html:42
323
+ msgid "Sign Up"
324
+ msgstr "Aanmelden"
116
325
 
326
+ #: src/sandwitches/views.py:73
117
327
  msgid "Your rating has been saved."
118
328
  msgstr "Je beoordeling is opgeslagen."
119
329
 
330
+ #: src/sandwitches/views.py:75
120
331
  msgid "Could not save rating."
121
332
  msgstr "Kon de beoordeling niet opslaan."
122
333
 
123
- msgid "Uploaded by"
124
- msgstr "Geüpload door"
334
+ #: src/sandwitches/views.py:101
335
+ msgid "Admin account created and signed in."
336
+ msgstr "Beheerderaccount aangemaakt en ingelogd."
125
337
 
126
- msgid "Search by title or tag"
127
- msgstr "Zoek op titel of tag"
338
+ #: src/sandwitches/views.py:120
339
+ msgid "Account created and signed in."
340
+ msgstr "Account aangemaakt en ingelogd."
128
341
 
129
- msgid "Search"
130
- msgstr "Zoeken"
342
+ #: src/sandwitches/views.py:131
343
+ msgid "Invalid Media Root Configuration"
344
+ msgstr "Ongeldige Media Root Configuratie"
131
345
 
132
- msgid "No sandwitches yet, please stay tuned."
133
- msgstr "Nog geen broodjes, kom later terug."
346
+ #: src/sandwitches/views.py:133
347
+ msgid "Invalid File Path"
348
+ msgstr "Ongeldig bestandspad"
134
349
 
135
- msgid "Edit"
136
- msgstr "Bewerk"
350
+ #: src/sandwitches/views.py:138
351
+ msgid "Access Denied"
352
+ msgstr "Toegang geweigerd"
137
353
 
138
- msgid "Sandwitches: sandwiches so good, they haunt you!"
139
- msgstr "Sandwitches: broodjes zo lekker, dat ze je achtervolgen!"
354
+ #: src/sandwitches/views.py:141
355
+ msgid "File not found"
356
+ msgstr "Bestand niet gevonden"
357
+
358
+ #: src/sandwitches/views.py:145
359
+ msgid "Access Denied: Only image files are allowed."
360
+ msgstr "Toegang geweigerd: Alleen afbeeldingsbestanden zijn toegestaan."
@@ -0,0 +1,23 @@
1
+ # Generated by Django 6.0 on 2025-12-29 17:58
2
+
3
+ import django.core.validators
4
+ from django.db import migrations, models
5
+
6
+
7
+ class Migration(migrations.Migration):
8
+ dependencies = [
9
+ ("sandwitches", "0006_profile"),
10
+ ]
11
+
12
+ operations = [
13
+ migrations.AlterField(
14
+ model_name="rating",
15
+ name="score",
16
+ field=models.FloatField(
17
+ validators=[
18
+ django.core.validators.MinValueValidator(0.0),
19
+ django.core.validators.MaxValueValidator(10.0),
20
+ ]
21
+ ),
22
+ ),
23
+ ]
sandwitches/models.py CHANGED
@@ -6,6 +6,7 @@ from django.contrib.auth import get_user_model
6
6
  from django.db.models import Avg
7
7
  from .tasks import email_users
8
8
  from django.conf import settings
9
+ from django.core.validators import MinValueValidator, MaxValueValidator
9
10
  import logging
10
11
  from django.urls import reverse
11
12
 
@@ -179,7 +180,9 @@ class Recipe(models.Model):
179
180
  class Rating(models.Model):
180
181
  recipe = models.ForeignKey(Recipe, related_name="ratings", on_delete=models.CASCADE)
181
182
  user = models.ForeignKey(User, related_name="ratings", on_delete=models.CASCADE)
182
- score = models.PositiveSmallIntegerField(choices=[(i, i) for i in range(1, 6)])
183
+ score = models.FloatField(
184
+ validators=[MinValueValidator(0.0), MaxValueValidator(10.0)]
185
+ )
183
186
  created_at = models.DateTimeField(auto_now_add=True)
184
187
  updated_at = models.DateTimeField(auto_now=True)
185
188
 
sandwitches/settings.py CHANGED
@@ -149,7 +149,7 @@ AUTH_PASSWORD_VALIDATORS = [
149
149
 
150
150
  # Media files (for uploaded images)
151
151
  MEDIA_URL = "/media/"
152
- MEDIA_ROOT = Path("/config/media")
152
+ MEDIA_ROOT = Path(os.environ.get("MEDIA_ROOT", default=BASE_DIR / "media")) # ty:ignore[no-matching-overload]
153
153
 
154
154
  # Static (for CSS etc)
155
155
  STATIC_URL = "/static/"
sandwitches/tasks.py CHANGED
@@ -7,6 +7,7 @@ from django.contrib.auth import get_user_model
7
7
 
8
8
  from django.core.mail import EmailMultiAlternatives
9
9
  from django.conf import settings
10
+ from django.utils.translation import gettext as _
10
11
 
11
12
 
12
13
  import textwrap
@@ -48,33 +49,44 @@ def send_emails(recipe_id, emails):
48
49
  else "http://localhost"
49
50
  ).rstrip("/")
50
51
 
51
- raw_message = f"""
52
- Hungry? We just added <strong>{recipe.title}</strong> to our collection.
52
+ raw_message_fmt = _("""
53
+ Hungry? We just added <strong>%(title)s</strong> to our collection.
53
54
 
54
55
  It's a delicious recipe that you won't want to miss!
55
- {recipe.description}
56
+ %(description)s
56
57
 
57
58
  Check out the full recipe, ingredients, and steps here:
58
- {base_url}{recipe_slug}
59
+ %(url)s
59
60
 
60
61
  Happy Cooking!
61
62
 
62
63
  The Sandwitches Team
63
- """
64
- wrapped_message = textwrap.fill(textwrap.dedent(raw_message), width=70)
64
+ """)
65
+
66
+ context_data = {
67
+ "title": recipe.title,
68
+ "uploaded_by": recipe.uploaded_by,
69
+ "description": recipe.description,
70
+ "url": f"{base_url}{recipe_slug}",
71
+ "image_url": f"{base_url}{recipe.image.url}" if recipe.image else "",
72
+ }
73
+
74
+ wrapped_message = textwrap.fill(
75
+ textwrap.dedent(raw_message_fmt) % context_data, width=70
76
+ )
65
77
 
66
- html_content = f"""
78
+ html_content_fmt = _("""
67
79
  <div style="font-family: 'Helvetica', sans-serif; max-width: 600px; margin: auto; border: 1px solid #eee; padding: 20px;">
68
- <h2 style="color: #d35400; text-align: center;">New Recipe: {recipe.title} by {recipe.uploaded_by}</h2>
80
+ <h2 style="color: #d35400; text-align: center;">New Recipe: %(title)s by %(uploaded_by)s</h2>
69
81
  <div style="text-align: center; margin: 20px 0;">
70
- <img src="{base_url}{recipe.image.url}" alt="{recipe.title}" style="width: 100%; border-radius: 8px;">
82
+ <img src="%(image_url)s" alt="%(title)s" style="width: 100%%; border-radius: 8px;">
71
83
  </div>
72
84
  <p style="font-size: 16px; line-height: 1.5; color: #333;">
73
- Hungry? We just added <strong>{recipe.title}</strong> to our collection.
85
+ Hungry? We just added <strong>%(title)s</strong> to our collection.
74
86
  <br>
75
87
  It's a delicious recipe that you won't want to miss!
76
88
  <br>
77
- {recipe.description}
89
+ %(description)s
78
90
  <br>
79
91
  Check out the full recipe, ingredients, and steps here:
80
92
  Click the button below to see how to make it!
@@ -84,13 +96,17 @@ def send_emails(recipe_id, emails):
84
96
  The Sandwitches Team
85
97
  </p>
86
98
  <div style="text-align: center; margin-top: 30px;">
87
- <a href="{base_url}{recipe_slug}" style="background-color: #e67e22; color: white; padding: 12px 25px; text-decoration: none; border-radius: 5px; font-weight: bold;">VIEW RECIPE</a>
99
+ <a href="%(url)s" style="background-color: #e67e22; color: white; padding: 12px 25px; text-decoration: none; border-radius: 5px; font-weight: bold;">VIEW RECIPE</a>
88
100
  </div>
89
101
  </div>
90
- """
102
+ """)
103
+
104
+ html_content = html_content_fmt % context_data
105
+
106
+ subject = _("Sandwitches - New Recipe: %(title)s by %(uploaded_by)s") % context_data
91
107
 
92
108
  msg = EmailMultiAlternatives(
93
- subject=f"Sandwitches - New Recipe: {recipe.title} by {recipe.uploaded_by}",
109
+ subject=subject,
94
110
  body=wrapped_message,
95
111
  from_email=from_email,
96
112
  bbc=emails,
@@ -83,24 +83,22 @@
83
83
  {% endif %}
84
84
 
85
85
  <div class="row">
86
- {% for i in "12345"|make_list %}
87
- <div class="col-sm-2">
88
- <label class="form-check">
89
- <input
90
- type="radio"
91
- name="{{ rating_form.score.name }}"
92
- id="rating-{{ i }}"
93
- value="{{ i }}"
94
- {% if user_rating and user_rating.score|stringformat:"s" == i %}
95
- checked
96
- {% elif rating_form.score.value == i %}
97
- checked
98
- {% endif %}
99
- />
100
- <span>{{ i }}</span>
101
- </label>
102
- </div>
103
- {% endfor %}
86
+ <div class="col-sm-12">
87
+ <label for="{{ rating_form.score.id_for_label }}">
88
+ {% trans "Score (0-10):" %} <span id="score-output">{{ rating_form.score.value|default:"5.0" }}</span>
89
+ </label>
90
+ <input
91
+ type="range"
92
+ name="{{ rating_form.score.name }}"
93
+ id="{{ rating_form.score.id_for_label }}"
94
+ min="0"
95
+ max="10"
96
+ step="0.1"
97
+ value="{{ rating_form.score.value|default:'5.0' }}"
98
+ oninput="document.getElementById('score-output').innerText = parseFloat(this.value).toFixed(1)"
99
+ style="width: 100%;"
100
+ >
101
+ </div>
104
102
  </div>
105
103
  </fieldset>
106
104
  <p style="margin-top:0.5rem;">
@@ -115,7 +113,7 @@
115
113
 
116
114
  {% if user.is_authenticated and user.is_staff %}
117
115
  <footer>
118
- <a href="/admin/sandwitches/recipe/{{ recipe.pk }}/change/">Edit</a>
116
+ <a href="/admin/sandwitches/recipe/{{ recipe.pk }}/change/">{% trans "Edit" %}</a>
119
117
  </footer>
120
118
  {% endif %}
121
119
  </article>
@@ -26,22 +26,22 @@
26
26
  </div>
27
27
  {% endif %}
28
28
 
29
- <label for="{{ form.username.id_for_label }}">Username</label>
29
+ <label for="{{ form.username.id_for_label }}">{% trans "Username" %}</label>
30
30
  {{ form.username }}
31
31
 
32
- <label for="{{ form.email.id_for_label }}">Email (optional)</label>
32
+ <label for="{{ form.email.id_for_label }}">{% trans "Email (optional)" %}</label>
33
33
  {{ form.email }}
34
34
 
35
- <label for="{{ form.first_name.id_for_label }}">First name</label>
35
+ <label for="{{ form.first_name.id_for_label }}">{% trans "First name" %}</label>
36
36
  {{ form.first_name }}
37
37
 
38
- <label for="{{ form.last_name.id_for_label }}">Last name</label>
38
+ <label for="{{ form.last_name.id_for_label }}">{% trans "Last name" %}</label>
39
39
  {{ form.last_name }}
40
40
 
41
- <label for="{{ form.password1.id_for_label }}">Password</label>
41
+ <label for="{{ form.password1.id_for_label }}">{% trans "Password" %}</label>
42
42
  {{ form.password1 }}
43
43
 
44
- <label for="{{ form.password2.id_for_label }}">Confirm password</label>
44
+ <label for="{{ form.password2.id_for_label }}">{% trans "Confirm password" %}</label>
45
45
  {{ form.password2 }}
46
46
 
47
47
  <p style="margin-top:1rem;">
sandwitches/views.py CHANGED
@@ -66,7 +66,7 @@ def recipe_rate(request, pk):
66
66
 
67
67
  form = RatingForm(request.POST)
68
68
  if form.is_valid():
69
- score = int(form.cleaned_data["score"])
69
+ score = float(form.cleaned_data["score"])
70
70
  Rating.objects.update_or_create( # ty:ignore[unresolved-attribute]
71
71
  recipe=recipe, user=request.user, defaults={"score": score}
72
72
  )
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: sandwitches
3
- Version: 1.2.0
3
+ Version: 1.3.1
4
4
  Summary: Add your description here
5
5
  Author: Martyn van Dijke
6
6
  Author-email: Martyn van Dijke <martijnvdijke600@gmail.com>
@@ -1,33 +1,34 @@
1
1
  sandwitches/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
2
2
  sandwitches/admin.py,sha256=IIVQOr_CIXsdFXDPQoZ_c83DuqbHVnw4Ht9tAtmvLM0,870
3
- sandwitches/api.py,sha256=r4HrlEv9IQMuvlr-WravamzTbUWcn85zS1e-iReHEuU,1968
3
+ sandwitches/api.py,sha256=YDadi-3f2Oz4ia5gXo_rgXVZkZ6LAOiOWraz9bpZzho,2404
4
4
  sandwitches/asgi.py,sha256=cygnXdXSSVspM7ZXuj47Ef6oz7HSTw4D7BPzgE2PU5w,399
5
- sandwitches/forms.py,sha256=NeGUi3xPzQpgw2cVWpo2ZKpYUXsCuJ3SzgGUkvxdV2A,2610
6
- sandwitches/locale/nl/LC_MESSAGES/django.mo,sha256=GgZ4aNmU-v0FStt7mafi_UCR4hC0PC06W0mIUPPWtUE,2906
7
- sandwitches/locale/nl/LC_MESSAGES/django.po,sha256=uk2W5ai6tyhKlA-rcucv5NpZfBrHNBev6Rp5wKWirqc,2843
5
+ sandwitches/forms.py,sha256=kYzotFafLyNli4a62PsL5hTqUXyno5jPqIwSv04vYaA,2749
6
+ sandwitches/locale/nl/LC_MESSAGES/django.mo,sha256=VdPE_fBnGxHPtizXpYA6E3RtduAoaVOtKTfQIgTa0yM,6934
7
+ sandwitches/locale/nl/LC_MESSAGES/django.po,sha256=8tJzlyUxyTZkyGQWSpxNU-uYhiFpPPzphiiPewUsZ3o,10303
8
8
  sandwitches/migrations/0001_initial.py,sha256=01IfkFbUyYMpTHV5GaBxJEKjzRIdUdPR5sY3AUTODww,2546
9
9
  sandwitches/migrations/0002_historicalrecipe.py,sha256=yU2KYssfjYhPXRYN6C8IMRFr-4QUGJozz-O167XuabM,2499
10
10
  sandwitches/migrations/0003_rating.py,sha256=iKk9M9lcBS5LwsJkMYsmYfHKqKs2QRTILgINbnilASM,1857
11
11
  sandwitches/migrations/0004_add_uploaded_by.py,sha256=gZawakGU-H0Abnn2Bw_Mswy9ngipzUC-BHRXRByKVZo,720
12
12
  sandwitches/migrations/0005_historicalrecipe_uploaded_by.py,sha256=xmYDXgBxjrQkbEtorQlDzW0K1Z70UNxRrUk75WjYdEI,766
13
13
  sandwitches/migrations/0006_profile.py,sha256=gzmxZWXmjldimYRdovuNouc2Y2idihg8UvEaU0ad_Ds,1558
14
+ sandwitches/migrations/0007_alter_rating_score.py,sha256=hHdJqjTwq2afcBi7TRSBofAjedDxu5YybbXLhUoMGUg,588
14
15
  sandwitches/migrations/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
15
- sandwitches/models.py,sha256=7Upm6abkEZjmk5rIzViW7M_4a3ai03-9tLWLkCty2hs,6390
16
- sandwitches/settings.py,sha256=5Qg0Wd-X1Kj1Fm7Teh0SRO8jOmVSUkpnUXfZRe1TjwI,5604
16
+ sandwitches/models.py,sha256=CxTwpzIej9hCnBb7kEF5bQAbw-lOvN_8w4SGplCDbFI,6484
17
+ sandwitches/settings.py,sha256=vkIxkZpbaobuLeqYydckYSBJBrCAs-4royx7vym2bQA,5680
17
18
  sandwitches/storage.py,sha256=HIiOEDa_LhpsbhCUBNO-SlCZDUJOoANUbyDIbspEcoE,2325
18
- sandwitches/tasks.py,sha256=aGLpTE42mCZbh2Pyt7fXIjbELm9lD727ir_2bto-wWw,3241
19
+ sandwitches/tasks.py,sha256=vB8HvaL4ooxowGOBxB-D2nOrKYfgc6kNaaTrEjpq1eY,3604
19
20
  sandwitches/templates/base.html,sha256=C9tUPfMKRvvdMqdDpE8ww21DH25bNktcVIwdrDEhajw,570
20
21
  sandwitches/templates/base_pico.html,sha256=yeWlWlrCfI9yVLStSyODvcNtSmxXvWm4SZORZN-31R4,8273
21
- sandwitches/templates/detail.html,sha256=p7FikXyse1bYxRCFCYH3GSwgNXHS7D9O6j4fUmmDkWQ,4592
22
+ sandwitches/templates/detail.html,sha256=arspmECgF-EuNDOv1shtIlj41oJE1V_tjbn-B8jjO5s,4653
22
23
  sandwitches/templates/form.html,sha256=S4yUq9p2v7vWifhA5xj_Z4ObpMoaqUq2w08TDw3vk0o,449
23
24
  sandwitches/templates/index.html,sha256=tgZFplcedG1r4s8pBc_qvJDXECPqrESPOSw56FnnCkw,2606
24
- sandwitches/templates/setup.html,sha256=btid1XtR0MO_qSWriWX7LeWUtNFA7rDB4zt5Md9UNhU,1787
25
+ sandwitches/templates/setup.html,sha256=yv9mBAPCa_XGsa92Q43pWqB-T6SJKPHKz6qnAEz0-5c,1871
25
26
  sandwitches/templates/signup.html,sha256=3qOdDMUHMshe2FoaQzR07lIm3Wbrzorb_h9Fp8Og-Eg,1585
26
27
  sandwitches/templatetags/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
27
28
  sandwitches/templatetags/markdown_extras.py,sha256=0ibmRzxE3r85x4k7kK71R-9UT0CgeegYF7MHzj3juTI,344
28
29
  sandwitches/urls.py,sha256=maKaIDfb_kNmMwrpS495TymuFnJJ6fbLoi5QRLjrFds,1582
29
- sandwitches/views.py,sha256=Av9dfTAygGkFkHO47NK6RDjprMTVRpDa4c63X_h9ur8,5192
30
+ sandwitches/views.py,sha256=7ukWJHBflhAM02j_ZGuoqlBEgN2QnIIq4VhtnvRU1rM,5194
30
31
  sandwitches/wsgi.py,sha256=Eyncpnahq_4s3Lr9ruB-R3Lu9j9zBXqgPbUj7qhIbwU,399
31
- sandwitches-1.2.0.dist-info/WHEEL,sha256=eh7sammvW2TypMMMGKgsM83HyA_3qQ5Lgg3ynoecH3M,79
32
- sandwitches-1.2.0.dist-info/METADATA,sha256=psetHwoaKHYa-_anYOLXOaBdi1UumZwg5lR8uStEg_I,731
33
- sandwitches-1.2.0.dist-info/RECORD,,
32
+ sandwitches-1.3.1.dist-info/WHEEL,sha256=eh7sammvW2TypMMMGKgsM83HyA_3qQ5Lgg3ynoecH3M,79
33
+ sandwitches-1.3.1.dist-info/METADATA,sha256=xedDolZeXZG1WF2hCMJPfVaTBZt1xrzNAQzfeaD8QFo,731
34
+ sandwitches-1.3.1.dist-info/RECORD,,