sandwitches 2.2.0__py3-none-any.whl → 2.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/admin.py +23 -2
- sandwitches/api.py +22 -1
- sandwitches/forms.py +49 -0
- sandwitches/management/__init__.py +0 -0
- sandwitches/management/commands/__init__.py +0 -0
- sandwitches/management/commands/reset_daily_orders.py +14 -0
- sandwitches/migrations/0007_historicalrecipe_price_recipe_price_order.py +86 -0
- sandwitches/migrations/0008_historicalrecipe_daily_orders_count_and_more.py +36 -0
- sandwitches/migrations/0009_historicalrecipe_is_approved_recipe_is_approved.py +22 -0
- sandwitches/models.py +63 -1
- sandwitches/settings.py +1 -0
- sandwitches/tasks.py +74 -0
- sandwitches/templates/admin/admin_base.html +4 -0
- sandwitches/templates/admin/dashboard.html +125 -61
- sandwitches/templates/admin/order_list.html +30 -0
- sandwitches/templates/admin/partials/dashboard_charts.html +90 -0
- sandwitches/templates/admin/partials/order_rows.html +28 -0
- sandwitches/templates/admin/rating_list.html +2 -0
- sandwitches/templates/admin/recipe_form.html +26 -0
- sandwitches/templates/admin/recipe_list.html +65 -15
- sandwitches/templates/base.html +12 -0
- sandwitches/templates/components/navbar.html +10 -5
- sandwitches/templates/components/recipe_header.html +17 -0
- sandwitches/templates/components/side_menu.html +4 -0
- sandwitches/templates/partials/recipe_list.html +7 -0
- sandwitches/templates/profile.html +95 -0
- sandwitches/templates/recipe_form.html +15 -1
- sandwitches/urls.py +9 -0
- sandwitches/views.py +178 -21
- {sandwitches-2.2.0.dist-info → sandwitches-2.3.1.dist-info}/METADATA +1 -1
- {sandwitches-2.2.0.dist-info → sandwitches-2.3.1.dist-info}/RECORD +32 -22
- {sandwitches-2.2.0.dist-info → sandwitches-2.3.1.dist-info}/WHEEL +0 -0
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
{% extends "base_beer.html" %}
|
|
2
|
+
{% load static i18n %}
|
|
3
|
+
{% block title %}{% trans "Your Profile" %}{% endblock %}
|
|
4
|
+
|
|
5
|
+
{% block content %}
|
|
6
|
+
<div class="large-space"></div>
|
|
7
|
+
|
|
8
|
+
<div class="grid">
|
|
9
|
+
<div class="s12 m10 l8 xl6 middle-align center-align" style="margin: 0 auto;">
|
|
10
|
+
<article class="round elevate">
|
|
11
|
+
<div class="padding">
|
|
12
|
+
<h4 class="center-align primary-text">{% trans "Edit your profile" %}</h4>
|
|
13
|
+
</div>
|
|
14
|
+
|
|
15
|
+
<form method="post" enctype="multipart/form-data" novalidate>
|
|
16
|
+
{% csrf_token %}
|
|
17
|
+
|
|
18
|
+
{% if form.non_field_errors %}
|
|
19
|
+
<div class="padding error surface round mb">
|
|
20
|
+
{% for err in form.non_field_errors %}
|
|
21
|
+
<div class="row align-center">
|
|
22
|
+
<i class="error-text">warning</i>
|
|
23
|
+
<span class="error-text">{{ err }}</span>
|
|
24
|
+
</div>
|
|
25
|
+
{% endfor %}
|
|
26
|
+
</div>
|
|
27
|
+
{% endif %}
|
|
28
|
+
|
|
29
|
+
<div class="grid">
|
|
30
|
+
<div class="s12 m6">
|
|
31
|
+
<div class="field label border round {% if form.first_name.errors %}error{% endif %}">
|
|
32
|
+
<input type="text" name="{{ form.first_name.name }}" id="{{ form.first_name.id_for_label }}" value="{{ form.first_name.value|default:'' }}">
|
|
33
|
+
<label>{% trans "First name" %}</label>
|
|
34
|
+
{% if form.first_name.errors %}
|
|
35
|
+
<span class="helper error-text">{{ form.first_name.errors.0 }}</span>
|
|
36
|
+
{% endif %}
|
|
37
|
+
</div>
|
|
38
|
+
</div>
|
|
39
|
+
<div class="s12 m6">
|
|
40
|
+
<div class="field label border round {% if form.last_name.errors %}error{% endif %}">
|
|
41
|
+
<input type="text" name="{{ form.last_name.name }}" id="{{ form.last_name.id_for_label }}" value="{{ form.last_name.value|default:'' }}">
|
|
42
|
+
<label>{% trans "Last name" %}</label>
|
|
43
|
+
{% if form.last_name.errors %}
|
|
44
|
+
<span class="helper error-text">{{ form.last_name.errors.0 }}</span>
|
|
45
|
+
{% endif %}
|
|
46
|
+
</div>
|
|
47
|
+
</div>
|
|
48
|
+
</div>
|
|
49
|
+
|
|
50
|
+
<div class="field label border round {% if form.email.errors %}error{% endif %}">
|
|
51
|
+
<input type="email" name="{{ form.email.name }}" id="{{ form.email.id_for_label }}" value="{{ form.email.value|default:'' }}">
|
|
52
|
+
<label>{% trans "Email" %}</label>
|
|
53
|
+
{% if form.email.errors %}
|
|
54
|
+
<span class="helper error-text">{{ form.email.errors.0 }}</span>
|
|
55
|
+
{% endif %}
|
|
56
|
+
</div>
|
|
57
|
+
|
|
58
|
+
<div class="field label border round textarea {% if form.bio.errors %}error{% endif %}">
|
|
59
|
+
<textarea name="{{ form.bio.name }}" id="{{ form.bio.id_for_label }}" rows="3">{{ form.bio.value|default:'' }}</textarea>
|
|
60
|
+
<label>{% trans "Bio" %}</label>
|
|
61
|
+
{% if form.bio.errors %}
|
|
62
|
+
<span class="helper error-text">{{ form.bio.errors.0 }}</span>
|
|
63
|
+
{% endif %}
|
|
64
|
+
</div>
|
|
65
|
+
|
|
66
|
+
<div class="field middle-align {% if form.avatar.errors %}error{% endif %}">
|
|
67
|
+
<nav>
|
|
68
|
+
<div class="max">
|
|
69
|
+
<label class="button border transparent round">
|
|
70
|
+
<input type="file" name="{{ form.avatar.name }}" id="{{ form.avatar.id_for_label }}" accept="image/*">
|
|
71
|
+
<i>upload</i>
|
|
72
|
+
<span>{% trans "Upload Profile Picture" %}</span>
|
|
73
|
+
</label>
|
|
74
|
+
</div>
|
|
75
|
+
{% if user.avatar %}
|
|
76
|
+
<img src="{{ user.avatar.url }}" class="circle small">
|
|
77
|
+
{% endif %}
|
|
78
|
+
</nav>
|
|
79
|
+
{% if form.avatar.errors %}
|
|
80
|
+
<span class="helper error-text">{{ form.avatar.errors.0 }}</span>
|
|
81
|
+
{% endif %}
|
|
82
|
+
</div>
|
|
83
|
+
|
|
84
|
+
<div class="large-space"></div>
|
|
85
|
+
|
|
86
|
+
<nav class="right-align">
|
|
87
|
+
<a class="button transparent border round" href="{% url 'index' %}">{% trans "Cancel" %}</a>
|
|
88
|
+
<button type="submit" class="button primary round">{% trans "Save changes" %}</button>
|
|
89
|
+
</nav>
|
|
90
|
+
|
|
91
|
+
</form>
|
|
92
|
+
</article>
|
|
93
|
+
</div>
|
|
94
|
+
</div>
|
|
95
|
+
{% endblock %}
|
|
@@ -68,9 +68,23 @@
|
|
|
68
68
|
</div>
|
|
69
69
|
</div>
|
|
70
70
|
|
|
71
|
+
<div class="s12 m6">
|
|
72
|
+
<div class="field label border round">
|
|
73
|
+
{{ form.price }}
|
|
74
|
+
<label>{% trans "Price" %}</label>
|
|
75
|
+
</div>
|
|
76
|
+
</div>
|
|
77
|
+
|
|
78
|
+
<div class="s12 m6">
|
|
79
|
+
<div class="field label border round">
|
|
80
|
+
{{ form.servings }}
|
|
81
|
+
<label>{% trans "Servings" %}</label>
|
|
82
|
+
</div>
|
|
83
|
+
</div>
|
|
84
|
+
|
|
71
85
|
<div class="s12">
|
|
72
86
|
<div class="field label border round">
|
|
73
|
-
{{ form.
|
|
87
|
+
{{ form.tags_string }}
|
|
74
88
|
<label>{% trans "Tags (comma separated)" %}</label>
|
|
75
89
|
</div>
|
|
76
90
|
</div>
|
sandwitches/urls.py
CHANGED
|
@@ -33,6 +33,8 @@ urlpatterns = [
|
|
|
33
33
|
path("signup/", views.signup, name="signup"),
|
|
34
34
|
path("login/", views.CustomLoginView.as_view(), name="login"),
|
|
35
35
|
path("logout/", LogoutView.as_view(next_page="index"), name="logout"),
|
|
36
|
+
path("profile/", views.user_profile, name="user_profile"),
|
|
37
|
+
path("submit-recipe/", views.submit_recipe, name="submit_recipe"),
|
|
36
38
|
path("admin/", admin.site.urls),
|
|
37
39
|
path("api/", api.urls),
|
|
38
40
|
path("media/<path:file_path>", views.media, name="media"),
|
|
@@ -48,6 +50,7 @@ urlpatterns += i18n_patterns(
|
|
|
48
50
|
path("recipes/<slug:slug>/", views.recipe_detail, name="recipe_detail"),
|
|
49
51
|
path("setup/", views.setup, name="setup"),
|
|
50
52
|
path("recipes/<int:pk>/rate/", views.recipe_rate, name="recipe_rate"),
|
|
53
|
+
path("recipes/<int:pk>/order/", views.order_recipe, name="order_recipe"),
|
|
51
54
|
path("recipes/<int:pk>/favorite/", views.toggle_favorite, name="toggle_favorite"),
|
|
52
55
|
path("dashboard/", views.admin_dashboard, name="admin_dashboard"),
|
|
53
56
|
path("dashboard/recipes/", views.admin_recipe_list, name="admin_recipe_list"),
|
|
@@ -62,6 +65,11 @@ urlpatterns += i18n_patterns(
|
|
|
62
65
|
views.admin_recipe_delete,
|
|
63
66
|
name="admin_recipe_delete",
|
|
64
67
|
),
|
|
68
|
+
path(
|
|
69
|
+
"dashboard/recipes/<int:pk>/approve/",
|
|
70
|
+
views.admin_recipe_approve,
|
|
71
|
+
name="admin_recipe_approve",
|
|
72
|
+
),
|
|
65
73
|
path(
|
|
66
74
|
"dashboard/recipes/<int:pk>/rotate/",
|
|
67
75
|
views.admin_recipe_rotate,
|
|
@@ -98,6 +106,7 @@ urlpatterns += i18n_patterns(
|
|
|
98
106
|
views.admin_rating_delete,
|
|
99
107
|
name="admin_rating_delete",
|
|
100
108
|
),
|
|
109
|
+
path("dashboard/orders/", views.admin_order_list, name="admin_order_list"),
|
|
101
110
|
prefix_default_language=True,
|
|
102
111
|
)
|
|
103
112
|
|
sandwitches/views.py
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
from django.core.exceptions import ValidationError
|
|
1
3
|
from django.shortcuts import render, get_object_or_404, redirect
|
|
2
4
|
from django.urls import reverse
|
|
3
5
|
from django.contrib import messages
|
|
@@ -6,7 +8,7 @@ from django.contrib.auth import get_user_model
|
|
|
6
8
|
from django.contrib.auth.decorators import login_required
|
|
7
9
|
from django.contrib.admin.views.decorators import staff_member_required
|
|
8
10
|
from django.utils.translation import gettext as _
|
|
9
|
-
from .models import Recipe, Rating, Tag
|
|
11
|
+
from .models import Recipe, Rating, Tag, Order
|
|
10
12
|
from .forms import (
|
|
11
13
|
RecipeForm,
|
|
12
14
|
AdminSetupForm,
|
|
@@ -14,10 +16,12 @@ from .forms import (
|
|
|
14
16
|
RatingForm,
|
|
15
17
|
UserEditForm,
|
|
16
18
|
TagForm,
|
|
19
|
+
UserProfileForm,
|
|
20
|
+
UserRecipeSubmissionForm,
|
|
17
21
|
)
|
|
18
|
-
from django.http import HttpResponseBadRequest
|
|
22
|
+
from django.http import HttpResponseBadRequest, Http404
|
|
19
23
|
from django.conf import settings
|
|
20
|
-
from django.http import FileResponse
|
|
24
|
+
from django.http import FileResponse
|
|
21
25
|
from pathlib import Path
|
|
22
26
|
import mimetypes
|
|
23
27
|
from PIL import Image
|
|
@@ -31,6 +35,30 @@ from sandwitches import __version__ as sandwitches_version
|
|
|
31
35
|
User = get_user_model()
|
|
32
36
|
|
|
33
37
|
|
|
38
|
+
@login_required
|
|
39
|
+
def submit_recipe(request):
|
|
40
|
+
if request.method == "POST":
|
|
41
|
+
form = UserRecipeSubmissionForm(request.POST, request.FILES)
|
|
42
|
+
if form.is_valid():
|
|
43
|
+
recipe = form.save(commit=False)
|
|
44
|
+
recipe.uploaded_by = request.user
|
|
45
|
+
recipe.is_approved = False # Explicitly set to False just in case
|
|
46
|
+
recipe.save()
|
|
47
|
+
form.save_m2m()
|
|
48
|
+
messages.success(
|
|
49
|
+
request,
|
|
50
|
+
_("Your recipe has been submitted and is awaiting admin approval."),
|
|
51
|
+
)
|
|
52
|
+
return redirect("user_profile")
|
|
53
|
+
else:
|
|
54
|
+
form = UserRecipeSubmissionForm()
|
|
55
|
+
return render(
|
|
56
|
+
request,
|
|
57
|
+
"recipe_form.html",
|
|
58
|
+
{"form": form, "title": _("Submit Recipe"), "version": sandwitches_version},
|
|
59
|
+
)
|
|
60
|
+
|
|
61
|
+
|
|
34
62
|
class CustomLoginView(LoginView):
|
|
35
63
|
template_name = "login.html"
|
|
36
64
|
redirect_authenticated_user = True
|
|
@@ -87,6 +115,15 @@ def admin_dashboard(request):
|
|
|
87
115
|
.order_by("date")
|
|
88
116
|
)
|
|
89
117
|
|
|
118
|
+
# Orders over time
|
|
119
|
+
order_data = (
|
|
120
|
+
Order.objects.filter(created_at__date__range=(start_date, end_date)) # ty:ignore[unresolved-attribute]
|
|
121
|
+
.annotate(date=TruncDate("created_at"))
|
|
122
|
+
.values("date")
|
|
123
|
+
.annotate(count=Count("id"))
|
|
124
|
+
.order_by("date")
|
|
125
|
+
)
|
|
126
|
+
|
|
90
127
|
# Prepare labels and data for JS
|
|
91
128
|
recipe_labels = [d["date"].strftime("%d/%m/%Y") for d in recipe_data]
|
|
92
129
|
recipe_counts = [d["count"] for d in recipe_data]
|
|
@@ -94,36 +131,71 @@ def admin_dashboard(request):
|
|
|
94
131
|
rating_labels = [d["date"].strftime("%d/%m/%Y") for d in rating_data]
|
|
95
132
|
rating_avgs = [float(d["avg"]) for d in rating_data]
|
|
96
133
|
|
|
134
|
+
order_labels = [d["date"].strftime("%d/%m/%Y") for d in order_data]
|
|
135
|
+
order_counts = [d["count"] for d in order_data]
|
|
136
|
+
|
|
137
|
+
pending_recipes = Recipe.objects.filter(is_approved=False).order_by("-created_at") # ty:ignore[unresolved-attribute]
|
|
138
|
+
|
|
139
|
+
context = {
|
|
140
|
+
"recipe_count": recipe_count,
|
|
141
|
+
"user_count": user_count,
|
|
142
|
+
"tag_count": tag_count,
|
|
143
|
+
"recent_recipes": recent_recipes,
|
|
144
|
+
"pending_recipes": pending_recipes,
|
|
145
|
+
"recipe_labels": recipe_labels,
|
|
146
|
+
"recipe_counts": recipe_counts,
|
|
147
|
+
"rating_labels": rating_labels,
|
|
148
|
+
"rating_avgs": rating_avgs,
|
|
149
|
+
"order_labels": order_labels,
|
|
150
|
+
"order_counts": order_counts,
|
|
151
|
+
"start_date": start_date.strftime("%Y-%m-%d"),
|
|
152
|
+
"end_date": end_date.strftime("%Y-%m-%d"),
|
|
153
|
+
"version": sandwitches_version,
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
if request.headers.get("HX-Request"):
|
|
157
|
+
return render(request, "admin/partials/dashboard_charts.html", context)
|
|
158
|
+
|
|
97
159
|
return render(
|
|
98
160
|
request,
|
|
99
161
|
"admin/dashboard.html",
|
|
100
|
-
|
|
101
|
-
"recipe_count": recipe_count,
|
|
102
|
-
"user_count": user_count,
|
|
103
|
-
"tag_count": tag_count,
|
|
104
|
-
"recent_recipes": recent_recipes,
|
|
105
|
-
"recipe_labels": recipe_labels,
|
|
106
|
-
"recipe_counts": recipe_counts,
|
|
107
|
-
"rating_labels": rating_labels,
|
|
108
|
-
"rating_avgs": rating_avgs,
|
|
109
|
-
"start_date": start_date.strftime("%Y-%m-%d"),
|
|
110
|
-
"end_date": end_date.strftime("%Y-%m-%d"),
|
|
111
|
-
"version": sandwitches_version,
|
|
112
|
-
},
|
|
162
|
+
context,
|
|
113
163
|
)
|
|
114
164
|
|
|
115
165
|
|
|
116
166
|
@staff_member_required
|
|
117
167
|
def admin_recipe_list(request):
|
|
168
|
+
sort_param = request.GET.get("sort", "-created_at")
|
|
169
|
+
allowed_sorts = {
|
|
170
|
+
"title": "title",
|
|
171
|
+
"-title": "-title",
|
|
172
|
+
"created_at": "created_at",
|
|
173
|
+
"-created_at": "-created_at",
|
|
174
|
+
"uploader": "uploaded_by__username",
|
|
175
|
+
"-uploader": "-uploaded_by__username",
|
|
176
|
+
"price": "price",
|
|
177
|
+
"-price": "-price",
|
|
178
|
+
"orders": "daily_orders_count",
|
|
179
|
+
"-orders": "-daily_orders_count",
|
|
180
|
+
"rating": "avg_rating",
|
|
181
|
+
"-rating": "-avg_rating",
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
order_by = allowed_sorts.get(sort_param, "-created_at")
|
|
185
|
+
|
|
118
186
|
recipes = (
|
|
119
187
|
Recipe.objects.annotate(avg_rating=Avg("ratings__score")) # ty:ignore[unresolved-attribute]
|
|
120
188
|
.prefetch_related("tags")
|
|
121
|
-
.
|
|
189
|
+
.order_by(order_by)
|
|
122
190
|
)
|
|
123
191
|
return render(
|
|
124
192
|
request,
|
|
125
193
|
"admin/recipe_list.html",
|
|
126
|
-
{
|
|
194
|
+
{
|
|
195
|
+
"recipes": recipes,
|
|
196
|
+
"version": sandwitches_version,
|
|
197
|
+
"current_sort": sort_param,
|
|
198
|
+
},
|
|
127
199
|
)
|
|
128
200
|
|
|
129
201
|
|
|
@@ -170,6 +242,17 @@ def admin_recipe_edit(request, pk):
|
|
|
170
242
|
)
|
|
171
243
|
|
|
172
244
|
|
|
245
|
+
@staff_member_required
|
|
246
|
+
def admin_recipe_approve(request, pk):
|
|
247
|
+
recipe = get_object_or_404(Recipe, pk=pk)
|
|
248
|
+
recipe.is_approved = True
|
|
249
|
+
recipe.save()
|
|
250
|
+
messages.success(
|
|
251
|
+
request, _("Recipe '%(title)s' approved.") % {"title": recipe.title}
|
|
252
|
+
)
|
|
253
|
+
return redirect("admin_recipe_list")
|
|
254
|
+
|
|
255
|
+
|
|
173
256
|
@staff_member_required
|
|
174
257
|
def admin_recipe_delete(request, pk):
|
|
175
258
|
recipe = get_object_or_404(Recipe, pk=pk)
|
|
@@ -365,8 +448,38 @@ def admin_rating_delete(request, pk):
|
|
|
365
448
|
)
|
|
366
449
|
|
|
367
450
|
|
|
451
|
+
@staff_member_required
|
|
452
|
+
def admin_order_list(request):
|
|
453
|
+
orders = (
|
|
454
|
+
Order.objects.select_related("user", "recipe") # ty:ignore[unresolved-attribute]
|
|
455
|
+
.all()
|
|
456
|
+
.order_by("-created_at")
|
|
457
|
+
)
|
|
458
|
+
|
|
459
|
+
if request.headers.get("HX-Request"):
|
|
460
|
+
return render(
|
|
461
|
+
request,
|
|
462
|
+
"admin/partials/order_rows.html",
|
|
463
|
+
{"orders": orders, "version": sandwitches_version},
|
|
464
|
+
)
|
|
465
|
+
|
|
466
|
+
return render(
|
|
467
|
+
request,
|
|
468
|
+
"admin/order_list.html",
|
|
469
|
+
{"orders": orders, "version": sandwitches_version},
|
|
470
|
+
)
|
|
471
|
+
|
|
472
|
+
|
|
368
473
|
def recipe_detail(request, slug):
|
|
369
474
|
recipe = get_object_or_404(Recipe, slug=slug)
|
|
475
|
+
|
|
476
|
+
if not recipe.is_approved:
|
|
477
|
+
if not (
|
|
478
|
+
request.user.is_authenticated
|
|
479
|
+
and (request.user.is_staff or recipe.uploaded_by == request.user)
|
|
480
|
+
):
|
|
481
|
+
raise Http404("Recipe not found or pending approval.")
|
|
482
|
+
|
|
370
483
|
avg = recipe.average_rating()
|
|
371
484
|
count = recipe.rating_count()
|
|
372
485
|
user_rating = None
|
|
@@ -400,6 +513,28 @@ def recipe_detail(request, slug):
|
|
|
400
513
|
)
|
|
401
514
|
|
|
402
515
|
|
|
516
|
+
@login_required
|
|
517
|
+
def order_recipe(request, pk):
|
|
518
|
+
"""
|
|
519
|
+
Create an order for the given recipe by the logged-in user.
|
|
520
|
+
"""
|
|
521
|
+
recipe = get_object_or_404(Recipe, pk=pk)
|
|
522
|
+
if request.method != "POST":
|
|
523
|
+
return redirect("recipe_detail", slug=recipe.slug)
|
|
524
|
+
|
|
525
|
+
try:
|
|
526
|
+
order = Order.objects.create(user=request.user, recipe=recipe) # ty:ignore[unresolved-attribute]
|
|
527
|
+
logging.debug(f"Created {order}")
|
|
528
|
+
messages.success(
|
|
529
|
+
request,
|
|
530
|
+
_("Your order for %(title)s has been submitted!") % {"title": recipe.title},
|
|
531
|
+
)
|
|
532
|
+
except (ValidationError, ValueError) as e:
|
|
533
|
+
messages.error(request, str(e))
|
|
534
|
+
|
|
535
|
+
return redirect("recipe_detail", slug=recipe.slug)
|
|
536
|
+
|
|
537
|
+
|
|
403
538
|
@login_required
|
|
404
539
|
def recipe_rate(request, pk):
|
|
405
540
|
"""
|
|
@@ -509,9 +644,16 @@ def favorites(request):
|
|
|
509
644
|
def index(request):
|
|
510
645
|
if not User.objects.filter(is_superuser=True).exists():
|
|
511
646
|
return redirect("setup")
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
647
|
+
|
|
648
|
+
recipes = Recipe.objects.all().prefetch_related("favorited_by") # ty:ignore[unresolved-attribute]
|
|
649
|
+
|
|
650
|
+
if not (request.user.is_authenticated and request.user.is_staff):
|
|
651
|
+
if request.user.is_authenticated:
|
|
652
|
+
# Show approved recipes OR recipes uploaded by the current user
|
|
653
|
+
recipes = recipes.filter(Q(is_approved=True) | Q(uploaded_by=request.user))
|
|
654
|
+
else:
|
|
655
|
+
# Show only approved recipes for anonymous users
|
|
656
|
+
recipes = recipes.filter(is_approved=True)
|
|
515
657
|
|
|
516
658
|
# Filtering
|
|
517
659
|
q = request.GET.get("q")
|
|
@@ -645,3 +787,18 @@ def media(request, file_path=None):
|
|
|
645
787
|
|
|
646
788
|
response = FileResponse(open(full_path, "rb"), as_attachment=True)
|
|
647
789
|
return response
|
|
790
|
+
|
|
791
|
+
|
|
792
|
+
@login_required
|
|
793
|
+
def user_profile(request):
|
|
794
|
+
if request.method == "POST":
|
|
795
|
+
form = UserProfileForm(request.POST, request.FILES, instance=request.user)
|
|
796
|
+
if form.is_valid():
|
|
797
|
+
form.save()
|
|
798
|
+
messages.success(request, _("Profile updated successfully."))
|
|
799
|
+
return redirect("user_profile")
|
|
800
|
+
else:
|
|
801
|
+
form = UserProfileForm(instance=request.user)
|
|
802
|
+
return render(
|
|
803
|
+
request, "profile.html", {"form": form, "version": sandwitches_version}
|
|
804
|
+
)
|
|
@@ -1,35 +1,44 @@
|
|
|
1
1
|
sandwitches/__init__.py,sha256=YTDsQDSdJmxV2Z0dTbBqZhuJRuXcNLSKL0SX73Lu2u8,195
|
|
2
|
-
sandwitches/admin.py,sha256
|
|
3
|
-
sandwitches/api.py,sha256=
|
|
2
|
+
sandwitches/admin.py,sha256=-02WqE8U3rxrVCoNB7sfvtyE4v_e3pt7mFwXfUlindo,2421
|
|
3
|
+
sandwitches/api.py,sha256=ruD5QeOPY-l9PvkJQiaOYoI0sRARDpqpFrFDgBxo9cQ,6389
|
|
4
4
|
sandwitches/asgi.py,sha256=cygnXdXSSVspM7ZXuj47Ef6oz7HSTw4D7BPzgE2PU5w,399
|
|
5
5
|
sandwitches/feeds.py,sha256=iz1d11dV0utA0ZNsB7VIAp0h8Zr5mFNSKJWHbw_j6YM,683
|
|
6
|
-
sandwitches/forms.py,sha256=
|
|
6
|
+
sandwitches/forms.py,sha256=YvkSTa9h_ag_b58ToOHCQIHBa3VeHMC9RKB9F7qI-gk,7152
|
|
7
7
|
sandwitches/locale/nl/LC_MESSAGES/django.mo,sha256=EzQWzIhz_Na3w9AS7F-YjB-Xv63t4sMRSAkEQ1-g32M,5965
|
|
8
8
|
sandwitches/locale/nl/LC_MESSAGES/django.po,sha256=znxspEoMwkmktusZtbVrt1KG1LDUwIEi4ZEIE3XGeoI,25904
|
|
9
|
+
sandwitches/management/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
10
|
+
sandwitches/management/commands/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
11
|
+
sandwitches/management/commands/reset_daily_orders.py,sha256=PrGPFfJ7r3J1SYYgyU5YDaFVvc7uvMGzkkePL07GDyo,480
|
|
9
12
|
sandwitches/migrations/0001_initial.py,sha256=hXnCAhoA91C6YCinXyUdIfQ7QL29NPBHFfTqLgulMsY,12507
|
|
10
13
|
sandwitches/migrations/0002_historicalrecipe_servings_recipe_servings.py,sha256=8E09y99FdVNzaQMn8R1d5zEcCN9jZICVCTW8MsGRBqs,743
|
|
11
14
|
sandwitches/migrations/0003_setting.py,sha256=DmugIrz0Wtftx7B0MrX6ms34j60kSOwcPz1sLRP4wE0,1200
|
|
12
15
|
sandwitches/migrations/0004_alter_setting_ai_api_key_and_more.py,sha256=516vcSxWzsZiJaskH1JC-1FgB0t4ijNC2L3PcfdRPNg,1109
|
|
13
16
|
sandwitches/migrations/0005_rating_comment.py,sha256=MRlTZkyATH1aJdkaesPlPkXkYt-mzqBik-MCcNrbLRI,398
|
|
14
17
|
sandwitches/migrations/0006_historicalrecipe_is_highlighted_and_more.py,sha256=BBCx4uxPK9udqsJ6qkoYpZ_3tH4uujquj9j0Gm7Ff6w,566
|
|
18
|
+
sandwitches/migrations/0007_historicalrecipe_price_recipe_price_order.py,sha256=o-hsVK39ArT_KkSkMnZfhCTQu66v81P6LbZPqRxjonU,2793
|
|
19
|
+
sandwitches/migrations/0008_historicalrecipe_daily_orders_count_and_more.py,sha256=PyjettGABOcH6ryCXApOpEPNKBIs23AFIK302uQxpiY,1105
|
|
20
|
+
sandwitches/migrations/0009_historicalrecipe_is_approved_recipe_is_approved.py,sha256=XP76J2_HiMOFeIU17Yu8AYXtswE-dcsNZq3lJGFgGtg,588
|
|
15
21
|
sandwitches/migrations/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
16
|
-
sandwitches/models.py,sha256=
|
|
17
|
-
sandwitches/settings.py,sha256=
|
|
22
|
+
sandwitches/models.py,sha256=p67bGRaacH-ccJmcS9Mfco4nCdidvOwHhWeiFakKYnA,9979
|
|
23
|
+
sandwitches/settings.py,sha256=5_eQAJCAV093hnhr3XOxHekT4IF-PEJcRiTecq71_SQ,5841
|
|
18
24
|
sandwitches/storage.py,sha256=ibBG6tVtArqzgEKsRimZPwsqW7i9j4WiPLLHrOJchow,3578
|
|
19
|
-
sandwitches/tasks.py,sha256=
|
|
20
|
-
sandwitches/templates/admin/admin_base.html,sha256=
|
|
25
|
+
sandwitches/tasks.py,sha256=YiliAT2rj0fh7hrwKq5_qWtv9AGhd5iulj_iBwZBBKg,6024
|
|
26
|
+
sandwitches/templates/admin/admin_base.html,sha256=5ZtJbZMstLjjyCc73smhIUJOhU7vscwqLuung1Gf15g,4489
|
|
21
27
|
sandwitches/templates/admin/confirm_delete.html,sha256=HfsZI_gV8JQTKz215TYgPWBrgrFhGv1UB3N-0Hln-14,804
|
|
22
|
-
sandwitches/templates/admin/dashboard.html,sha256=
|
|
23
|
-
sandwitches/templates/admin/
|
|
24
|
-
sandwitches/templates/admin/
|
|
25
|
-
sandwitches/templates/admin/
|
|
28
|
+
sandwitches/templates/admin/dashboard.html,sha256=lZhywLTC-PHtezIZdqGNnIC-G13a6mqjGohUnqCgoao,5758
|
|
29
|
+
sandwitches/templates/admin/order_list.html,sha256=eHFUn2speXaaj5_SFUG0Z0HfWVUR9-VCDRBeb8ufFb0,819
|
|
30
|
+
sandwitches/templates/admin/partials/dashboard_charts.html,sha256=NYrt-LDZO4__2KDWhAYL5K_f-2Zgj0iiuaZQiRZlBWg,3639
|
|
31
|
+
sandwitches/templates/admin/partials/order_rows.html,sha256=Ye35liahKbQ3rqa6fIGSTwb7seoXoqyqSw0wyNq2C_o,893
|
|
32
|
+
sandwitches/templates/admin/rating_list.html,sha256=8CHAsBfKfs4izhb-IyOiDjJXqAZxFcStoRSGh4pRlgM,1365
|
|
33
|
+
sandwitches/templates/admin/recipe_form.html,sha256=23wHT4hs128xnv2nkS6AtcKzY3sblia_dGVNnaeIp5Y,8734
|
|
34
|
+
sandwitches/templates/admin/recipe_list.html,sha256=5fGnRIQ7JfvM3yfG-sngEIEgiPnPDkjK1Tn3nO8EDh4,5359
|
|
26
35
|
sandwitches/templates/admin/tag_form.html,sha256=JRWgAl4fz_Oy-Kuo1K6Mex_CXdsHMABzzyPazthr1Kg,989
|
|
27
36
|
sandwitches/templates/admin/tag_list.html,sha256=ttxwXgfdxkEs4Cmrz5RHaGmaqLd7JDmWhjv80XIQqyw,1246
|
|
28
37
|
sandwitches/templates/admin/task_detail.html,sha256=dO5zHOG-yTY1ly3VPA3o3jjie0wmxw0gdddtSKpN-W8,3825
|
|
29
38
|
sandwitches/templates/admin/task_list.html,sha256=3YF7YQ3nbXnWjApKeA07Z7HkhdMuH4s6sLoN7gwg0eE,1535
|
|
30
39
|
sandwitches/templates/admin/user_form.html,sha256=7_6GShLROFeJJexL1XLFUXdW9_lYF87eT6cigB5bQo4,1314
|
|
31
40
|
sandwitches/templates/admin/user_list.html,sha256=6O1YctULY-tqJnagybJof9ERA_NL1LX_a8cAu6_aWVQ,2193
|
|
32
|
-
sandwitches/templates/base.html,sha256=
|
|
41
|
+
sandwitches/templates/base.html,sha256=mwCESNirfvvdyMg2e1Siy_LA8fLH29m0aS_Jv0Qom4U,3597
|
|
33
42
|
sandwitches/templates/base_beer.html,sha256=4QgU4_gu_RRMtimmRAhATDJ3mj_WANxtilQJYNgAL60,2077
|
|
34
43
|
sandwitches/templates/components/carousel_scripts.html,sha256=9vEL5JJv8zUUjEtsnHW-BwwXUNWqQ6w_vf6UdxgEv_I,1934
|
|
35
44
|
sandwitches/templates/components/favorites_search_form.html,sha256=tpD8SpS47TUDJBwxhMuvjhTN9pjWoRGFW50TBv48Ld4,5202
|
|
@@ -38,28 +47,29 @@ sandwitches/templates/components/ingredients_scripts.html,sha256=2zKTC65GYF589uW
|
|
|
38
47
|
sandwitches/templates/components/ingredients_section.html,sha256=XsaVXTs9MIwjfJeLjlzah3GWWj8oFU-_HJd9i9l1HAo,665
|
|
39
48
|
sandwitches/templates/components/instructions_section.html,sha256=RFlA4uPiI6vf1e2QgiD5KzGoy7Vg7y7nFY7TFabCYLA,277
|
|
40
49
|
sandwitches/templates/components/language_dialog.html,sha256=iz-6QhFe4f_dsVhGDhVx6KKKLgQz4grX8tbIqSQjDsg,1184
|
|
41
|
-
sandwitches/templates/components/navbar.html,sha256=
|
|
50
|
+
sandwitches/templates/components/navbar.html,sha256=X2qOPHhVrSl4TkTk4YY-60YjpwCG7IEe4L6a9ywMih4,1164
|
|
42
51
|
sandwitches/templates/components/rating_section.html,sha256=8O5IsFfQwnElMQZLnDpJiuCvvQMLa3jCS67u_RhMi7o,2717
|
|
43
|
-
sandwitches/templates/components/recipe_header.html,sha256
|
|
52
|
+
sandwitches/templates/components/recipe_header.html,sha256=jH9bnT6tis3OuePBq-xzP87IIx2ipDTuw2LO0BQwLXg,1996
|
|
44
53
|
sandwitches/templates/components/search_form.html,sha256=B8579Jo44gLrlmvkkc2-Vuv_QD93Ljt6F2J1WgTDV60,6617
|
|
45
54
|
sandwitches/templates/components/search_scripts.html,sha256=HvsO5e50DoTZeoFiYeNP5S8S5h7Zfr9VULOWKKR1i_M,3423
|
|
46
|
-
sandwitches/templates/components/side_menu.html,sha256=
|
|
55
|
+
sandwitches/templates/components/side_menu.html,sha256=QMkgPgWIUXBFyXe1ZEP_CYm1wnaq1h6q645tbDQjNdw,1804
|
|
47
56
|
sandwitches/templates/components/user_menu.html,sha256=c20cBpyLheGvHdQ5nn-c4fjNlhfnAt3FAsw1V46rTwQ,369
|
|
48
57
|
sandwitches/templates/detail.html,sha256=g-O_RsW9Ix9ivWC0nZ4FwHY2NhgYZ3bEGLpqGY0JSxg,5642
|
|
49
58
|
sandwitches/templates/favorites.html,sha256=0cPpW07N6Isrb8XpvA5Eh97L2-12QFZ43EzeJvbOlXo,917
|
|
50
59
|
sandwitches/templates/index.html,sha256=7anU7k8s80JYk59Rwsm8EdlNYd7B5clCvV7pKq2IUy0,2518
|
|
51
60
|
sandwitches/templates/login.html,sha256=LiQskhkOkfx0EE4ssA1ToqQ3oEll08OPYLDIkLjHfU8,2177
|
|
52
|
-
sandwitches/templates/partials/recipe_list.html,sha256=
|
|
53
|
-
sandwitches/templates/
|
|
61
|
+
sandwitches/templates/partials/recipe_list.html,sha256=oF5zlgAyX_uG63BwulyhFywrn8QLnWXatXVtZPVcPmE,4186
|
|
62
|
+
sandwitches/templates/profile.html,sha256=PQTL6_xn0pGUxqEOYuz5j0pmqAyG0Wr3KvyFBO8_k1s,4156
|
|
63
|
+
sandwitches/templates/recipe_form.html,sha256=eaQN6331f4hxqrN_FhDXmT1i86Mknf072xpFXoFvoAo,5261
|
|
54
64
|
sandwitches/templates/setup.html,sha256=iNveFgePATsCSO4XMbGPa8TnWHyvj8S_5WwcW6i7pbo,4661
|
|
55
65
|
sandwitches/templates/signup.html,sha256=pNBSlRGZI_B5ccF3dWpUgWBcjODkdLlq7HhyJLYIHCI,6176
|
|
56
66
|
sandwitches/templatetags/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
57
67
|
sandwitches/templatetags/custom_filters.py,sha256=0KDFlFz4b5LwlcURBAmzyYWKKea-LwydZytJGVkkuKA,243
|
|
58
68
|
sandwitches/templatetags/markdown_extras.py,sha256=0ibmRzxE3r85x4k7kK71R-9UT0CgeegYF7MHzj3juTI,344
|
|
59
|
-
sandwitches/urls.py,sha256=
|
|
69
|
+
sandwitches/urls.py,sha256=bbFsx0G2_pl7LuhO6wHtnt-X890IsRQFOEsN2mqfdzo,4351
|
|
60
70
|
sandwitches/utils.py,sha256=SJP-TkeRZ0OIfaMigYrOSbxRqYXswoqoWhwll3nFuAM,7245
|
|
61
|
-
sandwitches/views.py,sha256=
|
|
71
|
+
sandwitches/views.py,sha256=3gAUZuIIWevu2xW8ewxBWM5O-1hTarn8ZT9DJBTeDyw,25565
|
|
62
72
|
sandwitches/wsgi.py,sha256=Eyncpnahq_4s3Lr9ruB-R3Lu9j9zBXqgPbUj7qhIbwU,399
|
|
63
|
-
sandwitches-2.
|
|
64
|
-
sandwitches-2.
|
|
65
|
-
sandwitches-2.
|
|
73
|
+
sandwitches-2.3.1.dist-info/WHEEL,sha256=eh7sammvW2TypMMMGKgsM83HyA_3qQ5Lgg3ynoecH3M,79
|
|
74
|
+
sandwitches-2.3.1.dist-info/METADATA,sha256=0FOR_loqc7pHOjzBvUU5HOWTToY-FQJ0EnU0Vc4HqxM,3111
|
|
75
|
+
sandwitches-2.3.1.dist-info/RECORD,,
|
|
File without changes
|