add to book reimplemented

This commit is contained in:
vabene1111
2021-01-14 01:21:15 +01:00
parent b271f81af2
commit 08733751aa
14 changed files with 193 additions and 35 deletions

View File

@ -319,6 +319,12 @@ class RecipeBookEntry(models.Model):
def __str__(self):
return self.recipe.name
def get_owner(self):
try:
return self.book.created_by
except AttributeError:
return None
class Meta:
unique_together = (('recipe', 'book'),)

View File

@ -90,6 +90,20 @@ class SyncLogSerializer(serializers.ModelSerializer):
fields = ('id', 'sync', 'status', 'msg', 'created_at')
class KeywordLabelSerializer(serializers.ModelSerializer):
label = serializers.SerializerMethodField('get_label')
def get_label(self, obj):
return str(obj)
class Meta:
model = Keyword
fields = (
'id', 'label',
)
read_only_fields = ('id', 'label')
class KeywordSerializer(UniqueFieldsMixin, serializers.ModelSerializer):
label = serializers.SerializerMethodField('get_label')
@ -181,6 +195,19 @@ class NutritionInformationSerializer(serializers.ModelSerializer):
fields = ('carbohydrates', 'fats', 'proteins', 'calories', 'source')
class RecipeOverviewSerializer(WritableNestedModelSerializer):
keywords = KeywordLabelSerializer(many=True)
class Meta:
model = Recipe
fields = (
'id', 'name', 'description', 'image', 'keywords', 'working_time',
'waiting_time', 'created_by', 'created_at', 'updated_at',
'internal', 'servings', 'file_path'
)
read_only_fields = ['image', 'created_by', 'created_at']
class RecipeSerializer(WritableNestedModelSerializer):
nutrition = NutritionInformationSerializer(allow_null=True, required=False)
steps = StepSerializer(many=True)

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -32,6 +32,8 @@ router.register(r'shopping-list-entry', api.ShoppingListEntryViewSet)
router.register(r'shopping-list-recipe', api.ShoppingListRecipeViewSet)
router.register(r'view-log', api.ViewLogViewSet)
router.register(r'cook-log', api.CookLogViewSet)
router.register(r'recipe-book', api.RecipeBookViewSet)
router.register(r'recipe-book-entry', api.RecipeBookEntryViewSet)
urlpatterns = [
path('', views.index, name='index'),

View File

@ -21,7 +21,7 @@ from PIL import Image
from rest_framework import decorators, permissions, viewsets
from rest_framework.exceptions import APIException
from rest_framework.mixins import (ListModelMixin, RetrieveModelMixin,
UpdateModelMixin)
UpdateModelMixin, CreateModelMixin)
from rest_framework.parsers import MultiPartParser
from rest_framework.response import Response
from rest_framework.viewsets import ViewSetMixin
@ -35,7 +35,7 @@ from cookbook.models import (CookLog, Food, Ingredient, Keyword, MealPlan,
MealType, Recipe, RecipeBook, ShoppingList,
ShoppingListEntry, ShoppingListRecipe, Step,
Storage, Sync, SyncLog, Unit, UserPreference,
ViewLog)
ViewLog, RecipeBookEntry)
from cookbook.provider.dropbox import Dropbox
from cookbook.provider.nextcloud import Nextcloud
from cookbook.serializer import (FoodSerializer, IngredientSerializer,
@ -49,7 +49,7 @@ from cookbook.serializer import (FoodSerializer, IngredientSerializer,
StorageSerializer, SyncLogSerializer,
SyncSerializer, UnitSerializer,
UserNameSerializer, UserPreferenceSerializer,
ViewLogSerializer, CookLogSerializer)
ViewLogSerializer, CookLogSerializer, RecipeBookEntrySerializer, RecipeOverviewSerializer)
class UserNameViewSet(viewsets.ReadOnlyModelViewSet):
@ -121,6 +121,13 @@ class StandardFilterMixin(ViewSetMixin):
if query is not None:
queryset = queryset.filter(name__icontains=query)
updated_at = self.request.query_params.get('updated_at', None)
if updated_at is not None:
try:
queryset = queryset.filter(updated_at__gte=updated_at)
except FieldError:
pass
limit = self.request.query_params.get('limit', None)
random = self.request.query_params.get('random', False)
if limit is not None:
@ -157,16 +164,23 @@ class FoodViewSet(viewsets.ModelViewSet, StandardFilterMixin):
permission_classes = [CustomIsUser]
class RecipeBookViewSet(
RetrieveModelMixin,
UpdateModelMixin,
ListModelMixin,
viewsets.GenericViewSet
):
class RecipeBookViewSet(viewsets.ModelViewSet, StandardFilterMixin):
queryset = RecipeBook.objects.all()
serializer_class = RecipeBookSerializer
permission_classes = [CustomIsOwner, CustomIsAdmin]
def get_queryset(self):
self.queryset = super(RecipeBookViewSet, self).get_queryset()
if self.request.user.is_superuser:
return self.queryset
return self.queryset.filter(created_by=self.request.user)
class RecipeBookEntryViewSet(viewsets.ModelViewSet, viewsets.GenericViewSet):
queryset = RecipeBookEntry.objects.all()
serializer_class = RecipeBookEntrySerializer
permission_classes = [CustomIsOwner, CustomIsAdmin]
def get_queryset(self):
if self.request.user.is_superuser:
return self.queryset
@ -253,6 +267,11 @@ class RecipeViewSet(viewsets.ModelViewSet, StandardFilterMixin):
# TODO write extensive tests for permissions
def get_serializer_class(self):
if self.action == 'list':
return RecipeOverviewSerializer
return self.serializer_class
@decorators.action(
detail=True,
methods=['PUT'],
@ -338,6 +357,7 @@ class CookLogViewSet(viewsets.ModelViewSet):
queryset = ViewLog.objects.filter(created_by=self.request.user).all()[:5]
return queryset
# -------------- non django rest api views --------------------
def get_recipe_provider(recipe):

View File

@ -13,6 +13,7 @@
"core-js": "^3.6.5",
"moment": "^2.29.1",
"vue": "^2.6.11",
"vue-multiselect": "^2.1.6",
"vue-template-compiler": "^2.6.12",
"vuex": "^3.6.0"
},

View File

@ -70,9 +70,8 @@
</div>
<hr/>
<div class="row" style="margin-top: 2vh">
<div class="col-md-6 order-md-1 col-sm-12 order-sm-2 col-12 order-2" v-if="recipe && ingredient_count > 0"
style="margin-top: 2vh">
<div class="row">
<div class="col-md-6 order-md-1 col-sm-12 order-sm-2 col-12 order-2" v-if="recipe && ingredient_count > 0">
<div class="card border-primary">
<div class="card-body">
<div class="row">
@ -108,7 +107,7 @@
</div>
</div>
<div class="row" style="margin-top: 2vh">
<div class="row" style="margin-top: 2vh; margin-bottom: 2vh">
<div class="col-12">
<Nutrition :recipe="recipe" :ingredient_factor="ingredient_factor"></Nutrition>
</div>
@ -134,6 +133,8 @@
@update-start-time="updateStartTime" @checked-state-changed="updateIngredientCheckedState"></Step>
</div>
</div>
<add-recipe-to-book :recipe="recipe"></add-recipe-to-book>
</div>
</template>
@ -157,6 +158,7 @@ import Nutrition from "@/components/Nutrition";
import moment from 'moment'
import Keywords from "@/components/Keywords";
import LoadingSpinner from "@/components/LoadingSpinner";
import AddRecipeToBook from "@/components/AddRecipeToBook";
Vue.prototype.moment = moment
@ -178,6 +180,7 @@ export default {
Nutrition,
Keywords,
LoadingSpinner,
AddRecipeToBook,
},
computed: {
ingredient_factor: function () {

View File

@ -0,0 +1,73 @@
<template>
<div>
<b-modal class="modal" id="id_modal_add_book" :title="_('Add to Book')" :ok-title="_('Add')"
:cancel-title="_('Close')" @ok="addToBook()">
<multiselect
v-model="selected_book"
:options="books"
:preserve-search="true"
:placeholder="_('Select Book')"
label="name"
track-by="id"
id="id_books"
:multiple="false"
@search-change="loadBook">
</multiselect>
</b-modal>
</div>
</template>
<script>
import {GettextMixin} from "@/utils/utils";
import Multiselect from 'vue-multiselect'
import moment from 'moment'
Vue.prototype.moment = moment
import Vue from "vue";
import {BootstrapVue} from "bootstrap-vue";
import {apiAddRecipeBookEntry, apiLoadCookBooks, apiLogCooking} from "@/utils/api";
Vue.use(BootstrapVue)
export default {
name: 'AddRecipeToBook',
mixins: [
GettextMixin,
],
components: {
Multiselect
},
props: {
recipe: Object,
},
data() {
return {
books: [],
selected_book: null,
}
},
mounted() {
this.loadBook('')
},
methods: {
loadBook: function (query) {
apiLoadCookBooks(query).then(results => {
console.log(results)
this.books = results
})
},
addToBook() {
apiAddRecipeBookEntry({'recipe': this.recipe.id, 'book': this.selected_book.id})
},
}
}
</script>
<style src="vue-multiselect/dist/vue-multiselect.min.css"></style>

View File

@ -15,7 +15,7 @@
<a class="dropdown-item" :href="resolveDjangoUrl('edit_convert_recipe', recipe.id)" v-if="!recipe.internal"><i
class="fas fa-exchange-alt fa-fw"></i> {{ _('Convert to internal recipe') }}</a>
<button class="dropdown-item" onclick="$('#bookmarkModal').modal({'show':true})">
<button class="dropdown-item" @click="$bvModal.show('id_modal_add_book')">
<i class="fas fa-bookmark fa-fw"></i> {{ _('Add to Book') }}
</button>

View File

@ -154,7 +154,9 @@ export default {
return calculateAmount(x, this.ingredient_factor)
},
updateTime: function () {
this.$emit('update-start-time', moment(this.set_time_input).add(this.time_offset * -1, 'minutes').format('yyyy-MM-DDTHH:mm'))
let new_start_time = moment(this.set_time_input).add(this.step.time_offset * -1, 'minutes').format('yyyy-MM-DDTHH:mm')
this.$emit('update-start-time', new_start_time)
this.closePopover()
},
closePopover: function () {

View File

@ -28,6 +28,25 @@ export function apiLogCooking(cook_log) {
})
}
export function apiLoadCookBooks(query) {
return axios.get(resolveDjangoUrl('api:recipebook-list') + '?query=' + query).then((response) => {
console.log(response)
return response.data
}).catch((err) => {
handleError(err, 'There was an error creating a resource!', 'danger')
})
}
export function apiAddRecipeBookEntry(entry) {
return axios.post(resolveDjangoUrl('api:recipebookentry-list',), entry).then((response) => {
console.log(response)
makeToast('Saved', 'Recipe Book entry saved!', 'success')
}).catch((err) => {
handleError(err, 'There was an error creating a resource!', 'danger')
})
}
function handleError(error, message) {
if ('response' in error) {
console.log(error.response)

View File

@ -8266,6 +8266,11 @@ vue-loader@^15.9.2:
vue-hot-reload-api "^2.3.0"
vue-style-loader "^4.1.0"
vue-multiselect@^2.1.6:
version "2.1.6"
resolved "https://registry.yarnpkg.com/vue-multiselect/-/vue-multiselect-2.1.6.tgz#5be5d811a224804a15c43a4edbb7485028a89c7f"
integrity sha512-s7jmZPlm9FeueJg1RwJtnE9KNPtME/7C8uRWSfp9/yEN4M8XcS/d+bddoyVwVnvFyRh9msFo0HWeW0vTL8Qv+w==
vue-style-loader@^4.1.0, vue-style-loader@^4.1.2:
version "4.1.2"
resolved "https://registry.npm.taobao.org/vue-style-loader/download/vue-style-loader-4.1.2.tgz#dedf349806f25ceb4e64f3ad7c0a44fba735fcf8"