first parts of shopping rework

This commit is contained in:
vabene1111 2020-08-11 15:24:12 +02:00
parent 8055754455
commit be55e034bf
6 changed files with 160 additions and 42 deletions

View File

@ -4,7 +4,8 @@ from rest_framework import serializers
from rest_framework.exceptions import APIException, ValidationError
from rest_framework.fields import CurrentUserDefault
from cookbook.models import MealPlan, MealType, Recipe, ViewLog, UserPreference, Storage, Sync, SyncLog, Keyword, Unit, Ingredient, Comment, RecipeImport, RecipeBook, RecipeBookEntry, ShareLink, CookLog, Food, Step
from cookbook.models import MealPlan, MealType, Recipe, ViewLog, UserPreference, Storage, Sync, SyncLog, Keyword, Unit, Ingredient, Comment, RecipeImport, RecipeBook, RecipeBookEntry, ShareLink, CookLog, Food, Step, ShoppingList, \
ShoppingListEntry, ShoppingListRecipe
from cookbook.templatetags.custom_tags import markdown
@ -193,6 +194,34 @@ class MealPlanSerializer(serializers.ModelSerializer):
fields = ('id', 'title', 'recipe', 'note', 'note_markdown', 'date', 'meal_type', 'created_by', 'shared', 'recipe_name', 'meal_type_name')
class ShoppingListRecipeSerializer(serializers.ModelSerializer):
recipe = RecipeSerializer(read_only=True)
def create(self, validated_data):
return ShoppingListRecipe.objects.create(**validated_data)
def update(self, instance, validated_data):
return super(ShoppingListRecipeSerializer, self).update(instance, validated_data)
class Meta:
model = ShoppingListRecipe
fields = ('recipe', 'multiplier')
class ShoppingListEntrySerializer(serializers.ModelSerializer):
class Meta:
model = ShoppingListEntry
fields = ('list_recipe', 'food', 'unit', 'amount', 'order', 'checked')
class ShoppingListSerializer(serializers.ModelSerializer):
recipes = ShoppingListRecipeSerializer(many=True, allow_null=True, read_only=True)
class Meta:
model = ShoppingList
fields = ('id', 'uuid', 'note', 'recipes', 'shared', 'created_by', 'created_at',)
class ShareLinkSerializer(serializers.ModelSerializer):
class Meta:
model = ShareLink

View File

@ -7,11 +7,124 @@
{% block title %}{% trans "Shopping List" %}{% endblock %}
{% block extra_head %}
{% include 'include/vue_base.html' %}
<link rel="stylesheet" href="{% static 'css/vue-multiselect-bs4.min.css' %}">
<script src="{% static 'js/vue-multiselect.min.js' %}"></script>
<script src="{% static 'js/Sortable.min.js' %}"></script>
<script src="{% static 'js/vuedraggable.umd.min.js' %}"></script>
{% endblock %}
{% block content %}
<div class="row">
<div class="col col-9">
<h2>{% trans 'Shopping List' %}</h2>
</div>
<div class="col col-3">
<b-form-checkbox switch size="lg" v-model="edit_mode">{% trans 'Edit' %}</b-form-checkbox>
</div>
</div>
<div v-if="edit_mode">
<div class="row">
<div class="col col-6">
<input type="text" class="form-control" v-model="recipe_query" @keyup="getRecipes"
placeholder="{% trans 'Search Recipe' %}">
<ul class="list-group" style="margin-top: 8px">
<li class="list-group-item" v-for="x in recipes">[[x.name]]</li>
</ul>
</div>
</div>
</div>
<div v-else>
Non Edit
</div>
{% endblock %}
{% block script %}
<script type="application/javascript">
let csrftoken = Cookies.get('csrftoken');
Vue.http.headers.common['X-CSRFToken'] = csrftoken;
Vue.component('vue-multiselect', window.VueMultiselect.default)
let app = new Vue({
components: {
Multiselect: window.VueMultiselect.default
},
delimiters: ['[[', ']]'],
el: '#id_base_container',
data: {
edit_mode: true,
recipe_query: '',
recipes: [],
},
directives: {
tabindex: {
inserted(el) {
el.setAttribute('tabindex', 0);
}
}
},
/*
watch: {
recipe: {
deep: true,
handler() {
this.recipe_changed = this.recipe_changed !== undefined;
}
}
},
created() {
window.addEventListener('beforeunload', this.warnPageLeave)
},
*/
mounted: function () {
},
methods: {
/*
warnPageLeave: function (event) {
if (this.recipe_changed) {
event.returnValue = ''
return ''
}
},
*/
getRecipes: function () {
let url = "{% url 'api:recipe-list' %}?limit=5"
if (this.recipe_query !== '') {
url += '&query=' + this.recipe_query;
}
this.$http.get(url).then((response) => {
this.recipes = response.data;
}).catch((err) => {
console.log("getRecipes error: ", err);
this.makeToast('{% trans 'Error' %}', '{% trans 'There was an error loading a resource!' %}' + err.bodyText, 'danger')
})
},
searchKeywords: function (query) {
this.keywords_loading = true
this.$http.get("{% url 'api:keyword-list' %}" + '?query=' + query + '&limit=10').then((response) => {
this.keywords = response.data;
this.keywords_loading = false
}).catch((err) => {
console.log(err)
this.makeToast('{% trans 'Error' %}', '{% trans 'There was an error loading a resource!' %}' + err.bodyText, 'danger')
})
},
}
});
</script>
{% endblock %}

View File

@ -23,6 +23,7 @@ router.register(r'recipe', api.RecipeViewSet)
router.register(r'ingredient', api.IngredientViewSet)
router.register(r'meal-plan', api.MealPlanViewSet)
router.register(r'meal-type', api.MealTypeViewSet)
router.register(r'shopping-list', api.ShoppingListViewSet)
router.register(r'view-log', api.ViewLogViewSet)
urlpatterns = [
@ -34,6 +35,7 @@ urlpatterns = [
path('plan/', views.meal_plan, name='view_plan'),
path('plan/entry/<int:pk>', views.meal_plan_entry, name='view_plan_entry'),
path('shopping/', views.shopping_list, name='view_shopping'),
path('shopping/<int:pk>', views.shopping_list, name='view_shopping'),
path('settings/', views.user_settings, name='view_settings'),
path('history/', views.history, name='view_history'),
path('test/', views.test, name='view_test'),

View File

@ -28,11 +28,11 @@ from rest_framework.viewsets import ViewSetMixin
from cookbook.helper.permission_helper import group_required, CustomIsOwner, CustomIsAdmin, CustomIsUser, CustomIsGuest, CustomIsShare
from cookbook.helper.recipe_url_import import get_from_html
from cookbook.models import Recipe, Sync, Storage, CookLog, MealPlan, MealType, ViewLog, UserPreference, RecipeBook, Ingredient, Food, Step, Keyword, Unit, SyncLog
from cookbook.models import Recipe, Sync, Storage, CookLog, MealPlan, MealType, ViewLog, UserPreference, RecipeBook, Ingredient, Food, Step, Keyword, Unit, SyncLog, ShoppingListRecipe, ShoppingList
from cookbook.provider.dropbox import Dropbox
from cookbook.provider.nextcloud import Nextcloud
from cookbook.serializer import MealPlanSerializer, MealTypeSerializer, RecipeSerializer, ViewLogSerializer, UserNameSerializer, UserPreferenceSerializer, RecipeBookSerializer, IngredientSerializer, FoodSerializer, StepSerializer, \
KeywordSerializer, RecipeImageSerializer, StorageSerializer, SyncSerializer, SyncLogSerializer, UnitSerializer
KeywordSerializer, RecipeImageSerializer, StorageSerializer, SyncSerializer, SyncLogSerializer, UnitSerializer, ShoppingListSerializer
class UserNameViewSet(viewsets.ReadOnlyModelViewSet):
@ -233,6 +233,16 @@ class RecipeViewSet(viewsets.ModelViewSet, StandardFilterMixin):
return Response(serializer.errors, 400)
class ShoppingListViewSet(viewsets.ModelViewSet):
queryset = ShoppingList.objects.all()
serializer_class = ShoppingListSerializer
permission_classes = [CustomIsOwner]
def get_queryset(self):
queryset = self.queryset.filter(created_by=self.request.user).all()
return queryset
class ViewLogViewSet(viewsets.ModelViewSet):
queryset = ViewLog.objects.all()
serializer_class = ViewLogSerializer

View File

@ -50,7 +50,7 @@ def shopping_list(request):
table = ShoppingListTable(ShoppingList.objects.filter(created_by=request.user).all())
RequestConfig(request, paginate={'per_page': 25}).configure(table)
return render(request, 'generic/list_template.html', {'title': _("Shopping Lists"), 'table': table, 'create_url': 'new_storage'})
return render(request, 'generic/list_template.html', {'title': _("Shopping Lists"), 'table': table, 'create_url': 'view_shopping'})
@group_required('admin')

View File

@ -164,44 +164,8 @@ def meal_plan_entry(request, pk):
@group_required('user')
def shopping_list(request):
markdown_format = True
if request.method == "POST":
form = ShoppingForm(request.POST)
if form.is_valid():
recipes = form.cleaned_data['recipe']
markdown_format = form.cleaned_data['markdown_format']
else:
recipes = []
else:
raw_list = request.GET.getlist('r')
recipes = []
for r in raw_list:
if re.match(r'^([1-9])+$', r):
if Recipe.objects.filter(pk=int(r)).exists():
recipes.append(int(r))
markdown_format = False
form = ShoppingForm(initial={'recipe': recipes, 'markdown_format': False})
ingredients = []
for r in recipes:
for s in r.steps.all():
for ri in s.ingredients.exclude(unit__name__contains='Special:').all():
index = None
for x, ig in enumerate(ingredients):
if ri.food == ig.food and ri.unit == ig.unit:
index = x
if index:
ingredients[index].amount = ingredients[index].amount + ri.amount
else:
ingredients.append(ri)
return render(request, 'shopping_list.html', {'ingredients': ingredients, 'recipes': recipes, 'form': form, 'markdown_format': markdown_format})
def shopping_list(request, pk=None):
return render(request, 'shopping_list.html', {})
@group_required('guest')