mirror of
https://github.com/TandoorRecipes/recipes.git
synced 2025-12-30 21:49:50 -05:00
shopping cleanup
This commit is contained in:
@@ -1226,8 +1226,9 @@ class ShoppingListEntrySerializer(WritableNestedModelSerializer):
|
||||
# update the onhand for food if shopping_add_onhand is True
|
||||
if user.userpreference.shopping_add_onhand:
|
||||
if checked := validated_data.get('checked', None):
|
||||
validated_data['completed_at'] = timezone.now()
|
||||
instance.food.onhand_users.add(*user.userpreference.shopping_share.all(), user)
|
||||
elif checked == False:
|
||||
elif not checked:
|
||||
instance.food.onhand_users.remove(*user.userpreference.shopping_share.all(), user)
|
||||
return super().update(instance, validated_data)
|
||||
|
||||
|
||||
@@ -16,6 +16,7 @@ import PIL.Image
|
||||
import redis
|
||||
import requests
|
||||
from PIL import UnidentifiedImageError
|
||||
from PIL.features import check
|
||||
from django.contrib import messages
|
||||
from django.contrib.auth.models import Group, User
|
||||
from django.contrib.postgres.search import TrigramSimilarity
|
||||
@@ -1374,21 +1375,15 @@ class ShoppingListRecipeViewSet(LoggingMixin, viewsets.ModelViewSet):
|
||||
|
||||
|
||||
@extend_schema_view(list=extend_schema(parameters=[
|
||||
OpenApiParameter(name='id', description=_(
|
||||
'Returns the shopping list entry with a primary key of id. Multiple values allowed.'), type=int),
|
||||
OpenApiParameter(
|
||||
name='checked',
|
||||
description=_('Filter shopping list entries on checked. [''true'', ''false'', ''both'', ''<b>recent</b>'']<br> \
|
||||
- ''recent'' includes unchecked items and recently completed items.')
|
||||
),
|
||||
OpenApiParameter(name='supermarket',
|
||||
description=_('Returns the shopping list entries sorted by supermarket category order.'),
|
||||
type=int),
|
||||
OpenApiParameter(name='updated_after',
|
||||
description=_('Returns only elements updated after the given timestamp in ISO 8601 format.'),
|
||||
type=datetime.datetime),
|
||||
]))
|
||||
class ShoppingListEntryViewSet(LoggingMixin, viewsets.ModelViewSet):
|
||||
"""
|
||||
individual entries of a shopping list
|
||||
automatically filtered to only contain unchecked items that are not older than the shopping recent days setting to not bloat endpoint
|
||||
"""
|
||||
queryset = ShoppingListEntry.objects
|
||||
serializer_class = ShoppingListEntrySerializer
|
||||
permission_classes = [(CustomIsOwner | CustomIsShared) & CustomTokenHasReadWriteScope]
|
||||
@@ -1414,25 +1409,11 @@ class ShoppingListEntryViewSet(LoggingMixin, viewsets.ModelViewSet):
|
||||
|
||||
updated_after = self.request.query_params.get('updated_after', None)
|
||||
|
||||
if pk := self.request.query_params.getlist('id', []):
|
||||
self.queryset = self.queryset.filter(food__id__in=[int(i) for i in pk])
|
||||
|
||||
if 'checked' in self.request.query_params:
|
||||
return shopping_helper(self.queryset, self.request)
|
||||
elif not self.detail and not updated_after:
|
||||
if not self.detail:
|
||||
# to keep the endpoint small, only return entries as old as user preference recent days
|
||||
today_start = timezone.now().replace(hour=0, minute=0, second=0)
|
||||
week_ago = today_start - datetime.timedelta(
|
||||
days=min(self.request.user.userpreference.shopping_recent_days, 14))
|
||||
self.queryset = self.queryset.filter(Q(checked=False) | Q(completed_at__gte=week_ago))
|
||||
|
||||
try:
|
||||
last_autosync = self.request.query_params.get('last_autosync', None)
|
||||
if last_autosync:
|
||||
print('DEPRECATION WARNING: using last_autosync is deprecated and will be removed in future versions, please use updated_after')
|
||||
last_autosync = datetime.datetime.fromtimestamp(int(last_autosync) / 1000, datetime.timezone.utc)
|
||||
self.queryset = self.queryset.filter(updated_at__gte=last_autosync)
|
||||
except Exception:
|
||||
traceback.print_exc()
|
||||
week_ago = today_start - datetime.timedelta(days=min(self.request.user.userpreference.shopping_recent_days, 14))
|
||||
self.queryset = self.queryset.filter((Q(checked=False) | Q(completed_at__gte=week_ago)))
|
||||
|
||||
try:
|
||||
if updated_after:
|
||||
@@ -1442,7 +1423,6 @@ class ShoppingListEntryViewSet(LoggingMixin, viewsets.ModelViewSet):
|
||||
except Exception:
|
||||
traceback.print_exc()
|
||||
|
||||
# TODO once old shopping list is removed this needs updated to sharing users in preferences
|
||||
if self.detail:
|
||||
return self.queryset
|
||||
else:
|
||||
@@ -1454,7 +1434,6 @@ class ShoppingListEntryViewSet(LoggingMixin, viewsets.ModelViewSet):
|
||||
serializer = self.serializer_class(data=request.data)
|
||||
|
||||
if serializer.is_valid():
|
||||
print(serializer.validated_data)
|
||||
bulk_entries = ShoppingListEntry.objects.filter(
|
||||
Q(created_by=self.request.user) | Q(created_by__in=list(self.request.user.get_shopping_share()))
|
||||
).filter(
|
||||
@@ -1462,7 +1441,11 @@ class ShoppingListEntryViewSet(LoggingMixin, viewsets.ModelViewSet):
|
||||
)
|
||||
|
||||
update_timestamp = timezone.now()
|
||||
bulk_entries.update(checked=(checked := serializer.validated_data['checked']), updated_at=update_timestamp, )
|
||||
checked = serializer.validated_data['checked']
|
||||
if checked:
|
||||
bulk_entries.update(checked=checked, updated_at=update_timestamp, completed_at=update_timestamp)
|
||||
else:
|
||||
bulk_entries.update(checked=checked, updated_at=update_timestamp, completed_at=False)
|
||||
serializer.validated_data['timestamp'] = update_timestamp
|
||||
|
||||
# update the onhand for food if shopping_add_onhand is True
|
||||
|
||||
@@ -13,18 +13,19 @@
|
||||
<div class="d-flex flex-column pr-2">
|
||||
<span v-for="[i, a] in amounts" v-bind:key="a.key">
|
||||
<span>
|
||||
<i class="fas fa-check text-warning" v-if="a.checked && !isChecked"></i>
|
||||
<i class="fas fa-hourglass-half text-primary" v-if="a.delayed && !a.checked"></i> <b>
|
||||
<span :class="{'text-decoration-line-through': a.checked}">
|
||||
<i class="fas fa-check text-success fa-fw" v-if="a.checked"></i>
|
||||
<i class="fas fa-clock-rotate-left text-info fa-fw" v-if="a.delayed"></i> <b>
|
||||
<span :class="{'text-disabled': a.checked || a.delayed}">
|
||||
{{ a.amount }}
|
||||
<span v-if="a.unit">{{ a.unit.name }}</span>
|
||||
</span>
|
||||
<span v-if="a.unit">{{ a.unit.name }}</span>
|
||||
|
||||
</b>
|
||||
</span>
|
||||
<br/>
|
||||
</span>
|
||||
</div>
|
||||
<div class="d-flex flex-column flex-grow-1 align-self-center" :class="{'text-decoration-line-through': isChecked}">
|
||||
<div class="d-flex flex-column flex-grow-1 align-self-center" >
|
||||
{{ shoppingListFood.food.name }} <br/>
|
||||
<span v-if="infoRow"><small class="text-disabled">{{ infoRow }}</small></span>
|
||||
</div>
|
||||
|
||||
@@ -86,11 +86,15 @@
|
||||
</template>
|
||||
</v-text-field>
|
||||
|
||||
<v-list class="mt-3" density="compact">
|
||||
<v-list class="mt-3" density="compact" v-if="!useShoppingStore().initialized">
|
||||
<v-skeleton-loader type="list-item"></v-skeleton-loader>
|
||||
<v-skeleton-loader type="list-item"></v-skeleton-loader>
|
||||
<v-skeleton-loader type="list-item"></v-skeleton-loader>
|
||||
<v-skeleton-loader type="list-item"></v-skeleton-loader>
|
||||
</v-list>
|
||||
<v-list class="mt-3" density="compact" v-else>
|
||||
<template v-for="category in useShoppingStore().getEntriesByGroup" :key="category.name">
|
||||
<template v-if="(category.stats.countUnchecked > 0 || useUserPreferenceStore().deviceSettings.shopping_show_checked_entries)
|
||||
&& (category.stats.countUnchecked + category.stats.countChecked) > 0
|
||||
&& (category.stats.countUncheckedDelayed < category.stats.countUnchecked || useUserPreferenceStore().deviceSettings.shopping_show_delayed_entries)">
|
||||
<template v-if="isCategoryVisible(category)">
|
||||
|
||||
<v-list-subheader v-if="category.name === useShoppingStore().UNDEFINED_CATEGORY"><i>{{ $t('NoCategory') }}</i></v-list-subheader>
|
||||
<v-list-subheader v-else>{{ category.name }}</v-list-subheader>
|
||||
@@ -104,6 +108,7 @@
|
||||
</template>
|
||||
</template>
|
||||
</v-list>
|
||||
|
||||
</v-col>
|
||||
</v-row>
|
||||
|
||||
@@ -185,13 +190,13 @@
|
||||
|
||||
import {computed, onMounted, ref} from "vue";
|
||||
import {useShoppingStore} from "@/stores/ShoppingStore";
|
||||
import {ApiApi, Food, IngredientString, ShoppingListEntry, Unit} from "@/openapi";
|
||||
import {ApiApi, Food, IngredientString, ShoppingListEntry, SupermarketCategory, Unit} from "@/openapi";
|
||||
import {ErrorMessageType, useMessageStore} from "@/stores/MessageStore";
|
||||
import ShoppingLineItem from "@/components/display/ShoppingLineItem.vue";
|
||||
import {useUserPreferenceStore} from "@/stores/UserPreferenceStore";
|
||||
import ModelSelect from "@/components/inputs/ModelSelect.vue";
|
||||
import ShoppingLineItemDialog from "@/components/dialogs/ShoppingLineItemDialog.vue";
|
||||
import {IShoppingListFood, ShoppingGroupingOptions} from "@/types/Shopping";
|
||||
import {IShoppingListCategory, IShoppingListFood, ShoppingGroupingOptions} from "@/types/Shopping";
|
||||
import {useI18n} from "vue-i18n";
|
||||
import NumberScalerDialog from "@/components/inputs/NumberScalerDialog.vue";
|
||||
|
||||
@@ -256,6 +261,22 @@ function addIngredient() {
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* determines if a category as entries that should be visible
|
||||
* @param category
|
||||
*/
|
||||
function isCategoryVisible(category: IShoppingListCategory) {
|
||||
let entryCount = category.stats.countUnchecked
|
||||
|
||||
if (useUserPreferenceStore().deviceSettings.shopping_show_checked_entries){
|
||||
entryCount += category.stats.countChecked
|
||||
}
|
||||
if (useUserPreferenceStore().deviceSettings.shopping_show_delayed_entries){
|
||||
entryCount += category.stats.countUncheckedDelayed
|
||||
}
|
||||
return entryCount > 0
|
||||
}
|
||||
|
||||
/**
|
||||
* run the autosync function in a loop
|
||||
*/
|
||||
|
||||
@@ -34,6 +34,7 @@ export const useShoppingStore = defineStore(_STORE_ID, () => {
|
||||
|
||||
// internal
|
||||
let currentlyUpdating = ref(false)
|
||||
let initialized = ref(false)
|
||||
|
||||
let autoSyncLastTimestamp = ref(new Date('1970-01-01'))
|
||||
let autoSyncHasFocus = ref(true)
|
||||
@@ -89,9 +90,10 @@ export const useShoppingStore = defineStore(_STORE_ID, () => {
|
||||
if (entry.checked) {
|
||||
categoryStats.countChecked++
|
||||
} else {
|
||||
categoryStats.countUnchecked++
|
||||
if (isDelayed(entry)) {
|
||||
categoryStats.countUncheckedDelayed++
|
||||
} else {
|
||||
categoryStats.countUnchecked++
|
||||
}
|
||||
}
|
||||
})
|
||||
@@ -175,6 +177,7 @@ export const useShoppingStore = defineStore(_STORE_ID, () => {
|
||||
entries.value.set(e.id!, e)
|
||||
})
|
||||
currentlyUpdating.value = false
|
||||
initialized.value = true
|
||||
}).catch((err) => {
|
||||
currentlyUpdating.value = false
|
||||
useMessageStore().addError(ErrorMessageType.FETCH_ERROR, err)
|
||||
@@ -531,6 +534,7 @@ export const useShoppingStore = defineStore(_STORE_ID, () => {
|
||||
autoSyncHasFocus,
|
||||
autoSyncLastTimestamp,
|
||||
currentlyUpdating,
|
||||
initialized,
|
||||
getFlatEntries,
|
||||
hasFailedItems,
|
||||
itemCheckSyncQueue,
|
||||
|
||||
Reference in New Issue
Block a user