Merge branch 'develop' into feature/vue3

# Conflicts:
#	requirements.txt
This commit is contained in:
vabene1111
2024-04-04 20:58:44 +02:00
11 changed files with 210 additions and 146 deletions

View File

@@ -13,7 +13,7 @@ jobs:
node-version: ["18"] node-version: ["18"]
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
- uses: awalsh128/cache-apt-pkgs-action@v1.4.1 - uses: awalsh128/cache-apt-pkgs-action@v1.4.2
with: with:
packages: libsasl2-dev python3-dev libldap2-dev libssl-dev packages: libsasl2-dev python3-dev libldap2-dev libssl-dev
version: 1.0 version: 1.0

View File

@@ -6,6 +6,8 @@ RUN apk add --no-cache postgresql-libs postgresql-client gettext zlib libjpeg li
#Print all logs without buffering it. #Print all logs without buffering it.
ENV PYTHONUNBUFFERED 1 ENV PYTHONUNBUFFERED 1
ENV DOCKER true
#This port will be used by gunicorn. #This port will be used by gunicorn.
EXPOSE 8080 EXPOSE 8080
@@ -33,6 +35,12 @@ RUN apk add --no-cache --virtual .build-deps gcc musl-dev postgresql-dev zlib-de
#Copy project and execute it. #Copy project and execute it.
COPY . ./ COPY . ./
# collect the static files
RUN /opt/recipes/venv/bin/python manage.py collectstatic_js_reverse
RUN /opt/recipes/venv/bin/python manage.py collectstatic --noinput
# copy the collected static files to a different location, so they can be moved into a potentially mounted volume
RUN mv /opt/recipes/staticfiles /opt/recipes/staticfiles-collect
# collect information from git repositories # collect information from git repositories
RUN /opt/recipes/venv/bin/python version.py RUN /opt/recipes/venv/bin/python version.py
# delete git repositories to reduce image size # delete git repositories to reduce image size

17
boot.sh
View File

@@ -67,12 +67,21 @@ echo "Migrating database"
python manage.py migrate python manage.py migrate
echo "Generating static files" if [[ "${DOCKER}" == "true" ]]; then
echo "Copying cached static files from docker build"
python manage.py collectstatic_js_reverse mkdir -p /opt/recipes/staticfiles
python manage.py collectstatic --noinput rm -rf /opt/recipes/staticfiles/*
mv /opt/recipes/staticfiles-collect/* /opt/recipes/staticfiles
rm -rf /opt/recipes/staticfiles-collect
else
echo "Collecting static files, this may take a while..."
echo "Done" python manage.py collectstatic_js_reverse
python manage.py collectstatic --noinput
echo "Done"
fi
chmod -R 755 /opt/recipes/mediafiles chmod -R 755 /opt/recipes/mediafiles

View File

@@ -1,26 +1,35 @@
import logging import logging
from logging import Logger from logging import Logger
from typing import Dict, Tuple
from urllib.parse import urljoin
from homeassistant_api import Client, HomeassistantAPIError, Domain from aiohttp import ClientError, request
from cookbook.connectors.connector import Connector from cookbook.connectors.connector import Connector
from cookbook.models import ShoppingListEntry, ConnectorConfig, Space from cookbook.models import ShoppingListEntry, ConnectorConfig, Space
class HomeAssistant(Connector): class HomeAssistant(Connector):
_domains_cache: dict[str, Domain]
_config: ConnectorConfig _config: ConnectorConfig
_logger: Logger _logger: Logger
_client: Client
def __init__(self, config: ConnectorConfig): def __init__(self, config: ConnectorConfig):
if not config.token or not config.url or not config.todo_entity: if not config.token or not config.url or not config.todo_entity:
raise ValueError("config for HomeAssistantConnector in incomplete") raise ValueError("config for HomeAssistantConnector in incomplete")
self._domains_cache = dict() if config.url[-1] != "/":
config.url += "/"
self._config = config self._config = config
self._logger = logging.getLogger("connector.HomeAssistant") self._logger = logging.getLogger("connector.HomeAssistant")
self._client = Client(self._config.url, self._config.token, async_cache_session=False, use_async=True)
async def homeassistant_api_call(self, method: str, path: str, data: Dict) -> str:
headers = {
"Authorization": f"Bearer {self._config.token}",
"Content-Type": "application/json"
}
async with request(method, urljoin(self._config.url, path), headers=headers, json=data) as response:
response.raise_for_status()
return await response.json()
async def on_shopping_list_entry_created(self, space: Space, shopping_list_entry: ShoppingListEntry) -> None: async def on_shopping_list_entry_created(self, space: Space, shopping_list_entry: ShoppingListEntry) -> None:
if not self._config.on_shopping_list_entry_created_enabled: if not self._config.on_shopping_list_entry_created_enabled:
@@ -28,15 +37,17 @@ class HomeAssistant(Connector):
item, description = _format_shopping_list_entry(shopping_list_entry) item, description = _format_shopping_list_entry(shopping_list_entry)
todo_domain = self._domains_cache.get('todo') logging.debug(f"adding {item=} to {self._config.name}")
try:
if todo_domain is None:
todo_domain = await self._client.async_get_domain('todo')
self._domains_cache['todo'] = todo_domain
logging.debug(f"pushing {item} to {self._config.name}") data = {
await todo_domain.add_item(entity_id=self._config.todo_entity, item=item) "entity_id": self._config.todo_entity,
except HomeassistantAPIError as err: "item": item,
"description": description,
}
try:
await self.homeassistant_api_call("POST", "services/todo/add_item", data)
except ClientError as err:
self._logger.warning(f"[HomeAssistant {self._config.name}] Received an exception from the api: {err=}, {type(err)=}") self._logger.warning(f"[HomeAssistant {self._config.name}] Received an exception from the api: {err=}, {type(err)=}")
async def on_shopping_list_entry_updated(self, space: Space, shopping_list_entry: ShoppingListEntry) -> None: async def on_shopping_list_entry_updated(self, space: Space, shopping_list_entry: ShoppingListEntry) -> None:
@@ -48,24 +59,31 @@ class HomeAssistant(Connector):
if not self._config.on_shopping_list_entry_deleted_enabled: if not self._config.on_shopping_list_entry_deleted_enabled:
return return
item, description = _format_shopping_list_entry(shopping_list_entry) if not hasattr(shopping_list_entry._state.fields_cache, "food"):
# Sometimes the food foreign key is not loaded, and we cant load it from an async process
self._logger.debug("required property was not present in ShoppingListEntry")
return
item, _ = _format_shopping_list_entry(shopping_list_entry)
logging.debug(f"removing {item=} from {self._config.name}")
data = {
"entity_id": self._config.todo_entity,
"item": item,
}
todo_domain = self._domains_cache.get('todo')
try: try:
if todo_domain is None: await self.homeassistant_api_call("POST", "services/todo/remove_item", data)
todo_domain = await self._client.async_get_domain('todo') except ClientError as err:
self._domains_cache['todo'] = todo_domain # This error will always trigger if the item is not present/found
self._logger.debug(f"[HomeAssistant {self._config.name}] Received an exception from the api: {err=}, {type(err)=}")
logging.debug(f"deleting {item} from {self._config.name}")
await todo_domain.remove_item(entity_id=self._config.todo_entity, item=item)
except HomeassistantAPIError as err:
self._logger.warning(f"[HomeAssistant {self._config.name}] Received an exception from the api: {err=}, {type(err)=}")
async def close(self) -> None: async def close(self) -> None:
await self._client.async_cache_session.close() pass
def _format_shopping_list_entry(shopping_list_entry: ShoppingListEntry): def _format_shopping_list_entry(shopping_list_entry: ShoppingListEntry) -> Tuple[str, str]:
item = shopping_list_entry.food.name item = shopping_list_entry.food.name
if shopping_list_entry.amount > 0: if shopping_list_entry.amount > 0:
item += f" ({shopping_list_entry.amount:.2f}".rstrip('0').rstrip('.') item += f" ({shopping_list_entry.amount:.2f}".rstrip('0').rstrip('.')
@@ -76,10 +94,10 @@ def _format_shopping_list_entry(shopping_list_entry: ShoppingListEntry):
else: else:
item += ")" item += ")"
description = "Imported by TandoorRecipes" description = "From TandoorRecipes"
if shopping_list_entry.created_by.first_name and len(shopping_list_entry.created_by.first_name) > 0: if shopping_list_entry.created_by.first_name and len(shopping_list_entry.created_by.first_name) > 0:
description += f", created by {shopping_list_entry.created_by.first_name}" description += f", by {shopping_list_entry.created_by.first_name}"
else: else:
description += f", created by {shopping_list_entry.created_by.username}" description += f", by {shopping_list_entry.created_by.username}"
return item, description return item, description

View File

@@ -14,8 +14,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: 2024-03-21 14:39+0100\n" "POT-Creation-Date: 2024-03-21 14:39+0100\n"
"PO-Revision-Date: 2023-09-25 09:59+0000\n" "PO-Revision-Date: 2024-03-27 19:02+0000\n"
"Last-Translator: Matias Laporte <laportematias+weblate@gmail.com>\n" "Last-Translator: Axel Breiterman <axelbreiterman@gmail.com>\n"
"Language-Team: Spanish <http://translate.tandoor.dev/projects/tandoor/" "Language-Team: Spanish <http://translate.tandoor.dev/projects/tandoor/"
"recipes-backend/es/>\n" "recipes-backend/es/>\n"
"Language: es\n" "Language: es\n"
@@ -23,7 +23,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.15\n" "X-Generator: Weblate 5.4.2\n"
#: .\cookbook\forms.py:45 #: .\cookbook\forms.py:45
msgid "" msgid ""
@@ -97,14 +97,16 @@ msgid ""
"<a href=\"https://www.home-assistant.io/docs/authentication/#your-account-" "<a href=\"https://www.home-assistant.io/docs/authentication/#your-account-"
"profile\">Long Lived Access Token</a> for your HomeAssistant instance" "profile\">Long Lived Access Token</a> for your HomeAssistant instance"
msgstr "" msgstr ""
"<a href=\"https://www.home-assistant.io/docs/authentication/#your-account-"
"profile\">Token de larga duración</a>para tu instancia de HomeAssistant"
#: .\cookbook\forms.py:193 #: .\cookbook\forms.py:193
msgid "Something like http://homeassistant.local:8123/api" msgid "Something like http://homeassistant.local:8123/api"
msgstr "" msgstr "Algo similar a http://homeassistant.local:8123/api"
#: .\cookbook\forms.py:205 #: .\cookbook\forms.py:205
msgid "http://homeassistant.local:8123/api for example" msgid "http://homeassistant.local:8123/api for example"
msgstr "" msgstr "por ejemplo http://homeassistant.local:8123/api for example"
#: .\cookbook\forms.py:222 .\cookbook\views\edit.py:117 #: .\cookbook\forms.py:222 .\cookbook\views\edit.py:117
msgid "Storage" msgid "Storage"
@@ -279,7 +281,7 @@ msgstr "Ha alcanzado el número máximo de recetas para su espacio."
#: .\cookbook\helper\permission_helper.py:414 #: .\cookbook\helper\permission_helper.py:414
msgid "You have more users than allowed in your space." msgid "You have more users than allowed in your space."
msgstr "" msgstr "Tenés mas usuarios que los permitidos en tu espacio"
#: .\cookbook\helper\recipe_url_import.py:304 #: .\cookbook\helper\recipe_url_import.py:304
#, fuzzy #, fuzzy
@@ -309,7 +311,7 @@ msgstr "fermentar"
#: .\cookbook\helper\recipe_url_import.py:310 #: .\cookbook\helper\recipe_url_import.py:310
msgid "sous-vide" msgid "sous-vide"
msgstr "" msgstr "sous-vide"
#: .\cookbook\helper\shopping_helper.py:150 #: .\cookbook\helper\shopping_helper.py:150
msgid "You must supply a servings size" msgid "You must supply a servings size"
@@ -318,7 +320,7 @@ msgstr "Debe proporcionar un tamaño de porción"
#: .\cookbook\helper\template_helper.py:95 #: .\cookbook\helper\template_helper.py:95
#: .\cookbook\helper\template_helper.py:97 #: .\cookbook\helper\template_helper.py:97
msgid "Could not parse template code." msgid "Could not parse template code."
msgstr "" msgstr "No se pudo parsear el código de la planitlla."
#: .\cookbook\integration\copymethat.py:44 #: .\cookbook\integration\copymethat.py:44
#: .\cookbook\integration\melarecipes.py:37 #: .\cookbook\integration\melarecipes.py:37
@@ -342,6 +344,8 @@ msgid ""
"An unexpected error occurred during the import. Please make sure you have " "An unexpected error occurred during the import. Please make sure you have "
"uploaded a valid file." "uploaded a valid file."
msgstr "" msgstr ""
"Ocurrió un error inesperado al importar. Por favor asegurate de haber subido "
"un archivo válido."
#: .\cookbook\integration\integration.py:217 #: .\cookbook\integration\integration.py:217
msgid "The following recipes were ignored because they already existed:" msgid "The following recipes were ignored because they already existed:"
@@ -457,7 +461,7 @@ msgstr "Calorías"
#: .\cookbook\migrations\0190_auto_20230525_1506.py:20 #: .\cookbook\migrations\0190_auto_20230525_1506.py:20
msgid "kcal" msgid "kcal"
msgstr "" msgstr "kcal"
#: .\cookbook\models.py:325 #: .\cookbook\models.py:325
msgid "" msgid ""

View File

@@ -11,8 +11,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: 2024-03-21 14:39+0100\n" "POT-Creation-Date: 2024-03-21 14:39+0100\n"
"PO-Revision-Date: 2024-03-03 23:19+0000\n" "PO-Revision-Date: 2024-04-01 22:04+0000\n"
"Last-Translator: M Ugur <mugurd@gmail.com>\n" "Last-Translator: atom karinca <atomkarinca@tutanota.com>\n"
"Language-Team: Turkish <http://translate.tandoor.dev/projects/tandoor/" "Language-Team: Turkish <http://translate.tandoor.dev/projects/tandoor/"
"recipes-backend/tr/>\n" "recipes-backend/tr/>\n"
"Language: tr\n" "Language: tr\n"
@@ -20,7 +20,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.15\n" "X-Generator: Weblate 5.4.2\n"
#: .\cookbook\forms.py:45 #: .\cookbook\forms.py:45
msgid "" msgid ""
@@ -63,42 +63,48 @@ msgid ""
"To prevent duplicates recipes with the same name as existing ones are " "To prevent duplicates recipes with the same name as existing ones are "
"ignored. Check this box to import everything." "ignored. Check this box to import everything."
msgstr "" msgstr ""
"Varolan tariflerden benzer isimli olanlar mükerrerliği engellemek için "
"gözardı edilecektir. Tümünü içeri aktarmak için bu kutucuğu işaretleyin."
#: .\cookbook\forms.py:143 #: .\cookbook\forms.py:143
msgid "Add your comment: " msgid "Add your comment: "
msgstr "" msgstr "Yorum ekleyin: "
#: .\cookbook\forms.py:151 #: .\cookbook\forms.py:151
msgid "Leave empty for dropbox and enter app password for nextcloud." msgid "Leave empty for dropbox and enter app password for nextcloud."
msgstr "" msgstr "Dropbox için boş bırakın ve Nextcloud için uygulama şifresini girin."
#: .\cookbook\forms.py:154 #: .\cookbook\forms.py:154
msgid "Leave empty for nextcloud and enter api token for dropbox." msgid "Leave empty for nextcloud and enter api token for dropbox."
msgstr "" msgstr "Nextcloud için boş bırakın ve Dropbox için API anahtarını girin."
#: .\cookbook\forms.py:160 #: .\cookbook\forms.py:160
msgid "" msgid ""
"Leave empty for dropbox and enter only base url for nextcloud (<code>/remote." "Leave empty for dropbox and enter only base url for nextcloud (<code>/remote."
"php/webdav/</code> is added automatically)" "php/webdav/</code> is added automatically)"
msgstr "" msgstr ""
"Dropbox için boş bırakın ve Nextcloud için yalnızca ana URL'yi "
"girin(<code>/remote.php/webdav/</code> otomatik olarak eklenir)"
#: .\cookbook\forms.py:188 #: .\cookbook\forms.py:188
msgid "" msgid ""
"<a href=\"https://www.home-assistant.io/docs/authentication/#your-account-" "<a href=\"https://www.home-assistant.io/docs/authentication/#your-account-"
"profile\">Long Lived Access Token</a> for your HomeAssistant instance" "profile\">Long Lived Access Token</a> for your HomeAssistant instance"
msgstr "" msgstr ""
"HomeAssistant uygulamanız için <a href=\"https://www.home-assistant.io/docs/"
"authentication/#your-account-profile\">Uzun Süreli Erişim Anahtarı</a>"
#: .\cookbook\forms.py:193 #: .\cookbook\forms.py:193
msgid "Something like http://homeassistant.local:8123/api" msgid "Something like http://homeassistant.local:8123/api"
msgstr "" msgstr "Örneğin http://homeassistant.local:8123/api"
#: .\cookbook\forms.py:205 #: .\cookbook\forms.py:205
msgid "http://homeassistant.local:8123/api for example" msgid "http://homeassistant.local:8123/api for example"
msgstr "" msgstr "http://homeassistant.local:8123/api örneğin"
#: .\cookbook\forms.py:222 .\cookbook\views\edit.py:117 #: .\cookbook\forms.py:222 .\cookbook\views\edit.py:117
msgid "Storage" msgid "Storage"
msgstr "" msgstr "Depolama"
#: .\cookbook\forms.py:222 #: .\cookbook\forms.py:222
msgid "Active" msgid "Active"
@@ -106,51 +112,60 @@ msgstr "Aktif"
#: .\cookbook\forms.py:226 #: .\cookbook\forms.py:226
msgid "Search String" msgid "Search String"
msgstr "" msgstr "Arama Sorgusu"
#: .\cookbook\forms.py:246 #: .\cookbook\forms.py:246
msgid "File ID" msgid "File ID"
msgstr "" msgstr "Dosya ID"
#: .\cookbook\forms.py:262 #: .\cookbook\forms.py:262
msgid "Maximum number of users for this space reached." msgid "Maximum number of users for this space reached."
msgstr "" msgstr "Bu alan için maksimum kullanıcı sayısına ulaşıldı."
#: .\cookbook\forms.py:268 #: .\cookbook\forms.py:268
msgid "Email address already taken!" msgid "Email address already taken!"
msgstr "" msgstr "Email adresi zaten alınmış!"
#: .\cookbook\forms.py:275 #: .\cookbook\forms.py:275
msgid "" msgid ""
"An email address is not required but if present the invite link will be sent " "An email address is not required but if present the invite link will be sent "
"to the user." "to the user."
msgstr "" msgstr ""
"Email adresi zorunlu değildir fakat verilmesi halinde davet linki "
"kullanıcıya gönderilecektir."
#: .\cookbook\forms.py:287 #: .\cookbook\forms.py:287
msgid "Name already taken." msgid "Name already taken."
msgstr "" msgstr "İsim zaten alınmış."
#: .\cookbook\forms.py:298 #: .\cookbook\forms.py:298
msgid "Accept Terms and Privacy" msgid "Accept Terms and Privacy"
msgstr "" msgstr "Koşulları ve Gizliliği Onayla"
#: .\cookbook\forms.py:332 #: .\cookbook\forms.py:332
msgid "" msgid ""
"Determines how fuzzy a search is if it uses trigram similarity matching (e." "Determines how fuzzy a search is if it uses trigram similarity matching (e."
"g. low values mean more typos are ignored)." "g. low values mean more typos are ignored)."
msgstr "" msgstr ""
"Trigram benzerlik eşleşmesi kullanılması halinde aramanın ne kadar bulanık "
"olduğunu belirler (ör. düşük değerler daha fazla yazım hatasını gözardı "
"eder)."
#: .\cookbook\forms.py:340 #: .\cookbook\forms.py:340
msgid "" msgid ""
"Select type method of search. Click <a href=\"/docs/search/\">here</a> for " "Select type method of search. Click <a href=\"/docs/search/\">here</a> for "
"full description of choices." "full description of choices."
msgstr "" msgstr ""
"Arama tipi metodunu seçin. Seçeneklerin tam açıklamasını görmek için <a "
"href=\"/docs/search/\">buraya</a> tıklayın."
#: .\cookbook\forms.py:341 #: .\cookbook\forms.py:341
msgid "" msgid ""
"Use fuzzy matching on units, keywords and ingredients when editing and " "Use fuzzy matching on units, keywords and ingredients when editing and "
"importing recipes." "importing recipes."
msgstr "" msgstr ""
"Tarifleri düzenlerken ve içeri aktarırken birimler, anahtar kelimeler ve "
"malzemelerde bulanık eşleştirme kullan."
#: .\cookbook\forms.py:342 #: .\cookbook\forms.py:342
msgid "" msgid ""

View File

@@ -63,6 +63,8 @@ Modify the below to match your environment and add it to your `docker-compose.ym
``` yaml ``` yaml
pgbackup: pgbackup:
container_name: pgbackup container_name: pgbackup
env_file:
- ./.env
environment: environment:
BACKUP_KEEP_DAYS: "8" BACKUP_KEEP_DAYS: "8"
BACKUP_KEEP_MONTHS: "6" BACKUP_KEEP_MONTHS: "6"

View File

@@ -2,7 +2,7 @@ Django==4.2.11
cryptography===42.0.5 cryptography===42.0.5
django-annoying==0.10.6 django-annoying==0.10.6
django-cleanup==8.0.0 django-cleanup==8.0.0
django-crispy-forms==2.0 django-crispy-forms==2.1
crispy-bootstrap4==2022.1 crispy-bootstrap4==2022.1
django-tables2==2.7.0 django-tables2==2.7.0
djangorestframework==3.14.0 djangorestframework==3.14.0
@@ -10,12 +10,12 @@ drf-writable-nested==0.7.0
drf-spectacular==0.27.1 drf-spectacular==0.27.1
drf-spectacular-sidecar==2024.2.1 drf-spectacular-sidecar==2024.2.1
django-oauth-toolkit==2.3.0 django-oauth-toolkit==2.3.0
django-debug-toolbar==4.2.0 django-debug-toolbar==4.3.0
bleach==6.0.0 bleach==6.0.0
gunicorn==21.2.0 gunicorn==21.2.0
lxml==5.1.0 lxml==5.1.0
Markdown==3.5.1 Markdown==3.5.1
Pillow==10.2.0 Pillow==10.3.0
psycopg2-binary==2.9.9 psycopg2-binary==2.9.9
python-dotenv==1.0.0 python-dotenv==1.0.0
requests==2.31.0 requests==2.31.0
@@ -25,14 +25,14 @@ whitenoise==6.6.0
icalendar==5.0.11 icalendar==5.0.11
pyyaml==6.0.1 pyyaml==6.0.1
uritemplate==4.1.1 uritemplate==4.1.1
beautifulsoup4==4.12.2 beautifulsoup4==4.12.3
microdata==0.8.0 microdata==0.8.0
mock==5.1.0 mock==5.1.0
Jinja2==3.1.3 Jinja2==3.1.3
django-webpack-loader==3.0.1 django-webpack-loader==3.0.1
git+https://github.com/BITSOLVER/django-js-reverse@071e304fd600107bc64bbde6f2491f1fe049ec82 git+https://github.com/BITSOLVER/django-js-reverse@071e304fd600107bc64bbde6f2491f1fe049ec82
django-allauth==0.61.1 django-allauth==0.61.1
recipe-scrapers==14.52.0 recipe-scrapers==14.55.0
django-scopes==2.0.0 django-scopes==2.0.0
django-treebeard==4.7 django-treebeard==4.7
django-cors-headers==4.3.1 django-cors-headers==4.3.1
@@ -45,13 +45,13 @@ django-auth-ldap==4.6.0
pyppeteer==2.0.0 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
django-vite==3.0.3 django-vite==3.0.3
aiohttp==3.9.3
# Development # Development
pytest==8.0.0 pytest==8.0.0
pytest-django==4.8.0 pytest-django==4.8.0
pytest-cov===4.1.0 pytest-cov===5.0.0
pytest-factoryboy==2.6.0 pytest-factoryboy==2.6.0
pytest-html==4.1.1 pytest-html==4.1.1
pytest-asyncio==0.23.5 pytest-asyncio==0.23.5

View File

@@ -54,7 +54,7 @@
text-field="name" text-field="name"
value-field="id" value-field="id"
v-model="food.supermarket_category" v-model="food.supermarket_category"
@change="detail_modal_visible = false; updateFoodCategory(food)" @input="detail_modal_visible = false; updateFoodCategory(food)"
></b-form-select> ></b-form-select>
<b-button variant="info" block <b-button variant="info" block

View File

@@ -4,38 +4,38 @@
"err_creating_resource": "Kaynak oluşturulurken bir hata oluştu!", "err_creating_resource": "Kaynak oluşturulurken bir hata oluştu!",
"err_updating_resource": "Kaynak güncellenirken bir hata oluştu!", "err_updating_resource": "Kaynak güncellenirken bir hata oluştu!",
"err_deleting_resource": "Kaynak silinirken bir hata oluştu!", "err_deleting_resource": "Kaynak silinirken bir hata oluştu!",
"err_deleting_protected_resource": "", "err_deleting_protected_resource": "Silmeye çalıştığınız nesne hala kullanılıyor ve silinemedi.",
"err_moving_resource": "", "err_moving_resource": "Kaynak taşınırken bir hata oluştu!",
"err_merging_resource": "", "err_merging_resource": "Kaynak birleştirilirken bir hata oluştu!",
"success_fetching_resource": "Kaynak başarıyla getirildi!", "success_fetching_resource": "Kaynak başarıyla getirildi!",
"success_creating_resource": "Kaynak başarıyla oluşturuldu!", "success_creating_resource": "Kaynak başarıyla oluşturuldu!",
"success_updating_resource": "", "success_updating_resource": "Kaynak başarıyla güncellendi!",
"success_deleting_resource": "Kaynak başarıyla silindi!", "success_deleting_resource": "Kaynak başarıyla silindi!",
"success_moving_resource": "Kaynak başarıyla taşındı!", "success_moving_resource": "Kaynak başarıyla taşındı!",
"success_merging_resource": "Kaynak başarıyla birleştirildi!", "success_merging_resource": "Kaynak başarıyla birleştirildi!",
"file_upload_disabled": "Alanınız için dosya yükleme aktif değil.", "file_upload_disabled": "Alanınız için dosya yükleme aktif değil.",
"warning_space_delete": "Tüm tarifler, alışveriş listeleri, yemek planları ve oluşturduğunuz her şey dahil olmak üzere silinecektir. Bu geri alınamaz! Bunu yapmak istediğinizden emin misiniz?", "warning_space_delete": "Tüm tarifleri, alışveriş listelerini, yemek planlarını ve oluşturduğunuz diğer her şeyi içeren alanınızı silebilirsiniz. Bu geri alınamaz! Bunu yapmak istediğinizden emin misiniz?",
"food_inherit_info": "", "food_inherit_info": "Yiyeceklerdeki öntanımlı olarak aktarılması gereken alanlar.",
"step_time_minutes": "Dakika olarak adım süresi", "step_time_minutes": "Dakika olarak adım süresi",
"confirm_delete": "", "confirm_delete": "Bu {object}'yi silmek istediğinizden emin misiniz?",
"import_running": "", "import_running": "İçeri aktarım devam ediyor, lütfen bekleyin!",
"all_fields_optional": "", "all_fields_optional": "Bütün alanlar tercihe bağlıdır ve boş bırakılabilir.",
"convert_internal": "Dahili tarif'e dönüştür", "convert_internal": "Dahili tarif'e dönüştür",
"show_only_internal": "Sadece dahili tarifler", "show_only_internal": "Sadece dahili tarifleri göster",
"show_split_screen": "Bölünmüş Görünüm", "show_split_screen": "Bölünmüş Görünüm",
"Log_Recipe_Cooking": "", "Log_Recipe_Cooking": "Günlük Tarif Pişirme",
"External_Recipe_Image": "", "External_Recipe_Image": "Harici Tarif Resim",
"Add_to_Shopping": "Alışverişe Ekle", "Add_to_Shopping": "Alışverişe Ekle",
"Add_to_Plan": "", "Add_to_Plan": "Plana ekle",
"Step_start_time": "", "Step_start_time": "Adım başlangıç zamanı",
"Sort_by_new": "Yeniye göre sırala", "Sort_by_new": "Yeniye göre sırala",
"Table_of_Contents": "İçindekiler Tablosu", "Table_of_Contents": "İçindekiler Tablosu",
"Recipes_per_page": "Sayfa Başına Tarif", "Recipes_per_page": "Sayfa Başına Tarif",
"Show_as_header": "Başlığı Göster", "Show_as_header": "Başlık olarak göster",
"Hide_as_header": "Başlığı gizle", "Hide_as_header": "Başlık olarak gizle",
"Add_nutrition_recipe": "", "Add_nutrition_recipe": "Tarife besin değeri ekle",
"Remove_nutrition_recipe": "", "Remove_nutrition_recipe": "Tariften besin değeri sil",
"Copy_template_reference": "", "Copy_template_reference": "Şablon referansını kopyala",
"Save_and_View": "Kaydet & Görüntüle", "Save_and_View": "Kaydet & Görüntüle",
"Manage_Books": "Kitapları Yönet", "Manage_Books": "Kitapları Yönet",
"Meal_Plan": "Yemek Planı", "Meal_Plan": "Yemek Planı",
@@ -44,12 +44,12 @@
"Recipe_Image": "Tarif Resmi", "Recipe_Image": "Tarif Resmi",
"Import_finished": "İçeriye Aktarma Bitti", "Import_finished": "İçeriye Aktarma Bitti",
"View_Recipes": "Tarifleri Görüntüle", "View_Recipes": "Tarifleri Görüntüle",
"Log_Cooking": "", "Log_Cooking": "Günlük Pişirme",
"New_Recipe": "Yeni Tarif", "New_Recipe": "Yeni Tarif",
"Url_Import": "Url İçeri Aktar", "Url_Import": "Url İçeri Aktar",
"Reset_Search": "Aramayı Sıfırla", "Reset_Search": "Aramayı Sıfırla",
"Recently_Viewed": "Son Görüntülenen", "Recently_Viewed": "Son Görüntülenen",
"Load_More": "Daha Fazla", "Load_More": "Daha Fazla Yükle",
"New_Keyword": "Yeni Anahtar Kelime", "New_Keyword": "Yeni Anahtar Kelime",
"Delete_Keyword": "Anahtar Kelimeyi Sil", "Delete_Keyword": "Anahtar Kelimeyi Sil",
"Edit_Keyword": "Anahtar Kelimeyi Düzenle", "Edit_Keyword": "Anahtar Kelimeyi Düzenle",
@@ -57,20 +57,20 @@
"Move_Keyword": "Anahtar Kelimeyi Taşı", "Move_Keyword": "Anahtar Kelimeyi Taşı",
"Merge_Keyword": "Anahtar Kelimeyi Birleştir", "Merge_Keyword": "Anahtar Kelimeyi Birleştir",
"Hide_Keywords": "Anahtar Kelimeyi Gizle", "Hide_Keywords": "Anahtar Kelimeyi Gizle",
"Hide_Recipes": "Tarifi Gizle", "Hide_Recipes": "Tarifleri Gizle",
"Move_Up": "Yukarı Taşı", "Move_Up": "Yukarı Taşı",
"Move_Down": "Aşağıya Taşı", "Move_Down": "Aşağıya Taşı",
"Step_Name": "Adım Adı", "Step_Name": "Adım Adı",
"Step_Type": "Adım Tipi", "Step_Type": "Adım Tipi",
"Make_Header": "", "Make_Header": "Başlık Oluştur",
"Make_Ingredient": "", "Make_Ingredient": "Malzeme Oluştur",
"Enable_Amount": "Tutarı Etkinleştir", "Enable_Amount": "Tutarı Etkinleştir",
"Disable_Amount": "Tutarı Devre Dışı Bırak", "Disable_Amount": "Tutarı Devre Dışı Bırak",
"Ingredient Editor": "", "Ingredient Editor": "Malzeme Düzenleyici",
"Private_Recipe": "Özel Tarif", "Private_Recipe": "Özel Tarif",
"Private_Recipe_Help": "", "Private_Recipe_Help": "Tarif yalnızca size ve paylaştığınız kişilere gösterilir.",
"reusable_help_text": "", "reusable_help_text": "Davet bağlantısı birden fazla kullanıcı için kullanılabilir olsun mu.",
"Add_Step": "", "Add_Step": "Adım Ekle",
"Keywords": "Anahtar Kelimeler", "Keywords": "Anahtar Kelimeler",
"Books": "Kitaplar", "Books": "Kitaplar",
"Proteins": "Proteinler", "Proteins": "Proteinler",
@@ -78,7 +78,7 @@
"Carbohydrates": "Karbonhidratlar", "Carbohydrates": "Karbonhidratlar",
"Calories": "Kaloriler", "Calories": "Kaloriler",
"Energy": "Enerji", "Energy": "Enerji",
"Nutrition": "Besin", "Nutrition": "Besin Değeri",
"Date": "Tarih", "Date": "Tarih",
"Share": "Paylaş", "Share": "Paylaş",
"Automation": "Otomasyon", "Automation": "Otomasyon",
@@ -93,18 +93,18 @@
"New": "Yeni", "New": "Yeni",
"Note": "Not", "Note": "Not",
"Success": "Başarılı", "Success": "Başarılı",
"Failure": "Hata", "Failure": "Başarısız",
"Protected": "Korumalı", "Protected": "Korumalı",
"Ingredients": "Mazemeler", "Ingredients": "Malzemeler",
"Supermarket": "Market", "Supermarket": "Market",
"Categories": "Kategoriler", "Categories": "Kategoriler",
"Category": "Kategori", "Category": "Kategori",
"Selected": "Seçilen", "Selected": "Seçilen",
"min": "", "min": "min",
"Servings": "", "Servings": "Servis Sayısı",
"Waiting": "", "Waiting": "Bekleniyor",
"Preparation": "", "Preparation": "Hazırlama",
"External": "", "External": "Harici",
"Size": "Boyut", "Size": "Boyut",
"Files": "Dosyalar", "Files": "Dosyalar",
"File": "Dosya", "File": "Dosya",
@@ -112,7 +112,7 @@
"Image": "Resim", "Image": "Resim",
"Delete": "Sil", "Delete": "Sil",
"Open": "Aç", "Open": "Aç",
"Ok": "", "Ok": "Tamam",
"Save": "Kaydet", "Save": "Kaydet",
"Step": "Adım", "Step": "Adım",
"Search": "Ara", "Search": "Ara",
@@ -121,7 +121,7 @@
"Settings": "Ayarlar", "Settings": "Ayarlar",
"or": "veya", "or": "veya",
"and": "ve", "and": "ve",
"Information": "bilgi", "Information": "Bilgi",
"Download": "İndir", "Download": "İndir",
"Create": "Oluştur", "Create": "Oluştur",
"Search Settings": "Arama Ayarları", "Search Settings": "Arama Ayarları",
@@ -129,10 +129,10 @@
"Recipes": "Tarifler", "Recipes": "Tarifler",
"Move": "Taşı", "Move": "Taşı",
"Merge": "Birleştir", "Merge": "Birleştir",
"Parent": "", "Parent": "Üst Öğe",
"Copy Link": "", "Copy Link": "Bağlantıyı Kopyala",
"Copy Token": "", "Copy Token": "Anahtarı Kopyala",
"delete_confirmation": "", "delete_confirmation": "{source}'ı silmek istediğinizden emin misiniz?",
"move_confirmation": "", "move_confirmation": "",
"merge_confirmation": "", "merge_confirmation": "",
"create_rule": "", "create_rule": "",
@@ -457,5 +457,41 @@
"New_Supermarket": "Yeni Market", "New_Supermarket": "Yeni Market",
"New_Supermarket_Category": "Yeni Market Kategorisi", "New_Supermarket_Category": "Yeni Market Kategorisi",
"Are_You_Sure": "Emin misin?", "Are_You_Sure": "Emin misin?",
"Valid Until": "Geçerlilik Tarihi" "Valid Until": "Geçerlilik Tarihi",
"err_importing_recipe": "Tarif içeri aktarılırken bir hata oluştu!",
"Data_Import_Info": "Tarif koleksiyonunuzu geliştirmek için topluluk tarafından oluşturulmuş yiyecek, birim ve daha fazlasını olduğu listeleri içeri aktararak Alanlarınızı genişletin.",
"open_data_help_text": "The Tandoor Open Data projesi Tandoor için topluluk tarafından oluşturulmuş verileri sağlar. Bu alan içeri aktarım sırasında otomatik olarak doldurulur ve gelecekte güncellenebilir.",
"Description_Replace": "Açıklama Değiştir",
"FDC_ID": "FDC Kimlik",
"FDC_Search": "FDC Arama",
"FDC_ID_help": "FDC veritabanı Kimlik",
"Auto_Sort": "Otomatik Sırala",
"Welcome": "Hoşgeldiniz",
"recipe_property_info": "Yiyeceklere ayrıca özellikler ekleyebilir ve tarifinize göre bunları otomatik olarak hesaplayabilirsiniz!",
"Amount": "Miktar",
"per_serving": "servis başına",
"Instruction_Replace": "Talimat Değiştir",
"Open_Data_Slug": "Açık Veri Tanım",
"Input": "Giriş",
"Undo": "Geri Al",
"NoMoreUndo": "Yapılacak değişiklik yok.",
"Delete_All": "Tümünü sil",
"Update_Existing_Data": "Mevcut Verileri Güncelleyin",
"Use_Metric": "Metrik Birimler Kullan",
"Learn_More": "Daha Fazla",
"converted_unit": "Dönüştürülmüş Birim",
"converted_amount": "Dönüştürülmüş Miktar",
"base_unit": "Temel Birim",
"Number of Objects": "Nesne Sayısı",
"Datatype": "Veri tipi",
"StartDate": "Başlangıç Tarihi",
"EndDate": "Bitiş Tarihi",
"Auto_Sort_Help": "Tüm malzemeleri en uygun adıma taşı.",
"Open_Data_Import": "Açık Veri İçeri Aktar",
"Properties_Food_Amount": "Özellikler Yiyecek Miktar",
"Properties_Food_Unit": "Özellikler Yiyecek Birim",
"base_amount": "Temel Miktar",
"Calculator": "Hesap Makinesi",
"property_type_fdc_hint": "Yalnızca FDC kimliği olan özellik tipleri FDC veritabanından veri çekebilir",
"Alignment": "Hizalama"
} }

View File

@@ -3717,25 +3717,7 @@ bn.js@^5.0.0, bn.js@^5.2.1:
resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-5.2.1.tgz#0bc527a6a0d18d0aa8d5b0538ce4a77dccfa7b70" resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-5.2.1.tgz#0bc527a6a0d18d0aa8d5b0538ce4a77dccfa7b70"
integrity sha512-eXRvHzWyYPBuB4NBy0cmYQjGitUrtqwbvlzP3G6VFnNRbsZQIxQ10PbKKHt8gZ/HW/D/747aDl+QkDqg3KQLMQ== integrity sha512-eXRvHzWyYPBuB4NBy0cmYQjGitUrtqwbvlzP3G6VFnNRbsZQIxQ10PbKKHt8gZ/HW/D/747aDl+QkDqg3KQLMQ==
body-parser@1.20.1: body-parser@1.20.2, body-parser@^1.19.0:
version "1.20.1"
resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.20.1.tgz#b1812a8912c195cd371a3ee5e66faa2338a5c668"
integrity sha512-jWi7abTbYwajOytWCQc37VulmWiRae5RyTpaCyDcS5/lMdtwSz5lOpDE67srw/HYe35f1z3fDQw+3txg7gNtWw==
dependencies:
bytes "3.1.2"
content-type "~1.0.4"
debug "2.6.9"
depd "2.0.0"
destroy "1.2.0"
http-errors "2.0.0"
iconv-lite "0.4.24"
on-finished "2.4.1"
qs "6.11.0"
raw-body "2.5.1"
type-is "~1.6.18"
unpipe "1.0.0"
body-parser@^1.19.0:
version "1.20.2" version "1.20.2"
resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.20.2.tgz#6feb0e21c4724d06de7ff38da36dad4f57a747fd" resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.20.2.tgz#6feb0e21c4724d06de7ff38da36dad4f57a747fd"
integrity sha512-ml9pReCu3M61kGlqoTm2umSXTlRTuGTx0bfYj+uIUKKYycG5NtSbeetV3faSU6R7ajOPw0g/J1PvK4qNy7s5bA== integrity sha512-ml9pReCu3M61kGlqoTm2umSXTlRTuGTx0bfYj+uIUKKYycG5NtSbeetV3faSU6R7ajOPw0g/J1PvK4qNy7s5bA==
@@ -4511,10 +4493,10 @@ cookie-signature@1.0.6:
resolved "https://registry.yarnpkg.com/cookie-signature/-/cookie-signature-1.0.6.tgz#e303a882b342cc3ee8ca513a79999734dab3ae2c" resolved "https://registry.yarnpkg.com/cookie-signature/-/cookie-signature-1.0.6.tgz#e303a882b342cc3ee8ca513a79999734dab3ae2c"
integrity sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ== integrity sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==
cookie@0.5.0: cookie@0.6.0:
version "0.5.0" version "0.6.0"
resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.5.0.tgz#d1f5d71adec6558c58f389987c366aa47e994f8b" resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.6.0.tgz#2798b04b071b0ecbff0dbb62a505a8efa4e19051"
integrity sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw== integrity sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==
copy-concurrently@^1.0.0: copy-concurrently@^1.0.0:
version "1.0.5" version "1.0.5"
@@ -5652,16 +5634,16 @@ express-history-api-fallback@^2.2.1:
integrity sha512-swxwm3aP8vrOOvlzOdZvHlSZtJGwHKaY94J6AkrAgCTmcbko3IRwbkhLv2wKV1WeZhjxX58aLMpP3atDBnKuZg== integrity sha512-swxwm3aP8vrOOvlzOdZvHlSZtJGwHKaY94J6AkrAgCTmcbko3IRwbkhLv2wKV1WeZhjxX58aLMpP3atDBnKuZg==
express@^4.17.1, express@^4.17.3: express@^4.17.1, express@^4.17.3:
version "4.18.2" version "4.19.2"
resolved "https://registry.yarnpkg.com/express/-/express-4.18.2.tgz#3fabe08296e930c796c19e3c516979386ba9fd59" resolved "https://registry.yarnpkg.com/express/-/express-4.19.2.tgz#e25437827a3aa7f2a827bc8171bbbb664a356465"
integrity sha512-5/PsL6iGPdfQ/lKM1UuielYgv3BUoJfz1aUwU9vHZ+J7gyvwdQXFEBIEIaxeGf0GIcreATNyBExtalisDbuMqQ== integrity sha512-5T6nhjsT+EOMzuck8JjBHARTHfMht0POzlA60WV2pMD3gyXw2LZnZ+ueGdNxG+0calOJcWKbpFcuzLZ91YWq9Q==
dependencies: dependencies:
accepts "~1.3.8" accepts "~1.3.8"
array-flatten "1.1.1" array-flatten "1.1.1"
body-parser "1.20.1" body-parser "1.20.2"
content-disposition "0.5.4" content-disposition "0.5.4"
content-type "~1.0.4" content-type "~1.0.4"
cookie "0.5.0" cookie "0.6.0"
cookie-signature "1.0.6" cookie-signature "1.0.6"
debug "2.6.9" debug "2.6.9"
depd "2.0.0" depd "2.0.0"
@@ -9351,16 +9333,6 @@ range-parser@^1.2.1, range-parser@~1.2.1:
resolved "https://registry.yarnpkg.com/range-parser/-/range-parser-1.2.1.tgz#3cf37023d199e1c24d1a55b84800c2f3e6468031" resolved "https://registry.yarnpkg.com/range-parser/-/range-parser-1.2.1.tgz#3cf37023d199e1c24d1a55b84800c2f3e6468031"
integrity sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg== integrity sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==
raw-body@2.5.1:
version "2.5.1"
resolved "https://registry.yarnpkg.com/raw-body/-/raw-body-2.5.1.tgz#fe1b1628b181b700215e5fd42389f98b71392857"
integrity sha512-qqJBtEyVgS0ZmPGdCFPWJ3FreoqvG4MVQln/kCgF7Olq95IbOp0/BWyMwbdtn4VTvkM8Y7khCQ2Xgk/tcrCXig==
dependencies:
bytes "3.1.2"
http-errors "2.0.0"
iconv-lite "0.4.24"
unpipe "1.0.0"
raw-body@2.5.2: raw-body@2.5.2:
version "2.5.2" version "2.5.2"
resolved "https://registry.yarnpkg.com/raw-body/-/raw-body-2.5.2.tgz#99febd83b90e08975087e8f1f9419a149366b68a" resolved "https://registry.yarnpkg.com/raw-body/-/raw-body-2.5.2.tgz#99febd83b90e08975087e8f1f9419a149366b68a"