mirror of
https://github.com/TandoorRecipes/recipes.git
synced 2026-01-01 12:18:45 -05:00
Merge remote-tracking branch 'upstream/feature/vue3' into feature/vue3
This commit is contained in:
@@ -6,6 +6,8 @@ from cookbook.models import (Food, FoodProperty, Property, PropertyType, Superma
|
|||||||
SupermarketCategory, SupermarketCategoryRelation, Unit, UnitConversion)
|
SupermarketCategory, SupermarketCategoryRelation, Unit, UnitConversion)
|
||||||
import re
|
import re
|
||||||
|
|
||||||
|
from recipes.settings import DEBUG
|
||||||
|
|
||||||
|
|
||||||
class OpenDataImportResponse:
|
class OpenDataImportResponse:
|
||||||
total_created = 0
|
total_created = 0
|
||||||
@@ -367,12 +369,28 @@ class OpenDataImporter:
|
|||||||
create_list.append({'data': obj_dict})
|
create_list.append({'data': obj_dict})
|
||||||
|
|
||||||
if self.update_existing and len(update_list) > 0:
|
if self.update_existing and len(update_list) > 0:
|
||||||
model_type.objects.bulk_update(update_list, field_list)
|
try:
|
||||||
od_response.total_updated += len(update_list)
|
model_type.objects.bulk_update(update_list, field_list)
|
||||||
|
od_response.total_updated += len(update_list)
|
||||||
|
except Exception:
|
||||||
|
if DEBUG:
|
||||||
|
print('========= LOAD FOOD FAILED ============')
|
||||||
|
print(update_list)
|
||||||
|
print(existing_data_names)
|
||||||
|
print(existing_data_slugs)
|
||||||
|
traceback.print_exc()
|
||||||
|
|
||||||
if len(create_list) > 0:
|
if len(create_list) > 0:
|
||||||
Food.load_bulk(create_list, None)
|
try:
|
||||||
od_response.total_created += len(create_list)
|
Food.load_bulk(create_list, None)
|
||||||
|
od_response.total_created += len(create_list)
|
||||||
|
except Exception:
|
||||||
|
if DEBUG:
|
||||||
|
print('========= LOAD FOOD FAILED ============')
|
||||||
|
print(create_list)
|
||||||
|
print(existing_data_names)
|
||||||
|
print(existing_data_slugs)
|
||||||
|
traceback.print_exc()
|
||||||
|
|
||||||
# --------------- PROPERTY STUFF -----------------------
|
# --------------- PROPERTY STUFF -----------------------
|
||||||
model_type = Property
|
model_type = Property
|
||||||
|
|||||||
@@ -628,13 +628,11 @@ class FoodViewSet(TreeMixin):
|
|||||||
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',
|
food.properties_food_unit = Unit.objects.get_or_create(
|
||||||
space=self.request.space,
|
base_unit__iexact='g',
|
||||||
defaults={
|
space=self.request.space,
|
||||||
'name': 'g',
|
defaults={ 'name': 'g', 'base_unit': 'g', 'space': self.request.space}
|
||||||
'base_unit': 'g',
|
)[0]
|
||||||
'space': self.request.space
|
|
||||||
})[0]
|
|
||||||
|
|
||||||
food.save()
|
food.save()
|
||||||
|
|
||||||
@@ -930,39 +928,15 @@ class RecipePagination(PageNumberPagination):
|
|||||||
OpenApiParameter(name='books_and', description=_('Book IDs, repeat for multiple. Return recipes with all of the books.'), type=int),
|
OpenApiParameter(name='books_and', description=_('Book IDs, repeat for multiple. Return recipes with all of the books.'), type=int),
|
||||||
OpenApiParameter(name='books_or_not', description=_('Book IDs, repeat for multiple. Exclude recipes with any of the books.'), type=int),
|
OpenApiParameter(name='books_or_not', description=_('Book IDs, repeat for multiple. Exclude recipes with any of the books.'), type=int),
|
||||||
OpenApiParameter(name='books_and_not', description=_('Book IDs, repeat for multiple. Exclude recipes with all of the books.'), type=int),
|
OpenApiParameter(name='books_and_not', description=_('Book IDs, repeat for multiple. Exclude recipes with all of the books.'), type=int),
|
||||||
OpenApiParameter(name='internal', description=_('If only internal recipes should be returned. ['
|
OpenApiParameter(name='internal', description=_('If only internal recipes should be returned. [''true''/''<b>false</b>'']')),
|
||||||
'true'
|
OpenApiParameter(name='random', description=_('Returns the results in randomized order. [''true''/''<b>false</b>'']')),
|
||||||
'/'
|
OpenApiParameter(name='new', description=_('Returns new results first in search results. [''true''/''<b>false</b>'']')),
|
||||||
'<b>false</b>'
|
|
||||||
']')),
|
|
||||||
OpenApiParameter(name='random', description=_('Returns the results in randomized order. ['
|
|
||||||
'true'
|
|
||||||
'/'
|
|
||||||
'<b>false</b>'
|
|
||||||
']')),
|
|
||||||
OpenApiParameter(name='new', description=_('Returns new results first in search results. ['
|
|
||||||
'true'
|
|
||||||
'/'
|
|
||||||
'<b>false</b>'
|
|
||||||
']')),
|
|
||||||
OpenApiParameter(name='timescooked', description=_('Filter recipes cooked X times or more. Negative values returns cooked less than X times'), type=int),
|
OpenApiParameter(name='timescooked', description=_('Filter recipes cooked X times or more. Negative values returns cooked less than X times'), type=int),
|
||||||
OpenApiParameter(name='cookedon', description=_('Filter recipes last cooked on or after YYYY-MM-DD. Prepending '
|
OpenApiParameter(name='cookedon', description=_('Filter recipes last cooked on or after YYYY-MM-DD. Prepending ''-'' filters on or before date.')),
|
||||||
'-'
|
OpenApiParameter(name='createdon', description=_('Filter recipes created on or after YYYY-MM-DD. Prepending ''-'' filters on or before date.')),
|
||||||
' filters on or before date.')),
|
OpenApiParameter(name='updatedon', description=_('Filter recipes updated on or after YYYY-MM-DD. Prepending ''-'' filters on or before date.')),
|
||||||
OpenApiParameter(name='createdon', description=_('Filter recipes created on or after YYYY-MM-DD. Prepending '
|
OpenApiParameter(name='viewedon', description=_('Filter recipes lasts viewed on or after YYYY-MM-DD. Prepending ''-'' filters on or before date.')),
|
||||||
'-'
|
OpenApiParameter(name='makenow', description=_('Filter recipes that can be made with OnHand food. [''true''/''<b>false</b>'']')),
|
||||||
' filters on or before date.')),
|
|
||||||
OpenApiParameter(name='updatedon', description=_('Filter recipes updated on or after YYYY-MM-DD. Prepending '
|
|
||||||
'-'
|
|
||||||
' filters on or before date.')),
|
|
||||||
OpenApiParameter(name='viewedon', description=_('Filter recipes lasts viewed on or after YYYY-MM-DD. Prepending '
|
|
||||||
'-'
|
|
||||||
' filters on or before date.')),
|
|
||||||
OpenApiParameter(name='makenow', description=_('Filter recipes that can be made with OnHand food. ['
|
|
||||||
'true'
|
|
||||||
'/'
|
|
||||||
'<b>false</b>'
|
|
||||||
']')),
|
|
||||||
]))
|
]))
|
||||||
class RecipeViewSet(viewsets.ModelViewSet):
|
class RecipeViewSet(viewsets.ModelViewSet):
|
||||||
queryset = Recipe.objects
|
queryset = Recipe.objects
|
||||||
@@ -1153,21 +1127,13 @@ class ShoppingListRecipeViewSet(viewsets.ModelViewSet):
|
|||||||
|
|
||||||
|
|
||||||
@extend_schema_view(list=extend_schema(parameters=[
|
@extend_schema_view(list=extend_schema(parameters=[
|
||||||
OpenApiParameter(name='id', description=_('Returns the shopping list entry with a primary key of id. Multiple values allowed.'), type=int),
|
OpenApiParameter(name='id', description=_('Returns the shopping list entry with a primary key of id. Multiple values allowed.'), type=int),
|
||||||
OpenApiParameter(name='checked',
|
OpenApiParameter(
|
||||||
description=_('Filter shopping list entries on checked. ['
|
name='checked',
|
||||||
'true'
|
description=_('Filter shopping list entries on checked. [''true'', ''false'', ''both'', ''<b>recent</b>'']<br> \
|
||||||
', '
|
- ''recent'' includes unchecked items and recently completed items.')
|
||||||
'false'
|
),
|
||||||
', '
|
OpenApiParameter(name='supermarket', description=_('Returns the shopping list entries sorted by supermarket category order.'), type=int),
|
||||||
'both'
|
|
||||||
', '
|
|
||||||
'<b>recent</b>'
|
|
||||||
']<br> \
|
|
||||||
- '
|
|
||||||
'recent'
|
|
||||||
' includes unchecked items and recently completed items.')),
|
|
||||||
OpenApiParameter(name='supermarket', description=_('Returns the shopping list entries sorted by supermarket category order.'), type=int),
|
|
||||||
]))
|
]))
|
||||||
class ShoppingListEntryViewSet(viewsets.ModelViewSet):
|
class ShoppingListEntryViewSet(viewsets.ModelViewSet):
|
||||||
queryset = ShoppingListEntry.objects
|
queryset = ShoppingListEntry.objects
|
||||||
@@ -1344,7 +1310,8 @@ class AutomationViewSet(StandardFilterModelViewSet):
|
|||||||
}
|
}
|
||||||
|
|
||||||
@extend_schema(
|
@extend_schema(
|
||||||
parameters=[OpenApiParameter(name='automation_type', description=_('Return the Automations matching the automation type. Multiple values allowed.'), type=str)])
|
parameters=[OpenApiParameter(name='automation_type', description=_('Return the Automations matching the automation type. Multiple values allowed.'), type=str)]
|
||||||
|
)
|
||||||
def list(self, request, *args, **kwargs):
|
def list(self, request, *args, **kwargs):
|
||||||
return super().list(request, *args, **kwargs)
|
return super().list(request, *args, **kwargs)
|
||||||
|
|
||||||
@@ -1507,7 +1474,7 @@ class RecipeUrlImportView(APIView):
|
|||||||
'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({'error': True, 'msg': _('No usable data could be found.')}, status=status.HTTP_400_BAD_REQUEST)
|
return Response({'error': True, 'msg': _('No usable data could be found.')}, status=status.HTTP_400_BAD_REQUEST)
|
||||||
@@ -1718,7 +1685,7 @@ 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, 'link': request.build_absolute_uri(reverse('view_recipe', args=[pk, link.uuid]))})
|
return JsonResponse({'pk': pk, 'share': link.uuid, 'link': request.build_absolute_uri(reverse('view_recipe', args=[pk, link.uuid]))})
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ import re
|
|||||||
import socket
|
import socket
|
||||||
import sys
|
import sys
|
||||||
import traceback
|
import traceback
|
||||||
|
import socket
|
||||||
|
|
||||||
from django.contrib import messages
|
from django.contrib import messages
|
||||||
from django.utils.translation import gettext_lazy as _
|
from django.utils.translation import gettext_lazy as _
|
||||||
@@ -112,10 +113,34 @@ MESSAGE_TAGS = {messages.ERROR: 'danger'}
|
|||||||
# Application definition
|
# Application definition
|
||||||
|
|
||||||
INSTALLED_APPS = [
|
INSTALLED_APPS = [
|
||||||
'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.sites',
|
'django.contrib.admin',
|
||||||
'django.contrib.staticfiles', 'django.contrib.postgres', 'oauth2_provider', 'django_prometheus', 'django_tables2', 'corsheaders', 'crispy_forms', 'crispy_bootstrap4',
|
'django.contrib.auth',
|
||||||
'rest_framework', 'rest_framework.authtoken', 'drf_spectacular', 'drf_spectacular_sidecar', 'django_cleanup.apps.CleanupConfig', 'webpack_loader', 'django_vite',
|
'django.contrib.contenttypes',
|
||||||
'django_js_reverse', 'hcaptcha', 'allauth', 'allauth.account', 'allauth.socialaccount', 'cookbook.apps.CookbookConfig', 'treebeard',
|
'django.contrib.sessions',
|
||||||
|
'django.contrib.messages',
|
||||||
|
'django.contrib.sites',
|
||||||
|
'django.contrib.staticfiles',
|
||||||
|
'django.contrib.postgres',
|
||||||
|
'oauth2_provider',
|
||||||
|
'django_prometheus',
|
||||||
|
'django_tables2',
|
||||||
|
'corsheaders',
|
||||||
|
'crispy_forms',
|
||||||
|
'crispy_bootstrap4',
|
||||||
|
'rest_framework',
|
||||||
|
'rest_framework.authtoken',
|
||||||
|
'drf_spectacular',
|
||||||
|
'drf_spectacular_sidecar',
|
||||||
|
'django_cleanup.apps.CleanupConfig',
|
||||||
|
'webpack_loader',
|
||||||
|
'django_vite',
|
||||||
|
'django_js_reverse',
|
||||||
|
'hcaptcha',
|
||||||
|
'allauth',
|
||||||
|
'allauth.account',
|
||||||
|
'allauth.socialaccount',
|
||||||
|
'cookbook.apps.CookbookConfig',
|
||||||
|
'treebeard',
|
||||||
]
|
]
|
||||||
|
|
||||||
PLUGINS_DIRECTORY = os.path.join(BASE_DIR, 'recipes', 'plugins')
|
PLUGINS_DIRECTORY = os.path.join(BASE_DIR, 'recipes', 'plugins')
|
||||||
@@ -192,14 +217,14 @@ MIDDLEWARE = [
|
|||||||
]
|
]
|
||||||
|
|
||||||
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(int(os.getenv('DISABLE_TREE_FIX_STARTUP', False)))
|
DISABLE_TREE_FIX_STARTUP = bool(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',
|
||||||
@@ -278,13 +303,11 @@ WRITE_SCOPE = 'write'
|
|||||||
|
|
||||||
REST_FRAMEWORK = {
|
REST_FRAMEWORK = {
|
||||||
'DEFAULT_AUTHENTICATION_CLASSES':
|
'DEFAULT_AUTHENTICATION_CLASSES':
|
||||||
('rest_framework.authentication.SessionAuthentication', 'oauth2_provider.contrib.rest_framework.OAuth2Authentication', 'rest_framework.authentication.BasicAuthentication',
|
('rest_framework.authentication.SessionAuthentication', 'oauth2_provider.contrib.rest_framework.OAuth2Authentication', 'rest_framework.authentication.BasicAuthentication',
|
||||||
),
|
),
|
||||||
'DEFAULT_PERMISSION_CLASSES': ['rest_framework.permissions.IsAuthenticated', ],
|
'DEFAULT_PERMISSION_CLASSES': ['rest_framework.permissions.IsAuthenticated', ],
|
||||||
'DEFAULT_SCHEMA_CLASS':
|
'DEFAULT_SCHEMA_CLASS': 'drf_spectacular.openapi.AutoSchema',
|
||||||
'drf_spectacular.openapi.AutoSchema',
|
'COERCE_DECIMAL_TO_STRING': False,
|
||||||
'COERCE_DECIMAL_TO_STRING':
|
|
||||||
False,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
SPECTACULAR_SETTINGS = {
|
SPECTACULAR_SETTINGS = {
|
||||||
@@ -302,9 +325,7 @@ SPECTACULAR_SETTINGS = {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"SECURITY": [{
|
"SECURITY": [{ "ApiKeyAuth": [] }],
|
||||||
"ApiKeyAuth": []
|
|
||||||
}],
|
|
||||||
'SWAGGER_UI_DIST': 'SIDECAR',
|
'SWAGGER_UI_DIST': 'SIDECAR',
|
||||||
'SWAGGER_UI_FAVICON_HREF': 'SIDECAR',
|
'SWAGGER_UI_FAVICON_HREF': 'SIDECAR',
|
||||||
'REDOC_DIST': 'SIDECAR',
|
'REDOC_DIST': 'SIDECAR',
|
||||||
@@ -436,7 +457,14 @@ for p in PLUGINS:
|
|||||||
'IGNORE': [r'.+\.hot-update.js', r'.+\.map'],
|
'IGNORE': [r'.+\.hot-update.js', r'.+\.map'],
|
||||||
}
|
}
|
||||||
|
|
||||||
DJANGO_VITE = {"default": {"dev_mode": False, "static_url_prefix": 'vue3', "dev_server_port": 5173, "dev_server_host": os.getenv('DJANGO_VITE_DEV_SERVER_HOST', 'localhost'), }, }
|
DJANGO_VITE = {
|
||||||
|
"default": {
|
||||||
|
"dev_mode": False,
|
||||||
|
"static_url_prefix": 'vue3',
|
||||||
|
"dev_server_port": 5173,
|
||||||
|
"dev_server_host": os.getenv('DJANGO_VITE_DEV_SERVER_HOST', 'localhost'),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
|
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
|
||||||
s.settimeout(0.001)
|
s.settimeout(0.001)
|
||||||
@@ -531,9 +559,18 @@ DISABLE_EXTERNAL_CONNECTORS = bool(int(os.getenv('DISABLE_EXTERNAL_CONNECTORS',
|
|||||||
EXTERNAL_CONNECTORS_QUEUE_SIZE = int(os.getenv('EXTERNAL_CONNECTORS_QUEUE_SIZE', 100))
|
EXTERNAL_CONNECTORS_QUEUE_SIZE = int(os.getenv('EXTERNAL_CONNECTORS_QUEUE_SIZE', 100))
|
||||||
|
|
||||||
# ACCOUNT_SIGNUP_FORM_CLASS = 'cookbook.forms.AllAuthSignupForm'
|
# ACCOUNT_SIGNUP_FORM_CLASS = 'cookbook.forms.AllAuthSignupForm'
|
||||||
ACCOUNT_FORMS = {'signup': 'cookbook.forms.AllAuthSignupForm', 'reset_password': 'cookbook.forms.CustomPasswordResetForm'}
|
ACCOUNT_FORMS = {
|
||||||
|
'signup': 'cookbook.forms.AllAuthSignupForm',
|
||||||
|
'reset_password': 'cookbook.forms.CustomPasswordResetForm'
|
||||||
|
}
|
||||||
|
|
||||||
ACCOUNT_EMAIL_UNKNOWN_ACCOUNTS = False
|
ACCOUNT_EMAIL_UNKNOWN_ACCOUNTS = False
|
||||||
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", }
|
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",
|
||||||
|
}
|
||||||
|
|
||||||
mimetypes.add_type("text/javascript", ".js", True)
|
mimetypes.add_type("text/javascript", ".js", True)
|
||||||
|
|||||||
@@ -46,6 +46,7 @@ pyppeteer==2.0.0
|
|||||||
validators==0.20.0
|
validators==0.20.0
|
||||||
pytube==15.0.0
|
pytube==15.0.0
|
||||||
homeassistant-api==4.1.1.post2
|
homeassistant-api==4.1.1.post2
|
||||||
|
django-vite==3.0.3
|
||||||
|
|
||||||
# Development
|
# Development
|
||||||
pytest==8.0.0
|
pytest==8.0.0
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user