mirror of
https://github.com/TandoorRecipes/recipes.git
synced 2026-01-04 21:58:54 -05:00
Merge branch 'develop' into feature/vue3
# Conflicts: # recipes/settings.py # vue/vue.config.js
This commit is contained in:
@@ -13,7 +13,7 @@ from cookbook.managers import DICTIONARY
|
|||||||
from .models import (BookmarkletImport, Comment, CookLog, Food, ImportLog, Ingredient, InviteLink,
|
from .models import (BookmarkletImport, Comment, CookLog, Food, ImportLog, Ingredient, InviteLink,
|
||||||
Keyword, MealPlan, MealType, NutritionInformation, Property, PropertyType,
|
Keyword, MealPlan, MealType, NutritionInformation, Property, PropertyType,
|
||||||
Recipe, RecipeBook, RecipeBookEntry, RecipeImport, SearchPreference, ShareLink,
|
Recipe, RecipeBook, RecipeBookEntry, RecipeImport, SearchPreference, ShareLink,
|
||||||
ShoppingList, ShoppingListEntry, ShoppingListRecipe, Space, Step, Storage,
|
ShoppingListEntry, ShoppingListRecipe, Space, Step, Storage,
|
||||||
Supermarket, SupermarketCategory, SupermarketCategoryRelation, Sync, SyncLog,
|
Supermarket, SupermarketCategory, SupermarketCategoryRelation, Sync, SyncLog,
|
||||||
TelegramBot, Unit, UnitConversion, UserFile, UserPreference, UserSpace,
|
TelegramBot, Unit, UnitConversion, UserFile, UserPreference, UserSpace,
|
||||||
ViewLog, ConnectorConfig)
|
ViewLog, ConnectorConfig)
|
||||||
@@ -369,13 +369,6 @@ class ShoppingListEntryAdmin(admin.ModelAdmin):
|
|||||||
admin.site.register(ShoppingListEntry, ShoppingListEntryAdmin)
|
admin.site.register(ShoppingListEntry, ShoppingListEntryAdmin)
|
||||||
|
|
||||||
|
|
||||||
# class ShoppingListAdmin(admin.ModelAdmin):
|
|
||||||
# list_display = ('id', 'created_by', 'created_at')
|
|
||||||
|
|
||||||
|
|
||||||
# admin.site.register(ShoppingList, ShoppingListAdmin)
|
|
||||||
|
|
||||||
|
|
||||||
class ShareLinkAdmin(admin.ModelAdmin):
|
class ShareLinkAdmin(admin.ModelAdmin):
|
||||||
list_display = ('recipe', 'created_by', 'uuid', 'created_at',)
|
list_display = ('recipe', 'created_by', 'uuid', 'created_at',)
|
||||||
|
|
||||||
|
|||||||
@@ -160,6 +160,7 @@ class StorageForm(forms.ModelForm):
|
|||||||
help_texts = {'url': _('Leave empty for dropbox and enter only base url for nextcloud (<code>/remote.php/webdav/</code> is added automatically)'), }
|
help_texts = {'url': _('Leave empty for dropbox and enter only base url for nextcloud (<code>/remote.php/webdav/</code> is added automatically)'), }
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class ConnectorConfigForm(forms.ModelForm):
|
class ConnectorConfigForm(forms.ModelForm):
|
||||||
enabled = forms.BooleanField(
|
enabled = forms.BooleanField(
|
||||||
help_text="Is the connector enabled",
|
help_text="Is the connector enabled",
|
||||||
@@ -205,24 +206,6 @@ class ConnectorConfigForm(forms.ModelForm):
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
# TODO: Deprecate
|
|
||||||
# class RecipeBookEntryForm(forms.ModelForm):
|
|
||||||
# prefix = 'bookmark'
|
|
||||||
|
|
||||||
# def __init__(self, *args, **kwargs):
|
|
||||||
# space = kwargs.pop('space')
|
|
||||||
# super().__init__(*args, **kwargs)
|
|
||||||
# self.fields['book'].queryset = RecipeBook.objects.filter(space=space).all()
|
|
||||||
|
|
||||||
# class Meta:
|
|
||||||
# model = RecipeBookEntry
|
|
||||||
# fields = ('book',)
|
|
||||||
|
|
||||||
# field_classes = {
|
|
||||||
# 'book': SafeModelChoiceField,
|
|
||||||
# }
|
|
||||||
|
|
||||||
|
|
||||||
class SyncForm(forms.ModelForm):
|
class SyncForm(forms.ModelForm):
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
@@ -239,7 +222,6 @@ class SyncForm(forms.ModelForm):
|
|||||||
labels = {'storage': _('Storage'), 'path': _('Path'), 'active': _('Active')}
|
labels = {'storage': _('Storage'), 'path': _('Path'), 'active': _('Active')}
|
||||||
|
|
||||||
|
|
||||||
# TODO deprecate
|
|
||||||
class BatchEditForm(forms.Form):
|
class BatchEditForm(forms.Form):
|
||||||
search = forms.CharField(label=_('Search String'))
|
search = forms.CharField(label=_('Search String'))
|
||||||
keywords = forms.ModelMultipleChoiceField(queryset=Keyword.objects.none(), required=False, widget=MultiSelectWidget)
|
keywords = forms.ModelMultipleChoiceField(queryset=Keyword.objects.none(), required=False, widget=MultiSelectWidget)
|
||||||
@@ -373,73 +355,3 @@ class SearchPreferenceForm(forms.ModelForm):
|
|||||||
'search': SelectWidget, 'unaccent': MultiSelectWidget, 'icontains': MultiSelectWidget, 'istartswith': MultiSelectWidget, 'trigram': MultiSelectWidget, 'fulltext':
|
'search': SelectWidget, 'unaccent': MultiSelectWidget, 'icontains': MultiSelectWidget, 'istartswith': MultiSelectWidget, 'trigram': MultiSelectWidget, 'fulltext':
|
||||||
MultiSelectWidget,
|
MultiSelectWidget,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
# class ShoppingPreferenceForm(forms.ModelForm):
|
|
||||||
# prefix = 'shopping'
|
|
||||||
|
|
||||||
# class Meta:
|
|
||||||
# model = UserPreference
|
|
||||||
|
|
||||||
# fields = (
|
|
||||||
# 'shopping_share', 'shopping_auto_sync', 'mealplan_autoadd_shopping', 'mealplan_autoexclude_onhand',
|
|
||||||
# 'mealplan_autoinclude_related', 'shopping_add_onhand', 'default_delay', 'filter_to_supermarket', 'shopping_recent_days', 'csv_delim', 'csv_prefix'
|
|
||||||
# )
|
|
||||||
|
|
||||||
# help_texts = {
|
|
||||||
# 'shopping_share': _('Users will see all items you add to your shopping list. They must add you to see items on their list.'),
|
|
||||||
# 'shopping_auto_sync': _(
|
|
||||||
# 'Setting to 0 will disable auto sync. When viewing a shopping list the list is updated every set seconds to sync changes someone else might have made. Useful when shopping with multiple people but might use a little bit '
|
|
||||||
# 'of mobile data. If lower than instance limit it is reset when saving.'
|
|
||||||
# ),
|
|
||||||
# 'mealplan_autoadd_shopping': _('Automatically add meal plan ingredients to shopping list.'),
|
|
||||||
# 'mealplan_autoinclude_related': _('When adding a meal plan to the shopping list (manually or automatically), include all related recipes.'),
|
|
||||||
# 'mealplan_autoexclude_onhand': _('When adding a meal plan to the shopping list (manually or automatically), exclude ingredients that are on hand.'),
|
|
||||||
# 'default_delay': _('Default number of hours to delay a shopping list entry.'),
|
|
||||||
# 'filter_to_supermarket': _('Filter shopping list to only include supermarket categories.'),
|
|
||||||
# 'shopping_recent_days': _('Days of recent shopping list entries to display.'),
|
|
||||||
# 'shopping_add_onhand': _("Mark food 'On Hand' when checked off shopping list."),
|
|
||||||
# 'csv_delim': _('Delimiter to use for CSV exports.'),
|
|
||||||
# 'csv_prefix': _('Prefix to add when copying list to the clipboard.'),
|
|
||||||
|
|
||||||
# }
|
|
||||||
# labels = {
|
|
||||||
# 'shopping_share': _('Share Shopping List'),
|
|
||||||
# 'shopping_auto_sync': _('Autosync'),
|
|
||||||
# 'mealplan_autoadd_shopping': _('Auto Add Meal Plan'),
|
|
||||||
# 'mealplan_autoexclude_onhand': _('Exclude On Hand'),
|
|
||||||
# 'mealplan_autoinclude_related': _('Include Related'),
|
|
||||||
# 'default_delay': _('Default Delay Hours'),
|
|
||||||
# 'filter_to_supermarket': _('Filter to Supermarket'),
|
|
||||||
# 'shopping_recent_days': _('Recent Days'),
|
|
||||||
# 'csv_delim': _('CSV Delimiter'),
|
|
||||||
# "csv_prefix_label": _("List Prefix"),
|
|
||||||
# 'shopping_add_onhand': _("Auto On Hand"),
|
|
||||||
# }
|
|
||||||
|
|
||||||
# widgets = {
|
|
||||||
# 'shopping_share': MultiSelectWidget
|
|
||||||
# }
|
|
||||||
|
|
||||||
# class SpacePreferenceForm(forms.ModelForm):
|
|
||||||
# prefix = 'space'
|
|
||||||
# reset_food_inherit = forms.BooleanField(label=_("Reset Food Inheritance"), initial=False, required=False,
|
|
||||||
# help_text=_("Reset all food to inherit the fields configured."))
|
|
||||||
|
|
||||||
# def __init__(self, *args, **kwargs):
|
|
||||||
# super().__init__(*args, **kwargs) # populates the post
|
|
||||||
# self.fields['food_inherit'].queryset = Food.inheritable_fields
|
|
||||||
|
|
||||||
# class Meta:
|
|
||||||
# model = Space
|
|
||||||
|
|
||||||
# fields = ('food_inherit', 'reset_food_inherit',)
|
|
||||||
|
|
||||||
# help_texts = {
|
|
||||||
# 'food_inherit': _('Fields on food that should be inherited by default.'),
|
|
||||||
# 'use_plural': _('Use the plural form for units and food inside this space.'),
|
|
||||||
# }
|
|
||||||
|
|
||||||
# widgets = {
|
|
||||||
# 'food_inherit': MultiSelectWidget
|
|
||||||
# }
|
|
||||||
|
|||||||
@@ -1,6 +1,4 @@
|
|||||||
import cookbook.helper.dal
|
|
||||||
from cookbook.helper.AllAuthCustomAdapter import AllAuthCustomAdapter
|
from cookbook.helper.AllAuthCustomAdapter import AllAuthCustomAdapter
|
||||||
|
|
||||||
__all__ = [
|
__all__ = [
|
||||||
'dal',
|
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -1,34 +0,0 @@
|
|||||||
from cookbook.models import Food, Keyword, Recipe, Unit
|
|
||||||
|
|
||||||
from dal import autocomplete
|
|
||||||
|
|
||||||
|
|
||||||
class BaseAutocomplete(autocomplete.Select2QuerySetView):
|
|
||||||
model = None
|
|
||||||
|
|
||||||
def get_queryset(self):
|
|
||||||
if not self.request.user.is_authenticated:
|
|
||||||
return self.model.objects.none()
|
|
||||||
|
|
||||||
qs = self.model.objects.filter(space=self.request.space).all()
|
|
||||||
|
|
||||||
if self.q:
|
|
||||||
qs = qs.filter(name__icontains=self.q)
|
|
||||||
|
|
||||||
return qs
|
|
||||||
|
|
||||||
|
|
||||||
class KeywordAutocomplete(BaseAutocomplete):
|
|
||||||
model = Keyword
|
|
||||||
|
|
||||||
|
|
||||||
class IngredientsAutocomplete(BaseAutocomplete):
|
|
||||||
model = Food
|
|
||||||
|
|
||||||
|
|
||||||
class RecipeAutocomplete(BaseAutocomplete):
|
|
||||||
model = Recipe
|
|
||||||
|
|
||||||
|
|
||||||
class UnitAutocomplete(BaseAutocomplete):
|
|
||||||
model = Unit
|
|
||||||
@@ -75,7 +75,7 @@ def is_object_owner(user, obj):
|
|||||||
if not user.is_authenticated:
|
if not user.is_authenticated:
|
||||||
return False
|
return False
|
||||||
try:
|
try:
|
||||||
return obj.get_owner() == user
|
return obj.get_owner() == 'orphan' or obj.get_owner() == user
|
||||||
except Exception:
|
except Exception:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
|||||||
@@ -1,9 +1,8 @@
|
|||||||
from datetime import timedelta
|
|
||||||
from decimal import Decimal
|
from decimal import Decimal
|
||||||
|
|
||||||
from django.db.models import F, OuterRef, Q, Subquery, Value
|
from django.db.models import F, OuterRef, Q, Subquery, Value
|
||||||
from django.db.models.functions import Coalesce
|
from django.db.models.functions import Coalesce
|
||||||
from django.utils import timezone
|
|
||||||
from django.utils.translation import gettext as _
|
from django.utils.translation import gettext as _
|
||||||
|
|
||||||
from cookbook.models import (Ingredient, MealPlan, Recipe, ShoppingListEntry, ShoppingListRecipe,
|
from cookbook.models import (Ingredient, MealPlan, Recipe, ShoppingListEntry, ShoppingListRecipe,
|
||||||
@@ -76,10 +75,8 @@ class RecipeShoppingEditor():
|
|||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_shopping_list_recipe(id, user, space):
|
def get_shopping_list_recipe(id, user, space):
|
||||||
return ShoppingListRecipe.objects.filter(id=id).filter(Q(shoppinglist__space=space) | Q(entries__space=space)).filter(
|
return ShoppingListRecipe.objects.filter(id=id).filter(entries__space=space).filter(
|
||||||
Q(shoppinglist__created_by=user)
|
Q(entries__created_by=user)
|
||||||
| Q(shoppinglist__shared=user)
|
|
||||||
| Q(entries__created_by=user)
|
|
||||||
| Q(entries__created_by__in=list(user.get_shopping_share()))
|
| Q(entries__created_by__in=list(user.get_shopping_share()))
|
||||||
).prefetch_related('entries').first()
|
).prefetch_related('entries').first()
|
||||||
|
|
||||||
|
|||||||
@@ -8,8 +8,8 @@ msgstr ""
|
|||||||
"Project-Id-Version: PACKAGE VERSION\n"
|
"Project-Id-Version: PACKAGE VERSION\n"
|
||||||
"Report-Msgid-Bugs-To: \n"
|
"Report-Msgid-Bugs-To: \n"
|
||||||
"POT-Creation-Date: 2021-04-11 15:09+0200\n"
|
"POT-Creation-Date: 2021-04-11 15:09+0200\n"
|
||||||
"PO-Revision-Date: 2022-04-17 00:31+0000\n"
|
"PO-Revision-Date: 2024-02-27 12:19+0000\n"
|
||||||
"Last-Translator: Oskar Stenberg <01ste02@gmail.com>\n"
|
"Last-Translator: Lukas Åteg <lukas@ategsolutions.se>\n"
|
||||||
"Language-Team: Swedish <http://translate.tandoor.dev/projects/tandoor/"
|
"Language-Team: Swedish <http://translate.tandoor.dev/projects/tandoor/"
|
||||||
"recipes-backend/sv/>\n"
|
"recipes-backend/sv/>\n"
|
||||||
"Language: sv\n"
|
"Language: sv\n"
|
||||||
@@ -17,7 +17,7 @@ msgstr ""
|
|||||||
"Content-Type: text/plain; charset=UTF-8\n"
|
"Content-Type: text/plain; charset=UTF-8\n"
|
||||||
"Content-Transfer-Encoding: 8bit\n"
|
"Content-Transfer-Encoding: 8bit\n"
|
||||||
"Plural-Forms: nplurals=2; plural=n != 1;\n"
|
"Plural-Forms: nplurals=2; plural=n != 1;\n"
|
||||||
"X-Generator: Weblate 4.10.1\n"
|
"X-Generator: Weblate 4.15\n"
|
||||||
|
|
||||||
#: .\cookbook\filters.py:23 .\cookbook\templates\base.html:91
|
#: .\cookbook\filters.py:23 .\cookbook\templates\base.html:91
|
||||||
#: .\cookbook\templates\forms\edit_internal_recipe.html:219
|
#: .\cookbook\templates\forms\edit_internal_recipe.html:219
|
||||||
@@ -1812,7 +1812,7 @@ msgstr "Google ld+json info"
|
|||||||
|
|
||||||
#: .\cookbook\templates\url_import.html:268
|
#: .\cookbook\templates\url_import.html:268
|
||||||
msgid "GitHub Issues"
|
msgid "GitHub Issues"
|
||||||
msgstr "GitHub Issues"
|
msgstr "GitHub Problem"
|
||||||
|
|
||||||
#: .\cookbook\templates\url_import.html:270
|
#: .\cookbook\templates\url_import.html:270
|
||||||
msgid "Recipe Markup Specification"
|
msgid "Recipe Markup Specification"
|
||||||
@@ -1852,7 +1852,7 @@ msgstr "Kunde inte tolka korrekt..."
|
|||||||
msgid "Batch edit done. %(count)d recipe was updated."
|
msgid "Batch edit done. %(count)d recipe was updated."
|
||||||
msgid_plural "Batch edit done. %(count)d Recipes where updated."
|
msgid_plural "Batch edit done. %(count)d Recipes where updated."
|
||||||
msgstr[0] "Batchredigering klar. %(count)d recept uppdaterades."
|
msgstr[0] "Batchredigering klar. %(count)d recept uppdaterades."
|
||||||
msgstr[1] "Batchredigering klar. %(count)d recept uppdaterades."
|
msgstr[1] "Batchredigering klar. %(count)d recepten uppdaterades."
|
||||||
|
|
||||||
#: .\cookbook\views\delete.py:72
|
#: .\cookbook\views\delete.py:72
|
||||||
msgid "Monitor"
|
msgid "Monitor"
|
||||||
|
|||||||
18
cookbook/migrations/0214_delete_shopping_model.py
Normal file
18
cookbook/migrations/0214_delete_shopping_model.py
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
# Generated by Django 4.2.10 on 2024-02-19 20:21
|
||||||
|
|
||||||
|
from django.db import migrations
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
dependencies = [
|
||||||
|
(
|
||||||
|
"cookbook",
|
||||||
|
"0213_remove_property_property_unique_import_food_per_space_and_more",
|
||||||
|
),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.DeleteModel(
|
||||||
|
name="ShoppingList",
|
||||||
|
),
|
||||||
|
]
|
||||||
@@ -369,9 +369,8 @@ class Space(ExportModelOperationsMixin('space'), models.Model):
|
|||||||
Storage.objects.filter(space=self).delete()
|
Storage.objects.filter(space=self).delete()
|
||||||
ConnectorConfig.objects.filter(space=self).delete()
|
ConnectorConfig.objects.filter(space=self).delete()
|
||||||
|
|
||||||
ShoppingListEntry.objects.filter(shoppinglist__space=self).delete()
|
ShoppingListEntry.objects.filter(space=self).delete()
|
||||||
ShoppingListRecipe.objects.filter(shoppinglist__space=self).delete()
|
ShoppingListRecipe.objects.filter(recipe__space=self).delete()
|
||||||
ShoppingList.objects.filter(space=self).delete()
|
|
||||||
|
|
||||||
SupermarketCategoryRelation.objects.filter(supermarket__space=self).delete()
|
SupermarketCategoryRelation.objects.filter(supermarket__space=self).delete()
|
||||||
SupermarketCategory.objects.filter(space=self).delete()
|
SupermarketCategory.objects.filter(space=self).delete()
|
||||||
@@ -1195,7 +1194,10 @@ class ShoppingListRecipe(ExportModelOperationsMixin('shopping_list_recipe'), mod
|
|||||||
|
|
||||||
def get_owner(self):
|
def get_owner(self):
|
||||||
try:
|
try:
|
||||||
return getattr(self.entries.first(), 'created_by', None) or getattr(self.shoppinglist_set.first(), 'created_by', None)
|
if not self.entries.exists():
|
||||||
|
return 'orphan'
|
||||||
|
else:
|
||||||
|
return getattr(self.entries.first(), 'created_by', None)
|
||||||
except AttributeError:
|
except AttributeError:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
@@ -1218,53 +1220,19 @@ class ShoppingListEntry(ExportModelOperationsMixin('shopping_list_entry'), model
|
|||||||
space = models.ForeignKey(Space, on_delete=models.CASCADE)
|
space = models.ForeignKey(Space, on_delete=models.CASCADE)
|
||||||
objects = ScopedManager(space='space')
|
objects = ScopedManager(space='space')
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def get_space_key():
|
|
||||||
return 'shoppinglist', 'space'
|
|
||||||
|
|
||||||
def get_space(self):
|
|
||||||
return self.shoppinglist_set.first().space
|
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return f'Shopping list entry {self.id}'
|
return f'Shopping list entry {self.id}'
|
||||||
|
|
||||||
def get_shared(self):
|
def get_shared(self):
|
||||||
try:
|
return self.created_by.userpreference.shopping_share.all()
|
||||||
return self.shoppinglist_set.first().shared.all()
|
|
||||||
except AttributeError:
|
|
||||||
return self.created_by.userpreference.shopping_share.all()
|
|
||||||
|
|
||||||
def get_owner(self):
|
def get_owner(self):
|
||||||
try:
|
try:
|
||||||
return self.created_by or self.shoppinglist_set.first().created_by
|
return self.created_by
|
||||||
except AttributeError:
|
except AttributeError:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
class ShoppingList(ExportModelOperationsMixin('shopping_list'), models.Model, PermissionModelMixin):
|
|
||||||
uuid = models.UUIDField(default=uuid.uuid4)
|
|
||||||
note = models.TextField(blank=True, null=True)
|
|
||||||
recipes = models.ManyToManyField(ShoppingListRecipe, blank=True)
|
|
||||||
entries = models.ManyToManyField(ShoppingListEntry, blank=True)
|
|
||||||
shared = models.ManyToManyField(User, blank=True, related_name='list_share')
|
|
||||||
supermarket = models.ForeignKey(Supermarket, null=True, blank=True, on_delete=models.SET_NULL)
|
|
||||||
finished = models.BooleanField(default=False)
|
|
||||||
created_by = models.ForeignKey(User, on_delete=models.CASCADE)
|
|
||||||
created_at = models.DateTimeField(auto_now_add=True)
|
|
||||||
|
|
||||||
space = models.ForeignKey(Space, on_delete=models.CASCADE)
|
|
||||||
objects = ScopedManager(space='space')
|
|
||||||
|
|
||||||
def __str__(self):
|
|
||||||
return f'Shopping list {self.id}'
|
|
||||||
|
|
||||||
def get_shared(self):
|
|
||||||
try:
|
|
||||||
return self.shared.all() or self.created_by.userpreference.shopping_share.all()
|
|
||||||
except AttributeError:
|
|
||||||
return []
|
|
||||||
|
|
||||||
|
|
||||||
class ShareLink(ExportModelOperationsMixin('share_link'), models.Model, PermissionModelMixin):
|
class ShareLink(ExportModelOperationsMixin('share_link'), models.Model, PermissionModelMixin):
|
||||||
recipe = models.ForeignKey(Recipe, on_delete=models.CASCADE)
|
recipe = models.ForeignKey(Recipe, on_delete=models.CASCADE)
|
||||||
uuid = models.UUIDField(default=uuid.uuid4)
|
uuid = models.UUIDField(default=uuid.uuid4)
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
import traceback
|
|
||||||
import uuid
|
import uuid
|
||||||
from datetime import datetime, timedelta
|
from datetime import datetime, timedelta
|
||||||
from decimal import Decimal
|
from decimal import Decimal
|
||||||
@@ -31,7 +30,7 @@ from cookbook.models import (Automation, BookmarkletImport, Comment, CookLog, Cu
|
|||||||
ExportLog, Food, FoodInheritField, ImportLog, Ingredient, InviteLink,
|
ExportLog, Food, FoodInheritField, ImportLog, Ingredient, InviteLink,
|
||||||
Keyword, MealPlan, MealType, NutritionInformation, Property,
|
Keyword, MealPlan, MealType, NutritionInformation, Property,
|
||||||
PropertyType, Recipe, RecipeBook, RecipeBookEntry, RecipeImport,
|
PropertyType, Recipe, RecipeBook, RecipeBookEntry, RecipeImport,
|
||||||
ShareLink, ShoppingList, ShoppingListEntry, ShoppingListRecipe, Space,
|
ShareLink, ShoppingListEntry, ShoppingListRecipe, Space,
|
||||||
Step, Storage, Supermarket, SupermarketCategory,
|
Step, Storage, Supermarket, SupermarketCategory,
|
||||||
SupermarketCategoryRelation, Sync, SyncLog, Unit, UnitConversion,
|
SupermarketCategoryRelation, Sync, SyncLog, Unit, UnitConversion,
|
||||||
UserFile, UserPreference, UserSpace, ViewLog, ConnectorConfig)
|
UserFile, UserPreference, UserSpace, ViewLog, ConnectorConfig)
|
||||||
@@ -82,14 +81,12 @@ class ExtendedRecipeMixin(serializers.ModelSerializer):
|
|||||||
class OpenDataModelMixin(serializers.ModelSerializer):
|
class OpenDataModelMixin(serializers.ModelSerializer):
|
||||||
|
|
||||||
def create(self, validated_data):
|
def create(self, validated_data):
|
||||||
if 'open_data_slug' in validated_data and validated_data['open_data_slug'] is not None and validated_data[
|
if 'open_data_slug' in validated_data and validated_data['open_data_slug'] is not None and validated_data['open_data_slug'].strip() == '':
|
||||||
'open_data_slug'].strip() == '':
|
|
||||||
validated_data['open_data_slug'] = None
|
validated_data['open_data_slug'] = None
|
||||||
return super().create(validated_data)
|
return super().create(validated_data)
|
||||||
|
|
||||||
def update(self, instance, validated_data):
|
def update(self, instance, validated_data):
|
||||||
if 'open_data_slug' in validated_data and validated_data['open_data_slug'] is not None and validated_data[
|
if 'open_data_slug' in validated_data and validated_data['open_data_slug'] is not None and validated_data['open_data_slug'].strip() == '':
|
||||||
'open_data_slug'].strip() == '':
|
|
||||||
validated_data['open_data_slug'] = None
|
validated_data['open_data_slug'] = None
|
||||||
return super().update(instance, validated_data)
|
return super().update(instance, validated_data)
|
||||||
|
|
||||||
@@ -336,8 +333,7 @@ class UserSpaceSerializer(WritableNestedModelSerializer):
|
|||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = UserSpace
|
model = UserSpace
|
||||||
fields = (
|
fields = ('id', 'user', 'space', 'groups', 'active', 'internal_note', 'invite_link', 'created_at', 'updated_at',)
|
||||||
'id', 'user', 'space', 'groups', 'active', 'internal_note', 'invite_link', 'created_at', 'updated_at',)
|
|
||||||
read_only_fields = ('id', 'invite_link', 'created_at', 'updated_at', 'space')
|
read_only_fields = ('id', 'invite_link', 'created_at', 'updated_at', 'space')
|
||||||
|
|
||||||
|
|
||||||
@@ -877,8 +873,7 @@ class UnitConversionSerializer(WritableNestedModelSerializer, OpenDataModelMixin
|
|||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = UnitConversion
|
model = UnitConversion
|
||||||
fields = (
|
fields = ('id', 'name', 'base_amount', 'base_unit', 'converted_amount', 'converted_unit', 'food', 'open_data_slug')
|
||||||
'id', 'name', 'base_amount', 'base_unit', 'converted_amount', 'converted_unit', 'food', 'open_data_slug')
|
|
||||||
|
|
||||||
|
|
||||||
class NutritionInformationSerializer(serializers.ModelSerializer):
|
class NutritionInformationSerializer(serializers.ModelSerializer):
|
||||||
@@ -1198,37 +1193,6 @@ class ShoppingListEntryCheckedSerializer(serializers.ModelSerializer):
|
|||||||
fields = ('id', 'checked')
|
fields = ('id', 'checked')
|
||||||
|
|
||||||
|
|
||||||
# TODO deprecate
|
|
||||||
class ShoppingListSerializer(WritableNestedModelSerializer):
|
|
||||||
recipes = ShoppingListRecipeSerializer(many=True, allow_null=True)
|
|
||||||
entries = ShoppingListEntrySerializer(many=True, allow_null=True)
|
|
||||||
shared = UserSerializer(many=True)
|
|
||||||
supermarket = SupermarketSerializer(allow_null=True)
|
|
||||||
|
|
||||||
def create(self, validated_data):
|
|
||||||
validated_data['space'] = self.context['request'].space
|
|
||||||
validated_data['created_by'] = self.context['request'].user
|
|
||||||
return super().create(validated_data)
|
|
||||||
|
|
||||||
class Meta:
|
|
||||||
model = ShoppingList
|
|
||||||
fields = (
|
|
||||||
'id', 'uuid', 'note', 'recipes', 'entries',
|
|
||||||
'shared', 'finished', 'supermarket', 'created_by', 'created_at'
|
|
||||||
)
|
|
||||||
read_only_fields = ('id', 'created_by',)
|
|
||||||
|
|
||||||
|
|
||||||
# TODO deprecate
|
|
||||||
class ShoppingListAutoSyncSerializer(WritableNestedModelSerializer):
|
|
||||||
entries = ShoppingListEntryCheckedSerializer(many=True, allow_null=True)
|
|
||||||
|
|
||||||
class Meta:
|
|
||||||
model = ShoppingList
|
|
||||||
fields = ('id', 'entries',)
|
|
||||||
read_only_fields = ('id',)
|
|
||||||
|
|
||||||
|
|
||||||
class ShareLinkSerializer(SpacedModelSerializer):
|
class ShareLinkSerializer(SpacedModelSerializer):
|
||||||
class Meta:
|
class Meta:
|
||||||
model = ShareLink
|
model = ShareLink
|
||||||
|
|||||||
@@ -114,7 +114,7 @@
|
|||||||
class="fas fa-fw fa-calendar"></i> {% trans 'Meal-Plan' %}</a>
|
class="fas fa-fw fa-calendar"></i> {% trans 'Meal-Plan' %}</a>
|
||||||
</li>
|
</li>
|
||||||
<li class="nav-item {% if request.resolver_match.url_name in 'list_shopping_list,view_shopping' %}active{% endif %}">
|
<li class="nav-item {% if request.resolver_match.url_name in 'list_shopping_list,view_shopping' %}active{% endif %}">
|
||||||
<a class="nav-link" href="{% url 'list_shopping_list' %}"><i
|
<a class="nav-link" href="{% url 'view_shopping' %}"><i
|
||||||
class="fas fa-fw fa-shopping-cart"></i> {% trans 'Shopping' %}</a>
|
class="fas fa-fw fa-shopping-cart"></i> {% trans 'Shopping' %}</a>
|
||||||
</li>
|
</li>
|
||||||
<li class="nav-item {% if request.resolver_match.url_name in 'view_books' %}active{% endif %}">
|
<li class="nav-item {% if request.resolver_match.url_name in 'view_books' %}active{% endif %}">
|
||||||
|
|||||||
@@ -1,32 +0,0 @@
|
|||||||
{% extends "base.html" %}
|
|
||||||
{% load render_bundle from webpack_loader %}
|
|
||||||
{% load static %}
|
|
||||||
{% load i18n %}
|
|
||||||
{% load l10n %}
|
|
||||||
|
|
||||||
{% block title %}{% trans 'Profile' %}{% endblock %}
|
|
||||||
|
|
||||||
{% block content %}
|
|
||||||
|
|
||||||
<div id="app" >
|
|
||||||
<profile-view></profile-view>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
|
|
||||||
{% endblock %}
|
|
||||||
|
|
||||||
|
|
||||||
{% block script %}
|
|
||||||
{% if debug %}
|
|
||||||
<script src="{% url 'js_reverse' %}"></script>
|
|
||||||
{% else %}
|
|
||||||
<script src="{% static 'django_js_reverse/reverse.js' %}"></script>
|
|
||||||
{% endif %}
|
|
||||||
|
|
||||||
<script type="application/javascript">
|
|
||||||
|
|
||||||
window.CUSTOM_LOCALE = '{{ request.LANGUAGE_CODE }}'
|
|
||||||
</script>
|
|
||||||
|
|
||||||
{% render_bundle 'profile_view' %}
|
|
||||||
{% endblock %}
|
|
||||||
@@ -1,36 +0,0 @@
|
|||||||
{% extends "base.html" %}
|
|
||||||
{% load render_bundle from webpack_loader %}
|
|
||||||
{% load static %}
|
|
||||||
{% load i18n %}
|
|
||||||
{% load l10n %}
|
|
||||||
|
|
||||||
{% block title %}{% trans 'Supermarket' %}{% endblock %}
|
|
||||||
|
|
||||||
{% block extra_head %}
|
|
||||||
<link rel="stylesheet" href="{% static 'css/vue-multiselect-bs4.min.css' %}">
|
|
||||||
{% endblock %}
|
|
||||||
|
|
||||||
{% block content %}
|
|
||||||
|
|
||||||
<div id="app" >
|
|
||||||
<supermarket-view></supermarket-view>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
|
|
||||||
{% endblock %}
|
|
||||||
|
|
||||||
|
|
||||||
{% block script %}
|
|
||||||
{% if debug %}
|
|
||||||
<script src="{% url 'js_reverse' %}"></script>
|
|
||||||
{% else %}
|
|
||||||
<script src="{% static 'django_js_reverse/reverse.js' %}"></script>
|
|
||||||
{% endif %}
|
|
||||||
|
|
||||||
<script type="application/javascript">
|
|
||||||
|
|
||||||
window.CUSTOM_LOCALE = '{{ request.LANGUAGE_CODE }}'
|
|
||||||
</script>
|
|
||||||
|
|
||||||
{% render_bundle 'supermarket_view' %}
|
|
||||||
{% endblock %}
|
|
||||||
@@ -1,135 +0,0 @@
|
|||||||
import json
|
|
||||||
|
|
||||||
import pytest
|
|
||||||
from django.contrib import auth
|
|
||||||
from django.urls import reverse
|
|
||||||
from django_scopes import scopes_disabled
|
|
||||||
|
|
||||||
from cookbook.models import ShoppingList
|
|
||||||
|
|
||||||
LIST_URL = 'api:shoppinglist-list'
|
|
||||||
DETAIL_URL = 'api:shoppinglist-detail'
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture()
|
|
||||||
def obj_1(space_1, u1_s1):
|
|
||||||
return ShoppingList.objects.create(created_by=auth.get_user(u1_s1), space=space_1, )
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
|
||||||
def obj_2(space_1, u1_s1):
|
|
||||||
return ShoppingList.objects.create(created_by=auth.get_user(u1_s1), space=space_1, )
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize("arg", [
|
|
||||||
['a_u', 403],
|
|
||||||
['g1_s1', 200],
|
|
||||||
['u1_s1', 200],
|
|
||||||
['a1_s1', 200],
|
|
||||||
])
|
|
||||||
def test_list_permission(arg, request):
|
|
||||||
c = request.getfixturevalue(arg[0])
|
|
||||||
assert c.get(reverse(LIST_URL)).status_code == arg[1]
|
|
||||||
|
|
||||||
|
|
||||||
def test_list_space(obj_1, obj_2, u1_s1, u1_s2, space_2):
|
|
||||||
assert len(json.loads(u1_s1.get(reverse(LIST_URL)).content)) == 2
|
|
||||||
assert len(json.loads(u1_s2.get(reverse(LIST_URL)).content)) == 0
|
|
||||||
|
|
||||||
obj_1.space = space_2
|
|
||||||
obj_1.save()
|
|
||||||
|
|
||||||
assert len(json.loads(u1_s1.get(reverse(LIST_URL)).content)) == 1
|
|
||||||
assert len(json.loads(u1_s2.get(reverse(LIST_URL)).content)) == 0
|
|
||||||
|
|
||||||
|
|
||||||
def test_share(obj_1, u1_s1, u2_s1, u1_s2):
|
|
||||||
assert u1_s1.get(reverse(DETAIL_URL, args={obj_1.id})).status_code == 200
|
|
||||||
assert u2_s1.get(reverse(DETAIL_URL, args={obj_1.id})).status_code == 404
|
|
||||||
assert u1_s2.get(reverse(DETAIL_URL, args={obj_1.id})).status_code == 404
|
|
||||||
|
|
||||||
obj_1.shared.add(auth.get_user(u2_s1))
|
|
||||||
obj_1.shared.add(auth.get_user(u1_s2))
|
|
||||||
|
|
||||||
assert u1_s1.get(reverse(DETAIL_URL, args={obj_1.id})).status_code == 200
|
|
||||||
assert u2_s1.get(reverse(DETAIL_URL, args={obj_1.id})).status_code == 200
|
|
||||||
assert u1_s2.get(reverse(DETAIL_URL, args={obj_1.id})).status_code == 404
|
|
||||||
|
|
||||||
|
|
||||||
def test_new_share(request, obj_1, u1_s1, u2_s1, u1_s2):
|
|
||||||
assert u1_s1.get(reverse(DETAIL_URL, args={obj_1.id})).status_code == 200
|
|
||||||
assert u2_s1.get(reverse(DETAIL_URL, args={obj_1.id})).status_code == 404
|
|
||||||
assert u1_s2.get(reverse(DETAIL_URL, args={obj_1.id})).status_code == 404
|
|
||||||
|
|
||||||
with scopes_disabled():
|
|
||||||
user = auth.get_user(u1_s1)
|
|
||||||
user.userpreference.shopping_share.add(auth.get_user(u2_s1))
|
|
||||||
user.userpreference.save()
|
|
||||||
|
|
||||||
assert u1_s1.get(reverse(DETAIL_URL, args={obj_1.id})).status_code == 200
|
|
||||||
assert u2_s1.get(reverse(DETAIL_URL, args={obj_1.id})).status_code == 200
|
|
||||||
assert u1_s2.get(reverse(DETAIL_URL, args={obj_1.id})).status_code == 404
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize("arg", [
|
|
||||||
['a_u', 403],
|
|
||||||
['g1_s1', 404],
|
|
||||||
['u1_s1', 200],
|
|
||||||
['a1_s1', 404],
|
|
||||||
['g1_s2', 404],
|
|
||||||
['u1_s2', 404],
|
|
||||||
['a1_s2', 404],
|
|
||||||
])
|
|
||||||
def test_update(arg, request, obj_1):
|
|
||||||
c = request.getfixturevalue(arg[0])
|
|
||||||
r = c.patch(
|
|
||||||
reverse(
|
|
||||||
DETAIL_URL,
|
|
||||||
args={obj_1.id}
|
|
||||||
),
|
|
||||||
{'note': 'new'},
|
|
||||||
content_type='application/json'
|
|
||||||
)
|
|
||||||
assert r.status_code == arg[1]
|
|
||||||
if r.status_code == 200:
|
|
||||||
response = json.loads(r.content)
|
|
||||||
assert response['note'] == 'new'
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize("arg", [
|
|
||||||
['a_u', 403],
|
|
||||||
['g1_s1', 201],
|
|
||||||
['u1_s1', 201],
|
|
||||||
['a1_s1', 201],
|
|
||||||
])
|
|
||||||
def test_add(arg, request):
|
|
||||||
c = request.getfixturevalue(arg[0])
|
|
||||||
r = c.post(
|
|
||||||
reverse(LIST_URL),
|
|
||||||
{'note': 'test', 'recipes': [], 'shared': [], 'entries': [], 'supermarket': None},
|
|
||||||
content_type='application/json'
|
|
||||||
)
|
|
||||||
response = json.loads(r.content)
|
|
||||||
print(r.content)
|
|
||||||
assert r.status_code == arg[1]
|
|
||||||
if r.status_code == 201:
|
|
||||||
assert response['note'] == 'test'
|
|
||||||
|
|
||||||
|
|
||||||
def test_delete(u1_s1, u1_s2, obj_1):
|
|
||||||
r = u1_s2.delete(
|
|
||||||
reverse(
|
|
||||||
DETAIL_URL,
|
|
||||||
args={obj_1.id}
|
|
||||||
)
|
|
||||||
)
|
|
||||||
assert r.status_code == 404
|
|
||||||
|
|
||||||
r = u1_s1.delete(
|
|
||||||
reverse(
|
|
||||||
DETAIL_URL,
|
|
||||||
args={obj_1.id}
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
assert r.status_code == 204
|
|
||||||
@@ -1,148 +0,0 @@
|
|||||||
import json
|
|
||||||
|
|
||||||
import pytest
|
|
||||||
from django.contrib import auth
|
|
||||||
from django.urls import reverse
|
|
||||||
from django_scopes import scopes_disabled
|
|
||||||
|
|
||||||
from cookbook.models import Food, ShoppingList, ShoppingListEntry
|
|
||||||
|
|
||||||
LIST_URL = 'api:shoppinglistentry-list'
|
|
||||||
DETAIL_URL = 'api:shoppinglistentry-detail'
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture()
|
|
||||||
def obj_1(space_1, u1_s1):
|
|
||||||
e = ShoppingListEntry.objects.create(created_by=auth.get_user(u1_s1),
|
|
||||||
food=Food.objects.get_or_create(name='test 1', space=space_1)[0],
|
|
||||||
space=space_1)
|
|
||||||
s = ShoppingList.objects.create(created_by=auth.get_user(u1_s1), space=space_1, )
|
|
||||||
s.entries.add(e)
|
|
||||||
return e
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
|
||||||
def obj_2(space_1, u1_s1):
|
|
||||||
e = ShoppingListEntry.objects.create(created_by=auth.get_user(u1_s1),
|
|
||||||
food=Food.objects.get_or_create(name='test 2', space=space_1)[0],
|
|
||||||
space=space_1)
|
|
||||||
s = ShoppingList.objects.create(created_by=auth.get_user(u1_s1), space=space_1, )
|
|
||||||
s.entries.add(e)
|
|
||||||
return e
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize("arg", [
|
|
||||||
['a_u', 403],
|
|
||||||
['g1_s1', 200],
|
|
||||||
['u1_s1', 200],
|
|
||||||
['a1_s1', 200],
|
|
||||||
])
|
|
||||||
def test_list_permission(arg, request):
|
|
||||||
c = request.getfixturevalue(arg[0])
|
|
||||||
assert c.get(reverse(LIST_URL)).status_code == arg[1]
|
|
||||||
|
|
||||||
|
|
||||||
def test_list_space(obj_1, obj_2, u1_s1, u1_s2, space_2):
|
|
||||||
assert len(json.loads(u1_s1.get(reverse(LIST_URL)).content)) == 2
|
|
||||||
assert len(json.loads(u1_s2.get(reverse(LIST_URL)).content)) == 0
|
|
||||||
|
|
||||||
with scopes_disabled():
|
|
||||||
s = ShoppingList.objects.first()
|
|
||||||
e = ShoppingListEntry.objects.first()
|
|
||||||
s.space = space_2
|
|
||||||
e.space = space_2
|
|
||||||
s.save()
|
|
||||||
e.save()
|
|
||||||
|
|
||||||
assert len(json.loads(u1_s1.get(reverse(LIST_URL)).content)) == 1
|
|
||||||
assert len(json.loads(u1_s2.get(reverse(LIST_URL)).content)) == 0
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize("arg", [
|
|
||||||
['a_u', 403],
|
|
||||||
['g1_s1', 404],
|
|
||||||
['u1_s1', 200],
|
|
||||||
['a1_s1', 404],
|
|
||||||
['g1_s2', 404],
|
|
||||||
['u1_s2', 404],
|
|
||||||
['a1_s2', 404],
|
|
||||||
])
|
|
||||||
def test_update(arg, request, obj_1):
|
|
||||||
c = request.getfixturevalue(arg[0])
|
|
||||||
r = c.patch(
|
|
||||||
reverse(
|
|
||||||
DETAIL_URL,
|
|
||||||
args={obj_1.id}
|
|
||||||
),
|
|
||||||
{'amount': 2},
|
|
||||||
content_type='application/json'
|
|
||||||
)
|
|
||||||
assert r.status_code == arg[1]
|
|
||||||
if r.status_code == 200:
|
|
||||||
response = json.loads(r.content)
|
|
||||||
assert response['amount'] == 2
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize("arg", [
|
|
||||||
['a_u', 403],
|
|
||||||
['g1_s1', 403],
|
|
||||||
['u1_s1', 200],
|
|
||||||
['a1_s1', 200],
|
|
||||||
['g1_s2', 403],
|
|
||||||
['u1_s2', 200],
|
|
||||||
['a1_s2', 200],
|
|
||||||
])
|
|
||||||
def test_bulk_update(arg, request, obj_1, obj_2):
|
|
||||||
c = request.getfixturevalue(arg[0])
|
|
||||||
r = c.post(
|
|
||||||
reverse(LIST_URL, ) + 'bulk/',
|
|
||||||
{'ids': [obj_1.id, obj_2.id], 'checked': True},
|
|
||||||
content_type='application/json'
|
|
||||||
)
|
|
||||||
assert r.status_code == arg[1]
|
|
||||||
assert r
|
|
||||||
if r.status_code == 200:
|
|
||||||
obj_1.refresh_from_db()
|
|
||||||
assert obj_1.checked == (arg[0] == 'u1_s1')
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize("arg", [
|
|
||||||
['a_u', 403],
|
|
||||||
['g1_s1', 201],
|
|
||||||
['u1_s1', 201],
|
|
||||||
['a1_s1', 201],
|
|
||||||
])
|
|
||||||
def test_add(arg, request, obj_1):
|
|
||||||
c = request.getfixturevalue(arg[0])
|
|
||||||
r = c.post(
|
|
||||||
reverse(LIST_URL),
|
|
||||||
{'food': {
|
|
||||||
'id': obj_1.food.__dict__['id'],
|
|
||||||
'name': obj_1.food.__dict__['name'],
|
|
||||||
}, 'amount': 1},
|
|
||||||
content_type='application/json'
|
|
||||||
)
|
|
||||||
response = json.loads(r.content)
|
|
||||||
print(r.content)
|
|
||||||
assert r.status_code == arg[1]
|
|
||||||
if r.status_code == 201:
|
|
||||||
assert response['food']['id'] == obj_1.food.pk
|
|
||||||
|
|
||||||
|
|
||||||
def test_delete(u1_s1, u1_s2, obj_1):
|
|
||||||
r = u1_s2.delete(
|
|
||||||
reverse(
|
|
||||||
DETAIL_URL,
|
|
||||||
args={obj_1.id}
|
|
||||||
)
|
|
||||||
)
|
|
||||||
assert r.status_code == 404
|
|
||||||
|
|
||||||
r = u1_s1.delete(
|
|
||||||
reverse(
|
|
||||||
DETAIL_URL,
|
|
||||||
args={obj_1.id}
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
assert r.status_code == 204
|
|
||||||
@@ -214,7 +214,7 @@ def test_completed(sle, u1_s1):
|
|||||||
|
|
||||||
def test_recent(sle, u1_s1):
|
def test_recent(sle, u1_s1):
|
||||||
user = auth.get_user(u1_s1)
|
user = auth.get_user(u1_s1)
|
||||||
user.userpreference.shopping_recent_days = 7 # hardcoded API limit 14 days
|
user.userpreference.shopping_recent_days = 7 # hardcoded API limit 14 days
|
||||||
user.userpreference.save()
|
user.userpreference.save()
|
||||||
|
|
||||||
today_start = timezone.now().replace(hour=0, minute=0, second=0)
|
today_start = timezone.now().replace(hour=0, minute=0, second=0)
|
||||||
|
|||||||
@@ -3,9 +3,8 @@ import json
|
|||||||
import pytest
|
import pytest
|
||||||
from django.contrib import auth
|
from django.contrib import auth
|
||||||
from django.urls import reverse
|
from django.urls import reverse
|
||||||
from django_scopes import scopes_disabled
|
|
||||||
|
|
||||||
from cookbook.models import ShoppingList, ShoppingListRecipe
|
from cookbook.models import ShoppingListEntry, ShoppingListRecipe
|
||||||
|
|
||||||
LIST_URL = 'api:shoppinglistrecipe-list'
|
LIST_URL = 'api:shoppinglistrecipe-list'
|
||||||
DETAIL_URL = 'api:shoppinglistrecipe-detail'
|
DETAIL_URL = 'api:shoppinglistrecipe-detail'
|
||||||
@@ -14,81 +13,31 @@ DETAIL_URL = 'api:shoppinglistrecipe-detail'
|
|||||||
@pytest.fixture()
|
@pytest.fixture()
|
||||||
def obj_1(space_1, u1_s1, recipe_1_s1):
|
def obj_1(space_1, u1_s1, recipe_1_s1):
|
||||||
r = ShoppingListRecipe.objects.create(recipe=recipe_1_s1, servings=1)
|
r = ShoppingListRecipe.objects.create(recipe=recipe_1_s1, servings=1)
|
||||||
s = ShoppingList.objects.create(created_by=auth.get_user(u1_s1), space=space_1, )
|
for ing in r.recipe.steps.first().ingredients.all():
|
||||||
s.recipes.add(r)
|
ShoppingListEntry.objects.create(list_recipe=r, ingredient=ing, food=ing.food, unit=ing.unit, amount=ing.amount, created_by=auth.get_user(u1_s1), space=space_1)
|
||||||
return r
|
return r
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
@pytest.mark.parametrize("arg", [['a_u', 403], ['g1_s1', 200], ['u1_s1', 200], ['a1_s1', 200], ])
|
||||||
def obj_2(space_1, u1_s1, recipe_1_s1):
|
|
||||||
r = ShoppingListRecipe.objects.create(recipe=recipe_1_s1, servings=1)
|
|
||||||
s = ShoppingList.objects.create(created_by=auth.get_user(u1_s1), space=space_1, )
|
|
||||||
s.recipes.add(r)
|
|
||||||
return r
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize("arg", [
|
|
||||||
['a_u', 403],
|
|
||||||
['g1_s1', 200],
|
|
||||||
['u1_s1', 200],
|
|
||||||
['a1_s1', 200],
|
|
||||||
])
|
|
||||||
def test_list_permission(arg, request):
|
def test_list_permission(arg, request):
|
||||||
c = request.getfixturevalue(arg[0])
|
c = request.getfixturevalue(arg[0])
|
||||||
assert c.get(reverse(LIST_URL)).status_code == arg[1]
|
assert c.get(reverse(LIST_URL)).status_code == arg[1]
|
||||||
|
|
||||||
|
|
||||||
def test_list_space(obj_1, obj_2, u1_s1, u1_s2, space_2):
|
@pytest.mark.parametrize("arg", [['a_u', 403], ['g1_s1', 404], ['u1_s1', 200], ['a1_s1', 404], ['g1_s2', 404], ['u1_s2', 404], ['a1_s2', 404], ])
|
||||||
assert len(json.loads(u1_s1.get(reverse(LIST_URL)).content)) == 2
|
|
||||||
assert len(json.loads(u1_s2.get(reverse(LIST_URL)).content)) == 0
|
|
||||||
|
|
||||||
with scopes_disabled():
|
|
||||||
s = ShoppingList.objects.first()
|
|
||||||
s.space = space_2
|
|
||||||
s.save()
|
|
||||||
|
|
||||||
assert len(json.loads(u1_s1.get(reverse(LIST_URL)).content)) == 1
|
|
||||||
assert len(json.loads(u1_s2.get(reverse(LIST_URL)).content)) == 0
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize("arg", [
|
|
||||||
['a_u', 403],
|
|
||||||
['g1_s1', 404],
|
|
||||||
['u1_s1', 200],
|
|
||||||
['a1_s1', 404],
|
|
||||||
['g1_s2', 404],
|
|
||||||
['u1_s2', 404],
|
|
||||||
['a1_s2', 404],
|
|
||||||
])
|
|
||||||
def test_update(arg, request, obj_1):
|
def test_update(arg, request, obj_1):
|
||||||
c = request.getfixturevalue(arg[0])
|
c = request.getfixturevalue(arg[0])
|
||||||
r = c.patch(
|
r = c.patch(reverse(DETAIL_URL, args={obj_1.id}), {'servings': 2}, content_type='application/json')
|
||||||
reverse(
|
|
||||||
DETAIL_URL,
|
|
||||||
args={obj_1.id}
|
|
||||||
),
|
|
||||||
{'servings': 2},
|
|
||||||
content_type='application/json'
|
|
||||||
)
|
|
||||||
assert r.status_code == arg[1]
|
assert r.status_code == arg[1]
|
||||||
if r.status_code == 200:
|
if r.status_code == 200:
|
||||||
response = json.loads(r.content)
|
response = json.loads(r.content)
|
||||||
assert response['servings'] == 2
|
assert response['servings'] == 2
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize("arg", [
|
@pytest.mark.parametrize("arg", [['a_u', 403], ['g1_s1', 201], ['u1_s1', 201], ['a1_s1', 201], ])
|
||||||
['a_u', 403],
|
|
||||||
['g1_s1', 201],
|
|
||||||
['u1_s1', 201],
|
|
||||||
['a1_s1', 201],
|
|
||||||
])
|
|
||||||
def test_add(arg, request, obj_1, recipe_1_s1):
|
def test_add(arg, request, obj_1, recipe_1_s1):
|
||||||
c = request.getfixturevalue(arg[0])
|
c = request.getfixturevalue(arg[0])
|
||||||
r = c.post(
|
r = c.post(reverse(LIST_URL), {'recipe': recipe_1_s1.pk, 'servings': 1}, content_type='application/json')
|
||||||
reverse(LIST_URL),
|
|
||||||
{'recipe': recipe_1_s1.pk, 'servings': 1},
|
|
||||||
content_type='application/json'
|
|
||||||
)
|
|
||||||
response = json.loads(r.content)
|
response = json.loads(r.content)
|
||||||
print(r.content)
|
print(r.content)
|
||||||
assert r.status_code == arg[1]
|
assert r.status_code == arg[1]
|
||||||
@@ -97,19 +46,9 @@ def test_add(arg, request, obj_1, recipe_1_s1):
|
|||||||
|
|
||||||
|
|
||||||
def test_delete(u1_s1, u1_s2, obj_1):
|
def test_delete(u1_s1, u1_s2, obj_1):
|
||||||
r = u1_s2.delete(
|
r = u1_s2.delete(reverse(DETAIL_URL, args={obj_1.id}))
|
||||||
reverse(
|
|
||||||
DETAIL_URL,
|
|
||||||
args={obj_1.id}
|
|
||||||
)
|
|
||||||
)
|
|
||||||
assert r.status_code == 404
|
assert r.status_code == 404
|
||||||
|
|
||||||
r = u1_s1.delete(
|
r = u1_s1.delete(reverse(DETAIL_URL, args={obj_1.id}))
|
||||||
reverse(
|
|
||||||
DETAIL_URL,
|
|
||||||
args={obj_1.id}
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
assert r.status_code == 204
|
assert r.status_code == 204
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ from django.contrib import auth
|
|||||||
from django.urls import reverse
|
from django.urls import reverse
|
||||||
from django_scopes import scopes_disabled
|
from django_scopes import scopes_disabled
|
||||||
|
|
||||||
from cookbook.models import Food, Ingredient, ShoppingList, ShoppingListEntry, Unit
|
from cookbook.models import Food, Ingredient, ShoppingListEntry, Unit
|
||||||
|
|
||||||
LIST_URL = 'api:unit-list'
|
LIST_URL = 'api:unit-list'
|
||||||
DETAIL_URL = 'api:unit-detail'
|
DETAIL_URL = 'api:unit-detail'
|
||||||
@@ -50,8 +50,6 @@ def ing_3_s2(obj_3, space_2, u2_s2):
|
|||||||
@pytest.fixture()
|
@pytest.fixture()
|
||||||
def sle_1_s1(obj_1, u1_s1, space_1):
|
def sle_1_s1(obj_1, u1_s1, space_1):
|
||||||
e = ShoppingListEntry.objects.create(unit=obj_1, food=random_food(space_1, u1_s1), created_by=auth.get_user(u1_s1), space=space_1,)
|
e = ShoppingListEntry.objects.create(unit=obj_1, food=random_food(space_1, u1_s1), created_by=auth.get_user(u1_s1), space=space_1,)
|
||||||
s = ShoppingList.objects.create(created_by=auth.get_user(u1_s1), space=space_1, )
|
|
||||||
s.entries.add(e)
|
|
||||||
return e
|
return e
|
||||||
|
|
||||||
|
|
||||||
@@ -63,8 +61,6 @@ def sle_2_s1(obj_2, u1_s1, space_1):
|
|||||||
@pytest.fixture()
|
@pytest.fixture()
|
||||||
def sle_3_s2(obj_3, u2_s2, space_2):
|
def sle_3_s2(obj_3, u2_s2, space_2):
|
||||||
e = ShoppingListEntry.objects.create(unit=obj_3, food=random_food(space_2, u2_s2), created_by=auth.get_user(u2_s2), space=space_2)
|
e = ShoppingListEntry.objects.create(unit=obj_3, food=random_food(space_2, u2_s2), created_by=auth.get_user(u2_s2), space=space_2)
|
||||||
s = ShoppingList.objects.create(created_by=auth.get_user(u2_s2), space=space_2)
|
|
||||||
s.entries.add(e)
|
|
||||||
return e
|
return e
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -5,20 +5,22 @@ from django.views.generic import TemplateView
|
|||||||
from rest_framework import permissions, routers
|
from rest_framework import permissions, routers
|
||||||
from rest_framework.schemas import get_schema_view
|
from rest_framework.schemas import get_schema_view
|
||||||
|
|
||||||
from cookbook.helper import dal
|
|
||||||
from cookbook.version_info import TANDOOR_VERSION
|
from cookbook.version_info import TANDOOR_VERSION
|
||||||
from recipes.settings import DEBUG, PLUGINS
|
from recipes.settings import DEBUG, PLUGINS
|
||||||
|
|
||||||
|
|
||||||
from .models import (Automation, Comment, CustomFilter, Food, InviteLink, Keyword, PropertyType,
|
from .models import (Automation, Comment, CustomFilter, Food, InviteLink, Keyword, PropertyType,
|
||||||
Recipe, RecipeBook, RecipeBookEntry, RecipeImport, ShoppingList, Space, Step,
|
Recipe, RecipeBook, RecipeBookEntry, RecipeImport, Space, Step,
|
||||||
Storage, Supermarket, SupermarketCategory, Sync, SyncLog, Unit, UnitConversion,
|
Storage, Supermarket, SupermarketCategory, Sync, SyncLog, Unit, UnitConversion,
|
||||||
UserFile, UserSpace, get_model_name, ConnectorConfig)
|
UserFile, UserSpace, get_model_name, ConnectorConfig)
|
||||||
|
|
||||||
from .views import api, data, delete, edit, import_export, lists, new, telegram, views
|
from .views import api, data, delete, edit, import_export, lists, new, telegram, views
|
||||||
from .views.api import CustomAuthToken, ImportOpenData
|
from .views.api import CustomAuthToken, ImportOpenData
|
||||||
|
|
||||||
|
|
||||||
# extend DRF default router class to allow including additional routers
|
# extend DRF default router class to allow including additional routers
|
||||||
class DefaultRouter(routers.DefaultRouter):
|
class DefaultRouter(routers.DefaultRouter):
|
||||||
|
|
||||||
def extend(self, r):
|
def extend(self, r):
|
||||||
self.registry.extend(r.registry)
|
self.registry.extend(r.registry)
|
||||||
|
|
||||||
@@ -45,7 +47,6 @@ router.register(r'recipe-book-entry', api.RecipeBookEntryViewSet)
|
|||||||
router.register(r'unit-conversion', api.UnitConversionViewSet)
|
router.register(r'unit-conversion', api.UnitConversionViewSet)
|
||||||
router.register(r'food-property-type', api.PropertyTypeViewSet) # TODO rename + regenerate
|
router.register(r'food-property-type', api.PropertyTypeViewSet) # TODO rename + regenerate
|
||||||
router.register(r'food-property', api.PropertyViewSet)
|
router.register(r'food-property', api.PropertyViewSet)
|
||||||
router.register(r'shopping-list', api.ShoppingListViewSet)
|
|
||||||
router.register(r'shopping-list-entry', api.ShoppingListEntryViewSet)
|
router.register(r'shopping-list-entry', api.ShoppingListEntryViewSet)
|
||||||
router.register(r'shopping-list-recipe', api.ShoppingListRecipeViewSet)
|
router.register(r'shopping-list-recipe', api.ShoppingListRecipeViewSet)
|
||||||
router.register(r'space', api.SpaceViewSet)
|
router.register(r'space', api.SpaceViewSet)
|
||||||
@@ -80,7 +81,6 @@ urlpatterns = [
|
|||||||
path('space-overview', views.space_overview, name='view_space_overview'),
|
path('space-overview', views.space_overview, name='view_space_overview'),
|
||||||
path('space-manage/<int:space_id>', views.space_manage, name='view_space_manage'),
|
path('space-manage/<int:space_id>', views.space_manage, name='view_space_manage'),
|
||||||
path('switch-space/<int:space_id>', views.switch_space, name='view_switch_space'),
|
path('switch-space/<int:space_id>', views.switch_space, name='view_switch_space'),
|
||||||
path('profile/<int:user_id>', views.view_profile, name='view_profile'),
|
|
||||||
path('no-perm', views.no_perm, name='view_no_perm'),
|
path('no-perm', views.no_perm, name='view_no_perm'),
|
||||||
path('invite/<slug:token>', views.invite_link, name='view_invite'),
|
path('invite/<slug:token>', views.invite_link, name='view_invite'),
|
||||||
path('system/', views.system, name='view_system'),
|
path('system/', views.system, name='view_system'),
|
||||||
@@ -89,34 +89,27 @@ urlpatterns = [
|
|||||||
path('plan/', views.meal_plan, name='view_plan'),
|
path('plan/', views.meal_plan, name='view_plan'),
|
||||||
path('shopping/', lists.shopping_list, name='view_shopping'),
|
path('shopping/', lists.shopping_list, name='view_shopping'),
|
||||||
path('settings/', views.user_settings, name='view_settings'),
|
path('settings/', views.user_settings, name='view_settings'),
|
||||||
path('settings-shopping/', views.shopping_settings, name='view_shopping_settings'),
|
path('settings-shopping/', views.shopping_settings, name='view_shopping_settings'), # TODO rename to search settings
|
||||||
path('history/', views.history, name='view_history'),
|
path('history/', views.history, name='view_history'),
|
||||||
path('supermarket/', views.supermarket, name='view_supermarket'),
|
|
||||||
path('ingredient-editor/', views.ingredient_editor, name='view_ingredient_editor'),
|
path('ingredient-editor/', views.ingredient_editor, name='view_ingredient_editor'),
|
||||||
path('property-editor/<int:pk>', views.property_editor, name='view_property_editor'),
|
path('property-editor/<int:pk>', views.property_editor, name='view_property_editor'),
|
||||||
path('abuse/<slug:token>', views.report_share_abuse, name='view_report_share_abuse'),
|
path('abuse/<slug:token>', views.report_share_abuse, name='view_report_share_abuse'),
|
||||||
|
|
||||||
path('api/import/', api.import_files, name='view_import'),
|
path('api/import/', api.import_files, name='view_import'),
|
||||||
path('import-response/<int:pk>/', import_export.import_response, name='view_import_response'),
|
path('import-response/<int:pk>/', import_export.import_response, name='view_import_response'),
|
||||||
path('export/', import_export.export_recipe, name='view_export'),
|
path('export/', import_export.export_recipe, name='view_export'),
|
||||||
path('export-response/<int:pk>/', import_export.export_response, name='view_export_response'),
|
path('export-response/<int:pk>/', import_export.export_response, name='view_export_response'),
|
||||||
path('export-file/<int:pk>/', import_export.export_file, name='view_export_file'),
|
path('export-file/<int:pk>/', import_export.export_file, name='view_export_file'),
|
||||||
|
|
||||||
path('view/recipe/<int:pk>', views.recipe_view, name='view_recipe'),
|
path('view/recipe/<int:pk>', views.recipe_view, name='view_recipe'),
|
||||||
path('view/recipe/<int:pk>/<slug:share>', views.recipe_view, name='view_recipe'),
|
path('view/recipe/<int:pk>/<slug:share>', views.recipe_view, name='view_recipe'),
|
||||||
|
|
||||||
path('new/recipe-import/<int:import_id>/', new.create_new_external_recipe, name='new_recipe_import'),
|
path('new/recipe-import/<int:import_id>/', new.create_new_external_recipe, name='new_recipe_import'),
|
||||||
path('new/share-link/<int:pk>/', new.share_link, name='new_share_link'),
|
path('new/share-link/<int:pk>/', new.share_link, name='new_share_link'),
|
||||||
|
|
||||||
path('edit/recipe/<int:pk>/', edit.switch_recipe, name='edit_recipe'),
|
path('edit/recipe/<int:pk>/', edit.switch_recipe, name='edit_recipe'),
|
||||||
|
|
||||||
# for internal use only
|
# for internal use only
|
||||||
path('edit/recipe/internal/<int:pk>/', edit.internal_recipe_update, name='edit_internal_recipe'),
|
path('edit/recipe/internal/<int:pk>/', edit.internal_recipe_update, name='edit_internal_recipe'),
|
||||||
path('edit/recipe/external/<int:pk>/', edit.ExternalRecipeUpdate.as_view(), name='edit_external_recipe'),
|
path('edit/recipe/external/<int:pk>/', edit.ExternalRecipeUpdate.as_view(), name='edit_external_recipe'),
|
||||||
path('edit/recipe/convert/<int:pk>/', edit.convert_recipe, name='edit_convert_recipe'),
|
path('edit/recipe/convert/<int:pk>/', edit.convert_recipe, name='edit_convert_recipe'),
|
||||||
|
|
||||||
path('edit/storage/<int:pk>/', edit.edit_storage, name='edit_storage'),
|
path('edit/storage/<int:pk>/', edit.edit_storage, name='edit_storage'),
|
||||||
|
|
||||||
path('delete/recipe-source/<int:pk>/', delete.delete_recipe_source, name='delete_recipe_source'),
|
path('delete/recipe-source/<int:pk>/', delete.delete_recipe_source, name='delete_recipe_source'),
|
||||||
|
|
||||||
# TODO move to generic "new" view
|
# TODO move to generic "new" view
|
||||||
@@ -125,7 +118,6 @@ urlpatterns = [
|
|||||||
path('data/batch/import', data.batch_import, name='data_batch_import'),
|
path('data/batch/import', data.batch_import, name='data_batch_import'),
|
||||||
path('data/sync/wait', data.sync_wait, name='data_sync_wait'),
|
path('data/sync/wait', data.sync_wait, name='data_sync_wait'),
|
||||||
path('data/import/url', data.import_url, name='data_import_url'),
|
path('data/import/url', data.import_url, name='data_import_url'),
|
||||||
|
|
||||||
path('api/get_external_file_link/<int:recipe_id>/', api.get_external_file_link, name='api_get_external_file_link'),
|
path('api/get_external_file_link/<int:recipe_id>/', api.get_external_file_link, name='api_get_external_file_link'),
|
||||||
path('api/get_recipe_file/<int:recipe_id>/', api.get_recipe_file, name='api_get_recipe_file'),
|
path('api/get_recipe_file/<int:recipe_id>/', api.get_recipe_file, name='api_get_recipe_file'),
|
||||||
path('api/sync_all/', api.sync_all, name='api_sync'),
|
path('api/sync_all/', api.sync_all, name='api_sync'),
|
||||||
@@ -138,75 +130,45 @@ urlpatterns = [
|
|||||||
path('api/reset-food-inheritance/', api.reset_food_inheritance, name='api_reset_food_inheritance'),
|
path('api/reset-food-inheritance/', api.reset_food_inheritance, name='api_reset_food_inheritance'),
|
||||||
path('api/switch-active-space/<int:space_id>/', api.switch_active_space, name='api_switch_active_space'),
|
path('api/switch-active-space/<int:space_id>/', api.switch_active_space, name='api_switch_active_space'),
|
||||||
path('api/download-file/<int:file_id>/', api.download_file, name='api_download_file'),
|
path('api/download-file/<int:file_id>/', api.download_file, name='api_download_file'),
|
||||||
|
|
||||||
path('dal/keyword/', dal.KeywordAutocomplete.as_view(), name='dal_keyword'),
|
|
||||||
# TODO is this deprecated? not yet, some old forms remain, could likely be changed to generic API endpoints
|
|
||||||
path('dal/food/', dal.IngredientsAutocomplete.as_view(), name='dal_food'), # TODO is this deprecated?
|
|
||||||
path('dal/unit/', dal.UnitAutocomplete.as_view(), name='dal_unit'), # TODO is this deprecated?
|
|
||||||
|
|
||||||
path('telegram/setup/<int:pk>', telegram.setup_bot, name='telegram_setup'),
|
path('telegram/setup/<int:pk>', telegram.setup_bot, name='telegram_setup'),
|
||||||
path('telegram/remove/<int:pk>', telegram.remove_bot, name='telegram_remove'),
|
path('telegram/remove/<int:pk>', telegram.remove_bot, name='telegram_remove'),
|
||||||
path('telegram/hook/<slug:token>/', telegram.hook, name='telegram_hook'),
|
path('telegram/hook/<slug:token>/', telegram.hook, name='telegram_hook'),
|
||||||
|
|
||||||
path('docs/markdown/', views.markdown_info, name='docs_markdown'),
|
path('docs/markdown/', views.markdown_info, name='docs_markdown'),
|
||||||
path('docs/search/', views.search_info, name='docs_search'),
|
path('docs/search/', views.search_info, name='docs_search'),
|
||||||
path('docs/api/', views.api_info, name='docs_api'),
|
path('docs/api/', views.api_info, name='docs_api'),
|
||||||
|
path('openapi/', get_schema_view(title="Django Recipes", version=TANDOOR_VERSION, public=True, permission_classes=(permissions.AllowAny, )), name='openapi-schema'),
|
||||||
path('openapi/', get_schema_view(title="Django Recipes", version=TANDOOR_VERSION, public=True,
|
|
||||||
permission_classes=(permissions.AllowAny,)), name='openapi-schema'),
|
|
||||||
|
|
||||||
path('api/', include((router.urls, 'api'))),
|
path('api/', include((router.urls, 'api'))),
|
||||||
path('api-auth/', include('rest_framework.urls', namespace='rest_framework')),
|
path('api-auth/', include('rest_framework.urls', namespace='rest_framework')),
|
||||||
path('api-token-auth/', CustomAuthToken.as_view()),
|
path('api-token-auth/', CustomAuthToken.as_view()),
|
||||||
path('api-import-open-data/', ImportOpenData.as_view(), name='api_import_open_data'),
|
path('api-import-open-data/', ImportOpenData.as_view(), name='api_import_open_data'),
|
||||||
|
|
||||||
path('offline/', views.offline, name='view_offline'),
|
path('offline/', views.offline, name='view_offline'),
|
||||||
|
path('service-worker.js', (TemplateView.as_view(template_name="sw.js", content_type='application/javascript',
|
||||||
path('service-worker.js', (TemplateView.as_view(template_name="sw.js", content_type='application/javascript', )),
|
)), name='service_worker'),
|
||||||
name='service_worker'),
|
|
||||||
path('manifest.json', views.web_manifest, name='web_manifest'),
|
path('manifest.json', views.web_manifest, name='web_manifest'),
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
generic_models = (
|
generic_models = (
|
||||||
Recipe, RecipeImport, Storage, ConnectorConfig, RecipeBook, SyncLog, Sync,
|
Recipe, RecipeImport, Storage, ConnectorConfig, RecipeBook, SyncLog, Sync,
|
||||||
Comment, RecipeBookEntry, ShoppingList, InviteLink, UserSpace, Space
|
Comment, RecipeBookEntry, InviteLink, UserSpace, Space
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
for m in generic_models:
|
for m in generic_models:
|
||||||
py_name = get_model_name(m)
|
py_name = get_model_name(m)
|
||||||
url_name = py_name.replace('_', '-')
|
url_name = py_name.replace('_', '-')
|
||||||
|
|
||||||
if c := locate(f'cookbook.views.new.{m.__name__}Create'):
|
if c := locate(f'cookbook.views.new.{m.__name__}Create'):
|
||||||
urlpatterns.append(
|
urlpatterns.append(path(f'new/{url_name}/', c.as_view(), name=f'new_{py_name}'))
|
||||||
path(
|
|
||||||
f'new/{url_name}/', c.as_view(), name=f'new_{py_name}'
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
if c := locate(f'cookbook.views.edit.{m.__name__}Update'):
|
if c := locate(f'cookbook.views.edit.{m.__name__}Update'):
|
||||||
urlpatterns.append(
|
urlpatterns.append(path(f'edit/{url_name}/<int:pk>/', c.as_view(), name=f'edit_{py_name}'))
|
||||||
path(
|
|
||||||
f'edit/{url_name}/<int:pk>/',
|
|
||||||
c.as_view(),
|
|
||||||
name=f'edit_{py_name}'
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
if c := getattr(lists, py_name, None):
|
if c := getattr(lists, py_name, None):
|
||||||
urlpatterns.append(
|
urlpatterns.append(path(f'list/{url_name}/', c, name=f'list_{py_name}'))
|
||||||
path(
|
|
||||||
f'list/{url_name}/', c, name=f'list_{py_name}'
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
if c := locate(f'cookbook.views.delete.{m.__name__}Delete'):
|
if c := locate(f'cookbook.views.delete.{m.__name__}Delete'):
|
||||||
urlpatterns.append(
|
urlpatterns.append(path(f'delete/{url_name}/<int:pk>/', c.as_view(), name=f'delete_{py_name}'))
|
||||||
path(
|
|
||||||
f'delete/{url_name}/<int:pk>/',
|
|
||||||
c.as_view(),
|
|
||||||
name=f'delete_{py_name}'
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
vue_models = [Food, Keyword, Unit, Supermarket, SupermarketCategory, Automation, UserFile, Step, CustomFilter, UnitConversion, PropertyType]
|
vue_models = [Food, Keyword, Unit, Supermarket, SupermarketCategory, Automation, UserFile, Step, CustomFilter, UnitConversion, PropertyType]
|
||||||
for m in vue_models:
|
for m in vue_models:
|
||||||
@@ -214,11 +176,7 @@ for m in vue_models:
|
|||||||
url_name = py_name.replace('_', '-')
|
url_name = py_name.replace('_', '-')
|
||||||
|
|
||||||
if c := getattr(lists, py_name, None):
|
if c := getattr(lists, py_name, None):
|
||||||
urlpatterns.append(
|
urlpatterns.append(path(f'list/{url_name}/', c, name=f'list_{py_name}'))
|
||||||
path(
|
|
||||||
f'list/{url_name}/', c, name=f'list_{py_name}'
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
if DEBUG:
|
if DEBUG:
|
||||||
urlpatterns.append(path('test/', views.test, name='view_test'))
|
urlpatterns.append(path('test/', views.test, name='view_test'))
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ from zipfile import ZipFile
|
|||||||
|
|
||||||
import requests
|
import requests
|
||||||
import validators
|
import validators
|
||||||
|
from PIL import UnidentifiedImageError
|
||||||
from annoying.decorators import ajax_request
|
from annoying.decorators import ajax_request
|
||||||
from annoying.functions import get_object_or_None
|
from annoying.functions import get_object_or_None
|
||||||
from django.contrib import messages
|
from django.contrib import messages
|
||||||
@@ -30,12 +31,10 @@ from django.http import FileResponse, HttpResponse, JsonResponse
|
|||||||
from django.shortcuts import get_object_or_404, redirect
|
from django.shortcuts import get_object_or_404, redirect
|
||||||
from django.urls import reverse
|
from django.urls import reverse
|
||||||
from django.utils import timezone
|
from django.utils import timezone
|
||||||
from django.utils.timezone import make_aware
|
|
||||||
from django.utils.translation import gettext as _
|
from django.utils.translation import gettext as _
|
||||||
from django_scopes import scopes_disabled
|
from django_scopes import scopes_disabled
|
||||||
from icalendar import Calendar, Event
|
from icalendar import Calendar, Event
|
||||||
from oauth2_provider.models import AccessToken
|
from oauth2_provider.models import AccessToken
|
||||||
from PIL import UnidentifiedImageError
|
|
||||||
from recipe_scrapers import scrape_me
|
from recipe_scrapers import scrape_me
|
||||||
from recipe_scrapers._exceptions import NoSchemaFoundInWildMode
|
from recipe_scrapers._exceptions import NoSchemaFoundInWildMode
|
||||||
from requests.exceptions import MissingSchema
|
from requests.exceptions import MissingSchema
|
||||||
@@ -58,23 +57,18 @@ from cookbook.helper.HelperFunctions import str2bool
|
|||||||
from cookbook.helper.image_processing import handle_image
|
from cookbook.helper.image_processing import handle_image
|
||||||
from cookbook.helper.ingredient_parser import IngredientParser
|
from cookbook.helper.ingredient_parser import IngredientParser
|
||||||
from cookbook.helper.open_data_importer import OpenDataImporter
|
from cookbook.helper.open_data_importer import OpenDataImporter
|
||||||
from cookbook.helper.permission_helper import (CustomIsAdmin, CustomIsOwner, CustomIsOwnerReadOnly,
|
from cookbook.helper.permission_helper import (
|
||||||
CustomIsShared, CustomIsSpaceOwner, CustomIsUser,
|
CustomIsAdmin, CustomIsOwner, CustomIsOwnerReadOnly, CustomIsShared, CustomIsSpaceOwner, CustomIsUser, CustomRecipePermission, CustomTokenHasReadWriteScope,
|
||||||
CustomRecipePermission, CustomTokenHasReadWriteScope,
|
CustomTokenHasScope, CustomUserPermission, IsReadOnlyDRF, above_space_limit, group_required, has_group_permission, is_space_owner, switch_user_active_space,
|
||||||
CustomTokenHasScope, CustomUserPermission,
|
)
|
||||||
IsReadOnlyDRF, above_space_limit, group_required,
|
|
||||||
has_group_permission, is_space_owner,
|
|
||||||
switch_user_active_space)
|
|
||||||
from cookbook.helper.recipe_search import RecipeSearch
|
from cookbook.helper.recipe_search import RecipeSearch
|
||||||
from cookbook.helper.recipe_url_import import (clean_dict, get_from_youtube_scraper,
|
from cookbook.helper.recipe_url_import import clean_dict, get_from_youtube_scraper, get_images_from_soup
|
||||||
get_images_from_soup)
|
|
||||||
from cookbook.helper.scrapers.scrapers import text_scraper
|
from cookbook.helper.scrapers.scrapers import text_scraper
|
||||||
from cookbook.helper.shopping_helper import RecipeShoppingEditor, shopping_helper
|
from cookbook.helper.shopping_helper import RecipeShoppingEditor, shopping_helper
|
||||||
from cookbook.models import (Automation, BookmarkletImport, CookLog, CustomFilter, ExportLog, Food,
|
from cookbook.models import (Automation, BookmarkletImport, CookLog, CustomFilter, ExportLog, Food,
|
||||||
FoodInheritField, FoodProperty, ImportLog, Ingredient, InviteLink,
|
FoodInheritField, FoodProperty, ImportLog, Ingredient, InviteLink,
|
||||||
Keyword, MealPlan, MealType, Property, PropertyType, Recipe,
|
Keyword, MealPlan, MealType, Property, PropertyType, Recipe,
|
||||||
RecipeBook, RecipeBookEntry, ShareLink, ShoppingList,
|
RecipeBook, RecipeBookEntry, ShareLink, ShoppingListEntry, ShoppingListRecipe, Space, Step, Storage,
|
||||||
ShoppingListEntry, ShoppingListRecipe, Space, Step, Storage,
|
|
||||||
Supermarket, SupermarketCategory, SupermarketCategoryRelation, Sync,
|
Supermarket, SupermarketCategory, SupermarketCategoryRelation, Sync,
|
||||||
SyncLog, Unit, UnitConversion, UserFile, UserPreference, UserSpace,
|
SyncLog, Unit, UnitConversion, UserFile, UserPreference, UserSpace,
|
||||||
ViewLog, ConnectorConfig)
|
ViewLog, ConnectorConfig)
|
||||||
@@ -96,9 +90,8 @@ from cookbook.serializer import (AccessTokenSerializer, AutomationSerializer,
|
|||||||
RecipeExportSerializer, RecipeFromSourceSerializer,
|
RecipeExportSerializer, RecipeFromSourceSerializer,
|
||||||
RecipeImageSerializer, RecipeOverviewSerializer, RecipeSerializer,
|
RecipeImageSerializer, RecipeOverviewSerializer, RecipeSerializer,
|
||||||
RecipeShoppingUpdateSerializer, RecipeSimpleSerializer,
|
RecipeShoppingUpdateSerializer, RecipeSimpleSerializer,
|
||||||
ShoppingListAutoSyncSerializer, ShoppingListEntrySerializer,
|
ShoppingListEntrySerializer,
|
||||||
ShoppingListRecipeSerializer, ShoppingListSerializer,
|
ShoppingListRecipeSerializer, SpaceSerializer, StepSerializer, StorageSerializer,
|
||||||
SpaceSerializer, StepSerializer, StorageSerializer,
|
|
||||||
SupermarketCategoryRelationSerializer,
|
SupermarketCategoryRelationSerializer,
|
||||||
SupermarketCategorySerializer, SupermarketSerializer,
|
SupermarketCategorySerializer, SupermarketSerializer,
|
||||||
SyncLogSerializer, SyncSerializer, UnitConversionSerializer,
|
SyncLogSerializer, SyncSerializer, UnitConversionSerializer,
|
||||||
@@ -107,10 +100,11 @@ from cookbook.serializer import (AccessTokenSerializer, AutomationSerializer,
|
|||||||
ShoppingListEntryBulkSerializer, ConnectorConfigConfigSerializer, RecipeFlatSerializer)
|
ShoppingListEntryBulkSerializer, ConnectorConfigConfigSerializer, RecipeFlatSerializer)
|
||||||
from cookbook.views.import_export import get_integration
|
from cookbook.views.import_export import get_integration
|
||||||
from recipes import settings
|
from recipes import settings
|
||||||
from recipes.settings import FDC_API_KEY, DRF_THROTTLE_RECIPE_URL_IMPORT
|
from recipes.settings import DRF_THROTTLE_RECIPE_URL_IMPORT, FDC_API_KEY
|
||||||
|
|
||||||
|
|
||||||
class StandardFilterMixin(ViewSetMixin):
|
class StandardFilterMixin(ViewSetMixin):
|
||||||
|
|
||||||
def get_queryset(self):
|
def get_queryset(self):
|
||||||
queryset = self.queryset
|
queryset = self.queryset
|
||||||
query = self.request.query_params.get('query', None)
|
query = self.request.query_params.get('query', None)
|
||||||
@@ -161,12 +155,13 @@ class ExtendedRecipeMixin():
|
|||||||
queryset = queryset.annotate(recipe_count=Coalesce(Subquery(recipe_count), 0))
|
queryset = queryset.annotate(recipe_count=Coalesce(Subquery(recipe_count), 0))
|
||||||
|
|
||||||
# add a recipe image annotation to the query
|
# add a recipe image annotation to the query
|
||||||
image_subquery = Recipe.objects.filter(**{recipe_filter: OuterRef('id')}, space=space).exclude(
|
image_subquery = Recipe.objects.filter(**{
|
||||||
image__isnull=True).exclude(image__exact='').order_by("?").values('image')[:1]
|
recipe_filter: OuterRef('id')
|
||||||
|
}, space=space).exclude(image__isnull=True).exclude(image__exact='').order_by("?").values('image')[:1]
|
||||||
if tree:
|
if tree:
|
||||||
image_children_subquery = Recipe.objects.filter(
|
image_children_subquery = Recipe.objects.filter(**{
|
||||||
**{f"{recipe_filter}__path__startswith": OuterRef('path')},
|
f"{recipe_filter}__path__startswith": OuterRef('path')
|
||||||
space=space).exclude(image__isnull=True).exclude(image__exact='').order_by("?").values('image')[:1]
|
}, space=space).exclude(image__isnull=True).exclude(image__exact='').order_by("?").values('image')[:1]
|
||||||
else:
|
else:
|
||||||
image_children_subquery = None
|
image_children_subquery = None
|
||||||
if images:
|
if images:
|
||||||
@@ -183,17 +178,17 @@ class FuzzyFilterMixin(ViewSetMixin, ExtendedRecipeMixin):
|
|||||||
self.queryset = self.queryset.filter(space=self.request.space).order_by(Lower('name').asc())
|
self.queryset = self.queryset.filter(space=self.request.space).order_by(Lower('name').asc())
|
||||||
query = self.request.query_params.get('query', None)
|
query = self.request.query_params.get('query', None)
|
||||||
if self.request.user.is_authenticated:
|
if self.request.user.is_authenticated:
|
||||||
fuzzy = self.request.user.searchpreference.lookup or any([self.model.__name__.lower() in x for x in
|
fuzzy = self.request.user.searchpreference.lookup or any(
|
||||||
self.request.user.searchpreference.trigram.values_list(
|
[self.model.__name__.lower() in x for x in self.request.user.searchpreference.trigram.values_list('field', flat=True)])
|
||||||
'field', flat=True)])
|
|
||||||
else:
|
else:
|
||||||
fuzzy = True
|
fuzzy = True
|
||||||
|
|
||||||
if query is not None and query not in ["''", '']:
|
if query is not None and query not in ["''", '']:
|
||||||
if fuzzy and (settings.DATABASES['default']['ENGINE'] == 'django.db.backends.postgresql'):
|
if fuzzy and (settings.DATABASES['default']['ENGINE'] == 'django.db.backends.postgresql'):
|
||||||
if self.request.user.is_authenticated and any(
|
if self.request.user.is_authenticated and any(
|
||||||
[self.model.__name__.lower() in x for x in self.request.user.searchpreference.unaccent.values_list('field', flat=True)]):
|
[self.model.__name__.lower() in x for x in self.request.user.searchpreference.unaccent.values_list('field', flat=True)]
|
||||||
self.queryset = self.queryset.annotate(trigram=TrigramSimilarity('name__unaccent', query))
|
):
|
||||||
|
self.queryset = self.queryset.annotate(trigram=TrigramSimilarity('name__unaccent', query))
|
||||||
else:
|
else:
|
||||||
self.queryset = self.queryset.annotate(trigram=TrigramSimilarity('name', query))
|
self.queryset = self.queryset.annotate(trigram=TrigramSimilarity('name', query))
|
||||||
self.queryset = self.queryset.order_by('-trigram')
|
self.queryset = self.queryset.order_by('-trigram')
|
||||||
@@ -205,10 +200,9 @@ class FuzzyFilterMixin(ViewSetMixin, ExtendedRecipeMixin):
|
|||||||
filter |= Q(name__unaccent__icontains=query)
|
filter |= Q(name__unaccent__icontains=query)
|
||||||
|
|
||||||
self.queryset = (
|
self.queryset = (
|
||||||
self.queryset.annotate(starts=Case(When(name__istartswith=query, then=(Value(100))),
|
self.queryset.annotate(starts=Case(When(name__istartswith=query, then=(Value(100))), default=Value(0))) # put exact matches at the top of the result set
|
||||||
default=Value(0))) # put exact matches at the top of the result set
|
.filter(filter).order_by('-starts',
|
||||||
.filter(filter).order_by('-starts', Lower('name').asc())
|
Lower('name').asc()))
|
||||||
)
|
|
||||||
|
|
||||||
updated_at = self.request.query_params.get('updated_at', None)
|
updated_at = self.request.query_params.get('updated_at', None)
|
||||||
if updated_at is not None:
|
if updated_at is not None:
|
||||||
@@ -229,6 +223,7 @@ class FuzzyFilterMixin(ViewSetMixin, ExtendedRecipeMixin):
|
|||||||
|
|
||||||
|
|
||||||
class MergeMixin(ViewSetMixin):
|
class MergeMixin(ViewSetMixin):
|
||||||
|
|
||||||
@decorators.action(detail=True, url_path='merge/(?P<target>[^/.]+)', methods=['PUT'], )
|
@decorators.action(detail=True, url_path='merge/(?P<target>[^/.]+)', methods=['PUT'], )
|
||||||
@decorators.renderer_classes((TemplateHTMLRenderer, JSONRenderer))
|
@decorators.renderer_classes((TemplateHTMLRenderer, JSONRenderer))
|
||||||
def merge(self, request, pk, target):
|
def merge(self, request, pk, target):
|
||||||
@@ -296,8 +291,7 @@ class MergeMixin(ViewSetMixin):
|
|||||||
return Response(content, status=status.HTTP_200_OK)
|
return Response(content, status=status.HTTP_200_OK)
|
||||||
except Exception:
|
except Exception:
|
||||||
traceback.print_exc()
|
traceback.print_exc()
|
||||||
content = {'error': True,
|
content = {'error': True, 'msg': _(f'An error occurred attempting to merge {source.name} with {target.name}')}
|
||||||
'msg': _(f'An error occurred attempting to merge {source.name} with {target.name}')}
|
|
||||||
return Response(content, status=status.HTTP_400_BAD_REQUEST)
|
return Response(content, status=status.HTTP_400_BAD_REQUEST)
|
||||||
|
|
||||||
|
|
||||||
@@ -330,8 +324,7 @@ class TreeMixin(MergeMixin, FuzzyFilterMixin, ExtendedRecipeMixin):
|
|||||||
return self.annotate_recipe(queryset=super().get_queryset(), request=self.request, serializer=self.serializer_class, tree=True)
|
return self.annotate_recipe(queryset=super().get_queryset(), request=self.request, serializer=self.serializer_class, tree=True)
|
||||||
self.queryset = self.queryset.filter(space=self.request.space).order_by(Lower('name').asc())
|
self.queryset = self.queryset.filter(space=self.request.space).order_by(Lower('name').asc())
|
||||||
|
|
||||||
return self.annotate_recipe(queryset=self.queryset, request=self.request, serializer=self.serializer_class,
|
return self.annotate_recipe(queryset=self.queryset, request=self.request, serializer=self.serializer_class, tree=True)
|
||||||
tree=True)
|
|
||||||
|
|
||||||
@decorators.action(detail=True, url_path='move/(?P<parent>[^/.]+)', methods=['PUT'], )
|
@decorators.action(detail=True, url_path='move/(?P<parent>[^/.]+)', methods=['PUT'], )
|
||||||
@decorators.renderer_classes((TemplateHTMLRenderer, JSONRenderer))
|
@decorators.renderer_classes((TemplateHTMLRenderer, JSONRenderer))
|
||||||
@@ -561,8 +554,7 @@ class FoodViewSet(viewsets.ModelViewSet, TreeMixin):
|
|||||||
|
|
||||||
def get_queryset(self):
|
def get_queryset(self):
|
||||||
shared_users = []
|
shared_users = []
|
||||||
if c := caches['default'].get(
|
if c := caches['default'].get(f'shopping_shared_users_{self.request.space.id}_{self.request.user.id}', None):
|
||||||
f'shopping_shared_users_{self.request.space.id}_{self.request.user.id}', None):
|
|
||||||
shared_users = c
|
shared_users = c
|
||||||
else:
|
else:
|
||||||
try:
|
try:
|
||||||
@@ -573,8 +565,7 @@ class FoodViewSet(viewsets.ModelViewSet, TreeMixin):
|
|||||||
pass
|
pass
|
||||||
|
|
||||||
self.queryset = super().get_queryset()
|
self.queryset = super().get_queryset()
|
||||||
shopping_status = ShoppingListEntry.objects.filter(space=self.request.space, food=OuterRef('id'),
|
shopping_status = ShoppingListEntry.objects.filter(space=self.request.space, food=OuterRef('id'), checked=False).values('id')
|
||||||
checked=False).values('id')
|
|
||||||
# onhand_status = self.queryset.annotate(onhand_status=Exists(onhand_users_set__in=[shared_users]))
|
# onhand_status = self.queryset.annotate(onhand_status=Exists(onhand_users_set__in=[shared_users]))
|
||||||
return self.queryset \
|
return self.queryset \
|
||||||
.annotate(shopping_status=Exists(shopping_status)) \
|
.annotate(shopping_status=Exists(shopping_status)) \
|
||||||
@@ -595,8 +586,7 @@ class FoodViewSet(viewsets.ModelViewSet, TreeMixin):
|
|||||||
shared_users = list(self.request.user.get_shopping_share())
|
shared_users = list(self.request.user.get_shopping_share())
|
||||||
shared_users.append(request.user)
|
shared_users.append(request.user)
|
||||||
if request.data.get('_delete', False) == 'true':
|
if request.data.get('_delete', False) == 'true':
|
||||||
ShoppingListEntry.objects.filter(food=obj, checked=False, space=request.space,
|
ShoppingListEntry.objects.filter(food=obj, checked=False, space=request.space, created_by__in=shared_users).delete()
|
||||||
created_by__in=shared_users).delete()
|
|
||||||
content = {'msg': _(f'{obj.name} was removed from the shopping list.')}
|
content = {'msg': _(f'{obj.name} was removed from the shopping list.')}
|
||||||
return Response(content, status=status.HTTP_204_NO_CONTENT)
|
return Response(content, status=status.HTTP_204_NO_CONTENT)
|
||||||
|
|
||||||
@@ -604,8 +594,7 @@ class FoodViewSet(viewsets.ModelViewSet, TreeMixin):
|
|||||||
unit = request.data.get('unit', None)
|
unit = request.data.get('unit', None)
|
||||||
content = {'msg': _(f'{obj.name} was added to the shopping list.')}
|
content = {'msg': _(f'{obj.name} was added to the shopping list.')}
|
||||||
|
|
||||||
ShoppingListEntry.objects.create(food=obj, amount=amount, unit=unit, space=request.space,
|
ShoppingListEntry.objects.create(food=obj, amount=amount, unit=unit, space=request.space, created_by=request.user)
|
||||||
created_by=request.user)
|
|
||||||
return Response(content, status=status.HTTP_204_NO_CONTENT)
|
return Response(content, status=status.HTTP_204_NO_CONTENT)
|
||||||
|
|
||||||
@decorators.action(detail=True, methods=['POST'], )
|
@decorators.action(detail=True, methods=['POST'], )
|
||||||
@@ -616,19 +605,32 @@ class FoodViewSet(viewsets.ModelViewSet, TreeMixin):
|
|||||||
"""
|
"""
|
||||||
food = self.get_object()
|
food = self.get_object()
|
||||||
if not food.fdc_id:
|
if not food.fdc_id:
|
||||||
return JsonResponse({'msg': 'Food has no FDC ID associated.'}, status=400,
|
return JsonResponse({'msg': 'Food has no FDC ID associated.'}, status=400, json_dumps_params={'indent': 4})
|
||||||
json_dumps_params={'indent': 4})
|
|
||||||
|
|
||||||
response = requests.get(f'https://api.nal.usda.gov/fdc/v1/food/{food.fdc_id}?api_key={FDC_API_KEY}')
|
response = requests.get(f'https://api.nal.usda.gov/fdc/v1/food/{food.fdc_id}?api_key={FDC_API_KEY}')
|
||||||
if response.status_code == 429:
|
if response.status_code == 429:
|
||||||
return JsonResponse({'msg': 'API Key Rate Limit reached/exceeded, see https://api.data.gov/docs/rate-limits/ for more information. Configure your key in Tandoor using environment FDC_API_KEY variable.'}, status=429,
|
return JsonResponse(
|
||||||
json_dumps_params={'indent': 4})
|
{
|
||||||
|
'msg':
|
||||||
|
'API Key Rate Limit reached/exceeded, see https://api.data.gov/docs/rate-limits/ for more information. \
|
||||||
|
Configure your key in Tandoor using environment FDC_API_KEY variable.'
|
||||||
|
},
|
||||||
|
status=429,
|
||||||
|
json_dumps_params={'indent': 4})
|
||||||
if response.status_code != 200:
|
if response.status_code != 200:
|
||||||
return JsonResponse({'msg': f'Error while requesting FDC data using url https://api.nal.usda.gov/fdc/v1/food/{food.fdc_id}?api_key=****'}, status=response.status_code,
|
return JsonResponse({'msg': f'Error while requesting FDC data using url https://api.nal.usda.gov/fdc/v1/food/{food.fdc_id}?api_key=****'},
|
||||||
|
status=response.status_code,
|
||||||
json_dumps_params={'indent': 4})
|
json_dumps_params={'indent': 4})
|
||||||
|
|
||||||
food.properties_food_amount = 100
|
food.properties_food_amount = 100
|
||||||
food.properties_food_unit = Unit.objects.get_or_create(base_unit__iexact='g', space=self.request.space, defaults={'name': 'g', 'base_unit': 'g', 'space': self.request.space})[0]
|
food.properties_food_unit = Unit.objects.get_or_create(base_unit__iexact='g',
|
||||||
|
space=self.request.space,
|
||||||
|
defaults={
|
||||||
|
'name': 'g',
|
||||||
|
'base_unit': 'g',
|
||||||
|
'space': self.request.space
|
||||||
|
})[0]
|
||||||
|
|
||||||
food.save()
|
food.save()
|
||||||
|
|
||||||
try:
|
try:
|
||||||
@@ -694,8 +696,7 @@ class RecipeBookViewSet(viewsets.ModelViewSet, StandardFilterMixin):
|
|||||||
|
|
||||||
ordering = f"{'' if order_direction == 'asc' else '-'}{order_field}"
|
ordering = f"{'' if order_direction == 'asc' else '-'}{order_field}"
|
||||||
|
|
||||||
self.queryset = self.queryset.filter(Q(created_by=self.request.user) | Q(shared=self.request.user)).filter(
|
self.queryset = self.queryset.filter(Q(created_by=self.request.user) | Q(shared=self.request.user)).filter(space=self.request.space).distinct().order_by(ordering)
|
||||||
space=self.request.space).distinct().order_by(ordering)
|
|
||||||
return super().get_queryset()
|
return super().get_queryset()
|
||||||
|
|
||||||
|
|
||||||
@@ -713,9 +714,7 @@ class RecipeBookEntryViewSet(viewsets.ModelViewSet, viewsets.GenericViewSet):
|
|||||||
permission_classes = [(CustomIsOwner | CustomIsShared) & CustomTokenHasReadWriteScope]
|
permission_classes = [(CustomIsOwner | CustomIsShared) & CustomTokenHasReadWriteScope]
|
||||||
|
|
||||||
def get_queryset(self):
|
def get_queryset(self):
|
||||||
queryset = self.queryset.filter(
|
queryset = self.queryset.filter(Q(book__created_by=self.request.user) | Q(book__shared=self.request.user)).filter(book__space=self.request.space).distinct()
|
||||||
Q(book__created_by=self.request.user) | Q(book__shared=self.request.user)).filter(
|
|
||||||
book__space=self.request.space).distinct()
|
|
||||||
|
|
||||||
recipe_id = self.request.query_params.get('recipe', None)
|
recipe_id = self.request.query_params.get('recipe', None)
|
||||||
if recipe_id is not None:
|
if recipe_id is not None:
|
||||||
@@ -748,10 +747,7 @@ class MealPlanViewSet(viewsets.ModelViewSet):
|
|||||||
schema = QueryParamAutoSchema()
|
schema = QueryParamAutoSchema()
|
||||||
|
|
||||||
def get_queryset(self):
|
def get_queryset(self):
|
||||||
queryset = self.queryset.filter(
|
queryset = self.queryset.filter(Q(created_by=self.request.user) | Q(shared=self.request.user)).filter(space=self.request.space).distinct().all()
|
||||||
Q(created_by=self.request.user)
|
|
||||||
| Q(shared=self.request.user)
|
|
||||||
).filter(space=self.request.space).distinct().all()
|
|
||||||
|
|
||||||
from_date = self.request.query_params.get('from_date', None)
|
from_date = self.request.query_params.get('from_date', None)
|
||||||
if from_date is not None:
|
if from_date is not None:
|
||||||
@@ -769,6 +765,7 @@ class MealPlanViewSet(viewsets.ModelViewSet):
|
|||||||
|
|
||||||
|
|
||||||
class AutoPlanViewSet(viewsets.ViewSet):
|
class AutoPlanViewSet(viewsets.ViewSet):
|
||||||
|
|
||||||
def create(self, request):
|
def create(self, request):
|
||||||
serializer = AutoMealPlanSerializer(data=request.data)
|
serializer = AutoMealPlanSerializer(data=request.data)
|
||||||
|
|
||||||
@@ -798,10 +795,16 @@ class AutoPlanViewSet(viewsets.ViewSet):
|
|||||||
for i in range(0, days):
|
for i in range(0, days):
|
||||||
day = start_date + datetime.timedelta(i)
|
day = start_date + datetime.timedelta(i)
|
||||||
recipe = recipes[i % len(recipes)]
|
recipe = recipes[i % len(recipes)]
|
||||||
args = {'recipe_id': recipe['id'], 'servings': servings,
|
args = {
|
||||||
'created_by': request.user,
|
'recipe_id': recipe['id'],
|
||||||
'meal_type_id': serializer.validated_data['meal_type_id'],
|
'servings': servings,
|
||||||
'note': '', 'from_date': day, 'to_date': day, 'space': request.space}
|
'created_by': request.user,
|
||||||
|
'meal_type_id': serializer.validated_data['meal_type_id'],
|
||||||
|
'note': '',
|
||||||
|
'from_date': day,
|
||||||
|
'to_date': day,
|
||||||
|
'space': request.space
|
||||||
|
}
|
||||||
|
|
||||||
m = MealPlan(**args)
|
m = MealPlan(**args)
|
||||||
meal_plans.append(m)
|
meal_plans.append(m)
|
||||||
@@ -816,12 +819,7 @@ class AutoPlanViewSet(viewsets.ViewSet):
|
|||||||
SLR.create(mealplan=m, servings=servings)
|
SLR.create(mealplan=m, servings=servings)
|
||||||
|
|
||||||
else:
|
else:
|
||||||
post_save.send(
|
post_save.send(sender=m.__class__, instance=m, created=True, update_fields=None, )
|
||||||
sender=m.__class__,
|
|
||||||
instance=m,
|
|
||||||
created=True,
|
|
||||||
update_fields=None,
|
|
||||||
)
|
|
||||||
|
|
||||||
return Response(serializer.data)
|
return Response(serializer.data)
|
||||||
|
|
||||||
@@ -838,8 +836,7 @@ class MealTypeViewSet(viewsets.ModelViewSet):
|
|||||||
permission_classes = [CustomIsOwner & CustomTokenHasReadWriteScope]
|
permission_classes = [CustomIsOwner & CustomTokenHasReadWriteScope]
|
||||||
|
|
||||||
def get_queryset(self):
|
def get_queryset(self):
|
||||||
queryset = self.queryset.order_by('order', 'id').filter(created_by=self.request.user).filter(
|
queryset = self.queryset.order_by('order', 'id').filter(created_by=self.request.user).filter(space=self.request.space).all()
|
||||||
space=self.request.space).all()
|
|
||||||
return queryset
|
return queryset
|
||||||
|
|
||||||
|
|
||||||
@@ -899,12 +896,7 @@ class RecipePagination(PageNumberPagination):
|
|||||||
return super().paginate_queryset(queryset, request, view)
|
return super().paginate_queryset(queryset, request, view)
|
||||||
|
|
||||||
def get_paginated_response(self, data):
|
def get_paginated_response(self, data):
|
||||||
return Response(OrderedDict([
|
return Response(OrderedDict([('count', self.page.paginator.count), ('next', self.get_next_link()), ('previous', self.get_previous_link()), ('results', data), ]))
|
||||||
('count', self.page.paginator.count),
|
|
||||||
('next', self.get_next_link()),
|
|
||||||
('previous', self.get_previous_link()),
|
|
||||||
('results', data),
|
|
||||||
]))
|
|
||||||
|
|
||||||
|
|
||||||
class RecipeViewSet(viewsets.ModelViewSet):
|
class RecipeViewSet(viewsets.ModelViewSet):
|
||||||
@@ -950,49 +942,32 @@ class RecipeViewSet(viewsets.ModelViewSet):
|
|||||||
|
|
||||||
if self.detail: # if detail request and not list, private condition is verified by permission class
|
if self.detail: # if detail request and not list, private condition is verified by permission class
|
||||||
if not share: # filter for space only if not shared
|
if not share: # filter for space only if not shared
|
||||||
self.queryset = self.queryset.filter(space=self.request.space).prefetch_related(
|
self.queryset = self.queryset.filter(
|
||||||
'keywords',
|
space=self.request.space).prefetch_related('keywords', 'shared', 'properties', 'properties__property_type', 'steps', 'steps__ingredients',
|
||||||
'shared',
|
'steps__ingredients__step_set', 'steps__ingredients__step_set__recipe_set', 'steps__ingredients__food',
|
||||||
'properties',
|
'steps__ingredients__food__properties', 'steps__ingredients__food__properties__property_type',
|
||||||
'properties__property_type',
|
'steps__ingredients__food__inherit_fields', 'steps__ingredients__food__supermarket_category',
|
||||||
'steps',
|
'steps__ingredients__food__onhand_users', 'steps__ingredients__food__substitute',
|
||||||
'steps__ingredients',
|
'steps__ingredients__food__child_inherit_fields', 'steps__ingredients__unit',
|
||||||
'steps__ingredients__step_set',
|
'steps__ingredients__unit__unit_conversion_base_relation',
|
||||||
'steps__ingredients__step_set__recipe_set',
|
'steps__ingredients__unit__unit_conversion_base_relation__base_unit',
|
||||||
'steps__ingredients__food',
|
'steps__ingredients__unit__unit_conversion_converted_relation',
|
||||||
'steps__ingredients__food__properties',
|
'steps__ingredients__unit__unit_conversion_converted_relation__converted_unit', 'cooklog_set',
|
||||||
'steps__ingredients__food__properties__property_type',
|
).select_related('nutrition')
|
||||||
'steps__ingredients__food__inherit_fields',
|
|
||||||
'steps__ingredients__food__supermarket_category',
|
|
||||||
'steps__ingredients__food__onhand_users',
|
|
||||||
'steps__ingredients__food__substitute',
|
|
||||||
'steps__ingredients__food__child_inherit_fields',
|
|
||||||
|
|
||||||
'steps__ingredients__unit',
|
|
||||||
'steps__ingredients__unit__unit_conversion_base_relation',
|
|
||||||
'steps__ingredients__unit__unit_conversion_base_relation__base_unit',
|
|
||||||
'steps__ingredients__unit__unit_conversion_converted_relation',
|
|
||||||
'steps__ingredients__unit__unit_conversion_converted_relation__converted_unit',
|
|
||||||
'cooklog_set',
|
|
||||||
).select_related('nutrition')
|
|
||||||
|
|
||||||
return super().get_queryset()
|
return super().get_queryset()
|
||||||
|
|
||||||
self.queryset = self.queryset.filter(space=self.request.space).filter(
|
self.queryset = self.queryset.filter(
|
||||||
Q(private=False) | (Q(private=True) & (Q(created_by=self.request.user) | Q(shared=self.request.user)))
|
space=self.request.space).filter(Q(private=False) | (Q(private=True) & (Q(created_by=self.request.user) | Q(shared=self.request.user))))
|
||||||
)
|
|
||||||
|
|
||||||
params = {x: self.request.GET.get(x) if len({**self.request.GET}[x]) == 1 else self.request.GET.getlist(x) for x
|
params = {x: self.request.GET.get(x) if len({**self.request.GET}[x]) == 1 else self.request.GET.getlist(x) for x in list(self.request.GET)}
|
||||||
in list(self.request.GET)}
|
|
||||||
search = RecipeSearch(self.request, **params)
|
search = RecipeSearch(self.request, **params)
|
||||||
self.queryset = search.get_queryset(self.queryset).prefetch_related('keywords', 'cooklog_set')
|
self.queryset = search.get_queryset(self.queryset).prefetch_related('keywords', 'cooklog_set')
|
||||||
return self.queryset
|
return self.queryset
|
||||||
|
|
||||||
def list(self, request, *args, **kwargs):
|
def list(self, request, *args, **kwargs):
|
||||||
if self.request.GET.get('debug', False):
|
if self.request.GET.get('debug', False):
|
||||||
return JsonResponse({
|
return JsonResponse({'new': str(self.get_queryset().query), })
|
||||||
'new': str(self.get_queryset().query),
|
|
||||||
})
|
|
||||||
return super().list(request, *args, **kwargs)
|
return super().list(request, *args, **kwargs)
|
||||||
|
|
||||||
def get_serializer_class(self):
|
def get_serializer_class(self):
|
||||||
@@ -1000,12 +975,7 @@ class RecipeViewSet(viewsets.ModelViewSet):
|
|||||||
return RecipeOverviewSerializer
|
return RecipeOverviewSerializer
|
||||||
return self.serializer_class
|
return self.serializer_class
|
||||||
|
|
||||||
@decorators.action(
|
@decorators.action(detail=True, methods=['PUT'], serializer_class=RecipeImageSerializer, parser_classes=[MultiPartParser], )
|
||||||
detail=True,
|
|
||||||
methods=['PUT'],
|
|
||||||
serializer_class=RecipeImageSerializer,
|
|
||||||
parser_classes=[MultiPartParser],
|
|
||||||
)
|
|
||||||
def image(self, request, pk):
|
def image(self, request, pk):
|
||||||
obj = self.get_object()
|
obj = self.get_object()
|
||||||
|
|
||||||
@@ -1053,11 +1023,7 @@ class RecipeViewSet(viewsets.ModelViewSet):
|
|||||||
|
|
||||||
# TODO: refactor API to use post/put/delete or leave as put and change VUE to use list_recipe after creating
|
# TODO: refactor API to use post/put/delete or leave as put and change VUE to use list_recipe after creating
|
||||||
# DRF only allows one action in a decorator action without overriding get_operation_id_base()
|
# DRF only allows one action in a decorator action without overriding get_operation_id_base()
|
||||||
@decorators.action(
|
@decorators.action(detail=True, methods=['PUT'], serializer_class=RecipeShoppingUpdateSerializer, )
|
||||||
detail=True,
|
|
||||||
methods=['PUT'],
|
|
||||||
serializer_class=RecipeShoppingUpdateSerializer,
|
|
||||||
)
|
|
||||||
def shopping(self, request, pk):
|
def shopping(self, request, pk):
|
||||||
if self.request.space.demo:
|
if self.request.space.demo:
|
||||||
raise PermissionDenied(detail='Not available in demo', code=None)
|
raise PermissionDenied(detail='Not available in demo', code=None)
|
||||||
@@ -1087,11 +1053,7 @@ class RecipeViewSet(viewsets.ModelViewSet):
|
|||||||
|
|
||||||
return Response(content, status=http_status)
|
return Response(content, status=http_status)
|
||||||
|
|
||||||
@decorators.action(
|
@decorators.action(detail=True, methods=['GET'], serializer_class=RecipeSimpleSerializer)
|
||||||
detail=True,
|
|
||||||
methods=['GET'],
|
|
||||||
serializer_class=RecipeSimpleSerializer
|
|
||||||
)
|
|
||||||
def related(self, request, pk):
|
def related(self, request, pk):
|
||||||
obj = self.get_object()
|
obj = self.get_object()
|
||||||
if obj.get_space() != request.space:
|
if obj.get_space() != request.space:
|
||||||
@@ -1100,8 +1062,7 @@ class RecipeViewSet(viewsets.ModelViewSet):
|
|||||||
levels = int(request.query_params.get('levels', 1))
|
levels = int(request.query_params.get('levels', 1))
|
||||||
except (ValueError, TypeError):
|
except (ValueError, TypeError):
|
||||||
levels = 1
|
levels = 1
|
||||||
qs = obj.get_related_recipes(
|
qs = obj.get_related_recipes(levels=levels) # TODO: make levels a user setting, included in request data?, keep solely in the backend?
|
||||||
levels=levels) # TODO: make levels a user setting, included in request data?, keep solely in the backend?
|
|
||||||
return Response(self.serializer_class(qs, many=True).data)
|
return Response(self.serializer_class(qs, many=True).data)
|
||||||
|
|
||||||
@decorators.action(
|
@decorators.action(
|
||||||
@@ -1157,14 +1118,12 @@ class ShoppingListRecipeViewSet(viewsets.ModelViewSet):
|
|||||||
permission_classes = [(CustomIsOwner | CustomIsShared) & CustomTokenHasReadWriteScope]
|
permission_classes = [(CustomIsOwner | CustomIsShared) & CustomTokenHasReadWriteScope]
|
||||||
|
|
||||||
def get_queryset(self):
|
def get_queryset(self):
|
||||||
self.queryset = self.queryset.filter(
|
self.queryset = self.queryset.filter(Q(entries__space=self.request.space) | Q(recipe__space=self.request.space))
|
||||||
Q(shoppinglist__space=self.request.space) | Q(entries__space=self.request.space))
|
|
||||||
return self.queryset.filter(
|
return self.queryset.filter(
|
||||||
Q(shoppinglist__created_by=self.request.user)
|
Q(entries__isnull=True)
|
||||||
| Q(shoppinglist__shared=self.request.user)
|
|
||||||
| Q(entries__created_by=self.request.user)
|
| Q(entries__created_by=self.request.user)
|
||||||
| Q(entries__created_by__in=list(self.request.user.get_shopping_share()))
|
| Q(entries__created_by__in=list(self.request.user.get_shopping_share()))
|
||||||
).distinct().all()
|
).distinct().all()
|
||||||
|
|
||||||
|
|
||||||
class ShoppingListEntryViewSet(viewsets.ModelViewSet):
|
class ShoppingListEntryViewSet(viewsets.ModelViewSet):
|
||||||
@@ -1173,7 +1132,10 @@ class ShoppingListEntryViewSet(viewsets.ModelViewSet):
|
|||||||
permission_classes = [(CustomIsOwner | CustomIsShared) & CustomTokenHasReadWriteScope]
|
permission_classes = [(CustomIsOwner | CustomIsShared) & CustomTokenHasReadWriteScope]
|
||||||
query_params = [
|
query_params = [
|
||||||
QueryParam(name='id', description=_('Returns the shopping list entry with a primary key of id. Multiple values allowed.'), qtype='integer'),
|
QueryParam(name='id', description=_('Returns the shopping list entry with a primary key of id. Multiple values allowed.'), qtype='integer'),
|
||||||
QueryParam(name='checked', description=_('Filter shopping list entries on checked. [''true'', ''false'', ''both'', ''<b>recent</b>'']<br> - ''recent'' includes unchecked items and recently completed items.')
|
QueryParam(
|
||||||
|
name='checked',
|
||||||
|
description=_('Filter shopping list entries on checked. [''true'', ''false'', ''both'', ''<b>recent</b>'']<br> \
|
||||||
|
- ''recent'' includes unchecked items and recently completed items.')
|
||||||
),
|
),
|
||||||
QueryParam(name='supermarket', description=_('Returns the shopping list entries sorted by supermarket category order.'), qtype='integer'),
|
QueryParam(name='supermarket', description=_('Returns the shopping list entries sorted by supermarket category order.'), qtype='integer'),
|
||||||
]
|
]
|
||||||
@@ -1184,24 +1146,11 @@ class ShoppingListEntryViewSet(viewsets.ModelViewSet):
|
|||||||
|
|
||||||
self.queryset = self.queryset.filter(
|
self.queryset = self.queryset.filter(
|
||||||
Q(created_by=self.request.user)
|
Q(created_by=self.request.user)
|
||||||
| Q(shoppinglist__shared=self.request.user)
|
| Q(created_by__in=list(self.request.user.get_shopping_share()))).prefetch_related('created_by', 'food', 'food__properties', 'food__properties__property_type',
|
||||||
| Q(created_by__in=list(self.request.user.get_shopping_share()))
|
'food__inherit_fields', 'food__supermarket_category', 'food__onhand_users',
|
||||||
).prefetch_related(
|
'food__substitute', 'food__child_inherit_fields', 'unit', 'list_recipe',
|
||||||
'created_by',
|
'list_recipe__mealplan', 'list_recipe__mealplan__recipe',
|
||||||
'food',
|
).distinct().all()
|
||||||
'food__properties',
|
|
||||||
'food__properties__property_type',
|
|
||||||
'food__inherit_fields',
|
|
||||||
'food__supermarket_category',
|
|
||||||
'food__onhand_users',
|
|
||||||
'food__substitute',
|
|
||||||
'food__child_inherit_fields',
|
|
||||||
|
|
||||||
'unit',
|
|
||||||
'list_recipe',
|
|
||||||
'list_recipe__mealplan',
|
|
||||||
'list_recipe__mealplan__recipe',
|
|
||||||
).distinct().all()
|
|
||||||
|
|
||||||
if pk := self.request.query_params.getlist('id', []):
|
if pk := self.request.query_params.getlist('id', []):
|
||||||
self.queryset = self.queryset.filter(food__id__in=[int(i) for i in pk])
|
self.queryset = self.queryset.filter(food__id__in=[int(i) for i in pk])
|
||||||
@@ -1218,7 +1167,7 @@ class ShoppingListEntryViewSet(viewsets.ModelViewSet):
|
|||||||
if last_autosync:
|
if last_autosync:
|
||||||
last_autosync = datetime.datetime.fromtimestamp(int(last_autosync) / 1000, datetime.timezone.utc)
|
last_autosync = datetime.datetime.fromtimestamp(int(last_autosync) / 1000, datetime.timezone.utc)
|
||||||
self.queryset = self.queryset.filter(updated_at__gte=last_autosync)
|
self.queryset = self.queryset.filter(updated_at__gte=last_autosync)
|
||||||
except:
|
except Exception:
|
||||||
traceback.print_exc()
|
traceback.print_exc()
|
||||||
|
|
||||||
# TODO once old shopping list is removed this needs updated to sharing users in preferences
|
# TODO once old shopping list is removed this needs updated to sharing users in preferences
|
||||||
@@ -1227,53 +1176,20 @@ class ShoppingListEntryViewSet(viewsets.ModelViewSet):
|
|||||||
else:
|
else:
|
||||||
return self.queryset[:1000]
|
return self.queryset[:1000]
|
||||||
|
|
||||||
@decorators.action(
|
@decorators.action(detail=False, methods=['POST'], serializer_class=ShoppingListEntryBulkSerializer, permission_classes=[CustomIsUser])
|
||||||
detail=False,
|
|
||||||
methods=['POST'],
|
|
||||||
serializer_class=ShoppingListEntryBulkSerializer,
|
|
||||||
permission_classes=[CustomIsUser]
|
|
||||||
)
|
|
||||||
def bulk(self, request):
|
def bulk(self, request):
|
||||||
serializer = self.serializer_class(data=request.data)
|
serializer = self.serializer_class(data=request.data)
|
||||||
if serializer.is_valid():
|
if serializer.is_valid():
|
||||||
ShoppingListEntry.objects.filter(
|
ShoppingListEntry.objects.filter(Q(created_by=self.request.user)
|
||||||
Q(created_by=self.request.user)
|
| Q(created_by__in=list(self.request.user.get_shopping_share()))).filter(space=request.space, id__in=serializer.validated_data['ids']
|
||||||
| Q(shoppinglist__shared=self.request.user)
|
).update(checked=serializer.validated_data['checked'],
|
||||||
| Q(created_by__in=list(self.request.user.get_shopping_share()))
|
updated_at=timezone.now(),
|
||||||
).filter(space=request.space, id__in=serializer.validated_data['ids']).update(
|
)
|
||||||
checked=serializer.validated_data['checked'],
|
|
||||||
updated_at=timezone.now(),
|
|
||||||
)
|
|
||||||
return Response(serializer.data)
|
return Response(serializer.data)
|
||||||
else:
|
else:
|
||||||
return Response(serializer.errors, 400)
|
return Response(serializer.errors, 400)
|
||||||
|
|
||||||
|
|
||||||
# TODO deprecate
|
|
||||||
class ShoppingListViewSet(viewsets.ModelViewSet):
|
|
||||||
queryset = ShoppingList.objects
|
|
||||||
serializer_class = ShoppingListSerializer
|
|
||||||
permission_classes = [(CustomIsOwner | CustomIsShared) & CustomTokenHasReadWriteScope]
|
|
||||||
|
|
||||||
def get_queryset(self):
|
|
||||||
self.queryset = self.queryset.filter(
|
|
||||||
Q(created_by=self.request.user)
|
|
||||||
| Q(shared=self.request.user)
|
|
||||||
| Q(created_by__in=list(self.request.user.get_shopping_share()))
|
|
||||||
).filter(space=self.request.space)
|
|
||||||
|
|
||||||
return self.queryset.distinct()
|
|
||||||
|
|
||||||
def get_serializer_class(self):
|
|
||||||
try:
|
|
||||||
autosync = self.request.query_params.get('autosync', False)
|
|
||||||
if autosync:
|
|
||||||
return ShoppingListAutoSyncSerializer
|
|
||||||
except AttributeError: # Needed for the openapi schema to determine a serializer without a request
|
|
||||||
pass
|
|
||||||
return self.serializer_class
|
|
||||||
|
|
||||||
|
|
||||||
class ViewLogViewSet(viewsets.ModelViewSet):
|
class ViewLogViewSet(viewsets.ModelViewSet):
|
||||||
queryset = ViewLog.objects
|
queryset = ViewLog.objects
|
||||||
serializer_class = ViewLogSerializer
|
serializer_class = ViewLogSerializer
|
||||||
@@ -1348,11 +1264,52 @@ class UserFileViewSet(viewsets.ModelViewSet, StandardFilterMixin):
|
|||||||
|
|
||||||
|
|
||||||
class AutomationViewSet(viewsets.ModelViewSet, StandardFilterMixin):
|
class AutomationViewSet(viewsets.ModelViewSet, StandardFilterMixin):
|
||||||
|
"""
|
||||||
|
list:
|
||||||
|
optional parameters
|
||||||
|
|
||||||
|
- **automation_type**: Return the Automations matching the automation type. Multiple values allowed.
|
||||||
|
|
||||||
|
*Automation Types:*
|
||||||
|
- FS: Food Alias
|
||||||
|
- UA: Unit Alias
|
||||||
|
- KA: Keyword Alias
|
||||||
|
- DR: Description Replace
|
||||||
|
- IR: Instruction Replace
|
||||||
|
- NU: Never Unit
|
||||||
|
- TW: Transpose Words
|
||||||
|
- FR: Food Replace
|
||||||
|
- UR: Unit Replace
|
||||||
|
- NR: Name Replace
|
||||||
|
"""
|
||||||
|
|
||||||
queryset = Automation.objects
|
queryset = Automation.objects
|
||||||
serializer_class = AutomationSerializer
|
serializer_class = AutomationSerializer
|
||||||
permission_classes = [CustomIsUser & CustomTokenHasReadWriteScope]
|
permission_classes = [CustomIsUser & CustomTokenHasReadWriteScope]
|
||||||
|
pagination_class = DefaultPagination
|
||||||
|
|
||||||
|
query_params = [
|
||||||
|
QueryParam(name='automation_type', description=_('Return the Automations matching the automation type. Multiple values allowed.'), qtype='string'),
|
||||||
|
]
|
||||||
|
schema = QueryParamAutoSchema()
|
||||||
|
|
||||||
|
auto_type = {
|
||||||
|
'FS': 'FOOD_ALIAS',
|
||||||
|
'UA': 'UNIT_ALIAS',
|
||||||
|
'KA': 'KEYWORD_ALIAS',
|
||||||
|
'DR': 'DESCRIPTION_REPLACE',
|
||||||
|
'IR': 'INSTRUCTION_REPLACE',
|
||||||
|
'NU': 'NEVER_UNIT',
|
||||||
|
'TW': 'TRANSPOSE_WORDS',
|
||||||
|
'FR': 'FOOD_REPLACE',
|
||||||
|
'UR': 'UNIT_REPLACE',
|
||||||
|
'NR': 'NAME_REPLACE'
|
||||||
|
}
|
||||||
|
|
||||||
def get_queryset(self):
|
def get_queryset(self):
|
||||||
|
automation_type = self.request.query_params.getlist('automation_type', [])
|
||||||
|
if automation_type:
|
||||||
|
self.queryset = self.queryset.filter(type__in=[self.auto_type[x.upper()] for x in automation_type])
|
||||||
self.queryset = self.queryset.filter(space=self.request.space).all()
|
self.queryset = self.queryset.filter(space=self.request.space).all()
|
||||||
return super().get_queryset()
|
return super().get_queryset()
|
||||||
|
|
||||||
@@ -1379,10 +1336,10 @@ class CustomFilterViewSet(viewsets.ModelViewSet, StandardFilterMixin):
|
|||||||
queryset = CustomFilter.objects
|
queryset = CustomFilter.objects
|
||||||
serializer_class = CustomFilterSerializer
|
serializer_class = CustomFilterSerializer
|
||||||
permission_classes = [CustomIsOwner & CustomTokenHasReadWriteScope]
|
permission_classes = [CustomIsOwner & CustomTokenHasReadWriteScope]
|
||||||
|
pagination_class = DefaultPagination
|
||||||
|
|
||||||
def get_queryset(self):
|
def get_queryset(self):
|
||||||
self.queryset = self.queryset.filter(Q(created_by=self.request.user) | Q(shared=self.request.user)).filter(
|
self.queryset = self.queryset.filter(Q(created_by=self.request.user) | Q(shared=self.request.user)).filter(space=self.request.space).distinct()
|
||||||
space=self.request.space).distinct()
|
|
||||||
return super().get_queryset()
|
return super().get_queryset()
|
||||||
|
|
||||||
|
|
||||||
@@ -1397,6 +1354,7 @@ class AccessTokenViewSet(viewsets.ModelViewSet):
|
|||||||
|
|
||||||
# -------------- DRF custom views --------------------
|
# -------------- DRF custom views --------------------
|
||||||
|
|
||||||
|
|
||||||
class AuthTokenThrottle(AnonRateThrottle):
|
class AuthTokenThrottle(AnonRateThrottle):
|
||||||
rate = '10/day'
|
rate = '10/day'
|
||||||
|
|
||||||
@@ -1409,15 +1367,14 @@ class CustomAuthToken(ObtainAuthToken):
|
|||||||
throttle_classes = [AuthTokenThrottle]
|
throttle_classes = [AuthTokenThrottle]
|
||||||
|
|
||||||
def post(self, request, *args, **kwargs):
|
def post(self, request, *args, **kwargs):
|
||||||
serializer = self.serializer_class(data=request.data,
|
serializer = self.serializer_class(data=request.data, context={'request': request})
|
||||||
context={'request': request})
|
|
||||||
serializer.is_valid(raise_exception=True)
|
serializer.is_valid(raise_exception=True)
|
||||||
user = serializer.validated_data['user']
|
user = serializer.validated_data['user']
|
||||||
if token := AccessToken.objects.filter(user=user, expires__gt=timezone.now(), scope__contains='read').filter(
|
if token := AccessToken.objects.filter(user=user, expires__gt=timezone.now(), scope__contains='read').filter(scope__contains='write').first():
|
||||||
scope__contains='write').first():
|
|
||||||
access_token = token
|
access_token = token
|
||||||
else:
|
else:
|
||||||
access_token = AccessToken.objects.create(user=user, token=f'tda_{str(uuid.uuid4()).replace("-", "_")}',
|
access_token = AccessToken.objects.create(user=user,
|
||||||
|
token=f'tda_{str(uuid.uuid4()).replace("-", "_")}',
|
||||||
expires=(timezone.now() + timezone.timedelta(days=365 * 5)),
|
expires=(timezone.now() + timezone.timedelta(days=365 * 5)),
|
||||||
scope='read write app')
|
scope='read write app')
|
||||||
return Response({
|
return Response({
|
||||||
@@ -1447,8 +1404,7 @@ class RecipeUrlImportView(APIView):
|
|||||||
serializer = RecipeFromSourceSerializer(data=request.data)
|
serializer = RecipeFromSourceSerializer(data=request.data)
|
||||||
if serializer.is_valid():
|
if serializer.is_valid():
|
||||||
|
|
||||||
if (b_pk := serializer.validated_data.get('bookmarklet', None)) and (
|
if (b_pk := serializer.validated_data.get('bookmarklet', None)) and (bookmarklet := BookmarkletImport.objects.filter(pk=b_pk).first()):
|
||||||
bookmarklet := BookmarkletImport.objects.filter(pk=b_pk).first()):
|
|
||||||
serializer.validated_data['url'] = bookmarklet.url
|
serializer.validated_data['url'] = bookmarklet.url
|
||||||
serializer.validated_data['data'] = bookmarklet.html
|
serializer.validated_data['data'] = bookmarklet.html
|
||||||
bookmarklet.delete()
|
bookmarklet.delete()
|
||||||
@@ -1456,61 +1412,40 @@ class RecipeUrlImportView(APIView):
|
|||||||
url = serializer.validated_data.get('url', None)
|
url = serializer.validated_data.get('url', None)
|
||||||
data = unquote(serializer.validated_data.get('data', None))
|
data = unquote(serializer.validated_data.get('data', None))
|
||||||
if not url and not data:
|
if not url and not data:
|
||||||
return Response({
|
return Response({'error': True, 'msg': _('Nothing to do.')}, status=status.HTTP_400_BAD_REQUEST)
|
||||||
'error': True,
|
|
||||||
'msg': _('Nothing to do.')
|
|
||||||
}, status=status.HTTP_400_BAD_REQUEST)
|
|
||||||
|
|
||||||
elif url and not data:
|
elif url and not data:
|
||||||
if re.match('^(https?://)?(www\\.youtube\\.com|youtu\\.be)/.+$', url):
|
if re.match('^(https?://)?(www\\.youtube\\.com|youtu\\.be)/.+$', url):
|
||||||
if validators.url(url, public=True):
|
if validators.url(url, public=True):
|
||||||
return Response({
|
return Response({'recipe_json': get_from_youtube_scraper(url, request), 'recipe_images': [], }, status=status.HTTP_200_OK)
|
||||||
'recipe_json': get_from_youtube_scraper(url, request),
|
if re.match('^(.)*/view/recipe/[0-9]+/[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$', url):
|
||||||
'recipe_images': [],
|
|
||||||
}, status=status.HTTP_200_OK)
|
|
||||||
if re.match(
|
|
||||||
'^(.)*/view/recipe/[0-9]+/[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$',
|
|
||||||
url):
|
|
||||||
recipe_json = requests.get(
|
recipe_json = requests.get(
|
||||||
url.replace('/view/recipe/', '/api/recipe/').replace(re.split('/view/recipe/[0-9]+', url)[1],
|
url.replace('/view/recipe/', '/api/recipe/').replace(re.split('/view/recipe/[0-9]+', url)[1], '') + '?share='
|
||||||
'') + '?share=' +
|
+ re.split('/view/recipe/[0-9]+', url)[1].replace('/', '')).json()
|
||||||
re.split('/view/recipe/[0-9]+', url)[1].replace('/', '')).json()
|
|
||||||
recipe_json = clean_dict(recipe_json, 'id')
|
recipe_json = clean_dict(recipe_json, 'id')
|
||||||
serialized_recipe = RecipeExportSerializer(data=recipe_json, context={'request': request})
|
serialized_recipe = RecipeExportSerializer(data=recipe_json, context={'request': request})
|
||||||
if serialized_recipe.is_valid():
|
if serialized_recipe.is_valid():
|
||||||
recipe = serialized_recipe.save()
|
recipe = serialized_recipe.save()
|
||||||
if validators.url(recipe_json['image'], public=True):
|
if validators.url(recipe_json['image'], public=True):
|
||||||
recipe.image = File(handle_image(request,
|
recipe.image = File(handle_image(request,
|
||||||
File(io.BytesIO(requests.get(recipe_json['image']).content),
|
File(io.BytesIO(requests.get(recipe_json['image']).content), name='image'),
|
||||||
name='image'),
|
|
||||||
filetype=pathlib.Path(recipe_json['image']).suffix),
|
filetype=pathlib.Path(recipe_json['image']).suffix),
|
||||||
name=f'{uuid.uuid4()}_{recipe.pk}{pathlib.Path(recipe_json["image"]).suffix}')
|
name=f'{uuid.uuid4()}_{recipe.pk}{pathlib.Path(recipe_json["image"]).suffix}')
|
||||||
recipe.save()
|
recipe.save()
|
||||||
return Response({
|
return Response({'link': request.build_absolute_uri(reverse('view_recipe', args={recipe.pk}))}, status=status.HTTP_201_CREATED)
|
||||||
'link': request.build_absolute_uri(reverse('view_recipe', args={recipe.pk}))
|
|
||||||
}, status=status.HTTP_201_CREATED)
|
|
||||||
else:
|
else:
|
||||||
try:
|
try:
|
||||||
if validators.url(url, public=True):
|
if validators.url(url, public=True):
|
||||||
scrape = scrape_me(url_path=url, wild_mode=True)
|
scrape = scrape_me(url_path=url, wild_mode=True)
|
||||||
|
|
||||||
else:
|
else:
|
||||||
return Response({
|
return Response({'error': True, 'msg': _('Invalid Url')}, status=status.HTTP_400_BAD_REQUEST)
|
||||||
'error': True,
|
|
||||||
'msg': _('Invalid Url')
|
|
||||||
}, status=status.HTTP_400_BAD_REQUEST)
|
|
||||||
except NoSchemaFoundInWildMode:
|
except NoSchemaFoundInWildMode:
|
||||||
pass
|
pass
|
||||||
except requests.exceptions.ConnectionError:
|
except requests.exceptions.ConnectionError:
|
||||||
return Response({
|
return Response({'error': True, 'msg': _('Connection Refused.')}, status=status.HTTP_400_BAD_REQUEST)
|
||||||
'error': True,
|
|
||||||
'msg': _('Connection Refused.')
|
|
||||||
}, status=status.HTTP_400_BAD_REQUEST)
|
|
||||||
except requests.exceptions.MissingSchema:
|
except requests.exceptions.MissingSchema:
|
||||||
return Response({
|
return Response({'error': True, 'msg': _('Bad URL Schema.')}, status=status.HTTP_400_BAD_REQUEST)
|
||||||
'error': True,
|
|
||||||
'msg': _('Bad URL Schema.')
|
|
||||||
}, status=status.HTTP_400_BAD_REQUEST)
|
|
||||||
else:
|
else:
|
||||||
try:
|
try:
|
||||||
data_json = json.loads(data)
|
data_json = json.loads(data)
|
||||||
@@ -1529,13 +1464,11 @@ class RecipeUrlImportView(APIView):
|
|||||||
return Response({
|
return Response({
|
||||||
'recipe_json': helper.get_from_scraper(scrape, request),
|
'recipe_json': helper.get_from_scraper(scrape, request),
|
||||||
'recipe_images': list(dict.fromkeys(get_images_from_soup(scrape.soup, url))),
|
'recipe_images': list(dict.fromkeys(get_images_from_soup(scrape.soup, url))),
|
||||||
}, status=status.HTTP_200_OK)
|
},
|
||||||
|
status=status.HTTP_200_OK)
|
||||||
|
|
||||||
else:
|
else:
|
||||||
return Response({
|
return Response({'error': True, 'msg': _('No usable data could be found.')}, status=status.HTTP_400_BAD_REQUEST)
|
||||||
'error': True,
|
|
||||||
'msg': _('No usable data could be found.')
|
|
||||||
}, status=status.HTTP_400_BAD_REQUEST)
|
|
||||||
else:
|
else:
|
||||||
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
|
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
|
||||||
|
|
||||||
@@ -1627,8 +1560,7 @@ def import_files(request):
|
|||||||
|
|
||||||
return Response({'import_id': il.pk}, status=status.HTTP_200_OK)
|
return Response({'import_id': il.pk}, status=status.HTTP_200_OK)
|
||||||
except NotImplementedError:
|
except NotImplementedError:
|
||||||
return Response({'error': True, 'msg': _('Importing is not implemented for this provider')},
|
return Response({'error': True, 'msg': _('Importing is not implemented for this provider')}, status=status.HTTP_400_BAD_REQUEST)
|
||||||
status=status.HTTP_400_BAD_REQUEST)
|
|
||||||
else:
|
else:
|
||||||
return Response({'error': True, 'msg': form.errors}, status=status.HTTP_400_BAD_REQUEST)
|
return Response({'error': True, 'msg': form.errors}, status=status.HTTP_400_BAD_REQUEST)
|
||||||
|
|
||||||
@@ -1712,8 +1644,7 @@ def get_recipe_file(request, recipe_id):
|
|||||||
@group_required('user')
|
@group_required('user')
|
||||||
def sync_all(request):
|
def sync_all(request):
|
||||||
if request.space.demo or settings.HOSTED:
|
if request.space.demo or settings.HOSTED:
|
||||||
messages.add_message(request, messages.ERROR,
|
messages.add_message(request, messages.ERROR, _('This feature is not yet available in the hosted version of tandoor!'))
|
||||||
_('This feature is not yet available in the hosted version of tandoor!'))
|
|
||||||
return redirect('index')
|
return redirect('index')
|
||||||
|
|
||||||
monitors = Sync.objects.filter(active=True).filter(space=request.user.userspace_set.filter(active=1).first().space)
|
monitors = Sync.objects.filter(active=True).filter(space=request.user.userspace_set.filter(active=1).first().space)
|
||||||
@@ -1734,14 +1665,10 @@ def sync_all(request):
|
|||||||
error = True
|
error = True
|
||||||
|
|
||||||
if not error:
|
if not error:
|
||||||
messages.add_message(
|
messages.add_message(request, messages.SUCCESS, _('Sync successful!'))
|
||||||
request, messages.SUCCESS, _('Sync successful!')
|
|
||||||
)
|
|
||||||
return redirect('list_recipe_import')
|
return redirect('list_recipe_import')
|
||||||
else:
|
else:
|
||||||
messages.add_message(
|
messages.add_message(request, messages.ERROR, _('Error synchronizing with Storage'))
|
||||||
request, messages.ERROR, _('Error synchronizing with Storage')
|
|
||||||
)
|
|
||||||
return redirect('list_recipe_import')
|
return redirect('list_recipe_import')
|
||||||
|
|
||||||
|
|
||||||
@@ -1749,11 +1676,10 @@ def sync_all(request):
|
|||||||
# @schema(AutoSchema()) #TODO add proper schema
|
# @schema(AutoSchema()) #TODO add proper schema
|
||||||
@permission_classes([CustomIsUser & CustomTokenHasReadWriteScope])
|
@permission_classes([CustomIsUser & CustomTokenHasReadWriteScope])
|
||||||
def share_link(request, pk):
|
def share_link(request, pk):
|
||||||
if request.space.allow_sharing and has_group_permission(request.user, ('user',)):
|
if request.space.allow_sharing and has_group_permission(request.user, ('user', )):
|
||||||
recipe = get_object_or_404(Recipe, pk=pk, space=request.space)
|
recipe = get_object_or_404(Recipe, pk=pk, space=request.space)
|
||||||
link = ShareLink.objects.create(recipe=recipe, created_by=request.user, space=request.space)
|
link = ShareLink.objects.create(recipe=recipe, created_by=request.user, space=request.space)
|
||||||
return JsonResponse({'pk': pk, 'share': link.uuid,
|
return JsonResponse({'pk': pk, 'share': link.uuid, 'link': request.build_absolute_uri(reverse('view_recipe', args=[pk, link.uuid]))})
|
||||||
'link': request.build_absolute_uri(reverse('view_recipe', args=[pk, link.uuid]))})
|
|
||||||
else:
|
else:
|
||||||
return JsonResponse({'error': 'sharing_disabled'}, status=403)
|
return JsonResponse({'error': 'sharing_disabled'}, status=403)
|
||||||
|
|
||||||
@@ -1779,9 +1705,8 @@ def log_cooking(request, recipe_id):
|
|||||||
|
|
||||||
@group_required('user')
|
@group_required('user')
|
||||||
def get_plan_ical(request, from_date, to_date):
|
def get_plan_ical(request, from_date, to_date):
|
||||||
queryset = MealPlan.objects.filter(
|
queryset = MealPlan.objects.filter(Q(created_by=request.user)
|
||||||
Q(created_by=request.user) | Q(shared=request.user)
|
| Q(shared=request.user)).filter(space=request.user.userspace_set.filter(active=1).first().space).distinct().all()
|
||||||
).filter(space=request.user.userspace_set.filter(active=1).first().space).distinct().all()
|
|
||||||
|
|
||||||
if from_date is not None:
|
if from_date is not None:
|
||||||
queryset = queryset.filter(from_date__gte=from_date)
|
queryset = queryset.filter(from_date__gte=from_date)
|
||||||
@@ -1822,12 +1747,4 @@ def ingredient_from_string(request):
|
|||||||
ingredient_parser = IngredientParser(request, False)
|
ingredient_parser = IngredientParser(request, False)
|
||||||
amount, unit, food, note = ingredient_parser.parse(text)
|
amount, unit, food, note = ingredient_parser.parse(text)
|
||||||
|
|
||||||
return JsonResponse(
|
return JsonResponse({'amount': amount, 'unit': unit, 'food': food, 'note': note}, status=200)
|
||||||
{
|
|
||||||
'amount': amount,
|
|
||||||
'unit': unit,
|
|
||||||
'food': food,
|
|
||||||
'note': note
|
|
||||||
},
|
|
||||||
status=200
|
|
||||||
)
|
|
||||||
|
|||||||
@@ -8,8 +8,12 @@ from django.utils.translation import gettext as _
|
|||||||
from django.views.generic import DeleteView
|
from django.views.generic import DeleteView
|
||||||
|
|
||||||
from cookbook.helper.permission_helper import GroupRequiredMixin, OwnerRequiredMixin, group_required
|
from cookbook.helper.permission_helper import GroupRequiredMixin, OwnerRequiredMixin, group_required
|
||||||
|
|
||||||
|
from cookbook.models import Comment, InviteLink, Recipe, RecipeImport, Space, Storage, Sync, UserSpace
|
||||||
|
|
||||||
from cookbook.models import (Comment, InviteLink, MealPlan, Recipe, RecipeBook, RecipeBookEntry,
|
from cookbook.models import (Comment, InviteLink, MealPlan, Recipe, RecipeBook, RecipeBookEntry,
|
||||||
RecipeImport, Space, Storage, Sync, UserSpace, ConnectorConfig)
|
RecipeImport, Space, Storage, Sync, UserSpace, ConnectorConfig)
|
||||||
|
|
||||||
from cookbook.provider.dropbox import Dropbox
|
from cookbook.provider.dropbox import Dropbox
|
||||||
from cookbook.provider.local import Local
|
from cookbook.provider.local import Local
|
||||||
from cookbook.provider.nextcloud import Nextcloud
|
from cookbook.provider.nextcloud import Nextcloud
|
||||||
@@ -114,11 +118,9 @@ class StorageDelete(GroupRequiredMixin, DeleteView):
|
|||||||
try:
|
try:
|
||||||
return self.delete(request, *args, **kwargs)
|
return self.delete(request, *args, **kwargs)
|
||||||
except ProtectedError:
|
except ProtectedError:
|
||||||
messages.add_message(
|
messages.add_message(request, messages.WARNING,
|
||||||
request,
|
_('Could not delete this storage backend as it is used in at least one monitor.') # noqa: E501
|
||||||
messages.WARNING,
|
)
|
||||||
_('Could not delete this storage backend as it is used in at least one monitor.') # noqa: E501
|
|
||||||
)
|
|
||||||
return HttpResponseRedirect(reverse('list_storage'))
|
return HttpResponseRedirect(reverse('list_storage'))
|
||||||
|
|
||||||
|
|
||||||
@@ -145,40 +147,6 @@ class CommentDelete(OwnerRequiredMixin, DeleteView):
|
|||||||
return context
|
return context
|
||||||
|
|
||||||
|
|
||||||
class RecipeBookDelete(OwnerRequiredMixin, DeleteView):
|
|
||||||
template_name = "generic/delete_template.html"
|
|
||||||
model = RecipeBook
|
|
||||||
success_url = reverse_lazy('view_books')
|
|
||||||
|
|
||||||
def get_context_data(self, **kwargs):
|
|
||||||
context = super(RecipeBookDelete, self).get_context_data(**kwargs)
|
|
||||||
context['title'] = _("Recipe Book")
|
|
||||||
return context
|
|
||||||
|
|
||||||
|
|
||||||
class RecipeBookEntryDelete(OwnerRequiredMixin, DeleteView):
|
|
||||||
groups_required = ['user']
|
|
||||||
template_name = "generic/delete_template.html"
|
|
||||||
model = RecipeBookEntry
|
|
||||||
success_url = reverse_lazy('view_books')
|
|
||||||
|
|
||||||
def get_context_data(self, **kwargs):
|
|
||||||
context = super(RecipeBookEntryDelete, self).get_context_data(**kwargs)
|
|
||||||
context['title'] = _("Bookmarks")
|
|
||||||
return context
|
|
||||||
|
|
||||||
|
|
||||||
class MealPlanDelete(OwnerRequiredMixin, DeleteView):
|
|
||||||
template_name = "generic/delete_template.html"
|
|
||||||
model = MealPlan
|
|
||||||
success_url = reverse_lazy('view_plan')
|
|
||||||
|
|
||||||
def get_context_data(self, **kwargs):
|
|
||||||
context = super(MealPlanDelete, self).get_context_data(**kwargs)
|
|
||||||
context['title'] = _("Meal-Plan")
|
|
||||||
return context
|
|
||||||
|
|
||||||
|
|
||||||
class InviteLinkDelete(OwnerRequiredMixin, DeleteView):
|
class InviteLinkDelete(OwnerRequiredMixin, DeleteView):
|
||||||
template_name = "generic/delete_template.html"
|
template_name = "generic/delete_template.html"
|
||||||
model = InviteLink
|
model = InviteLink
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ class RecipeCreate(GroupRequiredMixin, CreateView):
|
|||||||
groups_required = ['user']
|
groups_required = ['user']
|
||||||
template_name = "generic/new_template.html"
|
template_name = "generic/new_template.html"
|
||||||
model = Recipe
|
model = Recipe
|
||||||
fields = ('name',)
|
fields = ('name', )
|
||||||
|
|
||||||
def form_valid(self, form):
|
def form_valid(self, form):
|
||||||
limit, msg = above_space_limit(self.request.space)
|
limit, msg = above_space_limit(self.request.space)
|
||||||
@@ -126,12 +126,6 @@ def create_new_external_recipe(request, import_id):
|
|||||||
messages.add_message(request, messages.ERROR, _('There was an error importing this recipe!'))
|
messages.add_message(request, messages.ERROR, _('There was an error importing this recipe!'))
|
||||||
else:
|
else:
|
||||||
new_recipe = get_object_or_404(RecipeImport, pk=import_id, space=request.space)
|
new_recipe = get_object_or_404(RecipeImport, pk=import_id, space=request.space)
|
||||||
form = ImportRecipeForm(
|
form = ImportRecipeForm(initial={'file_path': new_recipe.file_path, 'name': new_recipe.name, 'file_uid': new_recipe.file_uid}, space=request.space)
|
||||||
initial={
|
|
||||||
'file_path': new_recipe.file_path,
|
|
||||||
'name': new_recipe.name,
|
|
||||||
'file_uid': new_recipe.file_uid
|
|
||||||
}, space=request.space
|
|
||||||
)
|
|
||||||
|
|
||||||
return render(request, 'forms/edit_import_recipe.html', {'form': form})
|
return render(request, 'forms/edit_import_recipe.html', {'form': form})
|
||||||
|
|||||||
@@ -1,7 +1,5 @@
|
|||||||
import json
|
|
||||||
import os
|
import os
|
||||||
import re
|
import re
|
||||||
import subprocess
|
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
from io import StringIO
|
from io import StringIO
|
||||||
from uuid import UUID
|
from uuid import UUID
|
||||||
@@ -17,7 +15,6 @@ from django.core.management import call_command
|
|||||||
from django.db import models
|
from django.db import models
|
||||||
from django.http import HttpResponseRedirect, JsonResponse
|
from django.http import HttpResponseRedirect, JsonResponse
|
||||||
from django.shortcuts import get_object_or_404, redirect, render
|
from django.shortcuts import get_object_or_404, redirect, render
|
||||||
from django.templatetags.static import static
|
|
||||||
from django.urls import reverse, reverse_lazy
|
from django.urls import reverse, reverse_lazy
|
||||||
from django.utils import timezone
|
from django.utils import timezone
|
||||||
from django.utils.translation import gettext as _
|
from django.utils.translation import gettext as _
|
||||||
@@ -30,7 +27,7 @@ from cookbook.models import Comment, CookLog, InviteLink, SearchFields, SearchPr
|
|||||||
from cookbook.tables import CookLogTable, ViewLogTable
|
from cookbook.tables import CookLogTable, ViewLogTable
|
||||||
from cookbook.templatetags.theming_tags import get_theming_values
|
from cookbook.templatetags.theming_tags import get_theming_values
|
||||||
from cookbook.version_info import VERSION_INFO
|
from cookbook.version_info import VERSION_INFO
|
||||||
from recipes.settings import BASE_DIR, PLUGINS
|
from recipes.settings import PLUGINS
|
||||||
|
|
||||||
|
|
||||||
def index(request):
|
def index(request):
|
||||||
@@ -160,8 +157,8 @@ def recipe_view(request, pk, share=None):
|
|||||||
servings = recipe.servings
|
servings = recipe.servings
|
||||||
if request.method == "GET" and 'servings' in request.GET:
|
if request.method == "GET" and 'servings' in request.GET:
|
||||||
servings = request.GET.get("servings")
|
servings = request.GET.get("servings")
|
||||||
return render(request, 'recipe_view.html',
|
return render(request, 'recipe_view.html', {'recipe': recipe, 'comments': comments, 'comment_form': comment_form, 'share': share, 'servings': servings})
|
||||||
{'recipe': recipe, 'comments': comments, 'comment_form': comment_form, 'share': share, 'servings': servings})
|
|
||||||
|
|
||||||
|
|
||||||
@group_required('user')
|
@group_required('user')
|
||||||
@@ -174,16 +171,6 @@ def meal_plan(request):
|
|||||||
return render(request, 'meal_plan.html', {})
|
return render(request, 'meal_plan.html', {})
|
||||||
|
|
||||||
|
|
||||||
@group_required('user')
|
|
||||||
def supermarket(request):
|
|
||||||
return render(request, 'supermarket.html', {})
|
|
||||||
|
|
||||||
|
|
||||||
@group_required('user')
|
|
||||||
def view_profile(request, user_id):
|
|
||||||
return render(request, 'profile.html', {})
|
|
||||||
|
|
||||||
|
|
||||||
@group_required('guest')
|
@group_required('guest')
|
||||||
def user_settings(request):
|
def user_settings(request):
|
||||||
if request.space.demo:
|
if request.space.demo:
|
||||||
@@ -366,7 +353,8 @@ def setup(request):
|
|||||||
if User.objects.count() > 0 or 'django.contrib.auth.backends.RemoteUserBackend' in settings.AUTHENTICATION_BACKENDS:
|
if User.objects.count() > 0 or 'django.contrib.auth.backends.RemoteUserBackend' in settings.AUTHENTICATION_BACKENDS:
|
||||||
messages.add_message(
|
messages.add_message(
|
||||||
request, messages.ERROR,
|
request, messages.ERROR,
|
||||||
_('The setup page can only be used to create the first user! If you have forgotten your superuser credentials please consult the django documentation on how to reset passwords.'
|
_('The setup page can only be used to create the first user! \
|
||||||
|
If you have forgotten your superuser credentials please consult the django documentation on how to reset passwords.'
|
||||||
))
|
))
|
||||||
return HttpResponseRedirect(reverse('account_login'))
|
return HttpResponseRedirect(reverse('account_login'))
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ methods of central account management and authentication.
|
|||||||
|
|
||||||
## Allauth
|
## Allauth
|
||||||
[Django Allauth](https://django-allauth.readthedocs.io/en/latest/index.html) is an awesome project that
|
[Django Allauth](https://django-allauth.readthedocs.io/en/latest/index.html) is an awesome project that
|
||||||
allows you to use a [huge number](https://django-allauth.readthedocs.io/en/latest/providers.html) of different
|
allows you to use a [huge number](https://docs.allauth.org/en/latest/socialaccount/providers/index.html) of different
|
||||||
authentication providers.
|
authentication providers.
|
||||||
|
|
||||||
They basically explain everything in their documentation, but the following is a short overview on how to get started.
|
They basically explain everything in their documentation, but the following is a short overview on how to get started.
|
||||||
@@ -17,42 +17,50 @@ They basically explain everything in their documentation, but the following is a
|
|||||||
Choose a provider from the [list](https://docs.allauth.org/en/latest/socialaccount/providers/index.html) and install it using the environment variable `SOCIAL_PROVIDERS` as shown
|
Choose a provider from the [list](https://docs.allauth.org/en/latest/socialaccount/providers/index.html) and install it using the environment variable `SOCIAL_PROVIDERS` as shown
|
||||||
in the example below.
|
in the example below.
|
||||||
|
|
||||||
When at least one social provider is set up, the social login sign in buttons should appear on the login page.
|
When at least one social provider is set up, the social login sign in buttons should appear on the login page. The example below enables Nextcloud and the generic OpenID Connect providers.
|
||||||
|
|
||||||
```ini
|
```ini
|
||||||
SOCIAL_PROVIDERS=allauth.socialaccount.providers.github,allauth.socialaccount.providers.nextcloud
|
SOCIAL_PROVIDERS=allauth.socialaccount.providers.openid_connect,allauth.socialaccount.providers.nextcloud
|
||||||
```
|
```
|
||||||
|
|
||||||
!!! warning "Formatting"
|
!!! warning "Formatting"
|
||||||
The exact formatting is important so make sure to follow the steps explained here!
|
The exact formatting is important so make sure to follow the steps explained here!
|
||||||
|
|
||||||
|
### Configuration, via environment
|
||||||
|
|
||||||
Depending on your authentication provider you **might need** to configure it.
|
Depending on your authentication provider you **might need** to configure it.
|
||||||
This needs to be done through the settings system. To make the system flexible (allow multiple providers) and to
|
This needs to be done through the settings system. To make the system flexible (allow multiple providers) and to
|
||||||
not require another file to be mounted into the container the configuration ins done through a single
|
not require another file to be mounted into the container the configuration ins done through a single
|
||||||
environment variable. The downside of this approach is that the configuration needs to be put into a single line
|
environment variable. The downside of this approach is that the configuration needs to be put into a single line
|
||||||
as environment files loaded by docker compose don't support multiple lines for a single variable.
|
as environment files loaded by docker compose don't support multiple lines for a single variable.
|
||||||
|
|
||||||
|
The line data needs to either be in json or as Python dictionary syntax.
|
||||||
|
|
||||||
Take the example configuration from the allauth docs, fill in your settings and then inline the whole object
|
Take the example configuration from the allauth docs, fill in your settings and then inline the whole object
|
||||||
(you can use a service like [www.freeformatter.com](https://www.freeformatter.com/json-formatter.html) for formatting).
|
(you can use a service like [www.freeformatter.com](https://www.freeformatter.com/json-formatter.html) for formatting).
|
||||||
Assign it to the additional `SOCIALACCOUNT_PROVIDERS` variable.
|
Assign it to the additional `SOCIALACCOUNT_PROVIDERS` variable.
|
||||||
|
|
||||||
|
|
||||||
|
The example below is for a generic OIDC provider with PKCE enabled. Most values need to be customized for your specifics!
|
||||||
```ini
|
```ini
|
||||||
SOCIALACCOUNT_PROVIDERS={"nextcloud":{"SERVER":"https://nextcloud.example.org"}}
|
SOCIALACCOUNT_PROVIDERS = "{ 'openid_connect': { 'OAUTH_PKCE_ENABLED': True, 'APPS': [ { 'provider_id': 'oidc', 'name': 'My-IDM', 'client_id': 'my_client_id', 'secret': 'my_client_secret', 'settings': { 'server_url': 'https://idm.example.com/oidc/recipes' } } ] } }"
|
||||||
```
|
```
|
||||||
|
|
||||||
!!! success "Improvements ?"
|
!!! success "Improvements ?"
|
||||||
There are most likely ways to achieve the same goal but with a cleaner or simpler system.
|
There are most likely ways to achieve the same goal but with a cleaner or simpler system.
|
||||||
If you know such a way feel free to let me know.
|
If you know such a way feel free to let me know.
|
||||||
|
|
||||||
After that, use your superuser account to configure your authentication backend.
|
### Configuration, via Django Admin
|
||||||
Open the admin page and do the following
|
|
||||||
|
Instead of defining `SOCIALACCOUNT_PROVIDERS` in your environment, most configuration options can be done via the Admin interface. PKCE for `openid_connect` cannot currently be enabled this way.
|
||||||
|
Use your superuser account to configure your authentication backend by opening the admin page and do the following
|
||||||
|
|
||||||
1. Select `Sites` and edit the default site with the URL of your installation (or create a new).
|
1. Select `Sites` and edit the default site with the URL of your installation (or create a new).
|
||||||
2. Create a new `Social Application` with the required information as stated in the provider documentation of allauth.
|
2. Create a new `Social Application` with the required information as stated in the provider documentation of allauth.
|
||||||
3. Make sure to add your site to the list of available sites
|
3. Make sure to add your site to the list of available sites
|
||||||
|
|
||||||
Now the provider is configured and you should be able to sign up and sign in using the provider.
|
Now the provider is configured and you should be able to sign up and sign in using the provider.
|
||||||
Use the superuser account to grant permissions to the newly created users.
|
Use the superuser account to grant permissions to the newly created users, or enable default access via `SOCIAL_DEFAULT_ACCESS` & `SOCIAL_DEFAULT_GROUP`.
|
||||||
|
|
||||||
!!! info "WIP"
|
!!! info "WIP"
|
||||||
I do not have a ton of experience with using various single signon providers and also cannot test all of them.
|
I do not have a ton of experience with using various single signon providers and also cannot test all of them.
|
||||||
@@ -70,13 +78,7 @@ SOCIALACCOUNT_PROVIDERS='{"openid_connect":{"APPS":[{"provider_id":"keycloak","n
|
|||||||
'
|
'
|
||||||
```
|
```
|
||||||
|
|
||||||
1. Restart the service, login as superuser and open the `Admin` page.
|
You are now able to sign in using Keycloak after a restart of the service.
|
||||||
2. Make sure that the correct `Domain Name` is defined at `Sites`.
|
|
||||||
3. Select `Social Application` and chose `Keycloak` from the provider list.
|
|
||||||
4. Provide an arbitrary name for your authentication provider, and enter the `Client-ID` and `Secret Key` values obtained from Keycloak earlier.
|
|
||||||
5. Make sure to add your `Site` to the list of available sites and save the new `Social Application`.
|
|
||||||
|
|
||||||
You are now able to sign in using Keycloak.
|
|
||||||
|
|
||||||
### Linking accounts
|
### Linking accounts
|
||||||
To link an account to an already existing normal user go to the settings page of the user and link it.
|
To link an account to an already existing normal user go to the settings page of the user and link it.
|
||||||
|
|||||||
51
docs/install/archlinux.md
Normal file
51
docs/install/archlinux.md
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
!!! info "Community Contributed"
|
||||||
|
This guide was contributed by the community and is neither officially supported, nor updated or tested.
|
||||||
|
|
||||||
|
These are instructions for pacman based distributions, like ArchLinux. The package is available from the [AUR](https://aur.archlinux.org/packages/tandoor-recipes-git) or from [GitHub](https://github.com/jdecourval/tandoor-recipes-pkgbuild).
|
||||||
|
|
||||||
|
## Features
|
||||||
|
- systemd integration.
|
||||||
|
- Provide configuration for Nginx.
|
||||||
|
- Use socket activation.
|
||||||
|
- Use a non-root user.
|
||||||
|
- Apply migrations automatically.
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
1. Clone the package, build and install with makepkg:
|
||||||
|
```shell
|
||||||
|
git clone https://aur.archlinux.org/tandoor-recipes-git.git
|
||||||
|
cd tandoor-recipes-git
|
||||||
|
makepkg -si
|
||||||
|
```
|
||||||
|
or use your favourite AUR helper.
|
||||||
|
|
||||||
|
2. Setup a PostgreSQL database and user, as explained here: https://docs.tandoor.dev/install/manual/#setup-postgresql
|
||||||
|
|
||||||
|
3. Configure the service in `/etc/tandoor/tandoor.conf`.
|
||||||
|
|
||||||
|
4. Reinstall the package, or follow [the official instructions](https://docs.tandoor.dev/install/manual/#initialize-the-application) to have tandoor creates its DB tables.
|
||||||
|
|
||||||
|
5. Optionally configure a reverse proxy. A configuration for Nginx is provided, but you can Traefik, Apache, etc..
|
||||||
|
Edit `/etc/nginx/sites-available/tandoor.conf`. You may want to use another `server_name`, or configure TLS. Then:
|
||||||
|
```shell
|
||||||
|
cd /etc/nginx/sites-enabled
|
||||||
|
ln -s ../sites-available/tandoor.conf
|
||||||
|
systemctl restart nginx
|
||||||
|
```
|
||||||
|
|
||||||
|
6. Enable the service
|
||||||
|
```shell
|
||||||
|
systemctl enable --now tandoor
|
||||||
|
```
|
||||||
|
|
||||||
|
## Upgrade
|
||||||
|
```shell
|
||||||
|
cd tandoor-recipes-git
|
||||||
|
git pull
|
||||||
|
makepkg -sif
|
||||||
|
```
|
||||||
|
Or use your favourite AUR helper.
|
||||||
|
You shouldn't need to do anything else. This package applies migration automatically. If PostgreSQL has been updated to a new major version, you may need to [run pg_upgrade](https://wiki.archlinux.org/title/PostgreSQL#pg_upgrade).
|
||||||
|
|
||||||
|
## Help
|
||||||
|
This package is non-official. Issues should be posted to https://github.com/jdecourval/tandoor-recipes-pkgbuild or https://aur.archlinux.org/packages/tandoor-recipes-git.
|
||||||
@@ -35,6 +35,7 @@ nav:
|
|||||||
- KubeSail or PiBox: install/kubesail.md
|
- KubeSail or PiBox: install/kubesail.md
|
||||||
- TrueNAS Portainer: install/truenas_portainer.md
|
- TrueNAS Portainer: install/truenas_portainer.md
|
||||||
- WSL: install/wsl.md
|
- WSL: install/wsl.md
|
||||||
|
- ArchLinux: install/archlinux.md
|
||||||
- Manual: install/manual.md
|
- Manual: install/manual.md
|
||||||
- Other setups: install/other.md
|
- Other setups: install/other.md
|
||||||
- Features:
|
- Features:
|
||||||
|
|||||||
@@ -26,8 +26,7 @@ load_dotenv()
|
|||||||
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
||||||
|
|
||||||
# Get vars from .env files
|
# Get vars from .env files
|
||||||
SECRET_KEY = os.getenv('SECRET_KEY') if os.getenv(
|
SECRET_KEY = os.getenv('SECRET_KEY') if os.getenv('SECRET_KEY') else 'INSECURE_STANDARD_KEY_SET_IN_ENV'
|
||||||
'SECRET_KEY') else 'INSECURE_STANDARD_KEY_SET_IN_ENV'
|
|
||||||
|
|
||||||
DEBUG = bool(int(os.getenv('DEBUG', True)))
|
DEBUG = bool(int(os.getenv('DEBUG', True)))
|
||||||
DEBUG_TOOLBAR = bool(int(os.getenv('DEBUG_TOOLBAR', True)))
|
DEBUG_TOOLBAR = bool(int(os.getenv('DEBUG_TOOLBAR', True)))
|
||||||
@@ -38,11 +37,9 @@ SOCIAL_DEFAULT_GROUP = os.getenv('SOCIAL_DEFAULT_GROUP', 'guest')
|
|||||||
SPACE_DEFAULT_MAX_RECIPES = int(os.getenv('SPACE_DEFAULT_MAX_RECIPES', 0))
|
SPACE_DEFAULT_MAX_RECIPES = int(os.getenv('SPACE_DEFAULT_MAX_RECIPES', 0))
|
||||||
SPACE_DEFAULT_MAX_USERS = int(os.getenv('SPACE_DEFAULT_MAX_USERS', 0))
|
SPACE_DEFAULT_MAX_USERS = int(os.getenv('SPACE_DEFAULT_MAX_USERS', 0))
|
||||||
SPACE_DEFAULT_MAX_FILES = int(os.getenv('SPACE_DEFAULT_MAX_FILES', 0))
|
SPACE_DEFAULT_MAX_FILES = int(os.getenv('SPACE_DEFAULT_MAX_FILES', 0))
|
||||||
SPACE_DEFAULT_ALLOW_SHARING = bool(
|
SPACE_DEFAULT_ALLOW_SHARING = bool(int(os.getenv('SPACE_DEFAULT_ALLOW_SHARING', True)))
|
||||||
int(os.getenv('SPACE_DEFAULT_ALLOW_SHARING', True)))
|
|
||||||
|
|
||||||
INTERNAL_IPS = os.getenv('INTERNAL_IPS').split(
|
INTERNAL_IPS = os.getenv('INTERNAL_IPS').split(',') if os.getenv('INTERNAL_IPS') else ['127.0.0.1']
|
||||||
',') if os.getenv('INTERNAL_IPS') else ['127.0.0.1']
|
|
||||||
|
|
||||||
# allow djangos wsgi server to server mediafiles
|
# allow djangos wsgi server to server mediafiles
|
||||||
GUNICORN_MEDIA = bool(int(os.getenv('GUNICORN_MEDIA', False)))
|
GUNICORN_MEDIA = bool(int(os.getenv('GUNICORN_MEDIA', False)))
|
||||||
@@ -63,18 +60,15 @@ UNAUTHENTICATED_THEME_FROM_SPACE = int(os.getenv('UNAUTHENTICATED_THEME_FROM_SPA
|
|||||||
FORCE_THEME_FROM_SPACE = int(os.getenv('FORCE_THEME_FROM_SPACE', 0))
|
FORCE_THEME_FROM_SPACE = int(os.getenv('FORCE_THEME_FROM_SPACE', 0))
|
||||||
|
|
||||||
# minimum interval that users can set for automatic sync of shopping lists
|
# minimum interval that users can set for automatic sync of shopping lists
|
||||||
SHOPPING_MIN_AUTOSYNC_INTERVAL = int(
|
SHOPPING_MIN_AUTOSYNC_INTERVAL = int(os.getenv('SHOPPING_MIN_AUTOSYNC_INTERVAL', 5))
|
||||||
os.getenv('SHOPPING_MIN_AUTOSYNC_INTERVAL', 5))
|
|
||||||
|
|
||||||
ALLOWED_HOSTS = os.getenv('ALLOWED_HOSTS').split(
|
ALLOWED_HOSTS = os.getenv('ALLOWED_HOSTS').split(',') if os.getenv('ALLOWED_HOSTS') else ['*']
|
||||||
',') if os.getenv('ALLOWED_HOSTS') else ['*']
|
|
||||||
|
|
||||||
if os.getenv('CSRF_TRUSTED_ORIGINS'):
|
if os.getenv('CSRF_TRUSTED_ORIGINS'):
|
||||||
CSRF_TRUSTED_ORIGINS = os.getenv('CSRF_TRUSTED_ORIGINS').split(',')
|
CSRF_TRUSTED_ORIGINS = os.getenv('CSRF_TRUSTED_ORIGINS').split(',')
|
||||||
|
|
||||||
if CORS_ORIGIN_ALLOW_ALL := os.getenv('CORS_ORIGIN_ALLOW_ALL') is not None:
|
if CORS_ORIGIN_ALLOW_ALL := os.getenv('CORS_ORIGIN_ALLOW_ALL') is not None:
|
||||||
print(
|
print('DEPRECATION WARNING: Environment var "CORS_ORIGIN_ALLOW_ALL" is deprecated. Please use "CORS_ALLOW_ALL_ORIGINS."')
|
||||||
'DEPRECATION WARNING: Environment var "CORS_ORIGIN_ALLOW_ALL" is deprecated. Please use "CORS_ALLOW_ALL_ORIGINS."')
|
|
||||||
CORS_ALLOW_ALL_ORIGINS = CORS_ORIGIN_ALLOW_ALL
|
CORS_ALLOW_ALL_ORIGINS = CORS_ORIGIN_ALLOW_ALL
|
||||||
else:
|
else:
|
||||||
CORS_ALLOW_ALL_ORIGINS = bool(int(os.getenv("CORS_ALLOW_ALL_ORIGINS", True)))
|
CORS_ALLOW_ALL_ORIGINS = bool(int(os.getenv("CORS_ALLOW_ALL_ORIGINS", True)))
|
||||||
@@ -106,9 +100,7 @@ PRIVACY_URL = os.getenv('PRIVACY_URL', '')
|
|||||||
IMPRINT_URL = os.getenv('IMPRINT_URL', '')
|
IMPRINT_URL = os.getenv('IMPRINT_URL', '')
|
||||||
HOSTED = bool(int(os.getenv('HOSTED', False)))
|
HOSTED = bool(int(os.getenv('HOSTED', False)))
|
||||||
|
|
||||||
MESSAGE_TAGS = {
|
MESSAGE_TAGS = {messages.ERROR: 'danger'}
|
||||||
messages.ERROR: 'danger'
|
|
||||||
}
|
|
||||||
|
|
||||||
# Application definition
|
# Application definition
|
||||||
|
|
||||||
@@ -162,8 +154,7 @@ try:
|
|||||||
INSTALLED_APPS.append(plugin_module)
|
INSTALLED_APPS.append(plugin_module)
|
||||||
|
|
||||||
plugin_config = {
|
plugin_config = {
|
||||||
'name': plugin_class.verbose_name if hasattr(plugin_class,
|
'name': plugin_class.verbose_name if hasattr(plugin_class, 'verbose_name') else plugin_class.name,
|
||||||
'verbose_name') else plugin_class.name,
|
|
||||||
'version': plugin_class.VERSION if hasattr(plugin_class, 'VERSION') else 'unknown',
|
'version': plugin_class.VERSION if hasattr(plugin_class, 'VERSION') else 'unknown',
|
||||||
'website': plugin_class.website if hasattr(plugin_class, 'website') else '',
|
'website': plugin_class.website if hasattr(plugin_class, 'website') else '',
|
||||||
'github': plugin_class.github if hasattr(plugin_class, 'github') else '',
|
'github': plugin_class.github if hasattr(plugin_class, 'github') else '',
|
||||||
@@ -171,8 +162,7 @@ try:
|
|||||||
'base_path': os.path.join(BASE_DIR, 'recipes', 'plugins', d),
|
'base_path': os.path.join(BASE_DIR, 'recipes', 'plugins', d),
|
||||||
'base_url': plugin_class.base_url,
|
'base_url': plugin_class.base_url,
|
||||||
'bundle_name': plugin_class.bundle_name if hasattr(plugin_class, 'bundle_name') else '',
|
'bundle_name': plugin_class.bundle_name if hasattr(plugin_class, 'bundle_name') else '',
|
||||||
'api_router_name': plugin_class.api_router_name if hasattr(plugin_class,
|
'api_router_name': plugin_class.api_router_name if hasattr(plugin_class, 'api_router_name') else '',
|
||||||
'api_router_name') else '',
|
|
||||||
'nav_main': plugin_class.nav_main if hasattr(plugin_class, 'nav_main') else '',
|
'nav_main': plugin_class.nav_main if hasattr(plugin_class, 'nav_main') else '',
|
||||||
'nav_dropdown': plugin_class.nav_dropdown if hasattr(plugin_class, 'nav_dropdown') else '',
|
'nav_dropdown': plugin_class.nav_dropdown if hasattr(plugin_class, 'nav_dropdown') else '',
|
||||||
}
|
}
|
||||||
@@ -186,8 +176,7 @@ except Exception:
|
|||||||
if DEBUG:
|
if DEBUG:
|
||||||
print('ERROR failed to initialize plugins')
|
print('ERROR failed to initialize plugins')
|
||||||
|
|
||||||
SOCIAL_PROVIDERS = os.getenv('SOCIAL_PROVIDERS').split(
|
SOCIAL_PROVIDERS = os.getenv('SOCIAL_PROVIDERS').split(',') if os.getenv('SOCIAL_PROVIDERS') else []
|
||||||
',') if os.getenv('SOCIAL_PROVIDERS') else []
|
|
||||||
SOCIALACCOUNT_EMAIL_VERIFICATION = 'none'
|
SOCIALACCOUNT_EMAIL_VERIFICATION = 'none'
|
||||||
INSTALLED_APPS = INSTALLED_APPS + SOCIAL_PROVIDERS
|
INSTALLED_APPS = INSTALLED_APPS + SOCIAL_PROVIDERS
|
||||||
|
|
||||||
@@ -198,11 +187,9 @@ ACCOUNT_EMAIL_CONFIRMATION_EXPIRE_DAYS = 90
|
|||||||
ACCOUNT_LOGOUT_ON_GET = True
|
ACCOUNT_LOGOUT_ON_GET = True
|
||||||
|
|
||||||
try:
|
try:
|
||||||
SOCIALACCOUNT_PROVIDERS = ast.literal_eval(
|
SOCIALACCOUNT_PROVIDERS = ast.literal_eval(os.getenv('SOCIALACCOUNT_PROVIDERS') if os.getenv('SOCIALACCOUNT_PROVIDERS') else '{}')
|
||||||
os.getenv('SOCIALACCOUNT_PROVIDERS') if os.getenv('SOCIALACCOUNT_PROVIDERS') else '{}')
|
|
||||||
except ValueError:
|
except ValueError:
|
||||||
SOCIALACCOUNT_PROVIDERS = json.loads(
|
SOCIALACCOUNT_PROVIDERS = json.loads(os.getenv('SOCIALACCOUNT_PROVIDERS').replace("'", '"') if os.getenv('SOCIALACCOUNT_PROVIDERS') else '{}')
|
||||||
os.getenv('SOCIALACCOUNT_PROVIDERS').replace("'", '"') if os.getenv('SOCIALACCOUNT_PROVIDERS') else '{}')
|
|
||||||
|
|
||||||
SESSION_COOKIE_DOMAIN = os.getenv('SESSION_COOKIE_DOMAIN', None)
|
SESSION_COOKIE_DOMAIN = os.getenv('SESSION_COOKIE_DOMAIN', None)
|
||||||
SESSION_COOKIE_NAME = os.getenv('SESSION_COOKIE_NAME', 'sessionid')
|
SESSION_COOKIE_NAME = os.getenv('SESSION_COOKIE_NAME', 'sessionid')
|
||||||
@@ -215,30 +202,21 @@ ENABLE_PDF_EXPORT = bool(int(os.getenv('ENABLE_PDF_EXPORT', False)))
|
|||||||
EXPORT_FILE_CACHE_DURATION = int(os.getenv('EXPORT_FILE_CACHE_DURATION', 600))
|
EXPORT_FILE_CACHE_DURATION = int(os.getenv('EXPORT_FILE_CACHE_DURATION', 600))
|
||||||
|
|
||||||
MIDDLEWARE = [
|
MIDDLEWARE = [
|
||||||
'corsheaders.middleware.CorsMiddleware',
|
'corsheaders.middleware.CorsMiddleware', 'django.middleware.security.SecurityMiddleware', 'whitenoise.middleware.WhiteNoiseMiddleware',
|
||||||
'django.middleware.security.SecurityMiddleware',
|
'django.contrib.sessions.middleware.SessionMiddleware', 'django.middleware.common.CommonMiddleware', 'django.middleware.csrf.CsrfViewMiddleware',
|
||||||
'whitenoise.middleware.WhiteNoiseMiddleware',
|
'django.contrib.auth.middleware.AuthenticationMiddleware', 'django.contrib.messages.middleware.MessageMiddleware', 'django.middleware.locale.LocaleMiddleware',
|
||||||
'django.contrib.sessions.middleware.SessionMiddleware',
|
'django.middleware.clickjacking.XFrameOptionsMiddleware', 'cookbook.helper.scope_middleware.ScopeMiddleware', 'allauth.account.middleware.AccountMiddleware',
|
||||||
'django.middleware.common.CommonMiddleware',
|
|
||||||
'django.middleware.csrf.CsrfViewMiddleware',
|
|
||||||
'django.contrib.auth.middleware.AuthenticationMiddleware',
|
|
||||||
'django.contrib.messages.middleware.MessageMiddleware',
|
|
||||||
'django.middleware.locale.LocaleMiddleware',
|
|
||||||
'django.middleware.clickjacking.XFrameOptionsMiddleware',
|
|
||||||
'cookbook.helper.scope_middleware.ScopeMiddleware',
|
|
||||||
'allauth.account.middleware.AccountMiddleware',
|
|
||||||
]
|
]
|
||||||
|
|
||||||
if DEBUG_TOOLBAR:
|
if DEBUG_TOOLBAR:
|
||||||
MIDDLEWARE += ('debug_toolbar.middleware.DebugToolbarMiddleware',)
|
MIDDLEWARE += ('debug_toolbar.middleware.DebugToolbarMiddleware', )
|
||||||
INSTALLED_APPS += ('debug_toolbar',)
|
INSTALLED_APPS += ('debug_toolbar', )
|
||||||
|
|
||||||
SORT_TREE_BY_NAME = bool(int(os.getenv('SORT_TREE_BY_NAME', False)))
|
SORT_TREE_BY_NAME = bool(int(os.getenv('SORT_TREE_BY_NAME', False)))
|
||||||
DISABLE_TREE_FIX_STARTUP = bool(
|
DISABLE_TREE_FIX_STARTUP = bool(int(os.getenv('DISABLE_TREE_FIX_STARTUP', False)))
|
||||||
int(os.getenv('DISABLE_TREE_FIX_STARTUP', False)))
|
|
||||||
|
|
||||||
if bool(int(os.getenv('SQL_DEBUG', False))):
|
if bool(int(os.getenv('SQL_DEBUG', False))):
|
||||||
MIDDLEWARE += ('recipes.middleware.SqlPrintingMiddleware',)
|
MIDDLEWARE += ('recipes.middleware.SqlPrintingMiddleware', )
|
||||||
|
|
||||||
if ENABLE_METRICS:
|
if ENABLE_METRICS:
|
||||||
MIDDLEWARE += 'django_prometheus.middleware.PrometheusAfterMiddleware',
|
MIDDLEWARE += 'django_prometheus.middleware.PrometheusAfterMiddleware',
|
||||||
@@ -257,35 +235,34 @@ if LDAP_AUTH:
|
|||||||
AUTH_LDAP_START_TLS = bool(int(os.getenv('AUTH_LDAP_START_TLS', False)))
|
AUTH_LDAP_START_TLS = bool(int(os.getenv('AUTH_LDAP_START_TLS', False)))
|
||||||
AUTH_LDAP_BIND_DN = os.getenv('AUTH_LDAP_BIND_DN')
|
AUTH_LDAP_BIND_DN = os.getenv('AUTH_LDAP_BIND_DN')
|
||||||
AUTH_LDAP_BIND_PASSWORD = os.getenv('AUTH_LDAP_BIND_PASSWORD')
|
AUTH_LDAP_BIND_PASSWORD = os.getenv('AUTH_LDAP_BIND_PASSWORD')
|
||||||
AUTH_LDAP_USER_SEARCH = LDAPSearch(
|
AUTH_LDAP_USER_SEARCH = LDAPSearch(os.getenv('AUTH_LDAP_USER_SEARCH_BASE_DN'), ldap.SCOPE_SUBTREE, os.getenv('AUTH_LDAP_USER_SEARCH_FILTER_STR', '(uid=%(user)s)'), )
|
||||||
os.getenv('AUTH_LDAP_USER_SEARCH_BASE_DN'),
|
AUTH_LDAP_USER_ATTR_MAP = ast.literal_eval(os.getenv('AUTH_LDAP_USER_ATTR_MAP')) if os.getenv('AUTH_LDAP_USER_ATTR_MAP') else {
|
||||||
ldap.SCOPE_SUBTREE,
|
|
||||||
os.getenv('AUTH_LDAP_USER_SEARCH_FILTER_STR', '(uid=%(user)s)'),
|
|
||||||
)
|
|
||||||
AUTH_LDAP_USER_ATTR_MAP = ast.literal_eval(os.getenv('AUTH_LDAP_USER_ATTR_MAP')) if os.getenv(
|
|
||||||
'AUTH_LDAP_USER_ATTR_MAP') else {
|
|
||||||
'first_name': 'givenName',
|
'first_name': 'givenName',
|
||||||
'last_name': 'sn',
|
'last_name': 'sn',
|
||||||
'email': 'mail',
|
'email': 'mail',
|
||||||
}
|
}
|
||||||
AUTH_LDAP_ALWAYS_UPDATE_USER = bool(
|
AUTH_LDAP_ALWAYS_UPDATE_USER = bool(int(os.getenv('AUTH_LDAP_ALWAYS_UPDATE_USER', True)))
|
||||||
int(os.getenv('AUTH_LDAP_ALWAYS_UPDATE_USER', True)))
|
|
||||||
AUTH_LDAP_CACHE_TIMEOUT = int(os.getenv('AUTH_LDAP_CACHE_TIMEOUT', 3600))
|
AUTH_LDAP_CACHE_TIMEOUT = int(os.getenv('AUTH_LDAP_CACHE_TIMEOUT', 3600))
|
||||||
if 'AUTH_LDAP_TLS_CACERTFILE' in os.environ:
|
if 'AUTH_LDAP_TLS_CACERTFILE' in os.environ:
|
||||||
AUTH_LDAP_GLOBAL_OPTIONS = {
|
AUTH_LDAP_GLOBAL_OPTIONS = {ldap.OPT_X_TLS_CACERTFILE: os.getenv('AUTH_LDAP_TLS_CACERTFILE')}
|
||||||
ldap.OPT_X_TLS_CACERTFILE: os.getenv('AUTH_LDAP_TLS_CACERTFILE')}
|
|
||||||
if DEBUG:
|
if DEBUG:
|
||||||
LOGGING = {
|
LOGGING = {
|
||||||
"version": 1,
|
"version": 1,
|
||||||
"disable_existing_loggers": False,
|
"disable_existing_loggers": False,
|
||||||
"handlers": {"console": {"class": "logging.StreamHandler"}},
|
"handlers": {
|
||||||
"loggers": {"django_auth_ldap": {"level": "DEBUG", "handlers": ["console"]}},
|
"console": {
|
||||||
|
"class": "logging.StreamHandler"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"loggers": {
|
||||||
|
"django_auth_ldap": {
|
||||||
|
"level": "DEBUG",
|
||||||
|
"handlers": ["console"]
|
||||||
|
}
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
AUTHENTICATION_BACKENDS += [
|
AUTHENTICATION_BACKENDS += ['django.contrib.auth.backends.ModelBackend', 'allauth.account.auth_backends.AuthenticationBackend', ]
|
||||||
'django.contrib.auth.backends.ModelBackend',
|
|
||||||
'allauth.account.auth_backends.AuthenticationBackend',
|
|
||||||
]
|
|
||||||
|
|
||||||
# django allauth site id
|
# django allauth site id
|
||||||
SITE_ID = int(os.getenv('ALLAUTH_SITE_ID', 1))
|
SITE_ID = int(os.getenv('ALLAUTH_SITE_ID', 1))
|
||||||
@@ -294,75 +271,55 @@ ACCOUNT_ADAPTER = 'cookbook.helper.AllAuthCustomAdapter'
|
|||||||
|
|
||||||
if REMOTE_USER_AUTH:
|
if REMOTE_USER_AUTH:
|
||||||
MIDDLEWARE.insert(8, 'recipes.middleware.CustomRemoteUser')
|
MIDDLEWARE.insert(8, 'recipes.middleware.CustomRemoteUser')
|
||||||
AUTHENTICATION_BACKENDS.append(
|
AUTHENTICATION_BACKENDS.append('django.contrib.auth.backends.RemoteUserBackend')
|
||||||
'django.contrib.auth.backends.RemoteUserBackend')
|
|
||||||
|
|
||||||
# Password validation
|
# Password validation
|
||||||
# https://docs.djangoproject.com/en/2.0/ref/settings/#auth-password-validators
|
# https://docs.djangoproject.com/en/2.0/ref/settings/#auth-password-validators
|
||||||
|
|
||||||
AUTH_PASSWORD_VALIDATORS = [
|
AUTH_PASSWORD_VALIDATORS = [{
|
||||||
{
|
'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
|
||||||
'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
|
}, {
|
||||||
},
|
'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
|
||||||
{
|
}, {
|
||||||
'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
|
'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
|
||||||
},
|
}, {
|
||||||
{
|
'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
|
||||||
'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
|
}, ]
|
||||||
},
|
|
||||||
{
|
|
||||||
'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
|
|
||||||
},
|
|
||||||
]
|
|
||||||
|
|
||||||
SECURE_PROXY_SSL_HEADER = ('HTTP_X_FORWARDED_PROTO', 'https')
|
SECURE_PROXY_SSL_HEADER = ('HTTP_X_FORWARDED_PROTO', 'https')
|
||||||
|
|
||||||
OAUTH2_PROVIDER = {
|
OAUTH2_PROVIDER = {'SCOPES': {'read': 'Read scope', 'write': 'Write scope', 'bookmarklet': 'only access to bookmarklet'}}
|
||||||
'SCOPES': {'read': 'Read scope', 'write': 'Write scope', 'bookmarklet': 'only access to bookmarklet'}
|
|
||||||
}
|
|
||||||
READ_SCOPE = 'read'
|
READ_SCOPE = 'read'
|
||||||
WRITE_SCOPE = 'write'
|
WRITE_SCOPE = 'write'
|
||||||
|
|
||||||
REST_FRAMEWORK = {
|
REST_FRAMEWORK = {
|
||||||
'DEFAULT_AUTHENTICATION_CLASSES': (
|
'DEFAULT_AUTHENTICATION_CLASSES':
|
||||||
'rest_framework.authentication.SessionAuthentication',
|
('rest_framework.authentication.SessionAuthentication', 'oauth2_provider.contrib.rest_framework.OAuth2Authentication', 'rest_framework.authentication.BasicAuthentication',
|
||||||
'oauth2_provider.contrib.rest_framework.OAuth2Authentication',
|
),
|
||||||
'rest_framework.authentication.BasicAuthentication',
|
'DEFAULT_PERMISSION_CLASSES': ['rest_framework.permissions.IsAuthenticated', ],
|
||||||
),
|
|
||||||
'DEFAULT_PERMISSION_CLASSES': [
|
|
||||||
'rest_framework.permissions.IsAuthenticated',
|
|
||||||
],
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ROOT_URLCONF = 'recipes.urls'
|
ROOT_URLCONF = 'recipes.urls'
|
||||||
|
|
||||||
TEMPLATES = [
|
TEMPLATES = [{
|
||||||
{
|
'BACKEND': 'django.template.backends.django.DjangoTemplates',
|
||||||
'BACKEND': 'django.template.backends.django.DjangoTemplates',
|
'DIRS': [os.path.join(BASE_DIR, 'templates'), os.path.join(BASE_DIR, 'cookbook', 'templates')],
|
||||||
'DIRS': [os.path.join(BASE_DIR, 'templates'), os.path.join(BASE_DIR, 'cookbook', 'templates')],
|
'APP_DIRS': True,
|
||||||
'APP_DIRS': True,
|
'OPTIONS': {
|
||||||
'OPTIONS': {
|
'context_processors': [
|
||||||
'context_processors': [
|
'django.template.context_processors.debug', 'django.template.context_processors.request', 'django.contrib.auth.context_processors.auth',
|
||||||
'django.template.context_processors.debug',
|
'django.contrib.messages.context_processors.messages', 'django.template.context_processors.media', 'cookbook.helper.context_processors.context_settings',
|
||||||
'django.template.context_processors.request',
|
],
|
||||||
'django.contrib.auth.context_processors.auth',
|
|
||||||
'django.contrib.messages.context_processors.messages',
|
|
||||||
'django.template.context_processors.media',
|
|
||||||
'cookbook.helper.context_processors.context_settings',
|
|
||||||
],
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
]
|
}, ]
|
||||||
|
|
||||||
WSGI_APPLICATION = 'recipes.wsgi.application'
|
WSGI_APPLICATION = 'recipes.wsgi.application'
|
||||||
|
|
||||||
# Database
|
# Database
|
||||||
# Load settings from env files
|
# Load settings from env files
|
||||||
if os.getenv('DATABASE_URL'):
|
if os.getenv('DATABASE_URL'):
|
||||||
match = re.match(
|
match = re.match(r'(?P<schema>\w+):\/\/(?:(?P<user>[\w\d_-]+)(?::(?P<password>[^@]+))?@)?(?P<host>[^:/]+)(?::(?P<port>\d+))?(?:/(?P<database>[\w\d/._-]+))?',
|
||||||
r'(?P<schema>\w+):\/\/(?:(?P<user>[\w\d_-]+)(?::(?P<password>[^@]+))?@)?(?P<host>[^:/]+)(?::(?P<port>\d+))?(?:/(?P<database>[\w\d/._-]+))?',
|
os.getenv('DATABASE_URL'))
|
||||||
os.getenv('DATABASE_URL')
|
|
||||||
)
|
|
||||||
settings = match.groupdict()
|
settings = match.groupdict()
|
||||||
schema = settings['schema']
|
schema = settings['schema']
|
||||||
if schema.startswith('postgres'):
|
if schema.startswith('postgres'):
|
||||||
@@ -423,12 +380,7 @@ else:
|
|||||||
# }
|
# }
|
||||||
# }
|
# }
|
||||||
|
|
||||||
CACHES = {
|
CACHES = {'default': {'BACKEND': 'django.core.cache.backends.locmem.LocMemCache', 'LOCATION': 'default', }}
|
||||||
'default': {
|
|
||||||
'BACKEND': 'django.core.cache.backends.locmem.LocMemCache',
|
|
||||||
'LOCATION': 'default',
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
# Vue webpack settings
|
# Vue webpack settings
|
||||||
VUE_DIR = os.path.join(BASE_DIR, 'vue')
|
VUE_DIR = os.path.join(BASE_DIR, 'vue')
|
||||||
@@ -491,33 +443,16 @@ USE_L10N = True
|
|||||||
|
|
||||||
USE_TZ = True
|
USE_TZ = True
|
||||||
|
|
||||||
LANGUAGES = [
|
LANGUAGES = [('hy', _('Armenian ')), ('bg', _('Bulgarian')), ('ca', _('Catalan')), ('cs', _('Czech')), ('da', _('Danish')), ('nl', _('Dutch')), ('en', _('English')),
|
||||||
('hy', _('Armenian ')),
|
('fr', _('French')), ('de', _('German')), ('hu', _('Hungarian')), ('it', _('Italian')), ('lv', _('Latvian')), ('nb', _('Norwegian ')), ('pl', _('Polish')),
|
||||||
('bg', _('Bulgarian')),
|
('ru', _('Russian')), ('es', _('Spanish')), ('sv', _('Swedish')), ]
|
||||||
('ca', _('Catalan')),
|
|
||||||
('cs', _('Czech')),
|
|
||||||
('da', _('Danish')),
|
|
||||||
('nl', _('Dutch')),
|
|
||||||
('en', _('English')),
|
|
||||||
('fr', _('French')),
|
|
||||||
('de', _('German')),
|
|
||||||
('hu', _('Hungarian')),
|
|
||||||
('it', _('Italian')),
|
|
||||||
('lv', _('Latvian')),
|
|
||||||
('nb', _('Norwegian ')),
|
|
||||||
('pl', _('Polish')),
|
|
||||||
('ru', _('Russian')),
|
|
||||||
('es', _('Spanish')),
|
|
||||||
('sv', _('Swedish')),
|
|
||||||
]
|
|
||||||
|
|
||||||
# Static files (CSS, JavaScript, Images)
|
# Static files (CSS, JavaScript, Images)
|
||||||
# https://docs.djangoproject.com/en/2.0/howto/static-files/
|
# https://docs.djangoproject.com/en/2.0/howto/static-files/
|
||||||
|
|
||||||
SCRIPT_NAME = os.getenv('SCRIPT_NAME', '')
|
SCRIPT_NAME = os.getenv('SCRIPT_NAME', '')
|
||||||
# path for django_js_reverse to generate the javascript file containing all urls. Only done because the default command (collectstatic_js_reverse) fails to update the manifest
|
# path for django_js_reverse to generate the javascript file containing all urls. Only done because the default command (collectstatic_js_reverse) fails to update the manifest
|
||||||
JS_REVERSE_OUTPUT_PATH = os.path.join(
|
JS_REVERSE_OUTPUT_PATH = os.path.join(BASE_DIR, "cookbook/static/django_js_reverse")
|
||||||
BASE_DIR, "cookbook/static/django_js_reverse")
|
|
||||||
JS_REVERSE_SCRIPT_PREFIX = os.getenv('JS_REVERSE_SCRIPT_PREFIX', SCRIPT_NAME)
|
JS_REVERSE_SCRIPT_PREFIX = os.getenv('JS_REVERSE_SCRIPT_PREFIX', SCRIPT_NAME)
|
||||||
|
|
||||||
STATIC_URL = os.getenv('STATIC_URL', '/static/')
|
STATIC_URL = os.getenv('STATIC_URL', '/static/')
|
||||||
@@ -572,23 +507,13 @@ EMAIL_HOST_PASSWORD = os.getenv('EMAIL_HOST_PASSWORD', '')
|
|||||||
EMAIL_USE_TLS = bool(int(os.getenv('EMAIL_USE_TLS', False)))
|
EMAIL_USE_TLS = bool(int(os.getenv('EMAIL_USE_TLS', False)))
|
||||||
EMAIL_USE_SSL = bool(int(os.getenv('EMAIL_USE_SSL', False)))
|
EMAIL_USE_SSL = bool(int(os.getenv('EMAIL_USE_SSL', False)))
|
||||||
DEFAULT_FROM_EMAIL = os.getenv('DEFAULT_FROM_EMAIL', 'webmaster@localhost')
|
DEFAULT_FROM_EMAIL = os.getenv('DEFAULT_FROM_EMAIL', 'webmaster@localhost')
|
||||||
ACCOUNT_EMAIL_SUBJECT_PREFIX = os.getenv(
|
ACCOUNT_EMAIL_SUBJECT_PREFIX = os.getenv('ACCOUNT_EMAIL_SUBJECT_PREFIX', '[Tandoor Recipes] ') # allauth sender prefix
|
||||||
'ACCOUNT_EMAIL_SUBJECT_PREFIX', '[Tandoor Recipes] ') # allauth sender prefix
|
|
||||||
|
|
||||||
# ACCOUNT_SIGNUP_FORM_CLASS = 'cookbook.forms.AllAuthSignupForm'
|
# ACCOUNT_SIGNUP_FORM_CLASS = 'cookbook.forms.AllAuthSignupForm'
|
||||||
ACCOUNT_FORMS = {
|
ACCOUNT_FORMS = {'signup': 'cookbook.forms.AllAuthSignupForm', 'reset_password': 'cookbook.forms.CustomPasswordResetForm'}
|
||||||
'signup': 'cookbook.forms.AllAuthSignupForm',
|
|
||||||
'reset_password': 'cookbook.forms.CustomPasswordResetForm'
|
|
||||||
}
|
|
||||||
|
|
||||||
ACCOUNT_EMAIL_UNKNOWN_ACCOUNTS = False
|
ACCOUNT_EMAIL_UNKNOWN_ACCOUNTS = False
|
||||||
ACCOUNT_RATE_LIMITS = {
|
ACCOUNT_RATE_LIMITS = {"change_password": "1/m/user", "reset_password": "1/m/ip,1/m/key", "reset_password_from_key": "1/m/ip", "signup": "5/m/ip", "login": "5/m/ip", }
|
||||||
"change_password": "1/m/user",
|
|
||||||
"reset_password": "1/m/ip,1/m/key",
|
|
||||||
"reset_password_from_key": "1/m/ip",
|
|
||||||
"signup": "5/m/ip",
|
|
||||||
"login": "5/m/ip",
|
|
||||||
}
|
|
||||||
|
|
||||||
DISABLE_EXTERNAL_CONNECTORS = bool(int(os.getenv('DISABLE_EXTERNAL_CONNECTORS', False)))
|
DISABLE_EXTERNAL_CONNECTORS = bool(int(os.getenv('DISABLE_EXTERNAL_CONNECTORS', False)))
|
||||||
EXTERNAL_CONNECTORS_QUEUE_SIZE = int(os.getenv('EXTERNAL_CONNECTORS_QUEUE_SIZE', 100))
|
EXTERNAL_CONNECTORS_QUEUE_SIZE = int(os.getenv('EXTERNAL_CONNECTORS_QUEUE_SIZE', 100))
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
Django==4.2.10
|
Django==4.2.10
|
||||||
cryptography===42.0.4
|
cryptography===42.0.4
|
||||||
django-annoying==0.10.6
|
django-annoying==0.10.6
|
||||||
django-autocomplete-light==3.9.7
|
|
||||||
django-cleanup==8.0.0
|
django-cleanup==8.0.0
|
||||||
django-crispy-forms==2.0
|
django-crispy-forms==2.0
|
||||||
crispy-bootstrap4==2022.1
|
crispy-bootstrap4==2022.1
|
||||||
|
|||||||
@@ -1,177 +0,0 @@
|
|||||||
<template>
|
|
||||||
<!-- TODO: Deprecate -->
|
|
||||||
<div id="app">
|
|
||||||
<div class="row">
|
|
||||||
<div class="col col-md-12">
|
|
||||||
<h2>{{ $t("Supermarket") }}</h2>
|
|
||||||
|
|
||||||
<multiselect v-model="selected_supermarket" track-by="id" label="name" :options="supermarkets" @input="selectedSupermarketChanged"> </multiselect>
|
|
||||||
|
|
||||||
<b-button class="btn btn-primary btn-block" style="margin-top: 1vh" v-b-modal.modal-supermarket>
|
|
||||||
{{ $t("Edit") }}
|
|
||||||
</b-button>
|
|
||||||
<b-button class="btn btn-success btn-block" @click="selected_supermarket = { new: true, name: '' }" v-b-modal.modal-supermarket>{{ $t("New") }} </b-button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<hr />
|
|
||||||
|
|
||||||
<div class="row">
|
|
||||||
<div class="col col-md-6">
|
|
||||||
<h4>
|
|
||||||
{{ $t("Categories") }}
|
|
||||||
<button class="btn btn-success btn-sm" @click="selected_category = { new: true, name: '' }" v-b-modal.modal-category>{{ $t("New") }}</button>
|
|
||||||
</h4>
|
|
||||||
|
|
||||||
<draggable :list="selectable_categories" group="supermarket_categories" :empty-insert-threshold="10">
|
|
||||||
<div v-for="c in selectable_categories" :key="c.id">
|
|
||||||
<button class="btn btn-block btn-sm btn-primary" style="margin-top: 0.5vh">{{ c.name }}</button>
|
|
||||||
</div>
|
|
||||||
</draggable>
|
|
||||||
</div>
|
|
||||||
<div class="col col-md-6">
|
|
||||||
<h4>{{ $t("Selected") }} {{ $t("Categories") }}</h4>
|
|
||||||
|
|
||||||
<draggable :list="supermarket_categories" group="supermarket_categories" :empty-insert-threshold="10" @change="selectedCategoriesChanged">
|
|
||||||
<div v-for="c in supermarket_categories" :key="c.id">
|
|
||||||
<button class="btn btn-block btn-sm btn-primary" style="margin-top: 0.5vh">{{ c.name }}</button>
|
|
||||||
</div>
|
|
||||||
</draggable>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- EDIT MODALS -->
|
|
||||||
<b-modal id="modal-supermarket" v-bind:title="$t('Supermarket')" @ok="supermarketModalOk()">
|
|
||||||
<label v-if="selected_supermarket !== undefined">
|
|
||||||
{{ $t("Name") }}
|
|
||||||
<b-input v-model="selected_supermarket.name"></b-input>
|
|
||||||
</label>
|
|
||||||
</b-modal>
|
|
||||||
|
|
||||||
<b-modal id="modal-category" v-bind:title="$t('Category')" @ok="categoryModalOk()">
|
|
||||||
<label v-if="selected_category !== undefined">
|
|
||||||
{{ $t("Name") }}
|
|
||||||
<b-input v-model="selected_category.name"></b-input>
|
|
||||||
</label>
|
|
||||||
</b-modal>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
import Vue from "vue"
|
|
||||||
import { BootstrapVue } from "bootstrap-vue"
|
|
||||||
|
|
||||||
import "bootstrap-vue/dist/bootstrap-vue.css"
|
|
||||||
|
|
||||||
import { ResolveUrlMixin, ToastMixin } from "@/utils/utils"
|
|
||||||
|
|
||||||
import { ApiApiFactory } from "@/utils/openapi/api.ts"
|
|
||||||
|
|
||||||
Vue.use(BootstrapVue)
|
|
||||||
import draggable from "vuedraggable"
|
|
||||||
|
|
||||||
import axios from "axios"
|
|
||||||
import Multiselect from "vue-multiselect"
|
|
||||||
|
|
||||||
axios.defaults.xsrfHeaderName = "X-CSRFToken"
|
|
||||||
axios.defaults.xsrfCookieName = "csrftoken"
|
|
||||||
|
|
||||||
export default {
|
|
||||||
name: "SupermarketView",
|
|
||||||
mixins: [ResolveUrlMixin, ToastMixin],
|
|
||||||
components: {
|
|
||||||
Multiselect,
|
|
||||||
draggable,
|
|
||||||
},
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
supermarkets: [],
|
|
||||||
categories: [],
|
|
||||||
|
|
||||||
selected_supermarket: {},
|
|
||||||
selected_category: {},
|
|
||||||
|
|
||||||
selectable_categories: [],
|
|
||||||
supermarket_categories: [],
|
|
||||||
}
|
|
||||||
},
|
|
||||||
mounted() {
|
|
||||||
this.$i18n.locale = window.CUSTOM_LOCALE
|
|
||||||
this.loadInitial()
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
loadInitial: function () {
|
|
||||||
let apiClient = new ApiApiFactory()
|
|
||||||
apiClient.listSupermarkets().then((results) => {
|
|
||||||
this.supermarkets = results.data
|
|
||||||
})
|
|
||||||
apiClient.listSupermarketCategorys().then((results) => {
|
|
||||||
this.categories = results.data
|
|
||||||
this.selectable_categories = this.categories
|
|
||||||
})
|
|
||||||
},
|
|
||||||
selectedCategoriesChanged: function (data) {
|
|
||||||
let apiClient = new ApiApiFactory()
|
|
||||||
|
|
||||||
if ("removed" in data) {
|
|
||||||
let relation = this.selected_supermarket.category_to_supermarket.filter((el) => el.category.id === data.removed.element.id)[0]
|
|
||||||
apiClient.destroySupermarketCategoryRelation(relation.id)
|
|
||||||
}
|
|
||||||
|
|
||||||
if ("added" in data) {
|
|
||||||
apiClient
|
|
||||||
.createSupermarketCategoryRelation({
|
|
||||||
category: data.added.element,
|
|
||||||
supermarket: this.selected_supermarket.id,
|
|
||||||
order: 0,
|
|
||||||
})
|
|
||||||
.then((results) => {
|
|
||||||
this.selected_supermarket.category_to_supermarket.push(results.data)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
if ("moved" in data || "added" in data) {
|
|
||||||
this.supermarket_categories.forEach((element, index) => {
|
|
||||||
let relation = this.selected_supermarket.category_to_supermarket.filter((el) => el.category.id === element.id)[0]
|
|
||||||
apiClient.partialUpdateSupermarketCategoryRelation(relation.id, { order: index })
|
|
||||||
})
|
|
||||||
}
|
|
||||||
},
|
|
||||||
selectedSupermarketChanged: function (supermarket, id) {
|
|
||||||
this.supermarket_categories = []
|
|
||||||
this.selectable_categories = this.categories
|
|
||||||
|
|
||||||
for (let i of supermarket.category_to_supermarket) {
|
|
||||||
this.supermarket_categories.push(i.category)
|
|
||||||
this.selectable_categories = this.selectable_categories.filter(function (el) {
|
|
||||||
return el.id !== i.category.id
|
|
||||||
})
|
|
||||||
}
|
|
||||||
},
|
|
||||||
supermarketModalOk: function () {
|
|
||||||
let apiClient = new ApiApiFactory()
|
|
||||||
if (this.selected_supermarket.new) {
|
|
||||||
apiClient.createSupermarket({ name: this.selected_supermarket.name }).then((results) => {
|
|
||||||
this.selected_supermarket = undefined
|
|
||||||
this.loadInitial()
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
apiClient.partialUpdateSupermarket(this.selected_supermarket.id, { name: this.selected_supermarket.name })
|
|
||||||
}
|
|
||||||
},
|
|
||||||
categoryModalOk: function () {
|
|
||||||
let apiClient = new ApiApiFactory()
|
|
||||||
if (this.selected_category.new) {
|
|
||||||
apiClient.createSupermarketCategory({ name: this.selected_category.name }).then((results) => {
|
|
||||||
this.selected_category = {}
|
|
||||||
this.loadInitial()
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
apiClient.partialUpdateSupermarketCategory(this.selected_category.id, { name: this.selected_category.name })
|
|
||||||
}
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style></style>
|
|
||||||
@@ -1,22 +0,0 @@
|
|||||||
import Vue from 'vue'
|
|
||||||
import App from './SupermarketView.vue'
|
|
||||||
import i18n from '@/i18n'
|
|
||||||
import {createPinia, PiniaVuePlugin} from "pinia";
|
|
||||||
|
|
||||||
Vue.config.productionTip = false
|
|
||||||
|
|
||||||
// TODO move this and other default stuff to centralized JS file (verify nothing breaks)
|
|
||||||
let publicPath = localStorage.STATIC_URL + 'vue/'
|
|
||||||
if (process.env.NODE_ENV === 'development') {
|
|
||||||
publicPath = 'http://localhost:8080/'
|
|
||||||
}
|
|
||||||
export default __webpack_public_path__ = publicPath // eslint-disable-line
|
|
||||||
|
|
||||||
Vue.use(PiniaVuePlugin)
|
|
||||||
const pinia = createPinia()
|
|
||||||
|
|
||||||
new Vue({
|
|
||||||
pinia,
|
|
||||||
i18n,
|
|
||||||
render: h => h(App),
|
|
||||||
}).$mount('#app')
|
|
||||||
@@ -200,7 +200,7 @@
|
|||||||
"Keyword_Alias": "Nyckelord alias",
|
"Keyword_Alias": "Nyckelord alias",
|
||||||
"Recipe_Book": "Receptbok",
|
"Recipe_Book": "Receptbok",
|
||||||
"Search Settings": "Sökinställningar",
|
"Search Settings": "Sökinställningar",
|
||||||
"warning_feature_beta": "Den här funktionen är för närvarande i ett BETA-läge (testning). Vänligen förvänta dig buggar och eventuellt brytande ändringar i framtiden (möjligen att förlora funktionsrelaterad data) när du använder den här funktionen.",
|
"warning_feature_beta": "Den här funktionen är för närvarande i ett BETA-läge (testning). Förvänta dig buggar och eventuellt större ändringar i framtiden (möjligtvis framtida data kan gå förlorad) när du använder den här funktionen.",
|
||||||
"success_deleting_resource": "En resurs har raderats!",
|
"success_deleting_resource": "En resurs har raderats!",
|
||||||
"file_upload_disabled": "Filuppladdning är inte aktiverat för ditt utrymme.",
|
"file_upload_disabled": "Filuppladdning är inte aktiverat för ditt utrymme.",
|
||||||
"show_only_internal": "Visa endast interna recept",
|
"show_only_internal": "Visa endast interna recept",
|
||||||
@@ -246,7 +246,7 @@
|
|||||||
"CountMore": "...+{count} fler",
|
"CountMore": "...+{count} fler",
|
||||||
"IgnoreThis": "Lägg aldrig till {mat} automatiskt i inköpslista",
|
"IgnoreThis": "Lägg aldrig till {mat} automatiskt i inköpslista",
|
||||||
"DelayFor": "Fördröjning på {hours} timmar",
|
"DelayFor": "Fördröjning på {hours} timmar",
|
||||||
"ShowDelayed": "Visa fördröjda artiklar",
|
"ShowDelayed": "Visa fördröjda föremål",
|
||||||
"Completed": "Avslutad",
|
"Completed": "Avslutad",
|
||||||
"OfflineAlert": "Du är offline, inköpslistan kanske inte synkroniseras.",
|
"OfflineAlert": "Du är offline, inköpslistan kanske inte synkroniseras.",
|
||||||
"shopping_share": "Dela inköpslista",
|
"shopping_share": "Dela inköpslista",
|
||||||
@@ -477,5 +477,64 @@
|
|||||||
"Warning_Delete_Supermarket_Category": "Om du tar bort en mataffärskategori raderas också alla relationer till livsmedel. Är du säker?",
|
"Warning_Delete_Supermarket_Category": "Om du tar bort en mataffärskategori raderas också alla relationer till livsmedel. Är du säker?",
|
||||||
"Disabled": "Inaktiverad",
|
"Disabled": "Inaktiverad",
|
||||||
"Social_Authentication": "Social autentisering",
|
"Social_Authentication": "Social autentisering",
|
||||||
"Single": "Enstaka"
|
"Single": "Enstaka",
|
||||||
|
"Properties": "Egenskaper",
|
||||||
|
"err_importing_recipe": "Ett fel uppstod vid import av receptet!",
|
||||||
|
"recipe_property_info": "Du kan också lägga till egenskaper till maträtter för att beräkna dessa automatiskt baserat på ditt recept!",
|
||||||
|
"total": "totalt",
|
||||||
|
"CustomLogos": "Anpassade logotyper",
|
||||||
|
"Welcome": "Välkommen",
|
||||||
|
"Input": "Inmatning",
|
||||||
|
"Undo": "Ångra",
|
||||||
|
"NoMoreUndo": "Inga ändringar att ångra.",
|
||||||
|
"Delete_All": "Radera alla",
|
||||||
|
"Property": "Egendom",
|
||||||
|
"Property_Editor": "Egendom redigerare",
|
||||||
|
"Conversion": "Omvandling",
|
||||||
|
"created_by": "Skapad av",
|
||||||
|
"ShowRecentlyCompleted": "Visa nyligen genomförda föremål",
|
||||||
|
"ShoppingBackgroundSyncWarning": "Dålig uppkoppling, inväntar synkronisering...",
|
||||||
|
"show_step_ingredients": "Visa ingredienser för steget",
|
||||||
|
"hide_step_ingredients": "Dölj ingredienser för steget",
|
||||||
|
"Logo": "Logga",
|
||||||
|
"Show_Logo": "Visa logga",
|
||||||
|
"Show_Logo_Help": "Visa Tandoor eller hushålls-logga i navigationen.",
|
||||||
|
"Nav_Text_Mode": "Navigation Textläge",
|
||||||
|
"Nav_Text_Mode_Help": "Beter sig annorlunda för varje tema.",
|
||||||
|
"g": "gram [g] (metriskt, vikt)",
|
||||||
|
"kg": "kilogram [kg] (metriskt, vikt)",
|
||||||
|
"ounce": "ounce [oz] (vikt)",
|
||||||
|
"FDC_Search": "FDC Sök",
|
||||||
|
"property_type_fdc_hint": "Bara egendomstyper med ett FDC ID kan automatiskt hämta data från FDC databasen",
|
||||||
|
"Alignment": "Orientering",
|
||||||
|
"base_amount": "Basmängd",
|
||||||
|
"Datatype": "Datatyp",
|
||||||
|
"Number of Objects": "Antal objekt",
|
||||||
|
"StartDate": "Startdatum",
|
||||||
|
"EndDate": "Slutdatum",
|
||||||
|
"FDC_ID_help": "FDC databas ID",
|
||||||
|
"Data_Import_Info": "Förbättra din samling genom att importera en framtagen lista av livsmedel, enheter och mer för att förbättra din recept-samling.",
|
||||||
|
"Update_Existing_Data": "Uppdatera existerande data",
|
||||||
|
"Use_Metric": "Använd metriska enheter",
|
||||||
|
"Learn_More": "Läs mer",
|
||||||
|
"converted_unit": "Konverterad enhet",
|
||||||
|
"converted_amount": "Konverterad mängd",
|
||||||
|
"base_unit": "Basenhet",
|
||||||
|
"FDC_ID": "FDC ID",
|
||||||
|
"per_serving": "per servering",
|
||||||
|
"Properties_Food_Amount": "Egenskaper Livsmedel Mängd",
|
||||||
|
"Open_Data_Slug": "Öppen Data Slug",
|
||||||
|
"Open_Data_Import": "Öppen Data Import",
|
||||||
|
"Properties_Food_Unit": "Egenskaper Livsmedel Enhet",
|
||||||
|
"OrderInformation": "Objekt är sorterade från små till stora siffror.",
|
||||||
|
"show_step_ingredients_setting": "Visa ingredienser bredvid recept-steg",
|
||||||
|
"show_step_ingredients_setting_help": "Lägg till tabell med ingredienser bredvid recept-steg. Verkställs vid skapande. Kan skrivas över i redigering av receptvyn.",
|
||||||
|
"Space_Cosmetic_Settings": "Vissa kosmetiska inställningar kan ändras av hushålls-administratörer och skriver över klientinställningar för det hushållet.",
|
||||||
|
"show_ingredients_table": "Visa en tabell över ingredienserna bredvid stegets text",
|
||||||
|
"Enable": "Aktivera",
|
||||||
|
"CustomTheme": "Anpassat tema",
|
||||||
|
"CustomThemeHelp": "Skriv över nuvarande tema genom att ladda upp en anpassad CSS-fil.",
|
||||||
|
"CustomNavLogoHelp": "Ladda upp en bild att använda som meny-logga.",
|
||||||
|
"CustomImageHelp": "Ladda upp en bild som visas i överblicken.",
|
||||||
|
"CustomLogoHelp": "Ladda upp kvadratiska bilder i olika storlekar för att ändra logga i webbläsare."
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -518,6 +518,7 @@ export class Models {
|
|||||||
header_component: {
|
header_component: {
|
||||||
name: "BetaWarning",
|
name: "BetaWarning",
|
||||||
},
|
},
|
||||||
|
params: ["automation_type", "page", "pageSize", "options"],
|
||||||
},
|
},
|
||||||
create: {
|
create: {
|
||||||
params: [["name", "description", "type", "param_1", "param_2", "param_3", "order", "disabled"]],
|
params: [["name", "description", "type", "param_1", "param_2", "param_3", "order", "disabled"]],
|
||||||
@@ -620,7 +621,7 @@ export class Models {
|
|||||||
},
|
},
|
||||||
form_function: "AutomationOrderDefault",
|
form_function: "AutomationOrderDefault",
|
||||||
},
|
},
|
||||||
},
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static UNIT_CONVERSION = {
|
static UNIT_CONVERSION = {
|
||||||
@@ -1032,7 +1033,7 @@ export class Models {
|
|||||||
static CUSTOM_FILTER = {
|
static CUSTOM_FILTER = {
|
||||||
name: "Custom Filter",
|
name: "Custom Filter",
|
||||||
apiName: "CustomFilter",
|
apiName: "CustomFilter",
|
||||||
|
paginated: true,
|
||||||
create: {
|
create: {
|
||||||
params: [["name", "search", "shared"]],
|
params: [["name", "search", "shared"]],
|
||||||
form: {
|
form: {
|
||||||
@@ -1055,6 +1056,9 @@ export class Models {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
list: {
|
||||||
|
params: ["page", "pageSize", "options"],
|
||||||
|
},
|
||||||
}
|
}
|
||||||
static USER_NAME = {
|
static USER_NAME = {
|
||||||
name: "User",
|
name: "User",
|
||||||
@@ -1228,6 +1232,7 @@ export class Models {
|
|||||||
static STEP = {
|
static STEP = {
|
||||||
name: "Step",
|
name: "Step",
|
||||||
apiName: "Step",
|
apiName: "Step",
|
||||||
|
paginated: true,
|
||||||
list: {
|
list: {
|
||||||
params: ["recipe", "query", "page", "pageSize", "options"],
|
params: ["recipe", "query", "page", "pageSize", "options"],
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -1378,10 +1378,10 @@ export interface InlineResponse200 {
|
|||||||
previous?: string | null;
|
previous?: string | null;
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @type {Array<CookLog>}
|
* @type {Array<Automation>}
|
||||||
* @memberof InlineResponse200
|
* @memberof InlineResponse200
|
||||||
*/
|
*/
|
||||||
results?: Array<CookLog>;
|
results?: Array<Automation>;
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
@@ -1409,10 +1409,10 @@ export interface InlineResponse2001 {
|
|||||||
previous?: string | null;
|
previous?: string | null;
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @type {Array<Food>}
|
* @type {Array<CookLog>}
|
||||||
* @memberof InlineResponse2001
|
* @memberof InlineResponse2001
|
||||||
*/
|
*/
|
||||||
results?: Array<Food>;
|
results?: Array<CookLog>;
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
@@ -1440,10 +1440,10 @@ export interface InlineResponse20010 {
|
|||||||
previous?: string | null;
|
previous?: string | null;
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @type {Array<Unit>}
|
* @type {Array<SupermarketCategoryRelation>}
|
||||||
* @memberof InlineResponse20010
|
* @memberof InlineResponse20010
|
||||||
*/
|
*/
|
||||||
results?: Array<Unit>;
|
results?: Array<SupermarketCategoryRelation>;
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
@@ -1471,10 +1471,10 @@ export interface InlineResponse20011 {
|
|||||||
previous?: string | null;
|
previous?: string | null;
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @type {Array<UserSpace>}
|
* @type {Array<SyncLog>}
|
||||||
* @memberof InlineResponse20011
|
* @memberof InlineResponse20011
|
||||||
*/
|
*/
|
||||||
results?: Array<UserSpace>;
|
results?: Array<SyncLog>;
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
@@ -1502,9 +1502,71 @@ export interface InlineResponse20012 {
|
|||||||
previous?: string | null;
|
previous?: string | null;
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @type {Array<ViewLog>}
|
* @type {Array<Unit>}
|
||||||
* @memberof InlineResponse20012
|
* @memberof InlineResponse20012
|
||||||
*/
|
*/
|
||||||
|
results?: Array<Unit>;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @export
|
||||||
|
* @interface InlineResponse20013
|
||||||
|
*/
|
||||||
|
export interface InlineResponse20013 {
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @type {number}
|
||||||
|
* @memberof InlineResponse20013
|
||||||
|
*/
|
||||||
|
count?: number;
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @type {string}
|
||||||
|
* @memberof InlineResponse20013
|
||||||
|
*/
|
||||||
|
next?: string | null;
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @type {string}
|
||||||
|
* @memberof InlineResponse20013
|
||||||
|
*/
|
||||||
|
previous?: string | null;
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @type {Array<UserSpace>}
|
||||||
|
* @memberof InlineResponse20013
|
||||||
|
*/
|
||||||
|
results?: Array<UserSpace>;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @export
|
||||||
|
* @interface InlineResponse20014
|
||||||
|
*/
|
||||||
|
export interface InlineResponse20014 {
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @type {number}
|
||||||
|
* @memberof InlineResponse20014
|
||||||
|
*/
|
||||||
|
count?: number;
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @type {string}
|
||||||
|
* @memberof InlineResponse20014
|
||||||
|
*/
|
||||||
|
next?: string | null;
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @type {string}
|
||||||
|
* @memberof InlineResponse20014
|
||||||
|
*/
|
||||||
|
previous?: string | null;
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @type {Array<ViewLog>}
|
||||||
|
* @memberof InlineResponse20014
|
||||||
|
*/
|
||||||
results?: Array<ViewLog>;
|
results?: Array<ViewLog>;
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
@@ -1533,10 +1595,10 @@ export interface InlineResponse2002 {
|
|||||||
previous?: string | null;
|
previous?: string | null;
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @type {Array<ImportLog>}
|
* @type {Array<CustomFilter>}
|
||||||
* @memberof InlineResponse2002
|
* @memberof InlineResponse2002
|
||||||
*/
|
*/
|
||||||
results?: Array<ImportLog>;
|
results?: Array<CustomFilter>;
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
@@ -1564,10 +1626,10 @@ export interface InlineResponse2003 {
|
|||||||
previous?: string | null;
|
previous?: string | null;
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @type {Array<ExportLog>}
|
* @type {Array<Food>}
|
||||||
* @memberof InlineResponse2003
|
* @memberof InlineResponse2003
|
||||||
*/
|
*/
|
||||||
results?: Array<ExportLog>;
|
results?: Array<Food>;
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
@@ -1595,10 +1657,10 @@ export interface InlineResponse2004 {
|
|||||||
previous?: string | null;
|
previous?: string | null;
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @type {Array<Ingredient>}
|
* @type {Array<ImportLog>}
|
||||||
* @memberof InlineResponse2004
|
* @memberof InlineResponse2004
|
||||||
*/
|
*/
|
||||||
results?: Array<Ingredient>;
|
results?: Array<ImportLog>;
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
@@ -1626,10 +1688,10 @@ export interface InlineResponse2005 {
|
|||||||
previous?: string | null;
|
previous?: string | null;
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @type {Array<Keyword>}
|
* @type {Array<ExportLog>}
|
||||||
* @memberof InlineResponse2005
|
* @memberof InlineResponse2005
|
||||||
*/
|
*/
|
||||||
results?: Array<Keyword>;
|
results?: Array<ExportLog>;
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
@@ -1657,10 +1719,10 @@ export interface InlineResponse2006 {
|
|||||||
previous?: string | null;
|
previous?: string | null;
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @type {Array<RecipeOverview>}
|
* @type {Array<Ingredient>}
|
||||||
* @memberof InlineResponse2006
|
* @memberof InlineResponse2006
|
||||||
*/
|
*/
|
||||||
results?: Array<RecipeOverview>;
|
results?: Array<Ingredient>;
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
@@ -1688,10 +1750,10 @@ export interface InlineResponse2007 {
|
|||||||
previous?: string | null;
|
previous?: string | null;
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @type {Array<Step>}
|
* @type {Array<Keyword>}
|
||||||
* @memberof InlineResponse2007
|
* @memberof InlineResponse2007
|
||||||
*/
|
*/
|
||||||
results?: Array<Step>;
|
results?: Array<Keyword>;
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
@@ -1719,10 +1781,10 @@ export interface InlineResponse2008 {
|
|||||||
previous?: string | null;
|
previous?: string | null;
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @type {Array<SupermarketCategoryRelation>}
|
* @type {Array<RecipeOverview>}
|
||||||
* @memberof InlineResponse2008
|
* @memberof InlineResponse2008
|
||||||
*/
|
*/
|
||||||
results?: Array<SupermarketCategoryRelation>;
|
results?: Array<RecipeOverview>;
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
@@ -1750,10 +1812,10 @@ export interface InlineResponse2009 {
|
|||||||
previous?: string | null;
|
previous?: string | null;
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @type {Array<SyncLog>}
|
* @type {Array<Step>}
|
||||||
* @memberof InlineResponse2009
|
* @memberof InlineResponse2009
|
||||||
*/
|
*/
|
||||||
results?: Array<SyncLog>;
|
results?: Array<Step>;
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
@@ -8517,11 +8579,14 @@ export const ApiApiAxiosParamCreator = function (configuration?: Configuration)
|
|||||||
};
|
};
|
||||||
},
|
},
|
||||||
/**
|
/**
|
||||||
*
|
* optional parameters - **automation_type**: Return the Automations matching the automation type. Multiple values allowed. *Automation Types:* - FS: Food Alias - UA: Unit Alias - KA: Keyword Alias - DR: Description Replace - IR: Instruction Replace - NU: Never Unit - TW: Transpose Words - FR: Food Replace - UR: Unit Replace - NR: Name Replace
|
||||||
|
* @param {string} [automationType] Return the Automations matching the automation type. Multiple values allowed.
|
||||||
|
* @param {number} [page] A page number within the paginated result set.
|
||||||
|
* @param {number} [pageSize] Number of results to return per page.
|
||||||
* @param {*} [options] Override http request option.
|
* @param {*} [options] Override http request option.
|
||||||
* @throws {RequiredError}
|
* @throws {RequiredError}
|
||||||
*/
|
*/
|
||||||
listAutomations: async (options: any = {}): Promise<RequestArgs> => {
|
listAutomations: async (automationType?: string, page?: number, pageSize?: number, options: any = {}): Promise<RequestArgs> => {
|
||||||
const localVarPath = `/api/automation/`;
|
const localVarPath = `/api/automation/`;
|
||||||
// use dummy base URL string because the URL constructor only accepts absolute URLs.
|
// use dummy base URL string because the URL constructor only accepts absolute URLs.
|
||||||
const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL);
|
const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL);
|
||||||
@@ -8534,6 +8599,18 @@ export const ApiApiAxiosParamCreator = function (configuration?: Configuration)
|
|||||||
const localVarHeaderParameter = {} as any;
|
const localVarHeaderParameter = {} as any;
|
||||||
const localVarQueryParameter = {} as any;
|
const localVarQueryParameter = {} as any;
|
||||||
|
|
||||||
|
if (automationType !== undefined) {
|
||||||
|
localVarQueryParameter['automation_type'] = automationType;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (page !== undefined) {
|
||||||
|
localVarQueryParameter['page'] = page;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pageSize !== undefined) {
|
||||||
|
localVarQueryParameter['page_size'] = pageSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
setSearchParams(localVarUrlObj, localVarQueryParameter, options.query);
|
setSearchParams(localVarUrlObj, localVarQueryParameter, options.query);
|
||||||
@@ -8644,10 +8721,12 @@ export const ApiApiAxiosParamCreator = function (configuration?: Configuration)
|
|||||||
},
|
},
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
|
* @param {number} [page] A page number within the paginated result set.
|
||||||
|
* @param {number} [pageSize] Number of results to return per page.
|
||||||
* @param {*} [options] Override http request option.
|
* @param {*} [options] Override http request option.
|
||||||
* @throws {RequiredError}
|
* @throws {RequiredError}
|
||||||
*/
|
*/
|
||||||
listCustomFilters: async (options: any = {}): Promise<RequestArgs> => {
|
listCustomFilters: async (page?: number, pageSize?: number, options: any = {}): Promise<RequestArgs> => {
|
||||||
const localVarPath = `/api/custom-filter/`;
|
const localVarPath = `/api/custom-filter/`;
|
||||||
// use dummy base URL string because the URL constructor only accepts absolute URLs.
|
// use dummy base URL string because the URL constructor only accepts absolute URLs.
|
||||||
const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL);
|
const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL);
|
||||||
@@ -8660,6 +8739,14 @@ export const ApiApiAxiosParamCreator = function (configuration?: Configuration)
|
|||||||
const localVarHeaderParameter = {} as any;
|
const localVarHeaderParameter = {} as any;
|
||||||
const localVarQueryParameter = {} as any;
|
const localVarQueryParameter = {} as any;
|
||||||
|
|
||||||
|
if (page !== undefined) {
|
||||||
|
localVarQueryParameter['page'] = page;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pageSize !== undefined) {
|
||||||
|
localVarQueryParameter['page_size'] = pageSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
setSearchParams(localVarUrlObj, localVarQueryParameter, options.query);
|
setSearchParams(localVarUrlObj, localVarQueryParameter, options.query);
|
||||||
@@ -9581,7 +9668,7 @@ export const ApiApiAxiosParamCreator = function (configuration?: Configuration)
|
|||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @param {number} [id] Returns the shopping list entry with a primary key of id. Multiple values allowed.
|
* @param {number} [id] Returns the shopping list entry with a primary key of id. Multiple values allowed.
|
||||||
* @param {string} [checked] Filter shopping list entries on checked. [true, false, both, <b>recent</b>]<br> - recent includes unchecked items and recently completed items.
|
* @param {string} [checked] Filter shopping list entries on checked. [true, false, both, <b>recent</b>]<br> - recent includes unchecked items and recently completed items.
|
||||||
* @param {number} [supermarket] Returns the shopping list entries sorted by supermarket category order.
|
* @param {number} [supermarket] Returns the shopping list entries sorted by supermarket category order.
|
||||||
* @param {*} [options] Override http request option.
|
* @param {*} [options] Override http request option.
|
||||||
* @throws {RequiredError}
|
* @throws {RequiredError}
|
||||||
@@ -16231,12 +16318,15 @@ export const ApiApiFp = function(configuration?: Configuration) {
|
|||||||
return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration);
|
return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration);
|
||||||
},
|
},
|
||||||
/**
|
/**
|
||||||
*
|
* optional parameters - **automation_type**: Return the Automations matching the automation type. Multiple values allowed. *Automation Types:* - FS: Food Alias - UA: Unit Alias - KA: Keyword Alias - DR: Description Replace - IR: Instruction Replace - NU: Never Unit - TW: Transpose Words - FR: Food Replace - UR: Unit Replace - NR: Name Replace
|
||||||
|
* @param {string} [automationType] Return the Automations matching the automation type. Multiple values allowed.
|
||||||
|
* @param {number} [page] A page number within the paginated result set.
|
||||||
|
* @param {number} [pageSize] Number of results to return per page.
|
||||||
* @param {*} [options] Override http request option.
|
* @param {*} [options] Override http request option.
|
||||||
* @throws {RequiredError}
|
* @throws {RequiredError}
|
||||||
*/
|
*/
|
||||||
async listAutomations(options?: any): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise<Array<Automation>>> {
|
async listAutomations(automationType?: string, page?: number, pageSize?: number, options?: any): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise<InlineResponse200>> {
|
||||||
const localVarAxiosArgs = await localVarAxiosParamCreator.listAutomations(options);
|
const localVarAxiosArgs = await localVarAxiosParamCreator.listAutomations(automationType, page, pageSize, options);
|
||||||
return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration);
|
return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration);
|
||||||
},
|
},
|
||||||
/**
|
/**
|
||||||
@@ -16264,19 +16354,10 @@ export const ApiApiFp = function(configuration?: Configuration) {
|
|||||||
* @param {*} [options] Override http request option.
|
* @param {*} [options] Override http request option.
|
||||||
* @throws {RequiredError}
|
* @throws {RequiredError}
|
||||||
*/
|
*/
|
||||||
async listCookLogs(page?: number, pageSize?: number, options?: any): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise<InlineResponse200>> {
|
async listCookLogs(page?: number, pageSize?: number, options?: any): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise<InlineResponse2001>> {
|
||||||
const localVarAxiosArgs = await localVarAxiosParamCreator.listCookLogs(page, pageSize, options);
|
const localVarAxiosArgs = await localVarAxiosParamCreator.listCookLogs(page, pageSize, options);
|
||||||
return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration);
|
return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration);
|
||||||
},
|
},
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @param {*} [options] Override http request option.
|
|
||||||
* @throws {RequiredError}
|
|
||||||
*/
|
|
||||||
async listCustomFilters(options?: any): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise<Array<CustomFilter>>> {
|
|
||||||
const localVarAxiosArgs = await localVarAxiosParamCreator.listCustomFilters(options);
|
|
||||||
return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration);
|
|
||||||
},
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @param {number} [page] A page number within the paginated result set.
|
* @param {number} [page] A page number within the paginated result set.
|
||||||
@@ -16284,7 +16365,18 @@ export const ApiApiFp = function(configuration?: Configuration) {
|
|||||||
* @param {*} [options] Override http request option.
|
* @param {*} [options] Override http request option.
|
||||||
* @throws {RequiredError}
|
* @throws {RequiredError}
|
||||||
*/
|
*/
|
||||||
async listExportLogs(page?: number, pageSize?: number, options?: any): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise<InlineResponse2003>> {
|
async listCustomFilters(page?: number, pageSize?: number, options?: any): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise<InlineResponse2002>> {
|
||||||
|
const localVarAxiosArgs = await localVarAxiosParamCreator.listCustomFilters(page, pageSize, options);
|
||||||
|
return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration);
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param {number} [page] A page number within the paginated result set.
|
||||||
|
* @param {number} [pageSize] Number of results to return per page.
|
||||||
|
* @param {*} [options] Override http request option.
|
||||||
|
* @throws {RequiredError}
|
||||||
|
*/
|
||||||
|
async listExportLogs(page?: number, pageSize?: number, options?: any): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise<InlineResponse2005>> {
|
||||||
const localVarAxiosArgs = await localVarAxiosParamCreator.listExportLogs(page, pageSize, options);
|
const localVarAxiosArgs = await localVarAxiosParamCreator.listExportLogs(page, pageSize, options);
|
||||||
return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration);
|
return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration);
|
||||||
},
|
},
|
||||||
@@ -16307,7 +16399,7 @@ export const ApiApiFp = function(configuration?: Configuration) {
|
|||||||
* @param {*} [options] Override http request option.
|
* @param {*} [options] Override http request option.
|
||||||
* @throws {RequiredError}
|
* @throws {RequiredError}
|
||||||
*/
|
*/
|
||||||
async listFoods(query?: string, root?: number, tree?: number, page?: number, pageSize?: number, options?: any): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise<InlineResponse2001>> {
|
async listFoods(query?: string, root?: number, tree?: number, page?: number, pageSize?: number, options?: any): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise<InlineResponse2003>> {
|
||||||
const localVarAxiosArgs = await localVarAxiosParamCreator.listFoods(query, root, tree, page, pageSize, options);
|
const localVarAxiosArgs = await localVarAxiosParamCreator.listFoods(query, root, tree, page, pageSize, options);
|
||||||
return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration);
|
return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration);
|
||||||
},
|
},
|
||||||
@@ -16327,7 +16419,7 @@ export const ApiApiFp = function(configuration?: Configuration) {
|
|||||||
* @param {*} [options] Override http request option.
|
* @param {*} [options] Override http request option.
|
||||||
* @throws {RequiredError}
|
* @throws {RequiredError}
|
||||||
*/
|
*/
|
||||||
async listImportLogs(page?: number, pageSize?: number, options?: any): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise<InlineResponse2002>> {
|
async listImportLogs(page?: number, pageSize?: number, options?: any): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise<InlineResponse2004>> {
|
||||||
const localVarAxiosArgs = await localVarAxiosParamCreator.listImportLogs(page, pageSize, options);
|
const localVarAxiosArgs = await localVarAxiosParamCreator.listImportLogs(page, pageSize, options);
|
||||||
return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration);
|
return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration);
|
||||||
},
|
},
|
||||||
@@ -16338,7 +16430,7 @@ export const ApiApiFp = function(configuration?: Configuration) {
|
|||||||
* @param {*} [options] Override http request option.
|
* @param {*} [options] Override http request option.
|
||||||
* @throws {RequiredError}
|
* @throws {RequiredError}
|
||||||
*/
|
*/
|
||||||
async listIngredients(page?: number, pageSize?: number, options?: any): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise<InlineResponse2004>> {
|
async listIngredients(page?: number, pageSize?: number, options?: any): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise<InlineResponse2006>> {
|
||||||
const localVarAxiosArgs = await localVarAxiosParamCreator.listIngredients(page, pageSize, options);
|
const localVarAxiosArgs = await localVarAxiosParamCreator.listIngredients(page, pageSize, options);
|
||||||
return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration);
|
return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration);
|
||||||
},
|
},
|
||||||
@@ -16361,7 +16453,7 @@ export const ApiApiFp = function(configuration?: Configuration) {
|
|||||||
* @param {*} [options] Override http request option.
|
* @param {*} [options] Override http request option.
|
||||||
* @throws {RequiredError}
|
* @throws {RequiredError}
|
||||||
*/
|
*/
|
||||||
async listKeywords(query?: string, root?: number, tree?: number, page?: number, pageSize?: number, options?: any): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise<InlineResponse2005>> {
|
async listKeywords(query?: string, root?: number, tree?: number, page?: number, pageSize?: number, options?: any): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise<InlineResponse2007>> {
|
||||||
const localVarAxiosArgs = await localVarAxiosParamCreator.listKeywords(query, root, tree, page, pageSize, options);
|
const localVarAxiosArgs = await localVarAxiosParamCreator.listKeywords(query, root, tree, page, pageSize, options);
|
||||||
return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration);
|
return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration);
|
||||||
},
|
},
|
||||||
@@ -16528,14 +16620,14 @@ export const ApiApiFp = function(configuration?: Configuration) {
|
|||||||
* @param {*} [options] Override http request option.
|
* @param {*} [options] Override http request option.
|
||||||
* @throws {RequiredError}
|
* @throws {RequiredError}
|
||||||
*/
|
*/
|
||||||
async listRecipes(query?: string, keywords?: number, keywordsOr?: number, keywordsAnd?: number, keywordsOrNot?: number, keywordsAndNot?: number, foods?: number, foodsOr?: number, foodsAnd?: number, foodsOrNot?: number, foodsAndNot?: number, units?: number, rating?: number, books?: string, booksOr?: number, booksAnd?: number, booksOrNot?: number, booksAndNot?: number, internal?: string, random?: string, _new?: string, timescooked?: number, cookedon?: string, createdon?: string, updatedon?: string, viewedon?: string, makenow?: string, page?: number, pageSize?: number, options?: any): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise<InlineResponse2006>> {
|
async listRecipes(query?: string, keywords?: number, keywordsOr?: number, keywordsAnd?: number, keywordsOrNot?: number, keywordsAndNot?: number, foods?: number, foodsOr?: number, foodsAnd?: number, foodsOrNot?: number, foodsAndNot?: number, units?: number, rating?: number, books?: string, booksOr?: number, booksAnd?: number, booksOrNot?: number, booksAndNot?: number, internal?: string, random?: string, _new?: string, timescooked?: number, cookedon?: string, createdon?: string, updatedon?: string, viewedon?: string, makenow?: string, page?: number, pageSize?: number, options?: any): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise<InlineResponse2008>> {
|
||||||
const localVarAxiosArgs = await localVarAxiosParamCreator.listRecipes(query, keywords, keywordsOr, keywordsAnd, keywordsOrNot, keywordsAndNot, foods, foodsOr, foodsAnd, foodsOrNot, foodsAndNot, units, rating, books, booksOr, booksAnd, booksOrNot, booksAndNot, internal, random, _new, timescooked, cookedon, createdon, updatedon, viewedon, makenow, page, pageSize, options);
|
const localVarAxiosArgs = await localVarAxiosParamCreator.listRecipes(query, keywords, keywordsOr, keywordsAnd, keywordsOrNot, keywordsAndNot, foods, foodsOr, foodsAnd, foodsOrNot, foodsAndNot, units, rating, books, booksOr, booksAnd, booksOrNot, booksAndNot, internal, random, _new, timescooked, cookedon, createdon, updatedon, viewedon, makenow, page, pageSize, options);
|
||||||
return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration);
|
return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration);
|
||||||
},
|
},
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @param {number} [id] Returns the shopping list entry with a primary key of id. Multiple values allowed.
|
* @param {number} [id] Returns the shopping list entry with a primary key of id. Multiple values allowed.
|
||||||
* @param {string} [checked] Filter shopping list entries on checked. [true, false, both, <b>recent</b>]<br> - recent includes unchecked items and recently completed items.
|
* @param {string} [checked] Filter shopping list entries on checked. [true, false, both, <b>recent</b>]<br> - recent includes unchecked items and recently completed items.
|
||||||
* @param {number} [supermarket] Returns the shopping list entries sorted by supermarket category order.
|
* @param {number} [supermarket] Returns the shopping list entries sorted by supermarket category order.
|
||||||
* @param {*} [options] Override http request option.
|
* @param {*} [options] Override http request option.
|
||||||
* @throws {RequiredError}
|
* @throws {RequiredError}
|
||||||
@@ -16580,7 +16672,7 @@ export const ApiApiFp = function(configuration?: Configuration) {
|
|||||||
* @param {*} [options] Override http request option.
|
* @param {*} [options] Override http request option.
|
||||||
* @throws {RequiredError}
|
* @throws {RequiredError}
|
||||||
*/
|
*/
|
||||||
async listSteps(recipe?: number, query?: string, page?: number, pageSize?: number, options?: any): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise<InlineResponse2007>> {
|
async listSteps(recipe?: number, query?: string, page?: number, pageSize?: number, options?: any): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise<InlineResponse2009>> {
|
||||||
const localVarAxiosArgs = await localVarAxiosParamCreator.listSteps(recipe, query, page, pageSize, options);
|
const localVarAxiosArgs = await localVarAxiosParamCreator.listSteps(recipe, query, page, pageSize, options);
|
||||||
return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration);
|
return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration);
|
||||||
},
|
},
|
||||||
@@ -16600,7 +16692,7 @@ export const ApiApiFp = function(configuration?: Configuration) {
|
|||||||
* @param {*} [options] Override http request option.
|
* @param {*} [options] Override http request option.
|
||||||
* @throws {RequiredError}
|
* @throws {RequiredError}
|
||||||
*/
|
*/
|
||||||
async listSupermarketCategoryRelations(page?: number, pageSize?: number, options?: any): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise<InlineResponse2008>> {
|
async listSupermarketCategoryRelations(page?: number, pageSize?: number, options?: any): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise<InlineResponse20010>> {
|
||||||
const localVarAxiosArgs = await localVarAxiosParamCreator.listSupermarketCategoryRelations(page, pageSize, options);
|
const localVarAxiosArgs = await localVarAxiosParamCreator.listSupermarketCategoryRelations(page, pageSize, options);
|
||||||
return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration);
|
return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration);
|
||||||
},
|
},
|
||||||
@@ -16631,7 +16723,7 @@ export const ApiApiFp = function(configuration?: Configuration) {
|
|||||||
* @param {*} [options] Override http request option.
|
* @param {*} [options] Override http request option.
|
||||||
* @throws {RequiredError}
|
* @throws {RequiredError}
|
||||||
*/
|
*/
|
||||||
async listSyncLogs(page?: number, pageSize?: number, options?: any): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise<InlineResponse2009>> {
|
async listSyncLogs(page?: number, pageSize?: number, options?: any): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise<InlineResponse20011>> {
|
||||||
const localVarAxiosArgs = await localVarAxiosParamCreator.listSyncLogs(page, pageSize, options);
|
const localVarAxiosArgs = await localVarAxiosParamCreator.listSyncLogs(page, pageSize, options);
|
||||||
return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration);
|
return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration);
|
||||||
},
|
},
|
||||||
@@ -16662,7 +16754,7 @@ export const ApiApiFp = function(configuration?: Configuration) {
|
|||||||
* @param {*} [options] Override http request option.
|
* @param {*} [options] Override http request option.
|
||||||
* @throws {RequiredError}
|
* @throws {RequiredError}
|
||||||
*/
|
*/
|
||||||
async listUnits(query?: string, page?: number, pageSize?: number, options?: any): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise<InlineResponse20010>> {
|
async listUnits(query?: string, page?: number, pageSize?: number, options?: any): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise<InlineResponse20012>> {
|
||||||
const localVarAxiosArgs = await localVarAxiosParamCreator.listUnits(query, page, pageSize, options);
|
const localVarAxiosArgs = await localVarAxiosParamCreator.listUnits(query, page, pageSize, options);
|
||||||
return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration);
|
return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration);
|
||||||
},
|
},
|
||||||
@@ -16692,7 +16784,7 @@ export const ApiApiFp = function(configuration?: Configuration) {
|
|||||||
* @param {*} [options] Override http request option.
|
* @param {*} [options] Override http request option.
|
||||||
* @throws {RequiredError}
|
* @throws {RequiredError}
|
||||||
*/
|
*/
|
||||||
async listUserSpaces(page?: number, pageSize?: number, options?: any): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise<InlineResponse20011>> {
|
async listUserSpaces(page?: number, pageSize?: number, options?: any): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise<InlineResponse20013>> {
|
||||||
const localVarAxiosArgs = await localVarAxiosParamCreator.listUserSpaces(page, pageSize, options);
|
const localVarAxiosArgs = await localVarAxiosParamCreator.listUserSpaces(page, pageSize, options);
|
||||||
return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration);
|
return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration);
|
||||||
},
|
},
|
||||||
@@ -16712,7 +16804,7 @@ export const ApiApiFp = function(configuration?: Configuration) {
|
|||||||
* @param {*} [options] Override http request option.
|
* @param {*} [options] Override http request option.
|
||||||
* @throws {RequiredError}
|
* @throws {RequiredError}
|
||||||
*/
|
*/
|
||||||
async listViewLogs(page?: number, pageSize?: number, options?: any): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise<InlineResponse20012>> {
|
async listViewLogs(page?: number, pageSize?: number, options?: any): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise<InlineResponse20014>> {
|
||||||
const localVarAxiosArgs = await localVarAxiosParamCreator.listViewLogs(page, pageSize, options);
|
const localVarAxiosArgs = await localVarAxiosParamCreator.listViewLogs(page, pageSize, options);
|
||||||
return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration);
|
return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration);
|
||||||
},
|
},
|
||||||
@@ -19041,12 +19133,15 @@ export const ApiApiFactory = function (configuration?: Configuration, basePath?:
|
|||||||
return localVarFp.listAccessTokens(options).then((request) => request(axios, basePath));
|
return localVarFp.listAccessTokens(options).then((request) => request(axios, basePath));
|
||||||
},
|
},
|
||||||
/**
|
/**
|
||||||
*
|
* optional parameters - **automation_type**: Return the Automations matching the automation type. Multiple values allowed. *Automation Types:* - FS: Food Alias - UA: Unit Alias - KA: Keyword Alias - DR: Description Replace - IR: Instruction Replace - NU: Never Unit - TW: Transpose Words - FR: Food Replace - UR: Unit Replace - NR: Name Replace
|
||||||
|
* @param {string} [automationType] Return the Automations matching the automation type. Multiple values allowed.
|
||||||
|
* @param {number} [page] A page number within the paginated result set.
|
||||||
|
* @param {number} [pageSize] Number of results to return per page.
|
||||||
* @param {*} [options] Override http request option.
|
* @param {*} [options] Override http request option.
|
||||||
* @throws {RequiredError}
|
* @throws {RequiredError}
|
||||||
*/
|
*/
|
||||||
listAutomations(options?: any): AxiosPromise<Array<Automation>> {
|
listAutomations(automationType?: string, page?: number, pageSize?: number, options?: any): AxiosPromise<InlineResponse200> {
|
||||||
return localVarFp.listAutomations(options).then((request) => request(axios, basePath));
|
return localVarFp.listAutomations(automationType, page, pageSize, options).then((request) => request(axios, basePath));
|
||||||
},
|
},
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
@@ -19071,17 +19166,9 @@ export const ApiApiFactory = function (configuration?: Configuration, basePath?:
|
|||||||
* @param {*} [options] Override http request option.
|
* @param {*} [options] Override http request option.
|
||||||
* @throws {RequiredError}
|
* @throws {RequiredError}
|
||||||
*/
|
*/
|
||||||
listCookLogs(page?: number, pageSize?: number, options?: any): AxiosPromise<InlineResponse200> {
|
listCookLogs(page?: number, pageSize?: number, options?: any): AxiosPromise<InlineResponse2001> {
|
||||||
return localVarFp.listCookLogs(page, pageSize, options).then((request) => request(axios, basePath));
|
return localVarFp.listCookLogs(page, pageSize, options).then((request) => request(axios, basePath));
|
||||||
},
|
},
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @param {*} [options] Override http request option.
|
|
||||||
* @throws {RequiredError}
|
|
||||||
*/
|
|
||||||
listCustomFilters(options?: any): AxiosPromise<Array<CustomFilter>> {
|
|
||||||
return localVarFp.listCustomFilters(options).then((request) => request(axios, basePath));
|
|
||||||
},
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @param {number} [page] A page number within the paginated result set.
|
* @param {number} [page] A page number within the paginated result set.
|
||||||
@@ -19089,7 +19176,17 @@ export const ApiApiFactory = function (configuration?: Configuration, basePath?:
|
|||||||
* @param {*} [options] Override http request option.
|
* @param {*} [options] Override http request option.
|
||||||
* @throws {RequiredError}
|
* @throws {RequiredError}
|
||||||
*/
|
*/
|
||||||
listExportLogs(page?: number, pageSize?: number, options?: any): AxiosPromise<InlineResponse2003> {
|
listCustomFilters(page?: number, pageSize?: number, options?: any): AxiosPromise<InlineResponse2002> {
|
||||||
|
return localVarFp.listCustomFilters(page, pageSize, options).then((request) => request(axios, basePath));
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param {number} [page] A page number within the paginated result set.
|
||||||
|
* @param {number} [pageSize] Number of results to return per page.
|
||||||
|
* @param {*} [options] Override http request option.
|
||||||
|
* @throws {RequiredError}
|
||||||
|
*/
|
||||||
|
listExportLogs(page?: number, pageSize?: number, options?: any): AxiosPromise<InlineResponse2005> {
|
||||||
return localVarFp.listExportLogs(page, pageSize, options).then((request) => request(axios, basePath));
|
return localVarFp.listExportLogs(page, pageSize, options).then((request) => request(axios, basePath));
|
||||||
},
|
},
|
||||||
/**
|
/**
|
||||||
@@ -19110,7 +19207,7 @@ export const ApiApiFactory = function (configuration?: Configuration, basePath?:
|
|||||||
* @param {*} [options] Override http request option.
|
* @param {*} [options] Override http request option.
|
||||||
* @throws {RequiredError}
|
* @throws {RequiredError}
|
||||||
*/
|
*/
|
||||||
listFoods(query?: string, root?: number, tree?: number, page?: number, pageSize?: number, options?: any): AxiosPromise<InlineResponse2001> {
|
listFoods(query?: string, root?: number, tree?: number, page?: number, pageSize?: number, options?: any): AxiosPromise<InlineResponse2003> {
|
||||||
return localVarFp.listFoods(query, root, tree, page, pageSize, options).then((request) => request(axios, basePath));
|
return localVarFp.listFoods(query, root, tree, page, pageSize, options).then((request) => request(axios, basePath));
|
||||||
},
|
},
|
||||||
/**
|
/**
|
||||||
@@ -19128,7 +19225,7 @@ export const ApiApiFactory = function (configuration?: Configuration, basePath?:
|
|||||||
* @param {*} [options] Override http request option.
|
* @param {*} [options] Override http request option.
|
||||||
* @throws {RequiredError}
|
* @throws {RequiredError}
|
||||||
*/
|
*/
|
||||||
listImportLogs(page?: number, pageSize?: number, options?: any): AxiosPromise<InlineResponse2002> {
|
listImportLogs(page?: number, pageSize?: number, options?: any): AxiosPromise<InlineResponse2004> {
|
||||||
return localVarFp.listImportLogs(page, pageSize, options).then((request) => request(axios, basePath));
|
return localVarFp.listImportLogs(page, pageSize, options).then((request) => request(axios, basePath));
|
||||||
},
|
},
|
||||||
/**
|
/**
|
||||||
@@ -19138,7 +19235,7 @@ export const ApiApiFactory = function (configuration?: Configuration, basePath?:
|
|||||||
* @param {*} [options] Override http request option.
|
* @param {*} [options] Override http request option.
|
||||||
* @throws {RequiredError}
|
* @throws {RequiredError}
|
||||||
*/
|
*/
|
||||||
listIngredients(page?: number, pageSize?: number, options?: any): AxiosPromise<InlineResponse2004> {
|
listIngredients(page?: number, pageSize?: number, options?: any): AxiosPromise<InlineResponse2006> {
|
||||||
return localVarFp.listIngredients(page, pageSize, options).then((request) => request(axios, basePath));
|
return localVarFp.listIngredients(page, pageSize, options).then((request) => request(axios, basePath));
|
||||||
},
|
},
|
||||||
/**
|
/**
|
||||||
@@ -19159,7 +19256,7 @@ export const ApiApiFactory = function (configuration?: Configuration, basePath?:
|
|||||||
* @param {*} [options] Override http request option.
|
* @param {*} [options] Override http request option.
|
||||||
* @throws {RequiredError}
|
* @throws {RequiredError}
|
||||||
*/
|
*/
|
||||||
listKeywords(query?: string, root?: number, tree?: number, page?: number, pageSize?: number, options?: any): AxiosPromise<InlineResponse2005> {
|
listKeywords(query?: string, root?: number, tree?: number, page?: number, pageSize?: number, options?: any): AxiosPromise<InlineResponse2007> {
|
||||||
return localVarFp.listKeywords(query, root, tree, page, pageSize, options).then((request) => request(axios, basePath));
|
return localVarFp.listKeywords(query, root, tree, page, pageSize, options).then((request) => request(axios, basePath));
|
||||||
},
|
},
|
||||||
/**
|
/**
|
||||||
@@ -19311,13 +19408,13 @@ export const ApiApiFactory = function (configuration?: Configuration, basePath?:
|
|||||||
* @param {*} [options] Override http request option.
|
* @param {*} [options] Override http request option.
|
||||||
* @throws {RequiredError}
|
* @throws {RequiredError}
|
||||||
*/
|
*/
|
||||||
listRecipes(query?: string, keywords?: number, keywordsOr?: number, keywordsAnd?: number, keywordsOrNot?: number, keywordsAndNot?: number, foods?: number, foodsOr?: number, foodsAnd?: number, foodsOrNot?: number, foodsAndNot?: number, units?: number, rating?: number, books?: string, booksOr?: number, booksAnd?: number, booksOrNot?: number, booksAndNot?: number, internal?: string, random?: string, _new?: string, timescooked?: number, cookedon?: string, createdon?: string, updatedon?: string, viewedon?: string, makenow?: string, page?: number, pageSize?: number, options?: any): AxiosPromise<InlineResponse2006> {
|
listRecipes(query?: string, keywords?: number, keywordsOr?: number, keywordsAnd?: number, keywordsOrNot?: number, keywordsAndNot?: number, foods?: number, foodsOr?: number, foodsAnd?: number, foodsOrNot?: number, foodsAndNot?: number, units?: number, rating?: number, books?: string, booksOr?: number, booksAnd?: number, booksOrNot?: number, booksAndNot?: number, internal?: string, random?: string, _new?: string, timescooked?: number, cookedon?: string, createdon?: string, updatedon?: string, viewedon?: string, makenow?: string, page?: number, pageSize?: number, options?: any): AxiosPromise<InlineResponse2008> {
|
||||||
return localVarFp.listRecipes(query, keywords, keywordsOr, keywordsAnd, keywordsOrNot, keywordsAndNot, foods, foodsOr, foodsAnd, foodsOrNot, foodsAndNot, units, rating, books, booksOr, booksAnd, booksOrNot, booksAndNot, internal, random, _new, timescooked, cookedon, createdon, updatedon, viewedon, makenow, page, pageSize, options).then((request) => request(axios, basePath));
|
return localVarFp.listRecipes(query, keywords, keywordsOr, keywordsAnd, keywordsOrNot, keywordsAndNot, foods, foodsOr, foodsAnd, foodsOrNot, foodsAndNot, units, rating, books, booksOr, booksAnd, booksOrNot, booksAndNot, internal, random, _new, timescooked, cookedon, createdon, updatedon, viewedon, makenow, page, pageSize, options).then((request) => request(axios, basePath));
|
||||||
},
|
},
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @param {number} [id] Returns the shopping list entry with a primary key of id. Multiple values allowed.
|
* @param {number} [id] Returns the shopping list entry with a primary key of id. Multiple values allowed.
|
||||||
* @param {string} [checked] Filter shopping list entries on checked. [true, false, both, <b>recent</b>]<br> - recent includes unchecked items and recently completed items.
|
* @param {string} [checked] Filter shopping list entries on checked. [true, false, both, <b>recent</b>]<br> - recent includes unchecked items and recently completed items.
|
||||||
* @param {number} [supermarket] Returns the shopping list entries sorted by supermarket category order.
|
* @param {number} [supermarket] Returns the shopping list entries sorted by supermarket category order.
|
||||||
* @param {*} [options] Override http request option.
|
* @param {*} [options] Override http request option.
|
||||||
* @throws {RequiredError}
|
* @throws {RequiredError}
|
||||||
@@ -19358,7 +19455,7 @@ export const ApiApiFactory = function (configuration?: Configuration, basePath?:
|
|||||||
* @param {*} [options] Override http request option.
|
* @param {*} [options] Override http request option.
|
||||||
* @throws {RequiredError}
|
* @throws {RequiredError}
|
||||||
*/
|
*/
|
||||||
listSteps(recipe?: number, query?: string, page?: number, pageSize?: number, options?: any): AxiosPromise<InlineResponse2007> {
|
listSteps(recipe?: number, query?: string, page?: number, pageSize?: number, options?: any): AxiosPromise<InlineResponse2009> {
|
||||||
return localVarFp.listSteps(recipe, query, page, pageSize, options).then((request) => request(axios, basePath));
|
return localVarFp.listSteps(recipe, query, page, pageSize, options).then((request) => request(axios, basePath));
|
||||||
},
|
},
|
||||||
/**
|
/**
|
||||||
@@ -19376,7 +19473,7 @@ export const ApiApiFactory = function (configuration?: Configuration, basePath?:
|
|||||||
* @param {*} [options] Override http request option.
|
* @param {*} [options] Override http request option.
|
||||||
* @throws {RequiredError}
|
* @throws {RequiredError}
|
||||||
*/
|
*/
|
||||||
listSupermarketCategoryRelations(page?: number, pageSize?: number, options?: any): AxiosPromise<InlineResponse2008> {
|
listSupermarketCategoryRelations(page?: number, pageSize?: number, options?: any): AxiosPromise<InlineResponse20010> {
|
||||||
return localVarFp.listSupermarketCategoryRelations(page, pageSize, options).then((request) => request(axios, basePath));
|
return localVarFp.listSupermarketCategoryRelations(page, pageSize, options).then((request) => request(axios, basePath));
|
||||||
},
|
},
|
||||||
/**
|
/**
|
||||||
@@ -19404,7 +19501,7 @@ export const ApiApiFactory = function (configuration?: Configuration, basePath?:
|
|||||||
* @param {*} [options] Override http request option.
|
* @param {*} [options] Override http request option.
|
||||||
* @throws {RequiredError}
|
* @throws {RequiredError}
|
||||||
*/
|
*/
|
||||||
listSyncLogs(page?: number, pageSize?: number, options?: any): AxiosPromise<InlineResponse2009> {
|
listSyncLogs(page?: number, pageSize?: number, options?: any): AxiosPromise<InlineResponse20011> {
|
||||||
return localVarFp.listSyncLogs(page, pageSize, options).then((request) => request(axios, basePath));
|
return localVarFp.listSyncLogs(page, pageSize, options).then((request) => request(axios, basePath));
|
||||||
},
|
},
|
||||||
/**
|
/**
|
||||||
@@ -19432,7 +19529,7 @@ export const ApiApiFactory = function (configuration?: Configuration, basePath?:
|
|||||||
* @param {*} [options] Override http request option.
|
* @param {*} [options] Override http request option.
|
||||||
* @throws {RequiredError}
|
* @throws {RequiredError}
|
||||||
*/
|
*/
|
||||||
listUnits(query?: string, page?: number, pageSize?: number, options?: any): AxiosPromise<InlineResponse20010> {
|
listUnits(query?: string, page?: number, pageSize?: number, options?: any): AxiosPromise<InlineResponse20012> {
|
||||||
return localVarFp.listUnits(query, page, pageSize, options).then((request) => request(axios, basePath));
|
return localVarFp.listUnits(query, page, pageSize, options).then((request) => request(axios, basePath));
|
||||||
},
|
},
|
||||||
/**
|
/**
|
||||||
@@ -19459,7 +19556,7 @@ export const ApiApiFactory = function (configuration?: Configuration, basePath?:
|
|||||||
* @param {*} [options] Override http request option.
|
* @param {*} [options] Override http request option.
|
||||||
* @throws {RequiredError}
|
* @throws {RequiredError}
|
||||||
*/
|
*/
|
||||||
listUserSpaces(page?: number, pageSize?: number, options?: any): AxiosPromise<InlineResponse20011> {
|
listUserSpaces(page?: number, pageSize?: number, options?: any): AxiosPromise<InlineResponse20013> {
|
||||||
return localVarFp.listUserSpaces(page, pageSize, options).then((request) => request(axios, basePath));
|
return localVarFp.listUserSpaces(page, pageSize, options).then((request) => request(axios, basePath));
|
||||||
},
|
},
|
||||||
/**
|
/**
|
||||||
@@ -19477,7 +19574,7 @@ export const ApiApiFactory = function (configuration?: Configuration, basePath?:
|
|||||||
* @param {*} [options] Override http request option.
|
* @param {*} [options] Override http request option.
|
||||||
* @throws {RequiredError}
|
* @throws {RequiredError}
|
||||||
*/
|
*/
|
||||||
listViewLogs(page?: number, pageSize?: number, options?: any): AxiosPromise<InlineResponse20012> {
|
listViewLogs(page?: number, pageSize?: number, options?: any): AxiosPromise<InlineResponse20014> {
|
||||||
return localVarFp.listViewLogs(page, pageSize, options).then((request) => request(axios, basePath));
|
return localVarFp.listViewLogs(page, pageSize, options).then((request) => request(axios, basePath));
|
||||||
},
|
},
|
||||||
/**
|
/**
|
||||||
@@ -21837,13 +21934,16 @@ export class ApiApi extends BaseAPI {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
* optional parameters - **automation_type**: Return the Automations matching the automation type. Multiple values allowed. *Automation Types:* - FS: Food Alias - UA: Unit Alias - KA: Keyword Alias - DR: Description Replace - IR: Instruction Replace - NU: Never Unit - TW: Transpose Words - FR: Food Replace - UR: Unit Replace - NR: Name Replace
|
||||||
|
* @param {string} [automationType] Return the Automations matching the automation type. Multiple values allowed.
|
||||||
|
* @param {number} [page] A page number within the paginated result set.
|
||||||
|
* @param {number} [pageSize] Number of results to return per page.
|
||||||
* @param {*} [options] Override http request option.
|
* @param {*} [options] Override http request option.
|
||||||
* @throws {RequiredError}
|
* @throws {RequiredError}
|
||||||
* @memberof ApiApi
|
* @memberof ApiApi
|
||||||
*/
|
*/
|
||||||
public listAutomations(options?: any) {
|
public listAutomations(automationType?: string, page?: number, pageSize?: number, options?: any) {
|
||||||
return ApiApiFp(this.configuration).listAutomations(options).then((request) => request(this.axios, this.basePath));
|
return ApiApiFp(this.configuration).listAutomations(automationType, page, pageSize, options).then((request) => request(this.axios, this.basePath));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -21880,12 +21980,14 @@ export class ApiApi extends BaseAPI {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
|
* @param {number} [page] A page number within the paginated result set.
|
||||||
|
* @param {number} [pageSize] Number of results to return per page.
|
||||||
* @param {*} [options] Override http request option.
|
* @param {*} [options] Override http request option.
|
||||||
* @throws {RequiredError}
|
* @throws {RequiredError}
|
||||||
* @memberof ApiApi
|
* @memberof ApiApi
|
||||||
*/
|
*/
|
||||||
public listCustomFilters(options?: any) {
|
public listCustomFilters(page?: number, pageSize?: number, options?: any) {
|
||||||
return ApiApiFp(this.configuration).listCustomFilters(options).then((request) => request(this.axios, this.basePath));
|
return ApiApiFp(this.configuration).listCustomFilters(page, pageSize, options).then((request) => request(this.axios, this.basePath));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -22169,7 +22271,7 @@ export class ApiApi extends BaseAPI {
|
|||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @param {number} [id] Returns the shopping list entry with a primary key of id. Multiple values allowed.
|
* @param {number} [id] Returns the shopping list entry with a primary key of id. Multiple values allowed.
|
||||||
* @param {string} [checked] Filter shopping list entries on checked. [true, false, both, <b>recent</b>]<br> - recent includes unchecked items and recently completed items.
|
* @param {string} [checked] Filter shopping list entries on checked. [true, false, both, <b>recent</b>]<br> - recent includes unchecked items and recently completed items.
|
||||||
* @param {number} [supermarket] Returns the shopping list entries sorted by supermarket category order.
|
* @param {number} [supermarket] Returns the shopping list entries sorted by supermarket category order.
|
||||||
* @param {*} [options] Override http request option.
|
* @param {*} [options] Override http request option.
|
||||||
* @throws {RequiredError}
|
* @throws {RequiredError}
|
||||||
|
|||||||
@@ -29,10 +29,6 @@ const pages = {
|
|||||||
entry: "./src/apps/ExportView/main.ts",
|
entry: "./src/apps/ExportView/main.ts",
|
||||||
chunks: ["chunk-vendors","locales-chunk","api-chunk"],
|
chunks: ["chunk-vendors","locales-chunk","api-chunk"],
|
||||||
},
|
},
|
||||||
supermarket_view: {
|
|
||||||
entry: "./src/apps/SupermarketView/main.ts",
|
|
||||||
chunks: ["chunk-vendors","locales-chunk","api-chunk"],
|
|
||||||
},
|
|
||||||
model_list_view: {
|
model_list_view: {
|
||||||
entry: "./src/apps/ModelListView/main.ts",
|
entry: "./src/apps/ModelListView/main.ts",
|
||||||
chunks: ["chunk-vendors","locales-chunk","api-chunk"],
|
chunks: ["chunk-vendors","locales-chunk","api-chunk"],
|
||||||
@@ -65,10 +61,6 @@ const pages = {
|
|||||||
entry: "./src/apps/SpaceManageView/main.ts",
|
entry: "./src/apps/SpaceManageView/main.ts",
|
||||||
chunks: ["chunk-vendors","locales-chunk","api-chunk"],
|
chunks: ["chunk-vendors","locales-chunk","api-chunk"],
|
||||||
},
|
},
|
||||||
profile_view: {
|
|
||||||
entry: "./src/apps/ProfileView/main.ts",
|
|
||||||
chunks: ["chunk-vendors","locales-chunk","api-chunk"],
|
|
||||||
},
|
|
||||||
settings_view: {
|
settings_view: {
|
||||||
entry: "./src/apps/SettingsView/main.ts",
|
entry: "./src/apps/SettingsView/main.ts",
|
||||||
chunks: ["chunk-vendors","locales-chunk","api-chunk"],
|
chunks: ["chunk-vendors","locales-chunk","api-chunk"],
|
||||||
|
|||||||
Reference in New Issue
Block a user