Merge branch 'TandoorRecipes:develop' into develop

This commit is contained in:
blowk
2023-11-17 16:16:51 +01:00
committed by GitHub
21 changed files with 2660 additions and 132 deletions

1
.gitignore vendored
View File

@@ -74,6 +74,7 @@ mediafiles/
\.env
staticfiles/
postgresql/
data/
/docker-compose.override.yml

View File

@@ -35,7 +35,7 @@ echo "Waiting for database to be ready..."
attempt=0
max_attempts=20
if [ "${DB_ENGINE}" != 'django.db.backends.sqlite3' ]; then
if [ "${DB_ENGINE}" == 'django.db.backends.postgresql' ] || [ "${DATABASE_URL}" == 'postgres'* ]; then
# POSTGRES_PASSWORD (or a valid file at POSTGRES_PASSWORD_FILE) must be set in .env file

View File

@@ -192,7 +192,7 @@ class RecipeAdmin(admin.ModelAdmin):
def created_by(obj):
return obj.created_by.get_user_display_name()
if settings.DATABASES['default']['ENGINE'] in ['django.db.backends.postgresql_psycopg2', 'django.db.backends.postgresql']:
if settings.DATABASES['default']['ENGINE'] == 'django.db.backends.postgresql':
actions = [rebuild_index]

View File

@@ -9,8 +9,8 @@ from django_scopes import scopes_disabled
from django_scopes.forms import SafeModelChoiceField, SafeModelMultipleChoiceField
from hcaptcha.fields import hCaptchaField
from .models import (Comment, Food, InviteLink, Keyword, MealPlan, MealType, Recipe, RecipeBook,
RecipeBookEntry, SearchPreference, Space, Storage, Sync, User, UserPreference)
from .models import (Comment, Food, InviteLink, Keyword, Recipe, RecipeBook, RecipeBookEntry,
SearchPreference, Space, Storage, Sync, User, UserPreference)
class SelectWidget(widgets.Select):

View File

@@ -16,7 +16,7 @@ from recipes import settings
# TODO consider creating a simpleListRecipe API that only includes minimum of recipe info and minimal filtering
class RecipeSearch():
_postgres = settings.DATABASES['default']['ENGINE'] in ['django.db.backends.postgresql_psycopg2', 'django.db.backends.postgresql']
_postgres = settings.DATABASES['default']['ENGINE'] == 'django.db.backends.postgresql'
def __init__(self, request, **params):
self._request = request

File diff suppressed because it is too large Load Diff

View File

@@ -1,9 +1,9 @@
from django.conf import settings
from django.contrib.postgres.search import SearchVector
from django.core.management.base import BaseCommand
from django_scopes import scopes_disabled
from django.utils import translation
from django.utils.translation import gettext_lazy as _
from django_scopes import scopes_disabled
from cookbook.managers import DICTIONARY
from cookbook.models import Recipe, Step
@@ -14,7 +14,7 @@ class Command(BaseCommand):
help = _('Rebuilds full text search index on Recipe')
def handle(self, *args, **options):
if settings.DATABASES['default']['ENGINE'] not in ['django.db.backends.postgresql_psycopg2', 'django.db.backends.postgresql']:
if settings.DATABASES['default']['ENGINE'] != 'django.db.backends.postgresql':
self.stdout.write(self.style.WARNING(_('Only Postgresql databases use full text search, no index to rebuild')))
try:

View File

@@ -9,7 +9,7 @@ from django.utils import translation
from django_scopes import scopes_disabled
from cookbook.managers import DICTIONARY
from cookbook.models import (Index, PermissionModelMixin, Recipe, Step, SearchFields)
from cookbook.models import Index, PermissionModelMixin, Recipe, SearchFields, Step
def allSearchFields():
@@ -21,7 +21,7 @@ def nameSearchField():
def set_default_search_vector(apps, schema_editor):
if settings.DATABASES['default']['ENGINE'] not in ['django.db.backends.postgresql_psycopg2', 'django.db.backends.postgresql']:
if settings.DATABASES['default']['ENGINE'] != 'django.db.backends.postgresql':
return
language = DICTIONARY.get(translation.get_language(), 'simple')
with scopes_disabled():

View File

@@ -16,8 +16,7 @@ from cookbook.models import (Food, MealPlan, PropertyType, Recipe, SearchFields,
Step, Unit, UserPreference)
SQLITE = True
if settings.DATABASES['default']['ENGINE'] in ['django.db.backends.postgresql_psycopg2',
'django.db.backends.postgresql']:
if settings.DATABASES['default']['ENGINE'] == 'django.db.backends.postgresql':
SQLITE = False

View File

@@ -4,17 +4,14 @@
{% load i18n %}
{% load l10n %}
{% block title %}{% trans 'Export Recipes' %}{% endblock %}
{% block content %}
<div id="app">
<export-view></export-view>
</div>
{% endblock %}
{% block script %}
{% if debug %}
<script src="{% url 'js_reverse' %}"></script>
@@ -23,11 +20,13 @@
{% endif %}
<script type="application/javascript">
window.EXPORT_ID = {{pk}};
{% if pk %}
window.EXPORT_ID = {{ pk }}
{% else %}
window.EXPORT_ID = null
{% endif %}
window.CUSTOM_LOCALE = '{{ request.LANGUAGE_CODE }}';
</script>
{% render_bundle 'export_view' %}
{% endblock %}

View File

@@ -7,15 +7,12 @@
{% block title %}{% trans 'Export' %}{% endblock %}
{% block content %}
<div id="app">
<export-response-view></export-response-view>
</div>
{% endblock %}
{% block script %}
{% if debug %}
<script src="{% url 'js_reverse' %}"></script>
@@ -24,7 +21,11 @@
{% endif %}
<script type="application/javascript">
window.EXPORT_ID = {{pk}};
{% if pk %}
window.EXPORT_ID = {{ pk }}
{% else %}
window.EXPORT_ID = null
{% endif %}
window.CUSTOM_LOCALE = '{{ request.LANGUAGE_CODE }}'
</script>

View File

@@ -10,8 +10,7 @@ from django_scopes import scopes_disabled
from cookbook.models import Food, Ingredient
from cookbook.tests.factories import MealPlanFactory, RecipeFactory, StepFactory, UserFactory
if settings.DATABASES['default']['ENGINE'] in ['django.db.backends.postgresql_psycopg2',
'django.db.backends.postgresql']:
if settings.DATABASES['default']['ENGINE'] == 'django.db.backends.postgresql':
from django.db.backends.postgresql.features import DatabaseFeatures
DatabaseFeatures.can_defer_constraint_checks = False

View File

@@ -23,8 +23,7 @@ from cookbook.tests.factories import (CookLogFactory, FoodFactory, IngredientFac
# TODO makenow with above filters
# TODO test search food/keywords including/excluding children
LIST_URL = 'api:recipe-list'
sqlite = settings.DATABASES['default']['ENGINE'] not in [
'django.db.backends.postgresql_psycopg2', 'django.db.backends.postgresql']
sqlite = settings.DATABASES['default']['ENGINE'] != 'django.db.backends.postgresql'
@pytest.fixture

View File

@@ -186,8 +186,7 @@ class FuzzyFilterMixin(ViewSetMixin, ExtendedRecipeMixin):
fuzzy = True
if query is not None and query not in ["''", '']:
if fuzzy and (settings.DATABASES['default']['ENGINE'] in ['django.db.backends.postgresql_psycopg2',
'django.db.backends.postgresql']):
if fuzzy and (settings.DATABASES['default']['ENGINE'] == 'django.db.backends.postgresql'):
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.queryset = self.queryset.annotate(trigram=TrigramSimilarity('name__unaccent', query))

View File

@@ -16,12 +16,13 @@ from django.utils import timezone
from django.utils.translation import gettext as _
from django_scopes import scopes_disabled
from cookbook.forms import (CommentForm, Recipe, SearchPreferenceForm, SpaceCreateForm, SpaceJoinForm, User,
UserCreateForm, UserPreference)
from cookbook.helper.permission_helper import group_required, has_group_permission, share_link_valid, switch_user_active_space
from cookbook.models import (Comment, CookLog, InviteLink, SearchFields, SearchPreference, ShareLink,
Space, ViewLog, UserSpace)
from cookbook.tables import (CookLogTable, ViewLogTable)
from cookbook.forms import (CommentForm, Recipe, SearchPreferenceForm, SpaceCreateForm,
SpaceJoinForm, User, UserCreateForm, UserPreference)
from cookbook.helper.permission_helper import (group_required, has_group_permission,
share_link_valid, switch_user_active_space)
from cookbook.models import (Comment, CookLog, InviteLink, SearchFields, SearchPreference,
ShareLink, Space, UserSpace, ViewLog)
from cookbook.tables import CookLogTable, ViewLogTable
from cookbook.version_info import VERSION_INFO
from recipes.settings import PLUGINS
@@ -219,10 +220,10 @@ def shopping_settings(request):
if not sp:
sp = SearchPreferenceForm(user=request.user)
fields_searched = (
len(search_form.cleaned_data['icontains'])
+ len(search_form.cleaned_data['istartswith'])
+ len(search_form.cleaned_data['trigram'])
+ len(search_form.cleaned_data['fulltext'])
len(search_form.cleaned_data['icontains'])
+ len(search_form.cleaned_data['istartswith'])
+ len(search_form.cleaned_data['trigram'])
+ len(search_form.cleaned_data['fulltext'])
)
if search_form.cleaned_data['preset'] == 'fuzzy':
sp.search = SearchPreference.SIMPLE
@@ -278,8 +279,7 @@ def shopping_settings(request):
search_form = SearchPreferenceForm()
# these fields require postgresql - just disable them if postgresql isn't available
if not settings.DATABASES['default']['ENGINE'] in ['django.db.backends.postgresql_psycopg2',
'django.db.backends.postgresql']:
if not settings.DATABASES['default']['ENGINE'] == 'django.db.backends.postgresql':
sp.search = SearchPreference.SIMPLE
sp.trigram.clear()
sp.fulltext.clear()
@@ -309,10 +309,7 @@ def system(request):
if not request.user.is_superuser:
return HttpResponseRedirect(reverse('index'))
postgres = False if (
settings.DATABASES['default']['ENGINE'] == 'django.db.backends.postgresql_psycopg2' # noqa: E501
or settings.DATABASES['default']['ENGINE'] == 'django.db.backends.postgresql' # noqa: E501
) else True
postgres = settings.DATABASES['default']['ENGINE'] == 'django.db.backends.postgresql'
secret_key = False if os.getenv('SECRET_KEY') else True

View File

@@ -17,7 +17,7 @@ spec:
template:
metadata:
annotations:
backup.velero.io/backup-volumes: media,static
backup.velero.io/backup-volumes: media,static
labels:
app: recipes
tier: frontend
@@ -35,7 +35,7 @@ spec:
name: recipes
key: secret-key
- name: DB_ENGINE
value: django.db.backends.postgresql_psycopg2
value: django.db.backends.postgresql
- name: POSTGRES_HOST
value: recipes-postgresql
- name: POSTGRES_PORT
@@ -162,7 +162,7 @@ spec:
- name: GUNICORN_MEDIA
value: "0"
- name: DB_ENGINE
value: django.db.backends.postgresql_psycopg2
value: django.db.backends.postgresql
- name: POSTGRES_HOST
value: recipes-postgresql
- name: POSTGRES_PORT

View File

@@ -350,7 +350,7 @@ WSGI_APPLICATION = 'recipes.wsgi.application'
# Load settings from env files
if os.getenv('DATABASE_URL'):
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')
)
settings = match.groupdict()
@@ -358,6 +358,8 @@ if os.getenv('DATABASE_URL'):
if schema.startswith('postgres'):
engine = 'django.db.backends.postgresql'
elif schema == 'sqlite':
if not os.path.exists(db_path := os.path.dirname(settings['database'])):
os.makedirs(db_path)
engine = 'django.db.backends.sqlite3'
else:
raise Exception("Unsupported database schema: '%s'" % schema)

View File

@@ -19,10 +19,11 @@
{{ $t("If download did not start automatically: ") }}
<template v-if="export_info.expired">
<a disabled
><del>{{ $t("Download") }}</del></a
>
<template v-if="false">
<!--template v-if="export_info.expired" temporary disabling this to get around immediate expiration-->
<a disabled ref="downloadAnchor">
<del>{{ $t("Download") }}</del>
</a>
({{ $t("Expired") }})
</template>
<a v-else :href="`${resolveDjangoUrl('view_export_file', export_id)}`" ref="downloadAnchor">{{ $t("Download") }}</a>
@@ -53,20 +54,20 @@
</template>
<script>
import Vue from "vue"
import { BootstrapVue } from "bootstrap-vue"
import Vue from "vue"
import "bootstrap-vue/dist/bootstrap-vue.css"
import { ResolveUrlMixin, makeToast, ToastMixin } from "@/utils/utils"
import { ResolveUrlMixin, ToastMixin, makeToast } from "@/utils/utils"
import LoadingSpinner from "@/components/LoadingSpinner"
import { ApiApiFactory } from "@/utils/openapi/api.ts"
import VueSanitize from "vue-sanitize"
Vue.use(BootstrapVue)
import VueSanitize from "vue-sanitize";
Vue.use(VueSanitize);
Vue.use(VueSanitize)
export default {
name: "ExportResponseView",
mixins: [ResolveUrlMixin, ToastMixin],

View File

@@ -45,14 +45,12 @@
</template>
<script>
import Vue from "vue"
import { BootstrapVue } from "bootstrap-vue"
import Vue from "vue"
import "bootstrap-vue/dist/bootstrap-vue.css"
import LoadingSpinner from "@/components/LoadingSpinner"
import { StandardToasts, makeToast, resolveDjangoUrl, ApiMixin } from "@/utils/utils"
import { ApiMixin, StandardToasts, makeToast, resolveDjangoUrl } from "@/utils/utils"
// import Multiselect from "vue-multiselect"
import GenericMultiselect from "@/components/GenericMultiselect"
import { ApiApiFactory } from "@/utils/openapi/api.ts"
@@ -108,7 +106,7 @@ export default {
let formData = new FormData()
formData.append("type", this.recipe_app)
formData.append("all", this.export_all)
formData.append("custom_filter", this.filter?.id ?? null)
formData.append("custom_filter", this.filter?.id ?? "")
for (var i = 0; i < this.recipe_list.length; i++) {
formData.append("recipes", this.recipe_list[i].id)

View File

@@ -246,32 +246,32 @@
"New_Meal_Type": "סוג אוכל חדש",
"Use_Fractions": "השתמש בשברים",
"Use_Fractions_Help": "המר אוטומטית מדצמילי לשברים כאשר צופים במתכון.",
"AddFoodToShopping": "",
"RemoveFoodFromShopping": "",
"DeleteShoppingConfirm": "",
"IgnoredFood": "",
"Add_Servings_to_Shopping": "",
"Week_Numbers": "",
"Show_Week_Numbers": "",
"Export_As_ICal": "",
"Export_To_ICal": "",
"Cannot_Add_Notes_To_Shopping": "",
"Added_To_Shopping_List": "",
"Shopping_List_Empty": "",
"Next_Period": "",
"Previous_Period": "",
"Current_Period": "",
"Next_Day": "",
"Previous_Day": "",
"AddFoodToShopping": "הוסף {מזון} לרשימת הקניות",
"RemoveFoodFromShopping": "הסר {מזון} מרשימת הקניות",
"DeleteShoppingConfirm": "האם אתה בטוח שברצונך להסיר את כל ה{מזון} מרשימת הקניות ?",
"IgnoredFood": "{מזון} להתעלם בקנייה.",
"Add_Servings_to_Shopping": "הוסף{מנה}מנות לקנייה",
"Week_Numbers": "מספר השבוע",
"Show_Week_Numbers": "להציג מספר שבועות?",
"Export_As_ICal": "ייצוא תקופה נוכחית בפורמט iCal",
"Export_To_ICal": "ייצא .ics",
"Cannot_Add_Notes_To_Shopping": "לא ניתן להוסיף הערות לרשימת הקניות",
"Added_To_Shopping_List": "נוסף לרשימת הקניות",
"Shopping_List_Empty": "רשימת הקניות שלך ריקה כרגע. ניתן להוסיף פריטים דרך תפריט תוכנית אוכל (מקש ימני על הכרטיס או מקש שמאלי על האייקון בתפריט)",
"Next_Period": "התקופה הבאה",
"Previous_Period": "תקופה קודמת",
"Current_Period": "תקופה נוכחית",
"Next_Day": "היום הבא",
"Previous_Day": "יום קודם",
"Inherit": "",
"InheritFields": "",
"FoodInherit": "",
"ShowUncategorizedFood": "",
"GroupBy": "",
"Language": "",
"Theme": "",
"SupermarketCategoriesOnly": "",
"MoveCategory": "",
"ShowUncategorizedFood": "הצג לא מוגדר",
"GroupBy": "אסוף לפי",
"Language": "שפה",
"Theme": "נושא",
"SupermarketCategoriesOnly": "קטגוריות סופרמרקט בלבד",
"MoveCategory": "העבר אל: ",
"CountMore": "...+{count} עוד",
"IgnoreThis": "לעולם אל תוסיף {food} לרשימת הקניות",
"DelayFor": "השהה ל {hours} שעות",
@@ -316,7 +316,7 @@
"filter_to_supermarket_desc": "בברירת המחדל, רשימת קניות כוללה רק את הקטגוריות לסופרמקט הנבחר.",
"CategoryName": "שם קטגוריה",
"SupermarketName": "שם סופרמרקט",
"CategoryInstruction": "",
"CategoryInstruction": "גרור קטגוריות לשינוי הסדר שבו הן מופיעות ברשימת הקניות.",
"shopping_recent_days_desc": "",
"shopping_recent_days": "",
"download_pdf": "",
@@ -517,5 +517,9 @@
"Use_Plural_Food_Simple": "השתמש בצורת רבים למאכלים בצורה דינאמית",
"plural_usage_info": "תמיד השתמש בצורת רבים למאכלים במרחב זה.",
"Create Recipe": "צור מתכון",
"Import Recipe": "ייבא מתכון"
"Import Recipe": "ייבא מתכון",
"Alignment": "יישור",
"StartDate": "תאריך התחלה",
"EndDate": "תאריך סיום",
"OrderInformation": "המוצרים מוצגים מהמספר הקטן לגדול."
}

View File

@@ -24,53 +24,53 @@
"convert_internal": "Konvertuoti į vidinį receptą",
"show_only_internal": "Rodyti tik vidinius receptus",
"show_split_screen": "Padalintas vaizdas",
"Log_Recipe_Cooking": "",
"External_Recipe_Image": "",
"Add_to_Shopping": "",
"Add_to_Plan": "",
"Step_start_time": "",
"Sort_by_new": "",
"Table_of_Contents": "",
"Recipes_per_page": "",
"Show_as_header": "",
"Hide_as_header": "",
"Add_nutrition_recipe": "",
"Remove_nutrition_recipe": "",
"Copy_template_reference": "",
"per_serving": "",
"Save_and_View": "",
"Manage_Books": "",
"Meal_Plan": "",
"Select_Book": "",
"Select_File": "",
"Recipe_Image": "",
"Import_finished": "",
"View_Recipes": "",
"Log_Cooking": "",
"New_Recipe": "",
"Url_Import": "",
"Reset_Search": "",
"Recently_Viewed": "",
"Load_More": "",
"New_Keyword": "",
"Delete_Keyword": "",
"Edit_Keyword": "",
"Edit_Recipe": "",
"Move_Keyword": "",
"Merge_Keyword": "",
"Hide_Keywords": "",
"Hide_Recipes": "",
"Move_Up": "",
"Move_Down": "",
"Step_Name": "",
"Step_Type": "",
"Make_Header": "",
"Make_Ingredient": "",
"Amount": "",
"Enable_Amount": "",
"Disable_Amount": "",
"Ingredient Editor": "",
"Description_Replace": "",
"Log_Recipe_Cooking": "Užregistruoti recepto pagaminimą",
"External_Recipe_Image": "Išorinis recepto vaizdas",
"Add_to_Shopping": "Pridėti į apsipirkimo sąrašą",
"Add_to_Plan": "Pridėti į planą",
"Step_start_time": "Žingsnio pradžios laikas",
"Sort_by_new": "Rūšiuoti pagal naujumą",
"Table_of_Contents": "Turinys",
"Recipes_per_page": "Receptų skaičius per puslapį",
"Show_as_header": "Rodyti kaip antraštę",
"Hide_as_header": "Slėpti kaip antraštę",
"Add_nutrition_recipe": "Įtraukti mitybos informaciją į receptą",
"Remove_nutrition_recipe": "Ištrinti mitybos informaciją iš recepto",
"Copy_template_reference": "Nukopijuoti šablono nuorodą",
"per_serving": "per porciją",
"Save_and_View": "Išsaugoti ir peržiūrėti",
"Manage_Books": "Tvarkyti knygas",
"Meal_Plan": "Maisto planas",
"Select_Book": "Pasirinkti Knygą",
"Select_File": "Pasirinkti Failą",
"Recipe_Image": "Recepto nuotrauka",
"Import_finished": "Importavimas baigtas",
"View_Recipes": "Žiūrėti receptus",
"Log_Cooking": "Užregistruoti patiekalo gaminimą",
"New_Recipe": "Naujas Receptas",
"Url_Import": "URL importavimas",
"Reset_Search": "Iš naujo nustatyti paiešką",
"Recently_Viewed": "Neseniai Žiūrėta",
"Load_More": "Įkelti daugiau",
"New_Keyword": "Naujas Raktažodis",
"Delete_Keyword": "Ištrinti raktažodį",
"Edit_Keyword": "Redaguoti raktažodį",
"Edit_Recipe": "Redaguoti receptą",
"Move_Keyword": "Perkelti Raktažodį",
"Merge_Keyword": "Sujungti raktažodį",
"Hide_Keywords": "Paslėpti raktažodį",
"Hide_Recipes": "Paslėpti receptus",
"Move_Up": "Pakelti aukštyn",
"Move_Down": "Nuleisti žemyn",
"Step_Name": "Žingsnio pavadinimas",
"Step_Type": "Žingsnio tipas",
"Make_Header": "Padaryti antraštę",
"Make_Ingredient": "Padaryti ingredientą",
"Amount": "Suma",
"Enable_Amount": "Įjungti sumą",
"Disable_Amount": "Išjungti sumą",
"Ingredient Editor": "Ingredientų redaktorius",
"Description_Replace": "Pakeisti aprašymą",
"Instruction_Replace": "",
"Auto_Sort": "",
"Auto_Sort_Help": "",