mirror of
https://github.com/mealie-recipes/mealie.git
synced 2026-02-25 09:13:11 -05:00
Compare commits
16 Commits
auto-local
...
v3.10.0
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3de4024619 | ||
|
|
194771653d | ||
|
|
24aa8f3525 | ||
|
|
fb8e318739 | ||
|
|
6255c71609 | ||
|
|
f2d1569488 | ||
|
|
987c7209fc | ||
|
|
f6dbd1f1f1 | ||
|
|
d30118899d | ||
|
|
af241dad57 | ||
|
|
b86de79c6f | ||
|
|
86e86f8c81 | ||
|
|
d795f91938 | ||
|
|
a59511cc81 | ||
|
|
a5d4cae6d0 | ||
|
|
2987cf8ba6 |
35
.github/workflows/auto-merge-l10n.yml
vendored
35
.github/workflows/auto-merge-l10n.yml
vendored
@@ -14,13 +14,6 @@ jobs:
|
||||
if: contains(github.event.pull_request.labels.*.name, 'l10n')
|
||||
|
||||
steps:
|
||||
- name: Generate GitHub App Token
|
||||
id: app-token
|
||||
uses: actions/create-github-app-token@v1
|
||||
with:
|
||||
app-id: ${{ secrets.COMMIT_BOT_APP_ID }}
|
||||
private-key: ${{ secrets.COMMIT_BOT_APP_PRIVATE_KEY }}
|
||||
|
||||
- name: Validate PR author
|
||||
env:
|
||||
AUTHOR: ${{ github.event.pull_request.user.login }}
|
||||
@@ -61,18 +54,27 @@ jobs:
|
||||
FILES=$(gh pr view "$PR_NUMBER" --repo "$REPO" --json files --jq '.files[].path')
|
||||
|
||||
for file in $FILES; do
|
||||
if [[ ! "$file" =~ ^frontend/lang/ ]] && [[ ! "$file" =~ ^mealie/repos/seed/resources/[^/]+/locales/ ]]; then
|
||||
echo "::error::Invalid file path: $file"
|
||||
echo "Only files in frontend/lang/ or mealie/repos/seed/resources/*/locales/ are allowed"
|
||||
exit 1
|
||||
# Check if file matches any allowed path
|
||||
if [[ "$file" == "frontend/composables/use-locales/available-locales.ts" ]] || \
|
||||
[[ "$file" =~ ^frontend/lang/ ]] || \
|
||||
[[ "$file" =~ ^mealie/repos/seed/resources/[^/]+/locales/ ]]; then
|
||||
continue
|
||||
fi
|
||||
|
||||
# File doesn't match allowed paths
|
||||
echo "::error::Invalid file path: $file"
|
||||
echo "Only the following paths are allowed:"
|
||||
echo " - frontend/composables/use-locales/available-locales.ts"
|
||||
echo " - frontend/lang/"
|
||||
echo " - mealie/repos/seed/resources/*/locales/"
|
||||
exit 1
|
||||
done
|
||||
|
||||
echo "All files are in allowed paths"
|
||||
|
||||
- name: Approve PR
|
||||
env:
|
||||
GH_TOKEN: ${{ steps.app-token.outputs.token }}
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
PR_NUMBER: ${{ github.event.pull_request.number }}
|
||||
REPO: ${{ github.repository }}
|
||||
run: |
|
||||
@@ -92,9 +94,16 @@ jobs:
|
||||
--approve \
|
||||
--body "Auto-approved: l10n PR from trusted author with valid file paths"
|
||||
|
||||
- name: Generate GitHub App Token
|
||||
id: app-token
|
||||
uses: actions/create-github-app-token@v1
|
||||
with:
|
||||
app-id: ${{ secrets.COMMIT_BOT_APP_ID }}
|
||||
private-key: ${{ secrets.COMMIT_BOT_APP_PRIVATE_KEY }}
|
||||
|
||||
- name: Enable auto-merge
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
GH_TOKEN: ${{ steps.app-token.outputs.token }}
|
||||
PR_NUMBER: ${{ github.event.pull_request.number }}
|
||||
REPO: ${{ github.repository }}
|
||||
run: |
|
||||
|
||||
@@ -6,7 +6,7 @@ While this guide aims to simplify the migration process for developers, it's not
|
||||
|
||||
## V1 → V2
|
||||
|
||||
The biggest change between V1 and V2 is the introduction of Households. For more information on how households work in relation to groups/users, check out the [Groups and Households](./features.md#groups-and-households) section in the Features guide.
|
||||
The biggest change between V1 and V2 is the introduction of Households. For more information on how households work in relation to groups/users, check out the [Groups and Households](../../documentation/getting-started/features.md#groups-and-households) section in the Features guide.
|
||||
|
||||
### `updateAt` is now `updatedAt`
|
||||
|
||||
|
||||
@@ -16,7 +16,7 @@ Recipes extras are a key feature of the Mealie API. They allow you to create cus
|
||||
For example you could add `{"message": "Remember to thaw the chicken"}` to a recipe and use the webhooks built into mealie to send that message payload to a destination to be processed.
|
||||
|
||||
#### Shopping List and Food Extras
|
||||
Similarly to recipes, extras are supported on shopping lists, shopping list items, and foods. At this time they are only accessible through the API. Extras for these objects allow for rich integrations between the Mealie shopping list and your favorite list manager, such as Alexa, ToDoist, Trello, or any other list manager with an API.
|
||||
Similarly to recipes, extras are supported on shopping lists, shopping list items, and foods. At this time they are only accessible through the API. Extras for these objects allow for rich integrations between the Mealie shopping list and your favorite list manager, such as Todoist, Trello, or any other list manager with an API.
|
||||
|
||||
To keep shopping lists in sync, for instance, you can store your Trello list id on your Mealie shopping list: <br />
|
||||
`{"trello_list_id": "5abbe4b7ddc1b351ef961414"}`
|
||||
@@ -52,6 +52,7 @@ Many applications will keep track of the query and adjust the page parameter app
|
||||
Notice that the route does not contain the baseurl (e.g. `https://mymealieapplication.com/api`).
|
||||
|
||||
There are a few shorthands available to reduce the number of calls for certain common requests:
|
||||
|
||||
- if you want to return _all_ results, effectively disabling pagination, set `perPage = -1` (and fetch the first page)
|
||||
- if you want to fetch the _last_ page, set `page = -1`
|
||||
|
||||
@@ -89,6 +90,28 @@ This filter will find all recipes that don't start with the word "Test": <br>
|
||||
This filter will find all recipes that have particular slugs: <br>
|
||||
`slug IN ["pasta-fagioli", "delicious-ramen"]`
|
||||
|
||||
##### Placeholder Keywords
|
||||
You can use placeholders to insert dynamic values as opposed to static values. Currently the only supported placeholder keyword is `$NOW`, to insert the current time.
|
||||
|
||||
`$NOW` can optionally be paired with basic offsets. Here is an example of a filter which gives you recipes not made within the past 30 days: <br>
|
||||
`lastMade <= "$NOW-30d"`
|
||||
|
||||
Supported offsets operations include:
|
||||
|
||||
- `-` for subtracting a time (i.e. in the past)
|
||||
- `+` for adding a time (i.e. in the future)
|
||||
|
||||
Supported offset intervals include:
|
||||
|
||||
- `y` for years
|
||||
- `m` for months
|
||||
- `d` for days
|
||||
- `H` for hours
|
||||
- `M` for minutes
|
||||
- `S` for seconds
|
||||
|
||||
Note that intervals are _case sensitive_ (e.g. `s` is an invalid interval).
|
||||
|
||||
##### Nested Property filters
|
||||
When querying tables with relationships, you can filter properties on related tables. For instance, if you want to query all recipes owned by a particular user: <br>
|
||||
`user.username = "SousChef20220320"`
|
||||
@@ -96,7 +119,7 @@ When querying tables with relationships, you can filter properties on related ta
|
||||
This timeline event filter will return all timeline events for recipes that were created after a particular date: <br>
|
||||
`recipe.createdAt >= "2023-02-25"`
|
||||
|
||||
This recipe filter will return all recipes that contains a particular set of tags: <br>
|
||||
This recipe filter will return all recipes that contain a particular set of tags: <br>
|
||||
`tags.name CONTAINS ALL ["Easy", "Cajun"]`
|
||||
|
||||
##### Compound Filters
|
||||
|
||||
@@ -31,7 +31,7 @@ To deploy mealie on your local network, it is highly recommended to use Docker t
|
||||
We've gone through a few versions of Mealie v1 deployment targets. We have settled on a single container deployment, and we've begun publishing the nightly container on github containers. If you're looking to move from the old nightly (split containers _or_ the omni image) to the new nightly, there are a few things you need to do:
|
||||
|
||||
1. Take a backup just in case!
|
||||
2. Replace the image for the API container with `ghcr.io/mealie-recipes/mealie:v3.9.2`
|
||||
2. Replace the image for the API container with `ghcr.io/mealie-recipes/mealie:v3.10.0`
|
||||
3. Take the external port from the frontend container and set that as the port mapped to port `9000` on the new container. The frontend is now served on port 9000 from the new container, so it will need to be mapped for you to have access.
|
||||
4. Restart the container
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@ PostgreSQL might be considered if you need to support many concurrent users. In
|
||||
```yaml
|
||||
services:
|
||||
mealie:
|
||||
image: ghcr.io/mealie-recipes/mealie:v3.9.2 # (3)
|
||||
image: ghcr.io/mealie-recipes/mealie:v3.10.0 # (3)
|
||||
container_name: mealie
|
||||
restart: always
|
||||
ports:
|
||||
|
||||
@@ -11,7 +11,7 @@ SQLite is a popular, open source, self-contained, zero-configuration database th
|
||||
```yaml
|
||||
services:
|
||||
mealie:
|
||||
image: ghcr.io/mealie-recipes/mealie:v3.9.2 # (3)
|
||||
image: ghcr.io/mealie-recipes/mealie:v3.10.0 # (3)
|
||||
container_name: mealie
|
||||
restart: always
|
||||
ports:
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -91,7 +91,7 @@ const state = reactive({
|
||||
shoppingListDialog: false,
|
||||
menuItems: [
|
||||
{
|
||||
title: i18n.t("recipe.add-to-list"),
|
||||
title: i18n.t("meal-plan.add-day-to-list"),
|
||||
icon: $globals.icons.cartCheck,
|
||||
color: undefined,
|
||||
event: "shoppingList",
|
||||
@@ -123,8 +123,8 @@ async function getShoppingLists() {
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-invalid-void-type
|
||||
const eventHandlers: { [key: string]: () => void | Promise<any> } = {
|
||||
shoppingList: () => {
|
||||
getShoppingLists();
|
||||
shoppingList: async () => {
|
||||
await getShoppingLists();
|
||||
state.shoppingListDialog = true;
|
||||
},
|
||||
};
|
||||
|
||||
@@ -36,7 +36,7 @@
|
||||
import QueryFilterBuilder from "~/components/Domain/QueryFilterBuilder.vue";
|
||||
import type { FieldDefinition } from "~/composables/use-query-filter-builder";
|
||||
import { Organizer } from "~/lib/api/types/non-generated";
|
||||
import type { QueryFilterJSON } from "~/lib/api/types/response";
|
||||
import type { QueryFilterJSON } from "~/lib/api/types/non-generated";
|
||||
|
||||
interface Props {
|
||||
queryFilter?: QueryFilterJSON | null;
|
||||
|
||||
@@ -319,7 +319,7 @@ import { useDebounceFn } from "@vueuse/core";
|
||||
import { useHouseholdSelf } from "~/composables/use-households";
|
||||
import RecipeOrganizerSelector from "~/components/Domain/Recipe/RecipeOrganizerSelector.vue";
|
||||
import { Organizer } from "~/lib/api/types/non-generated";
|
||||
import type { LogicalOperator, QueryFilterJSON, QueryFilterJSONPart, RelationalKeyword, RelationalOperator } from "~/lib/api/types/response";
|
||||
import type { LogicalOperator, QueryFilterJSON, QueryFilterJSONPart, RelationalKeyword, RelationalOperator } from "~/lib/api/types/non-generated";
|
||||
import { useCategoryStore, useFoodStore, useHouseholdStore, useTagStore, useToolStore } from "~/composables/store";
|
||||
import { useUserStore } from "~/composables/store/use-user-store";
|
||||
import { type Field, type FieldDefinition, type FieldValue, type OrganizerBase, useQueryFilterBuilder } from "~/composables/use-query-filter-builder";
|
||||
|
||||
@@ -227,7 +227,7 @@ const currentHouseholdSlug = ref("");
|
||||
const filteredShoppingLists = ref<ShoppingListSummary[]>([]);
|
||||
|
||||
const state = reactive({
|
||||
shoppingListDialog: true,
|
||||
shoppingListDialog: false,
|
||||
shoppingListIngredientDialog: false,
|
||||
shoppingListShowAllToggled: false,
|
||||
});
|
||||
@@ -249,6 +249,7 @@ watch([dialog, () => preferences.value.viewAllLists], () => {
|
||||
openShoppingListIngredientDialog(selectedShoppingList.value);
|
||||
}
|
||||
else {
|
||||
state.shoppingListDialog = true;
|
||||
ready.value = true;
|
||||
}
|
||||
}
|
||||
@@ -371,7 +372,7 @@ async function consolidateRecipesIntoSections(recipes: RecipeWithScale[]) {
|
||||
}
|
||||
|
||||
function initState() {
|
||||
state.shoppingListDialog = true;
|
||||
state.shoppingListDialog = false;
|
||||
state.shoppingListIngredientDialog = false;
|
||||
state.shoppingListShowAllToggled = false;
|
||||
recipeIngredientSections.value = [];
|
||||
|
||||
@@ -7,6 +7,10 @@
|
||||
import DOMPurify from "isomorphic-dompurify";
|
||||
import { marked } from "marked";
|
||||
|
||||
enum DOMPurifyHook {
|
||||
UponSanitizeAttribute = "uponSanitizeAttribute",
|
||||
}
|
||||
|
||||
export default defineNuxtComponent({
|
||||
props: {
|
||||
source: {
|
||||
@@ -15,14 +19,26 @@ export default defineNuxtComponent({
|
||||
},
|
||||
},
|
||||
setup(props) {
|
||||
const ALLOWED_STYLE_TAGS = [
|
||||
"background-color", "color", "font-style", "font-weight", "text-decoration", "text-align",
|
||||
];
|
||||
|
||||
function sanitizeMarkdown(rawHtml: string | null | undefined): string {
|
||||
if (!rawHtml) {
|
||||
return "";
|
||||
}
|
||||
|
||||
DOMPurify.addHook(DOMPurifyHook.UponSanitizeAttribute, (node, data) => {
|
||||
if (data.attrName === "style") {
|
||||
const styles = data.attrValue.split(";").filter((style) => {
|
||||
const [property] = style.split(":");
|
||||
return ALLOWED_STYLE_TAGS.includes(property.trim().toLowerCase());
|
||||
});
|
||||
data.attrValue = styles.join(";");
|
||||
}
|
||||
});
|
||||
|
||||
const sanitized = DOMPurify.sanitize(rawHtml, {
|
||||
// List based on
|
||||
// https://support.zendesk.com/hc/en-us/articles/4408824584602-Allowing-unsafe-HTML-in-help-center-articles
|
||||
ALLOWED_TAGS: [
|
||||
"strong", "em", "b", "i", "u", "p", "code", "pre", "samp", "kbd", "var", "sub", "sup", "dfn", "cite",
|
||||
"small", "address", "hr", "br", "id", "div", "span", "h1", "h2", "h3", "h4", "h5", "h6",
|
||||
@@ -31,10 +47,14 @@ export default defineNuxtComponent({
|
||||
],
|
||||
ALLOWED_ATTR: [
|
||||
"href", "src", "alt", "height", "width", "class", "allow", "title", "allowfullscreen", "frameborder",
|
||||
"scrolling", "cite", "datetime", "name", "abbr", "target", "border", "start",
|
||||
"scrolling", "cite", "datetime", "name", "abbr", "target", "border", "start", "style",
|
||||
],
|
||||
});
|
||||
|
||||
Object.values(DOMPurifyHook).forEach((hook) => {
|
||||
DOMPurify.removeHook(hook);
|
||||
});
|
||||
|
||||
return sanitized;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { Organizer, type RecipeOrganizer } from "~/lib/api/types/non-generated";
|
||||
import type { LogicalOperator, RelationalKeyword, RelationalOperator } from "~/lib/api/types/response";
|
||||
import { Organizer } from "~/lib/api/types/non-generated";
|
||||
import type { LogicalOperator, RecipeOrganizer, RelationalKeyword, RelationalOperator } from "~/lib/api/types/non-generated";
|
||||
|
||||
export interface FieldLogicalOperator {
|
||||
label: string;
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { useLocalStorage, useSessionStorage } from "@vueuse/core";
|
||||
import { ActivityKey } from "~/lib/api/types/activity";
|
||||
import type { RegisteredParser, TimelineEventType } from "~/lib/api/types/recipe";
|
||||
import type { QueryFilterJSON } from "~/lib/api/types/response";
|
||||
import type { QueryFilterJSON } from "~/lib/api/types/non-generated";
|
||||
|
||||
export interface UserPrintPreferences {
|
||||
imagePosition: string;
|
||||
|
||||
@@ -369,7 +369,9 @@
|
||||
"recipe-rules": "Resepreëls",
|
||||
"applies-to-all-days": "Van toepassing op alle dae",
|
||||
"applies-on-days": "Van toepassing op {0}s",
|
||||
"meal-plan-settings": "Maaltydplan verstellings"
|
||||
"meal-plan-settings": "Maaltydplan verstellings",
|
||||
"add-all-to-list": "Add All to List",
|
||||
"add-day-to-list": "Add Day to List"
|
||||
},
|
||||
"migration": {
|
||||
"migration-data-removed": "Migrasiedata is uitgevee",
|
||||
|
||||
@@ -369,7 +369,9 @@
|
||||
"recipe-rules": "قواعد الوصفات",
|
||||
"applies-to-all-days": "ينطبق على جميع الأيام",
|
||||
"applies-on-days": "يطبق على أيام {0}",
|
||||
"meal-plan-settings": "إعدادات خِطَّة الوجبات الغذائية"
|
||||
"meal-plan-settings": "إعدادات خِطَّة الوجبات الغذائية",
|
||||
"add-all-to-list": "Add All to List",
|
||||
"add-day-to-list": "Add Day to List"
|
||||
},
|
||||
"migration": {
|
||||
"migration-data-removed": "حذف بيانات الهجرة",
|
||||
|
||||
@@ -369,7 +369,9 @@
|
||||
"recipe-rules": "Правила на рецептата",
|
||||
"applies-to-all-days": "Прилага се за всички дни",
|
||||
"applies-on-days": "Всеки/всяка {0}",
|
||||
"meal-plan-settings": "Настройки на плана за хранене"
|
||||
"meal-plan-settings": "Настройки на плана за хранене",
|
||||
"add-all-to-list": "Add All to List",
|
||||
"add-day-to-list": "Add Day to List"
|
||||
},
|
||||
"migration": {
|
||||
"migration-data-removed": "Данните за мигриране са премахнати",
|
||||
|
||||
@@ -369,7 +369,9 @@
|
||||
"recipe-rules": "Normes per la recepta",
|
||||
"applies-to-all-days": "Aplica a tots els dies",
|
||||
"applies-on-days": "S'aplicarà en {0}s",
|
||||
"meal-plan-settings": "Opcions de planificació de menús"
|
||||
"meal-plan-settings": "Opcions de planificació de menús",
|
||||
"add-all-to-list": "Add All to List",
|
||||
"add-day-to-list": "Add Day to List"
|
||||
},
|
||||
"migration": {
|
||||
"migration-data-removed": "S'han suprimit les dades migrades",
|
||||
|
||||
@@ -369,7 +369,9 @@
|
||||
"recipe-rules": "Pravidla receptu",
|
||||
"applies-to-all-days": "Použije se na všechny dny",
|
||||
"applies-on-days": "Platí pro {0}",
|
||||
"meal-plan-settings": "Nastavení jídelníčku"
|
||||
"meal-plan-settings": "Nastavení jídelníčku",
|
||||
"add-all-to-list": "Add All to List",
|
||||
"add-day-to-list": "Add Day to List"
|
||||
},
|
||||
"migration": {
|
||||
"migration-data-removed": "Data z migrace byla smazána",
|
||||
|
||||
@@ -369,7 +369,9 @@
|
||||
"recipe-rules": "Opskriftsregler",
|
||||
"applies-to-all-days": "Gælder for alle dage",
|
||||
"applies-on-days": "Gælder for {0}e",
|
||||
"meal-plan-settings": "Indstillinger for madplanlægning"
|
||||
"meal-plan-settings": "Indstillinger for madplanlægning",
|
||||
"add-all-to-list": "Add All to List",
|
||||
"add-day-to-list": "Add Day to List"
|
||||
},
|
||||
"migration": {
|
||||
"migration-data-removed": "Migreringsdata fjernet",
|
||||
|
||||
@@ -369,7 +369,9 @@
|
||||
"recipe-rules": "Rezeptregeln",
|
||||
"applies-to-all-days": "Gilt an allen Tagen",
|
||||
"applies-on-days": "Gilt {0}s",
|
||||
"meal-plan-settings": "Essensplan Einstellungen"
|
||||
"meal-plan-settings": "Essensplan Einstellungen",
|
||||
"add-all-to-list": "Add All to List",
|
||||
"add-day-to-list": "Add Day to List"
|
||||
},
|
||||
"migration": {
|
||||
"migration-data-removed": "Migrationsdaten entfernt",
|
||||
|
||||
@@ -369,7 +369,9 @@
|
||||
"recipe-rules": "Κανόνες Συνταγής",
|
||||
"applies-to-all-days": "Εφαρμόζεται για όλες τις ημέρες",
|
||||
"applies-on-days": "Εφαρμόζεται κάθε {0}",
|
||||
"meal-plan-settings": "Ρυθμίσεις προγράμματος γευμάτων"
|
||||
"meal-plan-settings": "Ρυθμίσεις προγράμματος γευμάτων",
|
||||
"add-all-to-list": "Προσθήκη όλων στη λίστα",
|
||||
"add-day-to-list": "Προσθήκη ημέρας στη λίστα"
|
||||
},
|
||||
"migration": {
|
||||
"migration-data-removed": "Τα δεδομένα μετεγκατάστασης καταργήθηκαν",
|
||||
|
||||
@@ -369,7 +369,9 @@
|
||||
"recipe-rules": "Recipe Rules",
|
||||
"applies-to-all-days": "Applies to all days",
|
||||
"applies-on-days": "Applies on {0}s",
|
||||
"meal-plan-settings": "Meal Plan Settings"
|
||||
"meal-plan-settings": "Meal Plan Settings",
|
||||
"add-all-to-list": "Add All to List",
|
||||
"add-day-to-list": "Add Day to List"
|
||||
},
|
||||
"migration": {
|
||||
"migration-data-removed": "Migration data removed",
|
||||
|
||||
@@ -369,7 +369,9 @@
|
||||
"recipe-rules": "Recipe Rules",
|
||||
"applies-to-all-days": "Applies to all days",
|
||||
"applies-on-days": "Applies on {0}s",
|
||||
"meal-plan-settings": "Meal Plan Settings"
|
||||
"meal-plan-settings": "Meal Plan Settings",
|
||||
"add-all-to-list": "Add All to List",
|
||||
"add-day-to-list": "Add Day to List"
|
||||
},
|
||||
"migration": {
|
||||
"migration-data-removed": "Migration data removed",
|
||||
|
||||
@@ -369,7 +369,9 @@
|
||||
"recipe-rules": "Reglas de Recetas",
|
||||
"applies-to-all-days": "Aplica para todos los días",
|
||||
"applies-on-days": "Se aplica en {0}s",
|
||||
"meal-plan-settings": "Configuración del Plan de Comidas"
|
||||
"meal-plan-settings": "Configuración del Plan de Comidas",
|
||||
"add-all-to-list": "Add All to List",
|
||||
"add-day-to-list": "Add Day to List"
|
||||
},
|
||||
"migration": {
|
||||
"migration-data-removed": "Datos de migración eliminados",
|
||||
|
||||
@@ -369,7 +369,9 @@
|
||||
"recipe-rules": "Retsepti reeglid",
|
||||
"applies-to-all-days": "Kehtib kõikide päevade kohta",
|
||||
"applies-on-days": "Kehtib {0}l",
|
||||
"meal-plan-settings": "Toitumisplaani sätted"
|
||||
"meal-plan-settings": "Toitumisplaani sätted",
|
||||
"add-all-to-list": "Add All to List",
|
||||
"add-day-to-list": "Add Day to List"
|
||||
},
|
||||
"migration": {
|
||||
"migration-data-removed": "Ületoomiste andmed eemaldatud",
|
||||
|
||||
@@ -369,7 +369,9 @@
|
||||
"recipe-rules": "Reseptimääritykset",
|
||||
"applies-to-all-days": "Sovelletaan kaikkiin päiviin",
|
||||
"applies-on-days": "Käytetään {0}",
|
||||
"meal-plan-settings": "Ateriasuunnitelman asetukset"
|
||||
"meal-plan-settings": "Ateriasuunnitelman asetukset",
|
||||
"add-all-to-list": "Add All to List",
|
||||
"add-day-to-list": "Add Day to List"
|
||||
},
|
||||
"migration": {
|
||||
"migration-data-removed": "Tuodut tiedot poistettu",
|
||||
|
||||
@@ -369,7 +369,9 @@
|
||||
"recipe-rules": "Règles de recette",
|
||||
"applies-to-all-days": "S'applique à tous les jours",
|
||||
"applies-on-days": "S'applique les {0}s",
|
||||
"meal-plan-settings": "Paramètres des menus"
|
||||
"meal-plan-settings": "Paramètres des menus",
|
||||
"add-all-to-list": "Add All to List",
|
||||
"add-day-to-list": "Add Day to List"
|
||||
},
|
||||
"migration": {
|
||||
"migration-data-removed": "Données de migration supprimées",
|
||||
|
||||
@@ -369,7 +369,9 @@
|
||||
"recipe-rules": "Règles de recette",
|
||||
"applies-to-all-days": "S'applique à tous les jours",
|
||||
"applies-on-days": "S'applique les {0}s",
|
||||
"meal-plan-settings": "Paramètres des menus"
|
||||
"meal-plan-settings": "Paramètres des menus",
|
||||
"add-all-to-list": "Add All to List",
|
||||
"add-day-to-list": "Add Day to List"
|
||||
},
|
||||
"migration": {
|
||||
"migration-data-removed": "Données de migration supprimées",
|
||||
|
||||
@@ -369,7 +369,9 @@
|
||||
"recipe-rules": "Règles de recette",
|
||||
"applies-to-all-days": "S'applique à tous les jours",
|
||||
"applies-on-days": "S'applique les {0}s",
|
||||
"meal-plan-settings": "Paramètres des menus"
|
||||
"meal-plan-settings": "Paramètres des menus",
|
||||
"add-all-to-list": "Add All to List",
|
||||
"add-day-to-list": "Add Day to List"
|
||||
},
|
||||
"migration": {
|
||||
"migration-data-removed": "Données de migration supprimées",
|
||||
|
||||
@@ -369,7 +369,9 @@
|
||||
"recipe-rules": "Regras da Receita",
|
||||
"applies-to-all-days": "Aplícase a todos os días",
|
||||
"applies-on-days": "Aplícase en {0}s",
|
||||
"meal-plan-settings": "Axustes do Menú"
|
||||
"meal-plan-settings": "Axustes do Menú",
|
||||
"add-all-to-list": "Add All to List",
|
||||
"add-day-to-list": "Add Day to List"
|
||||
},
|
||||
"migration": {
|
||||
"migration-data-removed": "Elimináronse os datos de migración",
|
||||
|
||||
@@ -369,7 +369,9 @@
|
||||
"recipe-rules": "חוקי מתכון",
|
||||
"applies-to-all-days": "החל על כל הימים",
|
||||
"applies-on-days": "חל על {0}",
|
||||
"meal-plan-settings": "הגדרות תכנון ארוחות"
|
||||
"meal-plan-settings": "הגדרות תכנון ארוחות",
|
||||
"add-all-to-list": "Add All to List",
|
||||
"add-day-to-list": "Add Day to List"
|
||||
},
|
||||
"migration": {
|
||||
"migration-data-removed": "מידע ממוגרץ נמחק",
|
||||
|
||||
@@ -369,7 +369,9 @@
|
||||
"recipe-rules": "Pravila Recepata",
|
||||
"applies-to-all-days": "Primjeni na sve dane",
|
||||
"applies-on-days": "Primjeni na {0}",
|
||||
"meal-plan-settings": "Postavke Plana Obroka"
|
||||
"meal-plan-settings": "Postavke Plana Obroka",
|
||||
"add-all-to-list": "Add All to List",
|
||||
"add-day-to-list": "Add Day to List"
|
||||
},
|
||||
"migration": {
|
||||
"migration-data-removed": "Podaci o migraciji su uklonjeni",
|
||||
|
||||
@@ -369,7 +369,9 @@
|
||||
"recipe-rules": "Recept szabályok",
|
||||
"applies-to-all-days": "Minden napra vonatkozóan",
|
||||
"applies-on-days": "Érvényes {0}-ként",
|
||||
"meal-plan-settings": "Menütervező beállításai"
|
||||
"meal-plan-settings": "Menütervező beállításai",
|
||||
"add-all-to-list": "Add All to List",
|
||||
"add-day-to-list": "Add Day to List"
|
||||
},
|
||||
"migration": {
|
||||
"migration-data-removed": "Migrációs adatok eltávolítva",
|
||||
|
||||
@@ -369,7 +369,9 @@
|
||||
"recipe-rules": "Uppskriftar reglur",
|
||||
"applies-to-all-days": "Á við alla daga",
|
||||
"applies-on-days": "Gildir þegar er {0},",
|
||||
"meal-plan-settings": "Stillingar matarplans"
|
||||
"meal-plan-settings": "Stillingar matarplans",
|
||||
"add-all-to-list": "Add All to List",
|
||||
"add-day-to-list": "Add Day to List"
|
||||
},
|
||||
"migration": {
|
||||
"migration-data-removed": "Gagnaflutningur fjarlægður",
|
||||
|
||||
@@ -369,7 +369,9 @@
|
||||
"recipe-rules": "Regole per le ricette",
|
||||
"applies-to-all-days": "Si applica a ogni giorno",
|
||||
"applies-on-days": "Si applica ai {0}",
|
||||
"meal-plan-settings": "Impostazioni del piano alimentare"
|
||||
"meal-plan-settings": "Impostazioni del piano alimentare",
|
||||
"add-all-to-list": "Add All to List",
|
||||
"add-day-to-list": "Add Day to List"
|
||||
},
|
||||
"migration": {
|
||||
"migration-data-removed": "Dati di migrazione rimossi",
|
||||
|
||||
@@ -369,7 +369,9 @@
|
||||
"recipe-rules": "レシピのルール",
|
||||
"applies-to-all-days": "すべての日に適用",
|
||||
"applies-on-days": "{0}曜日に適用",
|
||||
"meal-plan-settings": "献立設定"
|
||||
"meal-plan-settings": "献立設定",
|
||||
"add-all-to-list": "Add All to List",
|
||||
"add-day-to-list": "Add Day to List"
|
||||
},
|
||||
"migration": {
|
||||
"migration-data-removed": "移行データが削除されました",
|
||||
|
||||
@@ -369,7 +369,9 @@
|
||||
"recipe-rules": "레시피 규칙",
|
||||
"applies-to-all-days": "모든 날짜에 적용됨",
|
||||
"applies-on-days": "{0}에 적용됨",
|
||||
"meal-plan-settings": "식단 계획 설정"
|
||||
"meal-plan-settings": "식단 계획 설정",
|
||||
"add-all-to-list": "Add All to List",
|
||||
"add-day-to-list": "Add Day to List"
|
||||
},
|
||||
"migration": {
|
||||
"migration-data-removed": "이전된 데이터 제거됨",
|
||||
|
||||
@@ -369,7 +369,9 @@
|
||||
"recipe-rules": "Recepto taisyklės",
|
||||
"applies-to-all-days": "Galioja visoms dienoms",
|
||||
"applies-on-days": "Galioja {0}",
|
||||
"meal-plan-settings": "Valgių plano nustatymai"
|
||||
"meal-plan-settings": "Valgių plano nustatymai",
|
||||
"add-all-to-list": "Add All to List",
|
||||
"add-day-to-list": "Add Day to List"
|
||||
},
|
||||
"migration": {
|
||||
"migration-data-removed": "Perkėlimo duomenys pašalinti",
|
||||
|
||||
@@ -369,7 +369,9 @@
|
||||
"recipe-rules": "Recepšu noteikumi",
|
||||
"applies-to-all-days": "Attiecas uz visām dienām",
|
||||
"applies-on-days": "Attiecas uz {0} s",
|
||||
"meal-plan-settings": "Maltītes plāna iestatījumi"
|
||||
"meal-plan-settings": "Maltītes plāna iestatījumi",
|
||||
"add-all-to-list": "Add All to List",
|
||||
"add-day-to-list": "Add Day to List"
|
||||
},
|
||||
"migration": {
|
||||
"migration-data-removed": "Migrācijas dati noņemti",
|
||||
|
||||
@@ -369,7 +369,9 @@
|
||||
"recipe-rules": "Receptregels",
|
||||
"applies-to-all-days": "Van toepassing op alle dagen",
|
||||
"applies-on-days": "Van toepassing op {0}s",
|
||||
"meal-plan-settings": "Maaltijdplan-instellingen"
|
||||
"meal-plan-settings": "Maaltijdplan-instellingen",
|
||||
"add-all-to-list": "Add All to List",
|
||||
"add-day-to-list": "Add Day to List"
|
||||
},
|
||||
"migration": {
|
||||
"migration-data-removed": "Migratiegegevens verwijderd",
|
||||
|
||||
@@ -369,7 +369,9 @@
|
||||
"recipe-rules": "Regler for oppskrifter",
|
||||
"applies-to-all-days": "Gjelder for alle dager",
|
||||
"applies-on-days": "Gjelder på {0}er",
|
||||
"meal-plan-settings": "Innstillinger for måltidsplan"
|
||||
"meal-plan-settings": "Innstillinger for måltidsplan",
|
||||
"add-all-to-list": "Add All to List",
|
||||
"add-day-to-list": "Add Day to List"
|
||||
},
|
||||
"migration": {
|
||||
"migration-data-removed": "Overføringsdata er fjernet",
|
||||
|
||||
@@ -212,8 +212,8 @@
|
||||
"upload-file": "Prześlij plik",
|
||||
"created-on-date": "Utworzono dnia: {0}",
|
||||
"unsaved-changes": "Masz niezapisane zmiany. Czy chcesz zapisać przed wyjściem? Ok, aby zapisać, Anuluj, żeby odrzucić zmiany.",
|
||||
"discard-changes": "Discard Changes",
|
||||
"discard-changes-description": "You have unsaved changes. Are you sure you want to discard them?",
|
||||
"discard-changes": "Odrzuć zmiany",
|
||||
"discard-changes-description": "Masz niezapisane zmiany. Czy na pewno chcesz je odrzucić?",
|
||||
"clipboard-copy-failure": "Nie udało się skopiować do schowka.",
|
||||
"confirm-delete-generic-items": "Czy na pewno chcesz usunąć następujące elementy?",
|
||||
"organizers": "Organizatory",
|
||||
@@ -369,7 +369,9 @@
|
||||
"recipe-rules": "Reguły przepisów",
|
||||
"applies-to-all-days": "Dotyczy wszystkich dni",
|
||||
"applies-on-days": "Dotyczy {0}",
|
||||
"meal-plan-settings": "Ustawienia planera posiłków"
|
||||
"meal-plan-settings": "Ustawienia planera posiłków",
|
||||
"add-all-to-list": "Add All to List",
|
||||
"add-day-to-list": "Add Day to List"
|
||||
},
|
||||
"migration": {
|
||||
"migration-data-removed": "Dane migracji usunięte",
|
||||
@@ -642,7 +644,7 @@
|
||||
"scrape-recipe-website-being-blocked": "Witryna jest zablokowana?",
|
||||
"scrape-recipe-try-importing-raw-html-instead": "Zamiast tego spróbuj zaimportować surowy HTML.",
|
||||
"import-original-keywords-as-tags": "Importuj oryginalne słowa kluczowe jako tagi",
|
||||
"import-original-categories": "Import original categories",
|
||||
"import-original-categories": "Importuj oryginalne kategorie",
|
||||
"stay-in-edit-mode": "Pozostań w trybie edycji",
|
||||
"parse-recipe-ingredients-after-import": "Analizuj składniki receptury po zaimportowaniu",
|
||||
"import-from-zip": "Importuj z pliku Zip",
|
||||
|
||||
@@ -369,7 +369,9 @@
|
||||
"recipe-rules": "Regras das receitas",
|
||||
"applies-to-all-days": "Aplica-se diariamente",
|
||||
"applies-on-days": "Aplica-se em:",
|
||||
"meal-plan-settings": "Configurações de plano de refeições"
|
||||
"meal-plan-settings": "Configurações de plano de refeições",
|
||||
"add-all-to-list": "Add All to List",
|
||||
"add-day-to-list": "Add Day to List"
|
||||
},
|
||||
"migration": {
|
||||
"migration-data-removed": "Dados de migração removidos",
|
||||
|
||||
@@ -369,7 +369,9 @@
|
||||
"recipe-rules": "Regras das receitas",
|
||||
"applies-to-all-days": "Aplica-se a todos os dias",
|
||||
"applies-on-days": "Aplica-se em {0}s",
|
||||
"meal-plan-settings": "Definições do Plano de Refeições"
|
||||
"meal-plan-settings": "Definições do Plano de Refeições",
|
||||
"add-all-to-list": "Add All to List",
|
||||
"add-day-to-list": "Add Day to List"
|
||||
},
|
||||
"migration": {
|
||||
"migration-data-removed": "Dados de migração removidos",
|
||||
|
||||
@@ -369,7 +369,9 @@
|
||||
"recipe-rules": "Reguli rețetă",
|
||||
"applies-to-all-days": "Se aplică pentru toate zilele",
|
||||
"applies-on-days": "Aplică pe {0}s",
|
||||
"meal-plan-settings": "Setările Planului de Masă"
|
||||
"meal-plan-settings": "Setările Planului de Masă",
|
||||
"add-all-to-list": "Add All to List",
|
||||
"add-day-to-list": "Add Day to List"
|
||||
},
|
||||
"migration": {
|
||||
"migration-data-removed": "Datele migrării au fost șterse",
|
||||
|
||||
@@ -369,7 +369,9 @@
|
||||
"recipe-rules": "Правила рецептов",
|
||||
"applies-to-all-days": "Применяется к всем дням",
|
||||
"applies-on-days": "Применяется по {0}",
|
||||
"meal-plan-settings": "Настройки плана питания"
|
||||
"meal-plan-settings": "Настройки плана питания",
|
||||
"add-all-to-list": "Add All to List",
|
||||
"add-day-to-list": "Add Day to List"
|
||||
},
|
||||
"migration": {
|
||||
"migration-data-removed": "Данные миграции удалены",
|
||||
|
||||
@@ -369,7 +369,9 @@
|
||||
"recipe-rules": "Pravidlá receptov",
|
||||
"applies-to-all-days": "Platí pre všetky dni",
|
||||
"applies-on-days": "Platí v {0}",
|
||||
"meal-plan-settings": "Nastavenia jedálnička"
|
||||
"meal-plan-settings": "Nastavenia jedálnička",
|
||||
"add-all-to-list": "Add All to List",
|
||||
"add-day-to-list": "Add Day to List"
|
||||
},
|
||||
"migration": {
|
||||
"migration-data-removed": "Prenesené dáta odstránené",
|
||||
|
||||
@@ -369,7 +369,9 @@
|
||||
"recipe-rules": "Pravila za recepte",
|
||||
"applies-to-all-days": "Velja za vse dneve",
|
||||
"applies-on-days": "Velja za {0}",
|
||||
"meal-plan-settings": "Nastavitve jedilnika"
|
||||
"meal-plan-settings": "Nastavitve jedilnika",
|
||||
"add-all-to-list": "Dodaj vse na seznam",
|
||||
"add-day-to-list": "Dodaj dan na seznam"
|
||||
},
|
||||
"migration": {
|
||||
"migration-data-removed": "Migrirani podatki so odstranjeni",
|
||||
|
||||
@@ -369,7 +369,9 @@
|
||||
"recipe-rules": "Правила за рецепт",
|
||||
"applies-to-all-days": "Applies to all days",
|
||||
"applies-on-days": "Applies on {0}s",
|
||||
"meal-plan-settings": "Meal Plan Settings"
|
||||
"meal-plan-settings": "Meal Plan Settings",
|
||||
"add-all-to-list": "Add All to List",
|
||||
"add-day-to-list": "Add Day to List"
|
||||
},
|
||||
"migration": {
|
||||
"migration-data-removed": "Migration data removed",
|
||||
|
||||
@@ -369,7 +369,9 @@
|
||||
"recipe-rules": "Receptregler",
|
||||
"applies-to-all-days": "Gäller för alla dagar",
|
||||
"applies-on-days": "Gäller på {0}s",
|
||||
"meal-plan-settings": "Inställningar för måltidsplanering"
|
||||
"meal-plan-settings": "Inställningar för måltidsplanering",
|
||||
"add-all-to-list": "Add All to List",
|
||||
"add-day-to-list": "Add Day to List"
|
||||
},
|
||||
"migration": {
|
||||
"migration-data-removed": "Importerad data borttagen",
|
||||
|
||||
@@ -369,7 +369,9 @@
|
||||
"recipe-rules": "Tarif Kuralları",
|
||||
"applies-to-all-days": "Tüm günler için geçerlidir",
|
||||
"applies-on-days": "{0} günlerine geçerlidir",
|
||||
"meal-plan-settings": "Öğün Planı Ayarları"
|
||||
"meal-plan-settings": "Öğün Planı Ayarları",
|
||||
"add-all-to-list": "Add All to List",
|
||||
"add-day-to-list": "Add Day to List"
|
||||
},
|
||||
"migration": {
|
||||
"migration-data-removed": "Taşıma verileri kaldırıldı",
|
||||
|
||||
@@ -369,7 +369,9 @@
|
||||
"recipe-rules": "Правила рецептів",
|
||||
"applies-to-all-days": "Застосовується до всіх днів",
|
||||
"applies-on-days": "Застосовується на {0}сек",
|
||||
"meal-plan-settings": "Налаштування меню"
|
||||
"meal-plan-settings": "Налаштування меню",
|
||||
"add-all-to-list": "Add All to List",
|
||||
"add-day-to-list": "Add Day to List"
|
||||
},
|
||||
"migration": {
|
||||
"migration-data-removed": "Дані міграції видалені",
|
||||
|
||||
@@ -369,7 +369,9 @@
|
||||
"recipe-rules": "Recipe Rules",
|
||||
"applies-to-all-days": "Applies to all days",
|
||||
"applies-on-days": "Applies on {0}s",
|
||||
"meal-plan-settings": "Meal Plan Settings"
|
||||
"meal-plan-settings": "Meal Plan Settings",
|
||||
"add-all-to-list": "Add All to List",
|
||||
"add-day-to-list": "Add Day to List"
|
||||
},
|
||||
"migration": {
|
||||
"migration-data-removed": "Migration data removed",
|
||||
|
||||
@@ -369,7 +369,9 @@
|
||||
"recipe-rules": "食谱规则",
|
||||
"applies-to-all-days": "应用到所有日期",
|
||||
"applies-on-days": "应用于{0}",
|
||||
"meal-plan-settings": "饮食计划设置"
|
||||
"meal-plan-settings": "饮食计划设置",
|
||||
"add-all-to-list": "Add All to List",
|
||||
"add-day-to-list": "Add Day to List"
|
||||
},
|
||||
"migration": {
|
||||
"migration-data-removed": "已删除迁移数据",
|
||||
|
||||
@@ -369,7 +369,9 @@
|
||||
"recipe-rules": "Recipe Rules",
|
||||
"applies-to-all-days": "Applies to all days",
|
||||
"applies-on-days": "Applies on {0}s",
|
||||
"meal-plan-settings": "Meal Plan Settings"
|
||||
"meal-plan-settings": "Meal Plan Settings",
|
||||
"add-all-to-list": "Add All to List",
|
||||
"add-day-to-list": "Add Day to List"
|
||||
},
|
||||
"migration": {
|
||||
"migration-data-removed": "遷移數據已刪除",
|
||||
|
||||
@@ -44,6 +44,7 @@ export interface QueryFilterJSONPart {
|
||||
attributeName?: string | null;
|
||||
relationalOperator?: RelationalKeyword | RelationalOperator | null;
|
||||
value?: string | string[] | null;
|
||||
[k: string]: unknown;
|
||||
}
|
||||
export interface SaveCookBook {
|
||||
name: string;
|
||||
|
||||
@@ -53,6 +53,7 @@ export interface QueryFilterJSONPart {
|
||||
attributeName?: string | null;
|
||||
relationalOperator?: RelationalKeyword | RelationalOperator | null;
|
||||
value?: string | string[] | null;
|
||||
[k: string]: unknown;
|
||||
}
|
||||
export interface PlanRulesSave {
|
||||
day?: PlanRulesDay;
|
||||
|
||||
@@ -40,3 +40,20 @@ export enum Organizer {
|
||||
Household = "households",
|
||||
User = "users",
|
||||
}
|
||||
|
||||
export type LogicalOperator = "AND" | "OR";
|
||||
export type RelationalKeyword = "IS" | "IS NOT" | "IN" | "NOT IN" | "CONTAINS ALL" | "LIKE" | "NOT LIKE";
|
||||
export type RelationalOperator = "=" | "<>" | ">" | "<" | ">=" | "<=";
|
||||
|
||||
export interface QueryFilterJSON {
|
||||
parts?: QueryFilterJSONPart[];
|
||||
}
|
||||
|
||||
export interface QueryFilterJSONPart {
|
||||
leftParenthesis?: string | null;
|
||||
rightParenthesis?: string | null;
|
||||
logicalOperator?: LogicalOperator | null;
|
||||
attributeName?: string | null;
|
||||
relationalOperator?: RelationalKeyword | RelationalOperator | null;
|
||||
value?: string | string[] | null;
|
||||
}
|
||||
|
||||
@@ -16,7 +16,7 @@ export interface OpenAIIngredients {
|
||||
}
|
||||
export interface OpenAIRecipe {
|
||||
name: string;
|
||||
description: string | null;
|
||||
description?: string | null;
|
||||
recipe_yield?: string | null;
|
||||
total_time?: string | null;
|
||||
prep_time?: string | null;
|
||||
@@ -37,4 +37,7 @@ export interface OpenAIRecipeNotes {
|
||||
title?: string | null;
|
||||
text: string;
|
||||
}
|
||||
export interface OpenAIText {
|
||||
text: string;
|
||||
}
|
||||
export interface OpenAIBase {}
|
||||
|
||||
@@ -502,13 +502,16 @@ export interface SaveIngredientUnit {
|
||||
}
|
||||
export interface ScrapeRecipe {
|
||||
includeTags?: boolean;
|
||||
includeCategories?: boolean;
|
||||
url: string;
|
||||
}
|
||||
export interface ScrapeRecipeBase {
|
||||
includeTags?: boolean;
|
||||
includeCategories?: boolean;
|
||||
}
|
||||
export interface ScrapeRecipeData {
|
||||
includeTags?: boolean;
|
||||
includeCategories?: boolean;
|
||||
data: string;
|
||||
url?: string | null;
|
||||
}
|
||||
|
||||
@@ -7,9 +7,6 @@
|
||||
|
||||
export type OrderByNullPosition = "first" | "last";
|
||||
export type OrderDirection = "asc" | "desc";
|
||||
export type LogicalOperator = "AND" | "OR";
|
||||
export type RelationalKeyword = "IS" | "IS NOT" | "IN" | "NOT IN" | "CONTAINS ALL" | "LIKE" | "NOT LIKE";
|
||||
export type RelationalOperator = "=" | "<>" | ">" | "<" | ">=" | "<=";
|
||||
|
||||
export interface ErrorResponse {
|
||||
message: string;
|
||||
@@ -28,17 +25,6 @@ export interface PaginationQuery {
|
||||
page?: number;
|
||||
perPage?: number;
|
||||
}
|
||||
export interface QueryFilterJSON {
|
||||
parts?: QueryFilterJSONPart[];
|
||||
}
|
||||
export interface QueryFilterJSONPart {
|
||||
leftParenthesis?: string | null;
|
||||
rightParenthesis?: string | null;
|
||||
logicalOperator?: LogicalOperator | null;
|
||||
attributeName?: string | null;
|
||||
relationalOperator?: RelationalKeyword | RelationalOperator | null;
|
||||
value?: string | string[] | null;
|
||||
}
|
||||
export interface RecipeSearchQuery {
|
||||
cookbook?: string | null;
|
||||
requireAllCategories?: boolean;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "mealie",
|
||||
"version": "3.9.2",
|
||||
"version": "3.10.0",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"dev": "nuxt dev",
|
||||
|
||||
@@ -422,7 +422,7 @@ import { Organizer } from "~/lib/api/types/non-generated";
|
||||
import QueryFilterBuilder from "~/components/Domain/QueryFilterBuilder.vue";
|
||||
import RecipeSuggestion from "~/components/Domain/Recipe/RecipeSuggestion.vue";
|
||||
import SearchFilter from "~/components/Domain/SearchFilter.vue";
|
||||
import type { QueryFilterJSON } from "~/lib/api/types/response";
|
||||
import type { QueryFilterJSON } from "~/lib/api/types/non-generated";
|
||||
import type { FieldDefinition } from "~/composables/use-query-filter-builder";
|
||||
import { useRecipeFinderPreferences } from "~/composables/use-users/preferences";
|
||||
|
||||
|
||||
@@ -1,5 +1,11 @@
|
||||
<template>
|
||||
<v-container>
|
||||
<RecipeDialogAddToShoppingList
|
||||
v-if="shoppingLists"
|
||||
v-model="state.shoppingListDialog"
|
||||
:recipes="weekRecipesWithScales"
|
||||
:shopping-lists="shoppingLists"
|
||||
/>
|
||||
<v-menu
|
||||
v-model="state.picker"
|
||||
:close-on-content-click="false"
|
||||
@@ -45,13 +51,23 @@
|
||||
|
||||
<div class="d-flex flex-wrap align-center justify-space-between mb-2">
|
||||
<v-tabs style="width: fit-content;">
|
||||
<v-tab :to="{ name: 'household-mealplan-planner-view', query: route.query }">
|
||||
<v-tab :to="{ name: TABS.view, query: route.query }">
|
||||
{{ $t('meal-plan.meal-planner') }}
|
||||
</v-tab>
|
||||
<v-tab :to="{ name: 'household-mealplan-planner-edit', query: route.query }">
|
||||
<v-tab :to="{ name: TABS.edit, query: route.query }">
|
||||
{{ $t('general.edit') }}
|
||||
</v-tab>
|
||||
</v-tabs>
|
||||
<BaseButton
|
||||
v-if="route.name === TABS.view"
|
||||
color="info"
|
||||
:icon="$globals.icons.cartCheck"
|
||||
:text="$t('meal-plan.add-all-to-list')"
|
||||
:disabled="!hasRecipes"
|
||||
:loading="state.addAllLoading"
|
||||
class="ml-auto mr-4"
|
||||
@click="addAllToList"
|
||||
/>
|
||||
<ButtonLink
|
||||
:icon="$globals.icons.calendar"
|
||||
:to="`/household/mealplan/settings`"
|
||||
@@ -72,15 +88,27 @@
|
||||
|
||||
<script lang="ts">
|
||||
import { isSameDay, addDays, parseISO, format, isValid } from "date-fns";
|
||||
import RecipeDialogAddToShoppingList from "~/components/Domain/Recipe/RecipeDialogAddToShoppingList.vue";
|
||||
import { useHouseholdSelf } from "~/composables/use-households";
|
||||
import { useMealplans } from "~/composables/use-group-mealplan";
|
||||
import { useUserMealPlanPreferences } from "~/composables/use-users/preferences";
|
||||
import type { ShoppingListSummary } from "~/lib/api/types/household";
|
||||
import { useUserApi } from "~/composables/api";
|
||||
|
||||
export default defineNuxtComponent({
|
||||
components: {
|
||||
RecipeDialogAddToShoppingList,
|
||||
},
|
||||
setup() {
|
||||
const TABS = {
|
||||
view: "household-mealplan-planner-view",
|
||||
edit: "household-mealplan-planner-edit",
|
||||
};
|
||||
|
||||
const route = useRoute();
|
||||
const router = useRouter();
|
||||
const i18n = useI18n();
|
||||
const api = useUserApi();
|
||||
const { household } = useHouseholdSelf();
|
||||
|
||||
useSeoMeta({
|
||||
@@ -96,7 +124,7 @@ export default defineNuxtComponent({
|
||||
// Force to /view if current route is /planner
|
||||
if (route.path === "/household/mealplan/planner") {
|
||||
router.push({
|
||||
name: "household-mealplan-planner-view",
|
||||
name: TABS.view,
|
||||
query: route.query,
|
||||
});
|
||||
}
|
||||
@@ -120,8 +148,12 @@ export default defineNuxtComponent({
|
||||
start: initialStartDate,
|
||||
picker: false,
|
||||
end: initialEndDate,
|
||||
shoppingListDialog: false,
|
||||
addAllLoading: false,
|
||||
});
|
||||
|
||||
const shoppingLists = ref<ShoppingListSummary[]>();
|
||||
|
||||
const firstDayOfWeek = computed(() => {
|
||||
return household.value?.preferences?.firstDayOfWeek || 0;
|
||||
});
|
||||
@@ -145,7 +177,7 @@ export default defineNuxtComponent({
|
||||
watch(weekRange, (newRange) => {
|
||||
// Keep current route name and params, just update the query
|
||||
router.replace({
|
||||
name: route.name || "household-mealplan-planner-view",
|
||||
name: route.name || TABS.view,
|
||||
params: route.params,
|
||||
query: {
|
||||
...route.query,
|
||||
@@ -193,7 +225,41 @@ export default defineNuxtComponent({
|
||||
});
|
||||
});
|
||||
|
||||
const hasRecipes = computed(() => {
|
||||
return mealsByDate.value.some(day => day.meals.some(meal => meal.recipe));
|
||||
});
|
||||
|
||||
const weekRecipesWithScales = computed(() => {
|
||||
const allRecipes: any[] = [];
|
||||
for (const day of mealsByDate.value) {
|
||||
for (const meal of day.meals) {
|
||||
if (meal.recipe) {
|
||||
allRecipes.push(meal.recipe);
|
||||
}
|
||||
}
|
||||
}
|
||||
return allRecipes.map(recipe => ({
|
||||
scale: 1,
|
||||
...recipe,
|
||||
}));
|
||||
});
|
||||
|
||||
async function getShoppingLists() {
|
||||
const { data } = await api.shopping.lists.getAll(1, -1, { orderBy: "name", orderDirection: "asc" });
|
||||
if (data) {
|
||||
shoppingLists.value = data.items as ShoppingListSummary[] ?? [];
|
||||
}
|
||||
}
|
||||
|
||||
async function addAllToList() {
|
||||
state.value.addAllLoading = true;
|
||||
await getShoppingLists();
|
||||
state.value.shoppingListDialog = true;
|
||||
state.value.addAllLoading = false;
|
||||
}
|
||||
|
||||
return {
|
||||
TABS,
|
||||
route,
|
||||
state,
|
||||
actions,
|
||||
@@ -201,6 +267,10 @@ export default defineNuxtComponent({
|
||||
weekRange,
|
||||
firstDayOfWeek,
|
||||
numberOfDays,
|
||||
hasRecipes,
|
||||
shoppingLists,
|
||||
weekRecipesWithScales,
|
||||
addAllToList,
|
||||
};
|
||||
},
|
||||
});
|
||||
|
||||
@@ -24,8 +24,8 @@ from mealie.schema.response.pagination import (
|
||||
PaginationQuery,
|
||||
RequestQuery,
|
||||
)
|
||||
from mealie.schema.response.query_filter import QueryFilterBuilder
|
||||
from mealie.schema.response.query_search import SearchFilter
|
||||
from mealie.services.query_filter.builder import QueryFilterBuilder
|
||||
|
||||
from ._utils import NOT_SET, NotSet
|
||||
|
||||
|
||||
@@ -26,7 +26,7 @@ from mealie.schema.recipe.recipe_ingredient import IngredientFood
|
||||
from mealie.schema.recipe.recipe_suggestion import RecipeSuggestionQuery, RecipeSuggestionResponseItem
|
||||
from mealie.schema.recipe.recipe_tool import RecipeToolOut
|
||||
from mealie.schema.response.pagination import PaginationQuery
|
||||
from mealie.schema.response.query_filter import QueryFilterBuilder
|
||||
from mealie.services.query_filter.builder import QueryFilterBuilder
|
||||
|
||||
from ..db.models._model_base import SqlAlchemyBase
|
||||
from .repository_generic import HouseholdRepositoryGeneric
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
"aliases": [],
|
||||
"description": "",
|
||||
"name": "hvidløg",
|
||||
"plural_name": "garlic"
|
||||
"plural_name": "hvidløg"
|
||||
},
|
||||
"onion": {
|
||||
"aliases": [],
|
||||
@@ -59,13 +59,13 @@
|
||||
"aliases": [],
|
||||
"description": "",
|
||||
"name": "selleri",
|
||||
"plural_name": "celery"
|
||||
"plural_name": "selleri"
|
||||
},
|
||||
"jalapeño": {
|
||||
"aliases": [],
|
||||
"description": "",
|
||||
"name": "jalapeño",
|
||||
"plural_name": "jalapeños"
|
||||
"plural_name": "jalapeñoer"
|
||||
},
|
||||
"avocado": {
|
||||
"aliases": [],
|
||||
@@ -95,7 +95,7 @@
|
||||
"aliases": [],
|
||||
"description": "",
|
||||
"name": "spinat",
|
||||
"plural_name": "spinach"
|
||||
"plural_name": "spinat"
|
||||
},
|
||||
"sweet corn": {
|
||||
"aliases": [],
|
||||
@@ -145,7 +145,7 @@
|
||||
"aliases": [],
|
||||
"description": "",
|
||||
"name": "blomkål",
|
||||
"plural_name": "cauliflower"
|
||||
"plural_name": "blomkål"
|
||||
},
|
||||
"cabbage": {
|
||||
"aliases": [],
|
||||
@@ -156,14 +156,14 @@
|
||||
"asparagus": {
|
||||
"aliases": [],
|
||||
"description": "",
|
||||
"name": "asparagus",
|
||||
"plural_name": "asparagus"
|
||||
"name": "asparges",
|
||||
"plural_name": "asparges"
|
||||
},
|
||||
"kale": {
|
||||
"aliases": [],
|
||||
"description": "",
|
||||
"name": "grønkål",
|
||||
"plural_name": "kale"
|
||||
"plural_name": "grønkål"
|
||||
},
|
||||
"arugula": {
|
||||
"aliases": [],
|
||||
@@ -187,7 +187,7 @@
|
||||
"aliases": [],
|
||||
"description": "",
|
||||
"name": "salat",
|
||||
"plural_name": "lettuce"
|
||||
"plural_name": "salat"
|
||||
},
|
||||
"butternut squash": {
|
||||
"aliases": [],
|
||||
@@ -198,8 +198,8 @@
|
||||
"romaine lettuce": {
|
||||
"aliases": [],
|
||||
"description": "",
|
||||
"name": "romaine lettuce",
|
||||
"plural_name": "romaine lettuce"
|
||||
"name": "romainesalat",
|
||||
"plural_name": "romainesalat"
|
||||
},
|
||||
"beetroot": {
|
||||
"aliases": [],
|
||||
@@ -217,7 +217,7 @@
|
||||
"aliases": [],
|
||||
"description": "",
|
||||
"name": "fennikel",
|
||||
"plural_name": "fennel"
|
||||
"plural_name": "fennikkel"
|
||||
},
|
||||
"sun dried tomato": {
|
||||
"aliases": [],
|
||||
@@ -279,8 +279,8 @@
|
||||
"mixed vegetables": {
|
||||
"aliases": [],
|
||||
"description": "",
|
||||
"name": "mixed vegetables",
|
||||
"plural_name": "mixed vegetables"
|
||||
"name": "blandede grøntsager",
|
||||
"plural_name": "blandede grøntsager"
|
||||
},
|
||||
"poblano pepper": {
|
||||
"aliases": [],
|
||||
@@ -304,7 +304,7 @@
|
||||
"aliases": [],
|
||||
"description": "",
|
||||
"name": "cayennepeber",
|
||||
"plural_name": "cayenne pepper"
|
||||
"plural_name": "cayennepeber"
|
||||
},
|
||||
"green tomato": {
|
||||
"aliases": [],
|
||||
@@ -321,8 +321,8 @@
|
||||
"iceberg lettuce": {
|
||||
"aliases": [],
|
||||
"description": "",
|
||||
"name": "iceberg lettuce",
|
||||
"plural_name": "iceberg lettuce"
|
||||
"name": "cayennepeber",
|
||||
"plural_name": "icebergsalat"
|
||||
},
|
||||
"mashed potato": {
|
||||
"aliases": [],
|
||||
@@ -419,8 +419,8 @@
|
||||
"corn on the cob": {
|
||||
"aliases": [],
|
||||
"description": "",
|
||||
"name": "corn on the cob",
|
||||
"plural_name": "corn on the cob"
|
||||
"name": "majskolbe",
|
||||
"plural_name": "majskolber"
|
||||
},
|
||||
"radicchio": {
|
||||
"aliases": [],
|
||||
@@ -450,7 +450,7 @@
|
||||
"aliases": [],
|
||||
"description": "",
|
||||
"name": "pluksalat",
|
||||
"plural_name": "leaf lettuce"
|
||||
"plural_name": "bladsalat"
|
||||
},
|
||||
"pepperoncini": {
|
||||
"aliases": [],
|
||||
@@ -487,8 +487,8 @@
|
||||
"corn husk"
|
||||
],
|
||||
"description": "",
|
||||
"name": "maize",
|
||||
"plural_name": "maize"
|
||||
"name": "majs",
|
||||
"plural_name": "majs"
|
||||
},
|
||||
"collard greens": {
|
||||
"aliases": [],
|
||||
@@ -560,7 +560,7 @@
|
||||
"aliases": [],
|
||||
"description": "",
|
||||
"name": "glaskål",
|
||||
"plural_name": "kohlrabi"
|
||||
"plural_name": "kålrabi"
|
||||
},
|
||||
"fresno chile": {
|
||||
"aliases": [],
|
||||
@@ -579,8 +579,8 @@
|
||||
"curly endive"
|
||||
],
|
||||
"description": "",
|
||||
"name": "Frisée",
|
||||
"plural_name": "Frisées"
|
||||
"name": "friséesalat",
|
||||
"plural_name": "friséesalat"
|
||||
},
|
||||
"anaheim pepper": {
|
||||
"aliases": [],
|
||||
@@ -591,8 +591,8 @@
|
||||
"cress": {
|
||||
"aliases": [],
|
||||
"description": "",
|
||||
"name": "cress",
|
||||
"plural_name": "cress"
|
||||
"name": "karse",
|
||||
"plural_name": "karse"
|
||||
},
|
||||
"broccoli slaw": {
|
||||
"aliases": [],
|
||||
@@ -622,7 +622,7 @@
|
||||
"aliases": [],
|
||||
"description": "",
|
||||
"name": "lucerne",
|
||||
"plural_name": "alfalfa"
|
||||
"plural_name": "lucerne"
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -974,13 +974,13 @@
|
||||
"aliases": [],
|
||||
"description": "",
|
||||
"name": "pomelo",
|
||||
"plural_name": "pomelos"
|
||||
"plural_name": "pomeloer"
|
||||
},
|
||||
"chestnut purée": {
|
||||
"aliases": [],
|
||||
"description": "",
|
||||
"name": "chestnut purée",
|
||||
"plural_name": "chestnut purée"
|
||||
"name": "kastanjepuré",
|
||||
"plural_name": "kastanjepuré"
|
||||
},
|
||||
"prickly pear": {
|
||||
"aliases": [],
|
||||
@@ -1232,7 +1232,7 @@
|
||||
"aliases": [],
|
||||
"description": "",
|
||||
"name": "hornagurk / kaktusagurk",
|
||||
"plural_name": "kiwanos"
|
||||
"plural_name": "kiwanoer"
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -1295,7 +1295,7 @@
|
||||
"black fungu": {
|
||||
"aliases": [],
|
||||
"description": "",
|
||||
"name": "black fungus",
|
||||
"name": "judasøre",
|
||||
"plural_name": "sorte træører"
|
||||
},
|
||||
"black truffle": {
|
||||
@@ -1385,7 +1385,7 @@
|
||||
"boletu": {
|
||||
"aliases": [],
|
||||
"description": "",
|
||||
"name": "boletus",
|
||||
"name": "rørhat",
|
||||
"plural_name": "rørhatte"
|
||||
},
|
||||
"huitlacoche": {
|
||||
@@ -1403,8 +1403,8 @@
|
||||
"nameko": {
|
||||
"aliases": [],
|
||||
"description": "",
|
||||
"name": "namekosvamp",
|
||||
"plural_name": "namekos"
|
||||
"name": "nameko-skælhat",
|
||||
"plural_name": "nameko-skælhatte"
|
||||
},
|
||||
"djon djon mushroom": {
|
||||
"aliases": [],
|
||||
@@ -1427,7 +1427,7 @@
|
||||
"honey fungu": {
|
||||
"aliases": [],
|
||||
"description": "",
|
||||
"name": "honey fungus",
|
||||
"name": "honningsvamp",
|
||||
"plural_name": "honningsvampe"
|
||||
},
|
||||
"caesar's mushroom": {
|
||||
@@ -2003,22 +2003,22 @@
|
||||
"parmesan cheese": {
|
||||
"aliases": [],
|
||||
"description": "",
|
||||
"name": "parmesan cheese",
|
||||
"plural_name": "parmesan cheese"
|
||||
"name": "parmesanost",
|
||||
"plural_name": "parmesanost"
|
||||
},
|
||||
"cheddar cheese": {
|
||||
"aliases": [
|
||||
"cheddar cheese"
|
||||
"cheddar"
|
||||
],
|
||||
"description": "",
|
||||
"name": "cheddar cheese",
|
||||
"plural_name": "cheddar cheese"
|
||||
"name": "cheddar",
|
||||
"plural_name": "cheddar"
|
||||
},
|
||||
"cream cheese": {
|
||||
"aliases": [],
|
||||
"description": "",
|
||||
"name": "cream cheese",
|
||||
"plural_name": "cream cheese"
|
||||
"name": "flødeost",
|
||||
"plural_name": "flødeost"
|
||||
},
|
||||
"sharp cheddar cheese": {
|
||||
"aliases": [
|
||||
@@ -2031,26 +2031,26 @@
|
||||
"cheese": {
|
||||
"aliases": [],
|
||||
"description": "",
|
||||
"name": "cheese",
|
||||
"plural_name": "cheese"
|
||||
"name": "ost",
|
||||
"plural_name": "ost"
|
||||
},
|
||||
"mozzarella cheese": {
|
||||
"aliases": [],
|
||||
"description": "",
|
||||
"name": "mozzarella cheese",
|
||||
"plural_name": "mozzarella cheese"
|
||||
"name": "mozzarella",
|
||||
"plural_name": "mozzarella"
|
||||
},
|
||||
"feta cheese": {
|
||||
"aliases": [],
|
||||
"description": "",
|
||||
"name": "feta cheese",
|
||||
"plural_name": "feta cheese"
|
||||
"name": "fetaost",
|
||||
"plural_name": "fetaost"
|
||||
},
|
||||
"ricotta cheese": {
|
||||
"aliases": [],
|
||||
"description": "",
|
||||
"name": "ricotta cheese",
|
||||
"plural_name": "ricotta cheese"
|
||||
"name": "ricotta",
|
||||
"plural_name": "ricotta"
|
||||
},
|
||||
"cheddar-jack cheese": {
|
||||
"aliases": [],
|
||||
@@ -2067,20 +2067,20 @@
|
||||
"blue cheese": {
|
||||
"aliases": [],
|
||||
"description": "",
|
||||
"name": "blue cheese",
|
||||
"plural_name": "blue cheese"
|
||||
"name": "blåskimmelost",
|
||||
"plural_name": "blåskimmelost"
|
||||
},
|
||||
"goat cheese": {
|
||||
"aliases": [],
|
||||
"description": "",
|
||||
"name": "goat cheese",
|
||||
"plural_name": "goat cheese"
|
||||
"name": "gedeost",
|
||||
"plural_name": "gedeost"
|
||||
},
|
||||
"fresh mozzarella cheese": {
|
||||
"aliases": [],
|
||||
"description": "",
|
||||
"name": "fresh mozzarella cheese",
|
||||
"plural_name": "fresh mozzarella cheese"
|
||||
"name": "frisk mozzarella",
|
||||
"plural_name": "frisk mozzarella"
|
||||
},
|
||||
"swis cheese": {
|
||||
"aliases": [],
|
||||
@@ -2091,26 +2091,26 @@
|
||||
"pecorino cheese": {
|
||||
"aliases": [],
|
||||
"description": "",
|
||||
"name": "pecorino cheese",
|
||||
"plural_name": "pecorino cheese"
|
||||
"name": "pecorino",
|
||||
"plural_name": "pecorino"
|
||||
},
|
||||
"gruyere cheese": {
|
||||
"aliases": [],
|
||||
"description": "",
|
||||
"name": "gruyere cheese",
|
||||
"plural_name": "gruyere cheese"
|
||||
"name": "gryuère",
|
||||
"plural_name": "gryuère"
|
||||
},
|
||||
"mascarpone cheese": {
|
||||
"aliases": [],
|
||||
"description": "",
|
||||
"name": "mascarpone cheese",
|
||||
"plural_name": "mascarpone cheese"
|
||||
"name": "mascarpone",
|
||||
"plural_name": "mascarpone"
|
||||
},
|
||||
"cottage cheese": {
|
||||
"aliases": [],
|
||||
"description": "",
|
||||
"name": "cottage cheese",
|
||||
"plural_name": "cottage cheese"
|
||||
"name": "hytteost",
|
||||
"plural_name": "hytteost"
|
||||
},
|
||||
"american cheese": {
|
||||
"aliases": [],
|
||||
@@ -2139,25 +2139,25 @@
|
||||
"brie cheese": {
|
||||
"aliases": [],
|
||||
"description": "",
|
||||
"name": "brie cheese",
|
||||
"plural_name": "brie cheese"
|
||||
"name": "brie",
|
||||
"plural_name": "brie"
|
||||
},
|
||||
"paneer cheese": {
|
||||
"aliases": [],
|
||||
"description": "",
|
||||
"name": "paneer cheese",
|
||||
"plural_name": "paneer cheese"
|
||||
"name": "paneer",
|
||||
"plural_name": "paneer"
|
||||
},
|
||||
"fontina cheese": {
|
||||
"aliases": [],
|
||||
"description": "",
|
||||
"name": "fontina cheese",
|
||||
"plural_name": "fontina cheese"
|
||||
"plural_name": "queso fresco"
|
||||
},
|
||||
"queso fresco cheese": {
|
||||
"aliases": [],
|
||||
"description": "",
|
||||
"name": "queso fresco cheese",
|
||||
"name": "queso fresco",
|
||||
"plural_name": "queso fresco cheese"
|
||||
},
|
||||
"quark cheese": {
|
||||
@@ -2187,26 +2187,26 @@
|
||||
"smoked cheese": {
|
||||
"aliases": [],
|
||||
"description": "",
|
||||
"name": "smoked cheese",
|
||||
"plural_name": "smoked cheese"
|
||||
"name": "rygeost",
|
||||
"plural_name": "rygeost"
|
||||
},
|
||||
"halloumi cheese": {
|
||||
"aliases": [],
|
||||
"description": "",
|
||||
"name": "halloumi cheese",
|
||||
"plural_name": "halloumi cheese"
|
||||
"name": "halloumi",
|
||||
"plural_name": "halloumi"
|
||||
},
|
||||
"chevre cheese": {
|
||||
"aliases": [],
|
||||
"description": "",
|
||||
"name": "chevre cheese",
|
||||
"plural_name": "chevre cheese"
|
||||
"name": "gedeost",
|
||||
"plural_name": "gedeost"
|
||||
},
|
||||
"manchego cheese": {
|
||||
"aliases": [],
|
||||
"description": "",
|
||||
"name": "manchego cheese",
|
||||
"plural_name": "manchego cheese"
|
||||
"name": "manchego",
|
||||
"plural_name": "manchego"
|
||||
},
|
||||
"italian cheese blend": {
|
||||
"aliases": [],
|
||||
@@ -2217,8 +2217,8 @@
|
||||
"neufchatel cheese": {
|
||||
"aliases": [],
|
||||
"description": "",
|
||||
"name": "neufchatel cheese",
|
||||
"plural_name": "neufchatel cheese"
|
||||
"name": "neufchatel",
|
||||
"plural_name": "neufchatel"
|
||||
},
|
||||
"herb cream cheese": {
|
||||
"aliases": [],
|
||||
@@ -2229,14 +2229,14 @@
|
||||
"burrata cheese": {
|
||||
"aliases": [],
|
||||
"description": "",
|
||||
"name": "burrata cheese",
|
||||
"plural_name": "burrata cheese"
|
||||
"name": "burrata",
|
||||
"plural_name": "burrata"
|
||||
},
|
||||
"havarti cheese": {
|
||||
"aliases": [],
|
||||
"description": "",
|
||||
"name": "havarti cheese",
|
||||
"plural_name": "havarti cheese"
|
||||
"name": "havarti",
|
||||
"plural_name": "havarti"
|
||||
},
|
||||
"colby cheese": {
|
||||
"aliases": [],
|
||||
@@ -2247,14 +2247,14 @@
|
||||
"grana-padano cheese": {
|
||||
"aliases": [],
|
||||
"description": "",
|
||||
"name": "grana-padano cheese",
|
||||
"plural_name": "grana-padano cheese"
|
||||
"name": "grana padano",
|
||||
"plural_name": "grana padano"
|
||||
},
|
||||
"muenster cheese": {
|
||||
"aliases": [],
|
||||
"description": "",
|
||||
"name": "muenster cheese",
|
||||
"plural_name": "muenster cheese"
|
||||
"name": "münster",
|
||||
"plural_name": "münster"
|
||||
},
|
||||
"string cheese": {
|
||||
"aliases": [],
|
||||
@@ -2265,8 +2265,8 @@
|
||||
"camembert cheese": {
|
||||
"aliases": [],
|
||||
"description": "",
|
||||
"name": "camembert cheese",
|
||||
"plural_name": "camembert cheese"
|
||||
"name": "camembert",
|
||||
"plural_name": "camembert"
|
||||
},
|
||||
"soft cheese": {
|
||||
"aliases": [],
|
||||
@@ -2277,14 +2277,14 @@
|
||||
"stilton cheese": {
|
||||
"aliases": [],
|
||||
"description": "",
|
||||
"name": "stilton cheese",
|
||||
"plural_name": "stilton cheese"
|
||||
"name": "stilton",
|
||||
"plural_name": "stilton"
|
||||
},
|
||||
"raclette cheese": {
|
||||
"aliases": [],
|
||||
"description": "",
|
||||
"name": "raclette cheese",
|
||||
"plural_name": "raclette cheese"
|
||||
"name": "raclette",
|
||||
"plural_name": "raclette"
|
||||
},
|
||||
"colby-jack cheese": {
|
||||
"aliases": [],
|
||||
@@ -2295,14 +2295,14 @@
|
||||
"jarlsberg cheese": {
|
||||
"aliases": [],
|
||||
"description": "",
|
||||
"name": "jarlsberg cheese",
|
||||
"plural_name": "jarlsberg cheese"
|
||||
"name": "jarlsberg-ost",
|
||||
"plural_name": "jarlsberg-ost"
|
||||
},
|
||||
"taleggio cheese": {
|
||||
"aliases": [],
|
||||
"description": "",
|
||||
"name": "taleggio cheese",
|
||||
"plural_name": "taleggio cheese"
|
||||
"name": "taleggio",
|
||||
"plural_name": "taleggio"
|
||||
},
|
||||
"oaxaca cheese": {
|
||||
"aliases": [],
|
||||
@@ -2313,14 +2313,14 @@
|
||||
"labneh cheese": {
|
||||
"aliases": [],
|
||||
"description": "",
|
||||
"name": "labneh cheese",
|
||||
"plural_name": "labneh cheese"
|
||||
"name": "labneh",
|
||||
"plural_name": "labneh"
|
||||
},
|
||||
"edam cheese": {
|
||||
"aliases": [],
|
||||
"description": "",
|
||||
"name": "edam cheese",
|
||||
"plural_name": "edam cheese"
|
||||
"name": "edamer",
|
||||
"plural_name": "edamer"
|
||||
},
|
||||
"creamy cheese wedge": {
|
||||
"aliases": [],
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
"aliases": [],
|
||||
"description": "",
|
||||
"name": "Knoblauch",
|
||||
"plural_name": "garlic"
|
||||
"plural_name": "Knoblauch"
|
||||
},
|
||||
"onion": {
|
||||
"aliases": [],
|
||||
@@ -59,13 +59,13 @@
|
||||
"aliases": [],
|
||||
"description": "",
|
||||
"name": "Sellerie",
|
||||
"plural_name": "celery"
|
||||
"plural_name": "Sellerie"
|
||||
},
|
||||
"jalapeño": {
|
||||
"aliases": [],
|
||||
"description": "",
|
||||
"name": "jalapeño",
|
||||
"plural_name": "jalapeños"
|
||||
"name": "jalapeño",
|
||||
"plural_name": "jalapeños"
|
||||
},
|
||||
"avocado": {
|
||||
"aliases": [],
|
||||
@@ -95,13 +95,13 @@
|
||||
"aliases": [],
|
||||
"description": "",
|
||||
"name": "Spinat",
|
||||
"plural_name": "spinach"
|
||||
"plural_name": "Spinat"
|
||||
},
|
||||
"sweet corn": {
|
||||
"aliases": [],
|
||||
"description": "",
|
||||
"name": "Zuckermais",
|
||||
"plural_name": "sweet corn"
|
||||
"plural_name": "Mais"
|
||||
},
|
||||
"chile pepper": {
|
||||
"aliases": [
|
||||
@@ -121,19 +121,19 @@
|
||||
"aliases": [],
|
||||
"description": "",
|
||||
"name": "Brokkoli",
|
||||
"plural_name": "broccoli"
|
||||
"plural_name": "Brokkoli"
|
||||
},
|
||||
"heart of palm": {
|
||||
"aliases": [],
|
||||
"description": "",
|
||||
"name": "Palmherz",
|
||||
"plural_name": "heart of palm"
|
||||
"plural_name": "Palmherzen"
|
||||
},
|
||||
"baby greens": {
|
||||
"aliases": [],
|
||||
"description": "",
|
||||
"name": "baby greens",
|
||||
"plural_name": "baby greens"
|
||||
"name": "junges Blattgemüse",
|
||||
"plural_name": "junges Blattgemüse"
|
||||
},
|
||||
"pumpkin": {
|
||||
"aliases": [],
|
||||
@@ -145,7 +145,7 @@
|
||||
"aliases": [],
|
||||
"description": "",
|
||||
"name": "Blumenkohl",
|
||||
"plural_name": "cauliflower"
|
||||
"plural_name": "Blumenkohl"
|
||||
},
|
||||
"cabbage": {
|
||||
"aliases": [],
|
||||
@@ -156,20 +156,20 @@
|
||||
"asparagus": {
|
||||
"aliases": [],
|
||||
"description": "",
|
||||
"name": "asparagus",
|
||||
"plural_name": "asparagus"
|
||||
"name": "Spargel",
|
||||
"plural_name": "Spargel"
|
||||
},
|
||||
"kale": {
|
||||
"aliases": [],
|
||||
"description": "",
|
||||
"name": "Grünkohl",
|
||||
"plural_name": "kale"
|
||||
"plural_name": "Grünkohl"
|
||||
},
|
||||
"arugula": {
|
||||
"aliases": [],
|
||||
"description": "",
|
||||
"name": "Rucola",
|
||||
"plural_name": "arugula"
|
||||
"plural_name": "Rucola"
|
||||
},
|
||||
"leek": {
|
||||
"aliases": [],
|
||||
@@ -187,7 +187,7 @@
|
||||
"aliases": [],
|
||||
"description": "",
|
||||
"name": "Salat",
|
||||
"plural_name": "lettuce"
|
||||
"plural_name": "Salat"
|
||||
},
|
||||
"butternut squash": {
|
||||
"aliases": [],
|
||||
@@ -198,8 +198,8 @@
|
||||
"romaine lettuce": {
|
||||
"aliases": [],
|
||||
"description": "",
|
||||
"name": "romaine lettuce",
|
||||
"plural_name": "romaine lettuce"
|
||||
"name": "Romanasalat",
|
||||
"plural_name": "Romanasalat"
|
||||
},
|
||||
"beetroot": {
|
||||
"aliases": [],
|
||||
@@ -217,7 +217,7 @@
|
||||
"aliases": [],
|
||||
"description": "",
|
||||
"name": "Fenchel",
|
||||
"plural_name": "fennel"
|
||||
"plural_name": "Fenchel"
|
||||
},
|
||||
"sun dried tomato": {
|
||||
"aliases": [],
|
||||
@@ -261,8 +261,8 @@
|
||||
"mixed greens": {
|
||||
"aliases": [],
|
||||
"description": "",
|
||||
"name": "mixed greens",
|
||||
"plural_name": "mixed greens"
|
||||
"name": "Gemischte Blattgemüse",
|
||||
"plural_name": "Gemischte Blattgemüse"
|
||||
},
|
||||
"parsnip": {
|
||||
"aliases": [],
|
||||
@@ -279,8 +279,8 @@
|
||||
"mixed vegetables": {
|
||||
"aliases": [],
|
||||
"description": "",
|
||||
"name": "mixed vegetables",
|
||||
"plural_name": "mixed vegetables"
|
||||
"name": "Gemischtes Gemüse",
|
||||
"plural_name": "Gemischtes Gemüse"
|
||||
},
|
||||
"poblano pepper": {
|
||||
"aliases": [],
|
||||
@@ -304,7 +304,7 @@
|
||||
"aliases": [],
|
||||
"description": "",
|
||||
"name": "Cayennepfeffer",
|
||||
"plural_name": "cayenne pepper"
|
||||
"plural_name": "Cayennepfeffer"
|
||||
},
|
||||
"green tomato": {
|
||||
"aliases": [],
|
||||
@@ -321,8 +321,8 @@
|
||||
"iceberg lettuce": {
|
||||
"aliases": [],
|
||||
"description": "",
|
||||
"name": "iceberg lettuce",
|
||||
"plural_name": "iceberg lettuce"
|
||||
"name": "Eisbergsalat",
|
||||
"plural_name": "Eisbergsalat"
|
||||
},
|
||||
"mashed potato": {
|
||||
"aliases": [],
|
||||
@@ -340,13 +340,13 @@
|
||||
"aliases": [],
|
||||
"description": "",
|
||||
"name": "Mangold",
|
||||
"plural_name": "chard"
|
||||
"plural_name": "Mangold"
|
||||
},
|
||||
"pimiento": {
|
||||
"aliases": [],
|
||||
"description": "",
|
||||
"name": "Piment",
|
||||
"plural_name": "pimientos"
|
||||
"plural_name": "Pimientos"
|
||||
},
|
||||
"spaghetti squash": {
|
||||
"aliases": [],
|
||||
@@ -358,7 +358,7 @@
|
||||
"aliases": [],
|
||||
"description": "",
|
||||
"name": "Kopfsalat",
|
||||
"plural_name": "butter lettuce"
|
||||
"plural_name": "Buttersalat"
|
||||
},
|
||||
"hash brown": {
|
||||
"aliases": [],
|
||||
@@ -419,8 +419,8 @@
|
||||
"corn on the cob": {
|
||||
"aliases": [],
|
||||
"description": "",
|
||||
"name": "corn on the cob",
|
||||
"plural_name": "corn on the cob"
|
||||
"name": "Maiskolben",
|
||||
"plural_name": "Maiskolben"
|
||||
},
|
||||
"radicchio": {
|
||||
"aliases": [],
|
||||
@@ -438,7 +438,7 @@
|
||||
"aliases": [],
|
||||
"description": "",
|
||||
"name": "Stangenbrokkoli",
|
||||
"plural_name": "tenderstem broccoli"
|
||||
"plural_name": "Stangenbrokkoli"
|
||||
},
|
||||
"plantain": {
|
||||
"aliases": [],
|
||||
@@ -450,7 +450,7 @@
|
||||
"aliases": [],
|
||||
"description": "",
|
||||
"name": "Schnittsalat",
|
||||
"plural_name": "leaf lettuce"
|
||||
"plural_name": "Blattsalat"
|
||||
},
|
||||
"pepperoncini": {
|
||||
"aliases": [],
|
||||
@@ -462,7 +462,7 @@
|
||||
"aliases": [],
|
||||
"description": "",
|
||||
"name": "Baby Pak Choi",
|
||||
"plural_name": "baby bok choy"
|
||||
"plural_name": "Baby Pak Choi"
|
||||
},
|
||||
"jicama": {
|
||||
"aliases": [],
|
||||
@@ -484,17 +484,17 @@
|
||||
},
|
||||
"maize": {
|
||||
"aliases": [
|
||||
"corn husk"
|
||||
"Maiskolben-schale"
|
||||
],
|
||||
"description": "",
|
||||
"name": "maize",
|
||||
"plural_name": "maize"
|
||||
"name": "Mais",
|
||||
"plural_name": "Mais"
|
||||
},
|
||||
"collard greens": {
|
||||
"aliases": [],
|
||||
"description": "",
|
||||
"name": "collard greens",
|
||||
"plural_name": "collard greens"
|
||||
"name": "Kohlgemüse",
|
||||
"plural_name": "Kohlgemüse"
|
||||
},
|
||||
"french-fried onion": {
|
||||
"aliases": [],
|
||||
@@ -547,20 +547,20 @@
|
||||
"microgreens": {
|
||||
"aliases": [],
|
||||
"description": "",
|
||||
"name": "microgreens",
|
||||
"plural_name": "microgreens"
|
||||
"name": "Keimlinge",
|
||||
"plural_name": "Keimlinge"
|
||||
},
|
||||
"boston lettuce": {
|
||||
"aliases": [],
|
||||
"description": "",
|
||||
"name": "Kopfsalat",
|
||||
"plural_name": "boston lettuce"
|
||||
"plural_name": "Boston Salat"
|
||||
},
|
||||
"kohlrabi": {
|
||||
"aliases": [],
|
||||
"description": "",
|
||||
"name": "Kohlrabi",
|
||||
"plural_name": "kohlrabi"
|
||||
"plural_name": "Kohlrabi"
|
||||
},
|
||||
"fresno chile": {
|
||||
"aliases": [],
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
"aliases": [],
|
||||
"description": "",
|
||||
"name": "σκόρδο",
|
||||
"plural_name": "garlic"
|
||||
"plural_name": "σκόρδα"
|
||||
},
|
||||
"onion": {
|
||||
"aliases": [],
|
||||
@@ -59,13 +59,13 @@
|
||||
"aliases": [],
|
||||
"description": "",
|
||||
"name": "σέλινο",
|
||||
"plural_name": "celery"
|
||||
"plural_name": "σέλινα"
|
||||
},
|
||||
"jalapeño": {
|
||||
"aliases": [],
|
||||
"description": "",
|
||||
"name": "jalapeño",
|
||||
"plural_name": "jalapeños"
|
||||
"name": "jalapeño",
|
||||
"plural_name": "jalapeños"
|
||||
},
|
||||
"avocado": {
|
||||
"aliases": [],
|
||||
@@ -95,13 +95,13 @@
|
||||
"aliases": [],
|
||||
"description": "",
|
||||
"name": "σπανάκι",
|
||||
"plural_name": "spinach"
|
||||
"plural_name": "σπανάκια"
|
||||
},
|
||||
"sweet corn": {
|
||||
"aliases": [],
|
||||
"description": "",
|
||||
"name": "γλυκό καλαμπόκι",
|
||||
"plural_name": "sweet corn"
|
||||
"plural_name": "γλυκά καλαμπόκια"
|
||||
},
|
||||
"chile pepper": {
|
||||
"aliases": [
|
||||
@@ -121,19 +121,19 @@
|
||||
"aliases": [],
|
||||
"description": "",
|
||||
"name": "μπρόκολο",
|
||||
"plural_name": "broccoli"
|
||||
"plural_name": "μπρόκολα"
|
||||
},
|
||||
"heart of palm": {
|
||||
"aliases": [],
|
||||
"description": "",
|
||||
"name": "καρδιά φοίνικα",
|
||||
"plural_name": "heart of palm"
|
||||
"plural_name": "καρδιές φοινίκων"
|
||||
},
|
||||
"baby greens": {
|
||||
"aliases": [],
|
||||
"description": "",
|
||||
"name": "baby greens",
|
||||
"plural_name": "baby greens"
|
||||
"name": "νωπά πράσινα",
|
||||
"plural_name": "νωπά πράσινα"
|
||||
},
|
||||
"pumpkin": {
|
||||
"aliases": [],
|
||||
@@ -145,7 +145,7 @@
|
||||
"aliases": [],
|
||||
"description": "",
|
||||
"name": "κουνουπίδι",
|
||||
"plural_name": "cauliflower"
|
||||
"plural_name": "κουνουπίδια"
|
||||
},
|
||||
"cabbage": {
|
||||
"aliases": [],
|
||||
@@ -156,20 +156,20 @@
|
||||
"asparagus": {
|
||||
"aliases": [],
|
||||
"description": "",
|
||||
"name": "asparagus",
|
||||
"plural_name": "asparagus"
|
||||
"name": "σπαράγγι",
|
||||
"plural_name": "σπαράγγια"
|
||||
},
|
||||
"kale": {
|
||||
"aliases": [],
|
||||
"description": "",
|
||||
"name": "λαχανίδα",
|
||||
"plural_name": "kale"
|
||||
"plural_name": "λαχανίδες"
|
||||
},
|
||||
"arugula": {
|
||||
"aliases": [],
|
||||
"description": "",
|
||||
"name": "ρόκα",
|
||||
"plural_name": "arugula"
|
||||
"plural_name": "ρόκα"
|
||||
},
|
||||
"leek": {
|
||||
"aliases": [],
|
||||
@@ -187,7 +187,7 @@
|
||||
"aliases": [],
|
||||
"description": "",
|
||||
"name": "μαρούλι",
|
||||
"plural_name": "lettuce"
|
||||
"plural_name": "μαρούλια"
|
||||
},
|
||||
"butternut squash": {
|
||||
"aliases": [],
|
||||
@@ -198,8 +198,8 @@
|
||||
"romaine lettuce": {
|
||||
"aliases": [],
|
||||
"description": "",
|
||||
"name": "romaine lettuce",
|
||||
"plural_name": "romaine lettuce"
|
||||
"name": "μαρούλι romaine",
|
||||
"plural_name": "μαρούλια romaine"
|
||||
},
|
||||
"beetroot": {
|
||||
"aliases": [],
|
||||
@@ -217,7 +217,7 @@
|
||||
"aliases": [],
|
||||
"description": "",
|
||||
"name": "μάραθος",
|
||||
"plural_name": "fennel"
|
||||
"plural_name": "μάραθο"
|
||||
},
|
||||
"sun dried tomato": {
|
||||
"aliases": [],
|
||||
@@ -261,8 +261,8 @@
|
||||
"mixed greens": {
|
||||
"aliases": [],
|
||||
"description": "",
|
||||
"name": "mixed greens",
|
||||
"plural_name": "mixed greens"
|
||||
"name": "ανάμικτη πρασινάδα",
|
||||
"plural_name": "ανάμικτη πρασινάδα"
|
||||
},
|
||||
"parsnip": {
|
||||
"aliases": [],
|
||||
@@ -279,8 +279,8 @@
|
||||
"mixed vegetables": {
|
||||
"aliases": [],
|
||||
"description": "",
|
||||
"name": "mixed vegetables",
|
||||
"plural_name": "mixed vegetables"
|
||||
"name": "μιξ λαχανικών",
|
||||
"plural_name": "μιξ λαχανικών"
|
||||
},
|
||||
"poblano pepper": {
|
||||
"aliases": [],
|
||||
@@ -304,7 +304,7 @@
|
||||
"aliases": [],
|
||||
"description": "",
|
||||
"name": "πιπεριά καγιέν",
|
||||
"plural_name": "cayenne pepper"
|
||||
"plural_name": "πιπέρι καγιέν"
|
||||
},
|
||||
"green tomato": {
|
||||
"aliases": [],
|
||||
@@ -321,8 +321,8 @@
|
||||
"iceberg lettuce": {
|
||||
"aliases": [],
|
||||
"description": "",
|
||||
"name": "iceberg lettuce",
|
||||
"plural_name": "iceberg lettuce"
|
||||
"name": "μαρούλι iceberg",
|
||||
"plural_name": "μαρούλια iceberg"
|
||||
},
|
||||
"mashed potato": {
|
||||
"aliases": [],
|
||||
@@ -340,13 +340,13 @@
|
||||
"aliases": [],
|
||||
"description": "",
|
||||
"name": "σέσκουλο",
|
||||
"plural_name": "chard"
|
||||
"plural_name": "σέσκουλο"
|
||||
},
|
||||
"pimiento": {
|
||||
"aliases": [],
|
||||
"description": "",
|
||||
"name": "πιμιέντο",
|
||||
"plural_name": "pimientos"
|
||||
"plural_name": "πιμιέντο"
|
||||
},
|
||||
"spaghetti squash": {
|
||||
"aliases": [],
|
||||
@@ -512,7 +512,7 @@
|
||||
"aliases": [],
|
||||
"description": "",
|
||||
"name": "baby corn",
|
||||
"plural_name": "baby corns"
|
||||
"plural_name": ""
|
||||
},
|
||||
"broccoli rabe": {
|
||||
"aliases": [],
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
"aliases": [],
|
||||
"description": "",
|
||||
"name": "knoflook",
|
||||
"plural_name": "garlic"
|
||||
"plural_name": "knoflook"
|
||||
},
|
||||
"onion": {
|
||||
"aliases": [],
|
||||
@@ -132,8 +132,8 @@
|
||||
"baby greens": {
|
||||
"aliases": [],
|
||||
"description": "",
|
||||
"name": "baby greens",
|
||||
"plural_name": "baby greens"
|
||||
"name": "babybladgroenten",
|
||||
"plural_name": "babybladgroenten"
|
||||
},
|
||||
"pumpkin": {
|
||||
"aliases": [],
|
||||
@@ -169,7 +169,7 @@
|
||||
"aliases": [],
|
||||
"description": "",
|
||||
"name": "rucola",
|
||||
"plural_name": "arugula"
|
||||
"plural_name": "rucola"
|
||||
},
|
||||
"leek": {
|
||||
"aliases": [],
|
||||
@@ -199,7 +199,7 @@
|
||||
"aliases": [],
|
||||
"description": "",
|
||||
"name": "romaine sla",
|
||||
"plural_name": "romaine lettuce"
|
||||
"plural_name": "romaine sla"
|
||||
},
|
||||
"beetroot": {
|
||||
"aliases": [],
|
||||
@@ -261,8 +261,8 @@
|
||||
"mixed greens": {
|
||||
"aliases": [],
|
||||
"description": "",
|
||||
"name": "mixed greens",
|
||||
"plural_name": "mixed greens"
|
||||
"name": "gemengde groenten",
|
||||
"plural_name": "gemengde groenten"
|
||||
},
|
||||
"parsnip": {
|
||||
"aliases": [],
|
||||
@@ -279,8 +279,8 @@
|
||||
"mixed vegetables": {
|
||||
"aliases": [],
|
||||
"description": "",
|
||||
"name": "mixed vegetables",
|
||||
"plural_name": "mixed vegetables"
|
||||
"name": "gemengde groenten",
|
||||
"plural_name": "gemengde groenten"
|
||||
},
|
||||
"poblano pepper": {
|
||||
"aliases": [],
|
||||
@@ -304,7 +304,7 @@
|
||||
"aliases": [],
|
||||
"description": "",
|
||||
"name": "cayennepeper",
|
||||
"plural_name": "cayenne pepper"
|
||||
"plural_name": "cayenne peper"
|
||||
},
|
||||
"green tomato": {
|
||||
"aliases": [],
|
||||
@@ -321,8 +321,8 @@
|
||||
"iceberg lettuce": {
|
||||
"aliases": [],
|
||||
"description": "",
|
||||
"name": "iceberg lettuce",
|
||||
"plural_name": "iceberg lettuce"
|
||||
"name": "ijsberg sla",
|
||||
"plural_name": "ijsberg sla"
|
||||
},
|
||||
"mashed potato": {
|
||||
"aliases": [],
|
||||
@@ -340,13 +340,13 @@
|
||||
"aliases": [],
|
||||
"description": "",
|
||||
"name": "snijbiet",
|
||||
"plural_name": "chard"
|
||||
"plural_name": "snijbiet"
|
||||
},
|
||||
"pimiento": {
|
||||
"aliases": [],
|
||||
"description": "",
|
||||
"name": "pimiento",
|
||||
"plural_name": "pimientos"
|
||||
"plural_name": "spaanse peper"
|
||||
},
|
||||
"spaghetti squash": {
|
||||
"aliases": [],
|
||||
@@ -358,7 +358,7 @@
|
||||
"aliases": [],
|
||||
"description": "",
|
||||
"name": "kropsla",
|
||||
"plural_name": "butter lettuce"
|
||||
"plural_name": "kropsla"
|
||||
},
|
||||
"hash brown": {
|
||||
"aliases": [],
|
||||
@@ -419,8 +419,8 @@
|
||||
"corn on the cob": {
|
||||
"aliases": [],
|
||||
"description": "",
|
||||
"name": "corn on the cob",
|
||||
"plural_name": "corn on the cob"
|
||||
"name": "maïskolf",
|
||||
"plural_name": "maïskolf"
|
||||
},
|
||||
"radicchio": {
|
||||
"aliases": [],
|
||||
@@ -438,7 +438,7 @@
|
||||
"aliases": [],
|
||||
"description": "",
|
||||
"name": "broccolini",
|
||||
"plural_name": "tenderstem broccoli"
|
||||
"plural_name": "broccolini"
|
||||
},
|
||||
"plantain": {
|
||||
"aliases": [],
|
||||
@@ -450,7 +450,7 @@
|
||||
"aliases": [],
|
||||
"description": "",
|
||||
"name": "blad sla",
|
||||
"plural_name": "leaf lettuce"
|
||||
"plural_name": "blad sla"
|
||||
},
|
||||
"pepperoncini": {
|
||||
"aliases": [],
|
||||
@@ -462,7 +462,7 @@
|
||||
"aliases": [],
|
||||
"description": "",
|
||||
"name": "baby boksoi",
|
||||
"plural_name": "baby bok choy"
|
||||
"plural_name": "mini paksoi"
|
||||
},
|
||||
"jicama": {
|
||||
"aliases": [],
|
||||
@@ -484,17 +484,17 @@
|
||||
},
|
||||
"maize": {
|
||||
"aliases": [
|
||||
"corn husk"
|
||||
"maïsschil"
|
||||
],
|
||||
"description": "",
|
||||
"name": "maize",
|
||||
"plural_name": "maize"
|
||||
"name": "maïs",
|
||||
"plural_name": "maïs"
|
||||
},
|
||||
"collard greens": {
|
||||
"aliases": [],
|
||||
"description": "",
|
||||
"name": "collard greens",
|
||||
"plural_name": "collard greens"
|
||||
"name": "boerenkool",
|
||||
"plural_name": "boerenkool"
|
||||
},
|
||||
"french-fried onion": {
|
||||
"aliases": [],
|
||||
@@ -547,20 +547,20 @@
|
||||
"microgreens": {
|
||||
"aliases": [],
|
||||
"description": "",
|
||||
"name": "microgreens",
|
||||
"plural_name": "microgreens"
|
||||
"name": "kiemgroenten",
|
||||
"plural_name": "kiemgroenten"
|
||||
},
|
||||
"boston lettuce": {
|
||||
"aliases": [],
|
||||
"description": "",
|
||||
"name": "botersla",
|
||||
"plural_name": "boston lettuce"
|
||||
"plural_name": "botersla"
|
||||
},
|
||||
"kohlrabi": {
|
||||
"aliases": [],
|
||||
"description": "",
|
||||
"name": "koolrabi",
|
||||
"plural_name": "kohlrabi"
|
||||
"plural_name": "koolrabi"
|
||||
},
|
||||
"fresno chile": {
|
||||
"aliases": [],
|
||||
@@ -576,11 +576,11 @@
|
||||
},
|
||||
"Frisée": {
|
||||
"aliases": [
|
||||
"curly endive"
|
||||
"krulandijvie"
|
||||
],
|
||||
"description": "",
|
||||
"name": "Frisée",
|
||||
"plural_name": "Frisées"
|
||||
"name": "Krulandijvie",
|
||||
"plural_name": "Krulandijvie"
|
||||
},
|
||||
"anaheim pepper": {
|
||||
"aliases": [],
|
||||
@@ -591,14 +591,14 @@
|
||||
"cress": {
|
||||
"aliases": [],
|
||||
"description": "",
|
||||
"name": "cress",
|
||||
"plural_name": "cress"
|
||||
"name": "tuinkers",
|
||||
"plural_name": "tuinkers"
|
||||
},
|
||||
"broccoli slaw": {
|
||||
"aliases": [],
|
||||
"description": "",
|
||||
"name": "broccoli salade",
|
||||
"plural_name": "broccoli slaw"
|
||||
"plural_name": "broccoli salade"
|
||||
},
|
||||
"arbol chile pepper": {
|
||||
"aliases": [],
|
||||
@@ -974,13 +974,13 @@
|
||||
"aliases": [],
|
||||
"description": "",
|
||||
"name": "pompelmoes",
|
||||
"plural_name": "pomelos"
|
||||
"plural_name": "pomelo's"
|
||||
},
|
||||
"chestnut purée": {
|
||||
"aliases": [],
|
||||
"description": "",
|
||||
"name": "chestnut purée",
|
||||
"plural_name": "chestnut purée"
|
||||
"name": "kastanje puree",
|
||||
"plural_name": "kastanje puree"
|
||||
},
|
||||
"prickly pear": {
|
||||
"aliases": [],
|
||||
@@ -1028,7 +1028,7 @@
|
||||
"aliases": [],
|
||||
"description": "",
|
||||
"name": "tangelo",
|
||||
"plural_name": "tangelos"
|
||||
"plural_name": "tangelo"
|
||||
},
|
||||
"dried lime": {
|
||||
"aliases": [],
|
||||
@@ -1100,7 +1100,7 @@
|
||||
"aliases": [],
|
||||
"description": "",
|
||||
"name": "tamarillo",
|
||||
"plural_name": "tamarillos"
|
||||
"plural_name": "boomtomaat"
|
||||
},
|
||||
"ice-apple": {
|
||||
"aliases": [],
|
||||
@@ -1232,7 +1232,7 @@
|
||||
"aliases": [],
|
||||
"description": "",
|
||||
"name": "hoornmeloen",
|
||||
"plural_name": "kiwanos"
|
||||
"plural_name": "hoornmeloenen"
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -1295,7 +1295,7 @@
|
||||
"black fungu": {
|
||||
"aliases": [],
|
||||
"description": "",
|
||||
"name": "black fungus",
|
||||
"name": "boomoren",
|
||||
"plural_name": "boomoren"
|
||||
},
|
||||
"black truffle": {
|
||||
@@ -1361,7 +1361,7 @@
|
||||
"white fungu": {
|
||||
"aliases": [],
|
||||
"description": "",
|
||||
"name": "white fungus",
|
||||
"name": "witte zwammen",
|
||||
"plural_name": "witte zwammen"
|
||||
},
|
||||
"pioppini": {
|
||||
@@ -1373,7 +1373,7 @@
|
||||
"snow fungu": {
|
||||
"aliases": [],
|
||||
"description": "",
|
||||
"name": "snow fungus",
|
||||
"name": "sneeuwzwammen",
|
||||
"plural_name": "sneeuwzwammen"
|
||||
},
|
||||
"white beech mushroom": {
|
||||
@@ -1385,7 +1385,7 @@
|
||||
"boletu": {
|
||||
"aliases": [],
|
||||
"description": "",
|
||||
"name": "boletus",
|
||||
"name": "boleten",
|
||||
"plural_name": "boleten"
|
||||
},
|
||||
"huitlacoche": {
|
||||
@@ -1404,7 +1404,7 @@
|
||||
"aliases": [],
|
||||
"description": "",
|
||||
"name": "bundelzwam",
|
||||
"plural_name": "namekos"
|
||||
"plural_name": "bundelzwammen"
|
||||
},
|
||||
"djon djon mushroom": {
|
||||
"aliases": [],
|
||||
@@ -1427,7 +1427,7 @@
|
||||
"honey fungu": {
|
||||
"aliases": [],
|
||||
"description": "",
|
||||
"name": "honey fungus",
|
||||
"name": "honingzwammen",
|
||||
"plural_name": "honingzwammen"
|
||||
},
|
||||
"caesar's mushroom": {
|
||||
@@ -2003,179 +2003,179 @@
|
||||
"parmesan cheese": {
|
||||
"aliases": [],
|
||||
"description": "",
|
||||
"name": "parmesan cheese",
|
||||
"plural_name": "parmesan cheese"
|
||||
"name": "parmesaanse kaas",
|
||||
"plural_name": "parmezaanse kaas"
|
||||
},
|
||||
"cheddar cheese": {
|
||||
"aliases": [
|
||||
"cheddar cheese"
|
||||
"cheddar kaas"
|
||||
],
|
||||
"description": "",
|
||||
"name": "cheddar cheese",
|
||||
"plural_name": "cheddar cheese"
|
||||
"name": "cheddar kaas",
|
||||
"plural_name": "cheddar kaas"
|
||||
},
|
||||
"cream cheese": {
|
||||
"aliases": [],
|
||||
"description": "",
|
||||
"name": "cream cheese",
|
||||
"plural_name": "cream cheese"
|
||||
"name": "roomkaas",
|
||||
"plural_name": "roomkaas"
|
||||
},
|
||||
"sharp cheddar cheese": {
|
||||
"aliases": [
|
||||
"sharp cheddar"
|
||||
"pittige cheddar"
|
||||
],
|
||||
"description": "",
|
||||
"name": "sharp cheddar cheese",
|
||||
"plural_name": "sharp cheddar cheese"
|
||||
"name": "pittige cheddar kaas",
|
||||
"plural_name": "pittige cheddar kaas"
|
||||
},
|
||||
"cheese": {
|
||||
"aliases": [],
|
||||
"description": "",
|
||||
"name": "cheese",
|
||||
"plural_name": "cheese"
|
||||
"name": "kaas",
|
||||
"plural_name": "kaas"
|
||||
},
|
||||
"mozzarella cheese": {
|
||||
"aliases": [],
|
||||
"description": "",
|
||||
"name": "mozzarella cheese",
|
||||
"plural_name": "mozzarella cheese"
|
||||
"name": "mozzarella kaas",
|
||||
"plural_name": "mozzarella kaas"
|
||||
},
|
||||
"feta cheese": {
|
||||
"aliases": [],
|
||||
"description": "",
|
||||
"name": "feta cheese",
|
||||
"plural_name": "feta cheese"
|
||||
"name": "feta kaas",
|
||||
"plural_name": "feta kaas"
|
||||
},
|
||||
"ricotta cheese": {
|
||||
"aliases": [],
|
||||
"description": "",
|
||||
"name": "ricotta cheese",
|
||||
"plural_name": "ricotta cheese"
|
||||
"name": "ricotta kaas",
|
||||
"plural_name": "ricotta kaas"
|
||||
},
|
||||
"cheddar-jack cheese": {
|
||||
"aliases": [],
|
||||
"description": "",
|
||||
"name": "cheddar-jack cheese",
|
||||
"plural_name": "cheddar-jack cheese"
|
||||
"name": "cheddar-jack kaas",
|
||||
"plural_name": "cheddar-jack kaas"
|
||||
},
|
||||
"monterey jack cheese": {
|
||||
"aliases": [],
|
||||
"description": "",
|
||||
"name": "monterey jack cheese",
|
||||
"plural_name": "monterey jack cheese"
|
||||
"name": "monterey jack kaas",
|
||||
"plural_name": "monterey jack kaas"
|
||||
},
|
||||
"blue cheese": {
|
||||
"aliases": [],
|
||||
"description": "",
|
||||
"name": "blue cheese",
|
||||
"plural_name": "blue cheese"
|
||||
"name": "blauwschimmelkaas",
|
||||
"plural_name": "blauwschimmelkaas"
|
||||
},
|
||||
"goat cheese": {
|
||||
"aliases": [],
|
||||
"description": "",
|
||||
"name": "goat cheese",
|
||||
"plural_name": "goat cheese"
|
||||
"name": "geitenkaas",
|
||||
"plural_name": "geitenkaas"
|
||||
},
|
||||
"fresh mozzarella cheese": {
|
||||
"aliases": [],
|
||||
"description": "",
|
||||
"name": "fresh mozzarella cheese",
|
||||
"plural_name": "fresh mozzarella cheese"
|
||||
"name": "verse mozzarella kaas",
|
||||
"plural_name": "verse mozzarella kaas"
|
||||
},
|
||||
"swis cheese": {
|
||||
"aliases": [],
|
||||
"description": "",
|
||||
"name": "swis cheese",
|
||||
"plural_name": "swis cheese"
|
||||
"name": "Zwitserse kaas",
|
||||
"plural_name": "Zwitserse kaas"
|
||||
},
|
||||
"pecorino cheese": {
|
||||
"aliases": [],
|
||||
"description": "",
|
||||
"name": "pecorino cheese",
|
||||
"plural_name": "pecorino cheese"
|
||||
"name": "pecorino kaas",
|
||||
"plural_name": "pecorino kaas"
|
||||
},
|
||||
"gruyere cheese": {
|
||||
"aliases": [],
|
||||
"description": "",
|
||||
"name": "gruyere cheese",
|
||||
"plural_name": "gruyere cheese"
|
||||
"name": "gruyère kaas",
|
||||
"plural_name": "gruyère kaas"
|
||||
},
|
||||
"mascarpone cheese": {
|
||||
"aliases": [],
|
||||
"description": "",
|
||||
"name": "mascarpone cheese",
|
||||
"plural_name": "mascarpone cheese"
|
||||
"name": "mascarpone kaas",
|
||||
"plural_name": "mascarpone kaas"
|
||||
},
|
||||
"cottage cheese": {
|
||||
"aliases": [],
|
||||
"description": "",
|
||||
"name": "cottage cheese",
|
||||
"plural_name": "cottage cheese"
|
||||
"name": "kwark",
|
||||
"plural_name": "kwark"
|
||||
},
|
||||
"american cheese": {
|
||||
"aliases": [],
|
||||
"description": "",
|
||||
"name": "american cheese",
|
||||
"plural_name": "american cheese"
|
||||
"name": "Amerikaanse kaas",
|
||||
"plural_name": "Amerikaanse kaas"
|
||||
},
|
||||
"provolone cheese": {
|
||||
"aliases": [],
|
||||
"description": "",
|
||||
"name": "provolone cheese",
|
||||
"plural_name": "provolone cheese"
|
||||
"name": "provolone kaas",
|
||||
"plural_name": "provolone kaas"
|
||||
},
|
||||
"mexican cheese blend": {
|
||||
"aliases": [],
|
||||
"description": "",
|
||||
"name": "mexican cheese blend",
|
||||
"plural_name": "mexican cheese blend"
|
||||
"name": "Mexicaanse kaasmix",
|
||||
"plural_name": "Mexicaanse kaasmix"
|
||||
},
|
||||
"pepper jack cheese": {
|
||||
"aliases": [],
|
||||
"description": "",
|
||||
"name": "pepper jack cheese",
|
||||
"plural_name": "pepper jack cheese"
|
||||
"name": "pepper jack kaas",
|
||||
"plural_name": "pepper jack kaas"
|
||||
},
|
||||
"brie cheese": {
|
||||
"aliases": [],
|
||||
"description": "",
|
||||
"name": "brie cheese",
|
||||
"plural_name": "brie cheese"
|
||||
"name": "brie",
|
||||
"plural_name": "brie"
|
||||
},
|
||||
"paneer cheese": {
|
||||
"aliases": [],
|
||||
"description": "",
|
||||
"name": "paneer cheese",
|
||||
"plural_name": "paneer cheese"
|
||||
"name": "paneer kaas",
|
||||
"plural_name": "paneer kaas"
|
||||
},
|
||||
"fontina cheese": {
|
||||
"aliases": [],
|
||||
"description": "",
|
||||
"name": "fontina cheese",
|
||||
"plural_name": "fontina cheese"
|
||||
"name": "fontinakaas",
|
||||
"plural_name": "fontinakaas"
|
||||
},
|
||||
"queso fresco cheese": {
|
||||
"aliases": [],
|
||||
"description": "",
|
||||
"name": "queso fresco cheese",
|
||||
"plural_name": "queso fresco cheese"
|
||||
"name": "queso fresco kaas",
|
||||
"plural_name": "queso fresco kaas"
|
||||
},
|
||||
"quark cheese": {
|
||||
"aliases": [],
|
||||
"description": "",
|
||||
"name": "quark cheese",
|
||||
"plural_name": "quark cheese"
|
||||
"name": "kwark",
|
||||
"plural_name": "kwark"
|
||||
},
|
||||
"gouda cheese": {
|
||||
"aliases": [],
|
||||
"description": "",
|
||||
"name": "gouda cheese",
|
||||
"plural_name": "gouda cheese"
|
||||
"name": "goudse kaas",
|
||||
"plural_name": "goudse kaas"
|
||||
},
|
||||
"cotija cheese": {
|
||||
"aliases": [],
|
||||
"description": "",
|
||||
"name": "cotija cheese",
|
||||
"name": "cotija kaas",
|
||||
"plural_name": "cotija cheese"
|
||||
},
|
||||
"asiago cheese": {
|
||||
@@ -2187,8 +2187,8 @@
|
||||
"smoked cheese": {
|
||||
"aliases": [],
|
||||
"description": "",
|
||||
"name": "smoked cheese",
|
||||
"plural_name": "smoked cheese"
|
||||
"name": "gerookte kaas",
|
||||
"plural_name": "gerookte kaas"
|
||||
},
|
||||
"halloumi cheese": {
|
||||
"aliases": [],
|
||||
@@ -2223,8 +2223,8 @@
|
||||
"herb cream cheese": {
|
||||
"aliases": [],
|
||||
"description": "",
|
||||
"name": "herb cream cheese",
|
||||
"plural_name": "herb cream cheese"
|
||||
"name": "kruidenroomkaas",
|
||||
"plural_name": "kruidenroomkaas"
|
||||
},
|
||||
"burrata cheese": {
|
||||
"aliases": [],
|
||||
@@ -2247,8 +2247,8 @@
|
||||
"grana-padano cheese": {
|
||||
"aliases": [],
|
||||
"description": "",
|
||||
"name": "grana-padano cheese",
|
||||
"plural_name": "grana-padano cheese"
|
||||
"name": "grana-padano",
|
||||
"plural_name": "grana-padano"
|
||||
},
|
||||
"muenster cheese": {
|
||||
"aliases": [],
|
||||
@@ -2265,14 +2265,14 @@
|
||||
"camembert cheese": {
|
||||
"aliases": [],
|
||||
"description": "",
|
||||
"name": "camembert cheese",
|
||||
"plural_name": "camembert cheese"
|
||||
"name": "camembert",
|
||||
"plural_name": "camembert"
|
||||
},
|
||||
"soft cheese": {
|
||||
"aliases": [],
|
||||
"description": "",
|
||||
"name": "soft cheese",
|
||||
"plural_name": "soft cheese"
|
||||
"name": "jonge kaas",
|
||||
"plural_name": "jonge kaas"
|
||||
},
|
||||
"stilton cheese": {
|
||||
"aliases": [],
|
||||
@@ -2319,8 +2319,8 @@
|
||||
"edam cheese": {
|
||||
"aliases": [],
|
||||
"description": "",
|
||||
"name": "edam cheese",
|
||||
"plural_name": "edam cheese"
|
||||
"name": "edammer kaas",
|
||||
"plural_name": "edammer kaas"
|
||||
},
|
||||
"creamy cheese wedge": {
|
||||
"aliases": [],
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
"aliases": [],
|
||||
"description": "",
|
||||
"name": "czosnek",
|
||||
"plural_name": "garlic"
|
||||
"plural_name": "czosnek"
|
||||
},
|
||||
"onion": {
|
||||
"aliases": [],
|
||||
@@ -59,13 +59,13 @@
|
||||
"aliases": [],
|
||||
"description": "",
|
||||
"name": "seler naciowy",
|
||||
"plural_name": "celery"
|
||||
"plural_name": "seler"
|
||||
},
|
||||
"jalapeño": {
|
||||
"aliases": [],
|
||||
"description": "",
|
||||
"name": "jalapeño",
|
||||
"plural_name": "jalapeños"
|
||||
"name": "papryczka jalapeño",
|
||||
"plural_name": "papryczki jalapeño"
|
||||
},
|
||||
"avocado": {
|
||||
"aliases": [],
|
||||
@@ -95,13 +95,13 @@
|
||||
"aliases": [],
|
||||
"description": "",
|
||||
"name": "szpinak",
|
||||
"plural_name": "spinach"
|
||||
"plural_name": "szpinak"
|
||||
},
|
||||
"sweet corn": {
|
||||
"aliases": [],
|
||||
"description": "",
|
||||
"name": "kukurydza cukrowa",
|
||||
"plural_name": "sweet corn"
|
||||
"plural_name": "kukurydza cukrowa"
|
||||
},
|
||||
"chile pepper": {
|
||||
"aliases": [
|
||||
@@ -121,19 +121,19 @@
|
||||
"aliases": [],
|
||||
"description": "",
|
||||
"name": "brokuł",
|
||||
"plural_name": "broccoli"
|
||||
"plural_name": "brokuł"
|
||||
},
|
||||
"heart of palm": {
|
||||
"aliases": [],
|
||||
"description": "",
|
||||
"name": "serce palmy",
|
||||
"plural_name": "heart of palm"
|
||||
"plural_name": "serce palmy"
|
||||
},
|
||||
"baby greens": {
|
||||
"aliases": [],
|
||||
"description": "",
|
||||
"name": "baby greens",
|
||||
"plural_name": "baby greens"
|
||||
"name": "młode liście",
|
||||
"plural_name": "młode liście"
|
||||
},
|
||||
"pumpkin": {
|
||||
"aliases": [],
|
||||
@@ -145,7 +145,7 @@
|
||||
"aliases": [],
|
||||
"description": "",
|
||||
"name": "kalafior",
|
||||
"plural_name": "cauliflower"
|
||||
"plural_name": "kalafior"
|
||||
},
|
||||
"cabbage": {
|
||||
"aliases": [],
|
||||
@@ -156,20 +156,20 @@
|
||||
"asparagus": {
|
||||
"aliases": [],
|
||||
"description": "",
|
||||
"name": "asparagus",
|
||||
"plural_name": "asparagus"
|
||||
"name": "szparagi",
|
||||
"plural_name": "szparagi"
|
||||
},
|
||||
"kale": {
|
||||
"aliases": [],
|
||||
"description": "",
|
||||
"name": "jarmuż",
|
||||
"plural_name": "kale"
|
||||
"plural_name": "jarmuż"
|
||||
},
|
||||
"arugula": {
|
||||
"aliases": [],
|
||||
"description": "",
|
||||
"name": "rukola",
|
||||
"plural_name": "arugula"
|
||||
"plural_name": "rukola"
|
||||
},
|
||||
"leek": {
|
||||
"aliases": [],
|
||||
@@ -187,7 +187,7 @@
|
||||
"aliases": [],
|
||||
"description": "",
|
||||
"name": "sałata",
|
||||
"plural_name": "lettuce"
|
||||
"plural_name": "sałata"
|
||||
},
|
||||
"butternut squash": {
|
||||
"aliases": [],
|
||||
@@ -198,8 +198,8 @@
|
||||
"romaine lettuce": {
|
||||
"aliases": [],
|
||||
"description": "",
|
||||
"name": "romaine lettuce",
|
||||
"plural_name": "romaine lettuce"
|
||||
"name": "sałata rzymska",
|
||||
"plural_name": "sałata rzymska"
|
||||
},
|
||||
"beetroot": {
|
||||
"aliases": [],
|
||||
@@ -217,7 +217,7 @@
|
||||
"aliases": [],
|
||||
"description": "",
|
||||
"name": "koper włoski",
|
||||
"plural_name": "fennel"
|
||||
"plural_name": "koper włoski"
|
||||
},
|
||||
"sun dried tomato": {
|
||||
"aliases": [],
|
||||
@@ -261,8 +261,8 @@
|
||||
"mixed greens": {
|
||||
"aliases": [],
|
||||
"description": "",
|
||||
"name": "mixed greens",
|
||||
"plural_name": "mixed greens"
|
||||
"name": "mieszanka zielenin",
|
||||
"plural_name": "mieszanka zielenin"
|
||||
},
|
||||
"parsnip": {
|
||||
"aliases": [],
|
||||
@@ -279,8 +279,8 @@
|
||||
"mixed vegetables": {
|
||||
"aliases": [],
|
||||
"description": "",
|
||||
"name": "mixed vegetables",
|
||||
"plural_name": "mixed vegetables"
|
||||
"name": "mieszanka warzywna",
|
||||
"plural_name": "mieszanki warzywne"
|
||||
},
|
||||
"poblano pepper": {
|
||||
"aliases": [],
|
||||
@@ -304,7 +304,7 @@
|
||||
"aliases": [],
|
||||
"description": "",
|
||||
"name": "pieprz cayenne",
|
||||
"plural_name": "cayenne pepper"
|
||||
"plural_name": "pieprz cayenne"
|
||||
},
|
||||
"green tomato": {
|
||||
"aliases": [],
|
||||
@@ -321,8 +321,8 @@
|
||||
"iceberg lettuce": {
|
||||
"aliases": [],
|
||||
"description": "",
|
||||
"name": "iceberg lettuce",
|
||||
"plural_name": "iceberg lettuce"
|
||||
"name": "sałata lodowa",
|
||||
"plural_name": "sałaty lodowe"
|
||||
},
|
||||
"mashed potato": {
|
||||
"aliases": [],
|
||||
@@ -340,7 +340,7 @@
|
||||
"aliases": [],
|
||||
"description": "",
|
||||
"name": "botwina",
|
||||
"plural_name": "chard"
|
||||
"plural_name": "botwina"
|
||||
},
|
||||
"pimiento": {
|
||||
"aliases": [],
|
||||
|
||||
@@ -60,6 +60,6 @@
|
||||
"name": "Alcohol"
|
||||
},
|
||||
{
|
||||
"name": "Anders"
|
||||
"name": "Overige"
|
||||
}
|
||||
]
|
||||
|
||||
@@ -139,8 +139,8 @@
|
||||
"abbreviation": ""
|
||||
},
|
||||
"sprig": {
|
||||
"name": "sprig",
|
||||
"plural_name": "sprigs",
|
||||
"name": "gałązka",
|
||||
"plural_name": "gałązki",
|
||||
"description": "",
|
||||
"abbreviation": ""
|
||||
}
|
||||
|
||||
@@ -11,7 +11,7 @@ from mealie.db.models.household.cookbook import CookBook
|
||||
from mealie.db.models.recipe import RecipeModel
|
||||
from mealie.schema._mealie import MealieModel
|
||||
from mealie.schema.response.pagination import PaginationBase
|
||||
from mealie.schema.response.query_filter import QueryFilterBuilder, QueryFilterJSON
|
||||
from mealie.services.query_filter.builder import QueryFilterBuilder, QueryFilterJSON
|
||||
|
||||
logger = get_logger()
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@ from mealie.core.root_logger import get_logger
|
||||
from mealie.db.models.recipe import RecipeModel
|
||||
from mealie.schema._mealie import MealieModel
|
||||
from mealie.schema.response.pagination import PaginationBase
|
||||
from mealie.schema.response.query_filter import QueryFilterBuilder, QueryFilterJSON
|
||||
from mealie.services.query_filter.builder import QueryFilterBuilder, QueryFilterJSON
|
||||
|
||||
logger = get_logger()
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
# This file is auto-generated by gen_schema_exports.py
|
||||
from .general import OpenAIText
|
||||
from .recipe import OpenAIRecipe, OpenAIRecipeIngredient, OpenAIRecipeInstruction, OpenAIRecipeNotes
|
||||
from .recipe_ingredient import OpenAIIngredient, OpenAIIngredients
|
||||
|
||||
@@ -9,4 +10,5 @@ __all__ = [
|
||||
"OpenAIRecipeIngredient",
|
||||
"OpenAIRecipeInstruction",
|
||||
"OpenAIRecipeNotes",
|
||||
"OpenAIText",
|
||||
]
|
||||
|
||||
@@ -7,15 +7,6 @@ from .pagination import (
|
||||
RecipeSearchQuery,
|
||||
RequestQuery,
|
||||
)
|
||||
from .query_filter import (
|
||||
LogicalOperator,
|
||||
QueryFilterBuilder,
|
||||
QueryFilterBuilderComponent,
|
||||
QueryFilterJSON,
|
||||
QueryFilterJSONPart,
|
||||
RelationalKeyword,
|
||||
RelationalOperator,
|
||||
)
|
||||
from .query_search import SearchFilter
|
||||
from .responses import ErrorResponse, FileTokenResponse, SuccessResponse
|
||||
from .validation import ValidationResponse
|
||||
@@ -25,13 +16,6 @@ __all__ = [
|
||||
"FileTokenResponse",
|
||||
"SuccessResponse",
|
||||
"SearchFilter",
|
||||
"LogicalOperator",
|
||||
"QueryFilterBuilder",
|
||||
"QueryFilterBuilderComponent",
|
||||
"QueryFilterJSON",
|
||||
"QueryFilterJSONPart",
|
||||
"RelationalKeyword",
|
||||
"RelationalOperator",
|
||||
"OrderByNullPosition",
|
||||
"OrderDirection",
|
||||
"PaginationBase",
|
||||
|
||||
0
mealie/services/query_filter/__init__.py
Normal file
0
mealie/services/query_filter/__init__.py
Normal file
@@ -2,7 +2,6 @@ from __future__ import annotations
|
||||
|
||||
import re
|
||||
from collections import deque
|
||||
from enum import Enum
|
||||
from typing import Any, cast
|
||||
from uuid import UUID
|
||||
|
||||
@@ -19,88 +18,8 @@ from mealie.db.models._model_utils.datetime import NaiveDateTime
|
||||
from mealie.db.models._model_utils.guid import GUID
|
||||
from mealie.schema._mealie.mealie_model import MealieModel
|
||||
|
||||
|
||||
class RelationalKeyword(Enum):
|
||||
IS = "IS"
|
||||
IS_NOT = "IS NOT"
|
||||
IN = "IN"
|
||||
NOT_IN = "NOT IN"
|
||||
CONTAINS_ALL = "CONTAINS ALL"
|
||||
LIKE = "LIKE"
|
||||
NOT_LIKE = "NOT LIKE"
|
||||
|
||||
@classmethod
|
||||
def parse_component(cls, component: str) -> list[str] | None:
|
||||
"""
|
||||
Try to parse a component using a relational keyword
|
||||
|
||||
If no matching keyword is found, returns None
|
||||
"""
|
||||
|
||||
# extract the attribute name from the component
|
||||
parsed_component = component.split(maxsplit=1)
|
||||
if len(parsed_component) < 2:
|
||||
return None
|
||||
|
||||
# assume the component has already filtered out the value and try to match a keyword
|
||||
# if we try to filter out the value without checking first, keywords with spaces won't parse correctly
|
||||
possible_keyword = parsed_component[1].strip().lower()
|
||||
for rel_kw in sorted([keyword.value for keyword in cls], key=len, reverse=True):
|
||||
if rel_kw.lower() != possible_keyword:
|
||||
continue
|
||||
|
||||
parsed_component[1] = rel_kw
|
||||
return parsed_component
|
||||
|
||||
# there was no match, so the component may still have the value in it
|
||||
try:
|
||||
_possible_keyword, _value = parsed_component[-1].rsplit(maxsplit=1)
|
||||
parsed_component = [parsed_component[0], _possible_keyword, _value]
|
||||
except ValueError:
|
||||
# the component has no value to filter out
|
||||
return None
|
||||
|
||||
possible_keyword = parsed_component[1].strip().lower()
|
||||
for rel_kw in sorted([keyword.value for keyword in cls], key=len, reverse=True):
|
||||
if rel_kw.lower() != possible_keyword:
|
||||
continue
|
||||
|
||||
parsed_component[1] = rel_kw
|
||||
return parsed_component
|
||||
|
||||
return None
|
||||
|
||||
|
||||
class RelationalOperator(Enum):
|
||||
EQ = "="
|
||||
NOTEQ = "<>"
|
||||
GT = ">"
|
||||
LT = "<"
|
||||
GTE = ">="
|
||||
LTE = "<="
|
||||
|
||||
@classmethod
|
||||
def parse_component(cls, component: str) -> list[str] | None:
|
||||
"""
|
||||
Try to parse a component using a relational operator
|
||||
|
||||
If no matching operator is found, returns None
|
||||
"""
|
||||
|
||||
for rel_op in sorted([operator.value for operator in cls], key=len, reverse=True):
|
||||
if rel_op not in component:
|
||||
continue
|
||||
|
||||
parsed_component = [base_component.strip() for base_component in component.split(rel_op) if base_component]
|
||||
parsed_component.insert(1, rel_op)
|
||||
return parsed_component
|
||||
|
||||
return None
|
||||
|
||||
|
||||
class LogicalOperator(Enum):
|
||||
AND = "AND"
|
||||
OR = "OR"
|
||||
from .keywords import PlaceholderKeyword, RelationalKeyword
|
||||
from .operators import LogicalOperator, RelationalOperator
|
||||
|
||||
|
||||
class QueryFilterJSONPart(MealieModel):
|
||||
@@ -161,6 +80,9 @@ class QueryFilterBuilderComponent:
|
||||
else:
|
||||
self.value = value
|
||||
|
||||
# process placeholder keywords
|
||||
self.value = PlaceholderKeyword.parse_value(self.value)
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return f"[{self.attribute_name} {self.relationship.value} {self.value}]"
|
||||
|
||||
154
mealie/services/query_filter/keywords.py
Normal file
154
mealie/services/query_filter/keywords.py
Normal file
@@ -0,0 +1,154 @@
|
||||
from datetime import datetime
|
||||
from enum import Enum
|
||||
from typing import overload
|
||||
|
||||
from dateutil.relativedelta import relativedelta
|
||||
|
||||
|
||||
class PlaceholderKeyword(Enum):
|
||||
NOW = "$NOW"
|
||||
|
||||
@classmethod
|
||||
def _parse_now(cls, value: str) -> str:
|
||||
"""
|
||||
Parses a NOW value, with optional math using an int or float.
|
||||
|
||||
Operation:
|
||||
- '+'
|
||||
- '-'
|
||||
|
||||
Unit:
|
||||
- 'y' (year)
|
||||
- 'm' (month)
|
||||
- 'd' (day)
|
||||
- 'H' (hour)
|
||||
- 'M' (minute)
|
||||
- 'S' (second)
|
||||
|
||||
Examples:
|
||||
- '$NOW'
|
||||
- '$NOW+30d'
|
||||
- '$NOW-5M'
|
||||
"""
|
||||
|
||||
if not value.startswith(cls.NOW.value):
|
||||
return value
|
||||
|
||||
now = datetime.now(tz=None) # noqa: DTZ005
|
||||
remainder = value[len(cls.NOW.value) :]
|
||||
|
||||
if remainder:
|
||||
if len(remainder) < 3:
|
||||
raise ValueError(f"Invalid remainder in NOW string ({value})")
|
||||
|
||||
op = remainder[0]
|
||||
amount_str = remainder[1:-1]
|
||||
unit = remainder[-1]
|
||||
|
||||
try:
|
||||
amount = int(amount_str)
|
||||
except Exception as e:
|
||||
raise ValueError(f"Invalid amount in NOW string ({value})") from e
|
||||
|
||||
if op == "-":
|
||||
amount = -amount
|
||||
elif op != "+":
|
||||
raise ValueError(f"Invalid operator in NOW string ({value})")
|
||||
|
||||
if unit == "y":
|
||||
delta = relativedelta(years=amount)
|
||||
elif unit == "m":
|
||||
delta = relativedelta(months=amount)
|
||||
elif unit == "d":
|
||||
delta = relativedelta(days=amount)
|
||||
elif unit == "H":
|
||||
delta = relativedelta(hours=amount)
|
||||
elif unit == "M":
|
||||
delta = relativedelta(minutes=amount)
|
||||
elif unit == "S":
|
||||
delta = relativedelta(seconds=amount)
|
||||
else:
|
||||
raise ValueError(f"Invalid time unit in NOW string ({value})")
|
||||
|
||||
dt = now + delta
|
||||
|
||||
else:
|
||||
dt = now
|
||||
|
||||
return dt.isoformat()
|
||||
|
||||
@overload
|
||||
@classmethod
|
||||
def parse_value(cls, value: str) -> str: ...
|
||||
|
||||
@overload
|
||||
@classmethod
|
||||
def parse_value(cls, value: list[str]) -> list[str]: ...
|
||||
|
||||
@overload
|
||||
@classmethod
|
||||
def parse_value(cls, value: None) -> None: ...
|
||||
|
||||
@classmethod
|
||||
def parse_value(cls, value: str | list[str] | None) -> str | list[str] | None:
|
||||
if not value:
|
||||
return value
|
||||
|
||||
if isinstance(value, list):
|
||||
return [cls.parse_value(v) for v in value]
|
||||
|
||||
if value.startswith(PlaceholderKeyword.NOW.value):
|
||||
return cls._parse_now(value)
|
||||
|
||||
return value
|
||||
|
||||
|
||||
class RelationalKeyword(Enum):
|
||||
IS = "IS"
|
||||
IS_NOT = "IS NOT"
|
||||
IN = "IN"
|
||||
NOT_IN = "NOT IN"
|
||||
CONTAINS_ALL = "CONTAINS ALL"
|
||||
LIKE = "LIKE"
|
||||
NOT_LIKE = "NOT LIKE"
|
||||
|
||||
@classmethod
|
||||
def parse_component(cls, component: str) -> list[str] | None:
|
||||
"""
|
||||
Try to parse a component using a relational keyword
|
||||
|
||||
If no matching keyword is found, returns None
|
||||
"""
|
||||
|
||||
# extract the attribute name from the component
|
||||
parsed_component = component.split(maxsplit=1)
|
||||
if len(parsed_component) < 2:
|
||||
return None
|
||||
|
||||
# assume the component has already filtered out the value and try to match a keyword
|
||||
# if we try to filter out the value without checking first, keywords with spaces won't parse correctly
|
||||
possible_keyword = parsed_component[1].strip().lower()
|
||||
for rel_kw in sorted([keyword.value for keyword in cls], key=len, reverse=True):
|
||||
if rel_kw.lower() != possible_keyword:
|
||||
continue
|
||||
|
||||
parsed_component[1] = rel_kw
|
||||
return parsed_component
|
||||
|
||||
# there was no match, so the component may still have the value in it
|
||||
try:
|
||||
_possible_keyword, _value = parsed_component[-1].rsplit(maxsplit=1)
|
||||
parsed_component = [parsed_component[0], _possible_keyword, _value]
|
||||
except ValueError:
|
||||
# the component has no value to filter out
|
||||
return None
|
||||
|
||||
possible_keyword = parsed_component[1].strip().lower()
|
||||
for rel_kw in sorted([keyword.value for keyword in cls], key=len, reverse=True):
|
||||
if rel_kw.lower() != possible_keyword:
|
||||
continue
|
||||
|
||||
parsed_component[1] = rel_kw
|
||||
return parsed_component
|
||||
|
||||
return None
|
||||
33
mealie/services/query_filter/operators.py
Normal file
33
mealie/services/query_filter/operators.py
Normal file
@@ -0,0 +1,33 @@
|
||||
from enum import Enum
|
||||
|
||||
|
||||
class LogicalOperator(Enum):
|
||||
AND = "AND"
|
||||
OR = "OR"
|
||||
|
||||
|
||||
class RelationalOperator(Enum):
|
||||
EQ = "="
|
||||
NOTEQ = "<>"
|
||||
GT = ">"
|
||||
LT = "<"
|
||||
GTE = ">="
|
||||
LTE = "<="
|
||||
|
||||
@classmethod
|
||||
def parse_component(cls, component: str) -> list[str] | None:
|
||||
"""
|
||||
Try to parse a component using a relational operator
|
||||
|
||||
If no matching operator is found, returns None
|
||||
"""
|
||||
|
||||
for rel_op in sorted([operator.value for operator in cls], key=len, reverse=True):
|
||||
if rel_op not in component:
|
||||
continue
|
||||
|
||||
parsed_component = [base_component.strip() for base_component in component.split(rel_op) if base_component]
|
||||
parsed_component.insert(1, rel_op)
|
||||
return parsed_component
|
||||
|
||||
return None
|
||||
@@ -1,6 +1,6 @@
|
||||
[project]
|
||||
name = "mealie"
|
||||
version = "3.9.2"
|
||||
version = "3.10.0"
|
||||
description = "A Recipe Manager"
|
||||
authors = [{ name = "Hayden", email = "hay-kot@pm.me" }]
|
||||
license = "AGPL-3.0-only"
|
||||
@@ -20,7 +20,7 @@ dependencies = [
|
||||
"fastapi==0.128.0",
|
||||
"httpx==0.28.1",
|
||||
"lxml==6.0.2",
|
||||
"orjson==3.11.6",
|
||||
"orjson==3.11.7",
|
||||
"pydantic==2.12.5",
|
||||
"pyhumps==3.8.0",
|
||||
"python-dateutil==2.9.0.post0",
|
||||
@@ -69,7 +69,7 @@ dev = [
|
||||
"pylint==4.0.4",
|
||||
"pytest==9.0.2",
|
||||
"pytest-asyncio==1.3.0",
|
||||
"rich==14.3.1",
|
||||
"rich==14.3.2",
|
||||
"ruff==0.14.14",
|
||||
"types-PyYAML==6.0.12.20250915",
|
||||
"types-python-dateutil==2.9.0.20260124",
|
||||
@@ -77,6 +77,7 @@ dev = [
|
||||
"types-requests==2.32.4.20260107",
|
||||
"types-urllib3==1.26.25.14",
|
||||
"pydantic-to-typescript2==1.0.6",
|
||||
"freezegun==1.5.5",
|
||||
]
|
||||
|
||||
[build-system]
|
||||
|
||||
@@ -3,10 +3,13 @@ import time
|
||||
from collections import defaultdict
|
||||
from datetime import UTC, datetime, timedelta
|
||||
from random import randint
|
||||
from unittest.mock import patch
|
||||
from urllib.parse import parse_qsl, urlsplit
|
||||
|
||||
import pytest
|
||||
from dateutil.relativedelta import relativedelta
|
||||
from fastapi.testclient import TestClient
|
||||
from freezegun import freeze_time
|
||||
from humps import camelize
|
||||
from pydantic import UUID4
|
||||
|
||||
@@ -34,6 +37,7 @@ from mealie.schema.response.pagination import (
|
||||
PaginationQuery,
|
||||
)
|
||||
from mealie.schema.user.user import UserRatingUpdate
|
||||
from mealie.services.query_filter.builder import PlaceholderKeyword
|
||||
from mealie.services.seeder.seeder_service import SeederService
|
||||
from tests.utils import api_routes
|
||||
from tests.utils.factories import random_int, random_string
|
||||
@@ -1567,3 +1571,130 @@ def test_pagination_filter_by_custom_rating(api_client: TestClient, user_tuple:
|
||||
recipes_data = response.json()["items"]
|
||||
assert len(recipes_data) == 1
|
||||
assert recipes_data[0]["id"] == str(recipe_2.id)
|
||||
|
||||
|
||||
def test_parse_now_with_remainder_too_short():
|
||||
with pytest.raises(ValueError, match="Invalid remainder"):
|
||||
PlaceholderKeyword._parse_now("$NOW+d")
|
||||
|
||||
|
||||
def test_parse_now_without_arithmetic():
|
||||
result = PlaceholderKeyword._parse_now("$NOW")
|
||||
assert isinstance(result, str)
|
||||
dt = datetime.fromisoformat(result)
|
||||
assert isinstance(dt, datetime)
|
||||
|
||||
|
||||
def test_parse_now_passthrough_non_placeholder():
|
||||
test_string = "2024-01-15"
|
||||
result = PlaceholderKeyword._parse_now(test_string)
|
||||
assert result == test_string
|
||||
|
||||
|
||||
@freeze_time("2024-01-15 12:00:00")
|
||||
def test_parse_now_with_int_amount():
|
||||
result = PlaceholderKeyword._parse_now("$NOW+30d")
|
||||
assert isinstance(result, str)
|
||||
dt = datetime.fromisoformat(result)
|
||||
assert isinstance(dt, datetime)
|
||||
# Verify offset is exactly 30 days from the frozen time
|
||||
dt = dt.replace(tzinfo=UTC)
|
||||
expected = datetime(2024, 2, 14, 12, 0, 0, tzinfo=UTC)
|
||||
assert dt == expected
|
||||
|
||||
|
||||
@freeze_time("2024-01-15 12:00:00")
|
||||
def test_parse_now_with_single_digit_int():
|
||||
result = PlaceholderKeyword._parse_now("$NOW+1d")
|
||||
assert isinstance(result, str)
|
||||
dt = datetime.fromisoformat(result)
|
||||
assert isinstance(dt, datetime)
|
||||
# Verify offset is exactly 1 day from the frozen time
|
||||
dt = dt.replace(tzinfo=UTC)
|
||||
expected = datetime(2024, 1, 16, 12, 0, 0, tzinfo=UTC)
|
||||
assert dt == expected
|
||||
|
||||
|
||||
@pytest.mark.parametrize("invalid_amount", ["apple", "abc", "!@#"])
|
||||
def test_parse_now_with_invalid_amount(invalid_amount):
|
||||
with pytest.raises(ValueError, match="Invalid amount"):
|
||||
PlaceholderKeyword._parse_now(f"$NOW+{invalid_amount}d")
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"unit,offset_delta",
|
||||
[
|
||||
("y", relativedelta(years=5)),
|
||||
("m", relativedelta(months=5)),
|
||||
("d", timedelta(days=5)),
|
||||
("H", timedelta(hours=5)),
|
||||
("M", timedelta(minutes=5)),
|
||||
("S", timedelta(seconds=5)),
|
||||
],
|
||||
)
|
||||
@freeze_time("2024-01-15 12:00:00")
|
||||
def test_parse_now_with_valid_units(unit, offset_delta):
|
||||
result = PlaceholderKeyword._parse_now(f"$NOW+5{unit}")
|
||||
assert isinstance(result, str)
|
||||
dt = datetime.fromisoformat(result)
|
||||
assert isinstance(dt, datetime)
|
||||
# Verify offset is correct from the frozen time
|
||||
dt = dt.replace(tzinfo=UTC)
|
||||
frozen_time = datetime(2024, 1, 15, 12, 0, 0, tzinfo=UTC)
|
||||
expected = frozen_time + offset_delta
|
||||
assert dt == expected
|
||||
|
||||
|
||||
def test_parse_now_with_invalid_unit():
|
||||
with pytest.raises(ValueError, match="Invalid time unit"):
|
||||
PlaceholderKeyword._parse_now("$NOW+1x")
|
||||
|
||||
|
||||
def test_parse_now_with_missing_unit():
|
||||
with pytest.raises(ValueError, match="Invalid remainder"):
|
||||
PlaceholderKeyword._parse_now("$NOW+1")
|
||||
|
||||
|
||||
@pytest.mark.parametrize("operation,expected_sign", [("+", 1), ("-", -1)])
|
||||
@freeze_time("2024-01-15 12:00:00")
|
||||
def test_parse_now_with_valid_operations(operation, expected_sign):
|
||||
result = PlaceholderKeyword._parse_now(f"$NOW{operation}5d")
|
||||
assert isinstance(result, str)
|
||||
dt = datetime.fromisoformat(result)
|
||||
assert isinstance(dt, datetime)
|
||||
# Verify offset direction is correct from the frozen time
|
||||
dt = dt.replace(tzinfo=UTC)
|
||||
frozen_time = datetime(2024, 1, 15, 12, 0, 0, tzinfo=UTC)
|
||||
expected = frozen_time + (timedelta(days=5) * expected_sign)
|
||||
assert dt == expected
|
||||
|
||||
|
||||
@pytest.mark.parametrize("invalid_operation", ["*", "/", "=", "x"])
|
||||
def test_parse_now_with_invalid_operations(invalid_operation):
|
||||
with pytest.raises(ValueError, match="Invalid operator"):
|
||||
PlaceholderKeyword._parse_now(f"$NOW{invalid_operation}5d")
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"placeholder",
|
||||
[
|
||||
pytest.param("$NOW", id="now_current_day"),
|
||||
pytest.param("$NOW+1d", id="now_plus_one_day"),
|
||||
],
|
||||
)
|
||||
def test_e2e_parse_now_placeholder(
|
||||
api_client: TestClient,
|
||||
unique_user: TestUser,
|
||||
placeholder: str,
|
||||
):
|
||||
with patch.object(PlaceholderKeyword, "parse_value", wraps=PlaceholderKeyword.parse_value) as mock_parse:
|
||||
params = {
|
||||
"page": 1,
|
||||
"perPage": -1,
|
||||
"queryFilter": f'id="{unique_user.user_id}" AND lastMade >= "{placeholder}"',
|
||||
}
|
||||
response = api_client.get(api_routes.recipes, params=params, headers=unique_user.token)
|
||||
assert response.status_code == 200
|
||||
|
||||
# Verify that the placeholder parsing was called
|
||||
assert mock_parse.call_count
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
from mealie.schema.response.query_filter import (
|
||||
from mealie.services.query_filter.builder import (
|
||||
LogicalOperator,
|
||||
QueryFilterBuilder,
|
||||
QueryFilterJSON,
|
||||
|
||||
60
uv.lock
generated
60
uv.lock
generated
@@ -443,6 +443,18 @@ wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/fe/5e/3be305568fe5f34448807976dc82fc151d76c3e0e03958f34770286278c1/flexparser-0.4-py3-none-any.whl", hash = "sha256:3738b456192dcb3e15620f324c447721023c0293f6af9955b481e91d00179846", size = 27625, upload-time = "2024-11-07T02:00:54.523Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "freezegun"
|
||||
version = "1.5.5"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "python-dateutil" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/95/dd/23e2f4e357f8fd3bdff613c1fe4466d21bfb00a6177f238079b17f7b1c84/freezegun-1.5.5.tar.gz", hash = "sha256:ac7742a6cc6c25a2c35e9292dfd554b897b517d2dec26891a2e8debf205cb94a", size = 35914, upload-time = "2025-08-09T10:39:08.338Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/5e/2e/b41d8a1a917d6581fc27a35d05561037b048e47df50f27f8ac9c7e27a710/freezegun-1.5.5-py3-none-any.whl", hash = "sha256:cd557f4a75cf074e84bc374249b9dd491eaeacd61376b9eb3c423282211619d2", size = 19266, upload-time = "2025-08-09T10:39:06.636Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ghp-import"
|
||||
version = "2.1.0"
|
||||
@@ -810,7 +822,7 @@ wheels = [
|
||||
|
||||
[[package]]
|
||||
name = "mealie"
|
||||
version = "3.9.2"
|
||||
version = "3.10.0"
|
||||
source = { editable = "." }
|
||||
dependencies = [
|
||||
{ name = "aiofiles" },
|
||||
@@ -864,6 +876,7 @@ pgsql = [
|
||||
dev = [
|
||||
{ name = "coverage" },
|
||||
{ name = "coveragepy-lcov" },
|
||||
{ name = "freezegun" },
|
||||
{ name = "mkdocs-material" },
|
||||
{ name = "mypy" },
|
||||
{ name = "pre-commit" },
|
||||
@@ -903,7 +916,7 @@ requires-dist = [
|
||||
{ name = "jinja2", specifier = "==3.1.6" },
|
||||
{ name = "lxml", specifier = "==6.0.2" },
|
||||
{ name = "openai", specifier = "==2.16.0" },
|
||||
{ name = "orjson", specifier = "==3.11.6" },
|
||||
{ name = "orjson", specifier = "==3.11.7" },
|
||||
{ name = "paho-mqtt", specifier = "==1.6.1" },
|
||||
{ name = "pillow", specifier = "==12.1.0" },
|
||||
{ name = "pillow-heif", specifier = "==1.2.0" },
|
||||
@@ -933,6 +946,7 @@ provides-extras = ["pgsql"]
|
||||
dev = [
|
||||
{ name = "coverage", specifier = "==7.13.2" },
|
||||
{ name = "coveragepy-lcov", specifier = "==0.1.2" },
|
||||
{ name = "freezegun", specifier = "==1.5.5" },
|
||||
{ name = "mkdocs-material", specifier = "==9.7.1" },
|
||||
{ name = "mypy", specifier = "==1.19.1" },
|
||||
{ name = "pre-commit", specifier = "==4.5.1" },
|
||||
@@ -940,7 +954,7 @@ dev = [
|
||||
{ name = "pylint", specifier = "==4.0.4" },
|
||||
{ name = "pytest", specifier = "==9.0.2" },
|
||||
{ name = "pytest-asyncio", specifier = "==1.3.0" },
|
||||
{ name = "rich", specifier = "==14.3.1" },
|
||||
{ name = "rich", specifier = "==14.3.2" },
|
||||
{ name = "ruff", specifier = "==0.14.14" },
|
||||
{ name = "types-python-dateutil", specifier = "==2.9.0.20260124" },
|
||||
{ name = "types-python-slugify", specifier = "==8.0.2.20240310" },
|
||||
@@ -1145,25 +1159,25 @@ wheels = [
|
||||
|
||||
[[package]]
|
||||
name = "orjson"
|
||||
version = "3.11.6"
|
||||
version = "3.11.7"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/70/a3/4e09c61a5f0c521cba0bb433639610ae037437669f1a4cbc93799e731d78/orjson-3.11.6.tar.gz", hash = "sha256:0a54c72259f35299fd033042367df781c2f66d10252955ca1efb7db309b954cb", size = 6175856, upload-time = "2026-01-29T15:13:07.942Z" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/53/45/b268004f745ede84e5798b48ee12b05129d19235d0e15267aa57dcdb400b/orjson-3.11.7.tar.gz", hash = "sha256:9b1a67243945819ce55d24a30b59d6a168e86220452d2c96f4d1f093e71c0c49", size = 6144992, upload-time = "2026-02-02T15:38:49.29Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/14/ba/759f2879f41910b7e5e0cdbd9cf82a4f017c527fb0e972e9869ca7fe4c8e/orjson-3.11.6-cp312-cp312-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:6f03f30cd8953f75f2a439070c743c7336d10ee940da918d71c6f3556af3ddcf", size = 249988, upload-time = "2026-01-29T15:11:58.294Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/f0/70/54cecb929e6c8b10104fcf580b0cc7dc551aa193e83787dd6f3daba28bb5/orjson-3.11.6-cp312-cp312-macosx_15_0_arm64.whl", hash = "sha256:af44baae65ef386ad971469a8557a0673bb042b0b9fd4397becd9c2dfaa02588", size = 134445, upload-time = "2026-01-29T15:11:59.819Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/f2/6f/ec0309154457b9ba1ad05f11faa4441f76037152f75e1ac577db3ce7ca96/orjson-3.11.6-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c310a48542094e4f7dbb6ac076880994986dda8ca9186a58c3cb70a3514d3231", size = 137708, upload-time = "2026-01-29T15:12:01.488Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/20/52/3c71b80840f8bab9cb26417302707b7716b7d25f863f3a541bcfa232fe6e/orjson-3.11.6-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:d8dfa7a5d387f15ecad94cb6b2d2d5f4aeea64efd8d526bfc03c9812d01e1cc0", size = 134798, upload-time = "2026-01-29T15:12:02.705Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/30/51/b490a43b22ff736282360bd02e6bded455cf31dfc3224e01cd39f919bbd2/orjson-3.11.6-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ba8daee3e999411b50f8b50dbb0a3071dd1845f3f9a1a0a6fa6de86d1689d84d", size = 140839, upload-time = "2026-01-29T15:12:03.956Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/95/bc/4bcfe4280c1bc63c5291bb96f98298845b6355da2226d3400e17e7b51e53/orjson-3.11.6-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f89d104c974eafd7436d7a5fdbc57f7a1e776789959a2f4f1b2eab5c62a339f4", size = 144080, upload-time = "2026-01-29T15:12:05.151Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/01/74/22970f9ead9ab1f1b5f8c227a6c3aa8d71cd2c5acd005868a1d44f2362fa/orjson-3.11.6-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b2e2e2456788ca5ea75616c40da06fc885a7dc0389780e8a41bf7c5389ba257b", size = 142435, upload-time = "2026-01-29T15:12:06.641Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/29/34/d564aff85847ab92c82ee43a7a203683566c2fca0723a5f50aebbe759603/orjson-3.11.6-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2a42efebc45afabb1448001e90458c4020d5c64fbac8a8dc4045b777db76cb5a", size = 145631, upload-time = "2026-01-29T15:12:08.351Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/e7/ef/016957a3890752c4aa2368326ea69fa53cdc1fdae0a94a542b6410dbdf52/orjson-3.11.6-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:71b7cbef8471324966c3738c90ba38775563ef01b512feb5ad4805682188d1b9", size = 147058, upload-time = "2026-01-29T15:12:10.023Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/56/cc/9a899c3972085645b3225569f91a30e221f441e5dc8126e6d060b971c252/orjson-3.11.6-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:f8515e5910f454fe9a8e13c2bb9dc4bae4c1836313e967e72eb8a4ad874f0248", size = 421161, upload-time = "2026-01-29T15:12:11.308Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/21/a8/767d3fbd6d9b8fdee76974db40619399355fd49bf91a6dd2c4b6909ccf05/orjson-3.11.6-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:300360edf27c8c9bf7047345a94fddf3a8b8922df0ff69d71d854a170cb375cf", size = 155757, upload-time = "2026-01-29T15:12:12.776Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/ad/0b/205cd69ac87e2272e13ef3f5f03a3d4657e317e38c1b08aaa2ef97060bbc/orjson-3.11.6-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:caaed4dad39e271adfadc106fab634d173b2bb23d9cf7e67bd645f879175ebfc", size = 147446, upload-time = "2026-01-29T15:12:14.166Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/de/c5/dd9f22aa9f27c54c7d05cc32f4580c9ac9b6f13811eeb81d6c4c3f50d6b1/orjson-3.11.6-cp312-cp312-win32.whl", hash = "sha256:955368c11808c89793e847830e1b1007503a5923ddadc108547d3b77df761044", size = 139717, upload-time = "2026-01-29T15:12:15.7Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/23/a1/e62fc50d904486970315a1654b8cfb5832eb46abb18cd5405118e7e1fc79/orjson-3.11.6-cp312-cp312-win_amd64.whl", hash = "sha256:2c68de30131481150073d90a5d227a4a421982f42c025ecdfb66157f9579e06f", size = 136711, upload-time = "2026-01-29T15:12:17.055Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/04/3d/b4fefad8bdf91e0fe212eb04975aeb36ea92997269d68857efcc7eb1dda3/orjson-3.11.6-cp312-cp312-win_arm64.whl", hash = "sha256:65dfa096f4e3a5e02834b681f539a87fbe85adc82001383c0db907557f666bfc", size = 135212, upload-time = "2026-01-29T15:12:18.3Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/80/bf/76f4f1665f6983385938f0e2a5d7efa12a58171b8456c252f3bae8a4cf75/orjson-3.11.7-cp312-cp312-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:bd03ea7606833655048dab1a00734a2875e3e86c276e1d772b2a02556f0d895f", size = 228545, upload-time = "2026-02-02T15:37:46.376Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/79/53/6c72c002cb13b5a978a068add59b25a8bdf2800ac1c9c8ecdb26d6d97064/orjson-3.11.7-cp312-cp312-macosx_15_0_arm64.whl", hash = "sha256:89e440ebc74ce8ab5c7bc4ce6757b4a6b1041becb127df818f6997b5c71aa60b", size = 125224, upload-time = "2026-02-02T15:37:47.697Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/2c/83/10e48852865e5dd151bdfe652c06f7da484578ed02c5fca938e3632cb0b8/orjson-3.11.7-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5ede977b5fe5ac91b1dffc0a517ca4542d2ec8a6a4ff7b2652d94f640796342a", size = 128154, upload-time = "2026-02-02T15:37:48.954Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/6e/52/a66e22a2b9abaa374b4a081d410edab6d1e30024707b87eab7c734afe28d/orjson-3.11.7-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:b7b1dae39230a393df353827c855a5f176271c23434cfd2db74e0e424e693e10", size = 123548, upload-time = "2026-02-02T15:37:50.187Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/de/38/605d371417021359f4910c496f764c48ceb8997605f8c25bf1dfe58c0ebe/orjson-3.11.7-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ed46f17096e28fb28d2975834836a639af7278aa87c84f68ab08fbe5b8bd75fa", size = 129000, upload-time = "2026-02-02T15:37:51.426Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/44/98/af32e842b0ffd2335c89714d48ca4e3917b42f5d6ee5537832e069a4b3ac/orjson-3.11.7-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3726be79e36e526e3d9c1aceaadbfb4a04ee80a72ab47b3f3c17fefb9812e7b8", size = 141686, upload-time = "2026-02-02T15:37:52.607Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/96/0b/fc793858dfa54be6feee940c1463370ece34b3c39c1ca0aa3845f5ba9892/orjson-3.11.7-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0724e265bc548af1dedebd9cb3d24b4e1c1e685a343be43e87ba922a5c5fff2f", size = 130812, upload-time = "2026-02-02T15:37:53.944Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/dc/91/98a52415059db3f374757d0b7f0f16e3b5cd5976c90d1c2b56acaea039e6/orjson-3.11.7-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e7745312efa9e11c17fbd3cb3097262d079da26930ae9ae7ba28fb738367cbad", size = 133440, upload-time = "2026-02-02T15:37:55.615Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/dc/b6/cb540117bda61791f46381f8c26c8f93e802892830a6055748d3bb1925ab/orjson-3.11.7-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:f904c24bdeabd4298f7a977ef14ca2a022ca921ed670b92ecd16ab6f3d01f867", size = 138386, upload-time = "2026-02-02T15:37:56.814Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/63/1a/50a3201c334a7f17c231eee5f841342190723794e3b06293f26e7cf87d31/orjson-3.11.7-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:b9fc4d0f81f394689e0814617aadc4f2ea0e8025f38c226cbf22d3b5ddbf025d", size = 408853, upload-time = "2026-02-02T15:37:58.291Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/87/cd/8de1c67d0be44fdc22701e5989c0d015a2adf391498ad42c4dc589cd3013/orjson-3.11.7-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:849e38203e5be40b776ed2718e587faf204d184fc9a008ae441f9442320c0cab", size = 144130, upload-time = "2026-02-02T15:38:00.163Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/0f/fe/d605d700c35dd55f51710d159fc54516a280923cd1b7e47508982fbb387d/orjson-3.11.7-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:4682d1db3bcebd2b64757e0ddf9e87ae5f00d29d16c5cdf3a62f561d08cc3dd2", size = 134818, upload-time = "2026-02-02T15:38:01.507Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/e4/e4/15ecc67edb3ddb3e2f46ae04475f2d294e8b60c1825fbe28a428b93b3fbd/orjson-3.11.7-cp312-cp312-win32.whl", hash = "sha256:f4f7c956b5215d949a1f65334cf9d7612dde38f20a95f2315deef167def91a6f", size = 127923, upload-time = "2026-02-02T15:38:02.75Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/34/70/2e0855361f76198a3965273048c8e50a9695d88cd75811a5b46444895845/orjson-3.11.7-cp312-cp312-win_amd64.whl", hash = "sha256:bf742e149121dc5648ba0a08ea0871e87b660467ef168a3a5e53bc1fbd64bb74", size = 125007, upload-time = "2026-02-02T15:38:04.032Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/68/40/c2051bd19fc467610fed469dc29e43ac65891571138f476834ca192bc290/orjson-3.11.7-cp312-cp312-win_arm64.whl", hash = "sha256:26c3b9132f783b7d7903bf1efb095fed8d4a3a85ec0d334ee8beff3d7a4749d5", size = 126089, upload-time = "2026-02-02T15:38:05.297Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1710,15 +1724,15 @@ wheels = [
|
||||
|
||||
[[package]]
|
||||
name = "rich"
|
||||
version = "14.3.1"
|
||||
version = "14.3.2"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "markdown-it-py" },
|
||||
{ name = "pygments" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/a1/84/4831f881aa6ff3c976f6d6809b58cdfa350593ffc0dc3c58f5f6586780fb/rich-14.3.1.tar.gz", hash = "sha256:b8c5f568a3a749f9290ec6bddedf835cec33696bfc1e48bcfecb276c7386e4b8", size = 230125, upload-time = "2026-01-24T21:40:44.847Z" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/74/99/a4cab2acbb884f80e558b0771e97e21e939c5dfb460f488d19df485e8298/rich-14.3.2.tar.gz", hash = "sha256:e712f11c1a562a11843306f5ed999475f09ac31ffb64281f73ab29ffdda8b3b8", size = 230143, upload-time = "2026-02-01T16:20:47.908Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/87/2a/a1810c8627b9ec8c57ec5ec325d306701ae7be50235e8fd81266e002a3cc/rich-14.3.1-py3-none-any.whl", hash = "sha256:da750b1aebbff0b372557426fb3f35ba56de8ef954b3190315eb64076d6fb54e", size = 309952, upload-time = "2026-01-24T21:40:42.969Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/ef/45/615f5babd880b4bd7d405cc0dc348234c5ffb6ed1ea33e152ede08b2072d/rich-14.3.2-py3-none-any.whl", hash = "sha256:08e67c3e90884651da3239ea668222d19bea7b589149d8014a21c633420dbb69", size = 309963, upload-time = "2026-02-01T16:20:46.078Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
||||
Reference in New Issue
Block a user