some fixes

This commit is contained in:
vabene1111
2025-03-20 16:16:09 +01:00
parent d34f39a9e0
commit 73c376427c
38 changed files with 376 additions and 35 deletions

View File

@@ -1184,6 +1184,7 @@ class ShoppingListRecipeSerializer(serializers.ModelSerializer):
meal_plan_data = MealPlanSerializer(source='mealplan', read_only=True, required=False)
servings = CustomDecimalField()
created_by = UserSerializer(read_only=True)
def create(self, validated_data):
validated_data['space'] = self.context['request'].space
validated_data['created_by'] = self.context['request'].user
@@ -1197,8 +1198,8 @@ class ShoppingListRecipeSerializer(serializers.ModelSerializer):
class Meta:
model = ShoppingListRecipe
fields = ('id', 'name', 'recipe', 'recipe_data', 'mealplan', 'meal_plan_data', 'servings','created_by',)
read_only_fields = ('id','created_by',)
fields = ('id', 'name', 'recipe', 'recipe_data', 'mealplan', 'meal_plan_data', 'servings', 'created_by',)
read_only_fields = ('id', 'created_by',)
class ShoppingListEntrySerializer(WritableNestedModelSerializer):
@@ -1248,7 +1249,8 @@ class ShoppingListEntrySerializer(WritableNestedModelSerializer):
if existing_slr := ShoppingListRecipe.objects.filter(mealplan_id=validated_data['mealplan_id'], space=self.context['request'].space).first():
validated_data['list_recipe'] = existing_slr
else:
validated_data['list_recipe'] = ShoppingListRecipe.objects.create(mealplan_id=validated_data['mealplan_id'], space=self.context['request'].space, created_by=self.context['request'].user)
validated_data['list_recipe'] = ShoppingListRecipe.objects.create(mealplan_id=validated_data['mealplan_id'], space=self.context['request'].space,
created_by=self.context['request'].user)
del validated_data['mealplan_id']
return super().create(validated_data)
@@ -1464,6 +1466,9 @@ class AccessTokenSerializer(serializers.ModelSerializer):
def get_token(self, obj):
if (timezone.now() - obj.created).seconds < 15:
return obj.token
if obj.scope == 'bookmarklet':
# bookmarklet only tokens are always returned because they have very limited access and are needed for the bookmarklet function to work
return obj.token
return f'tda_************_******_***********{obj.token[len(obj.token) - 4:]}'
class Meta:

View File

@@ -88,7 +88,7 @@ urlpatterns = [
path('settings-shopping/', views.shopping_settings, name='view_shopping_settings'), # TODO rename to search settings
path('abuse/<slug:token>', views.report_share_abuse, name='view_report_share_abuse'),
path('api/import/', api.import_files, name='view_import'),
path('import-response/<int:pk>/', import_export.import_response, name='view_import_response'),
path('export/', import_export.export_recipe, name='view_export'),
path('export-response/<int:pk>/', import_export.export_response, name='view_export_response'),
@@ -110,6 +110,7 @@ urlpatterns = [
path('data/batch/edit', data.batch_edit, name='data_batch_edit'),
path('data/batch/import', data.batch_import, name='data_batch_import'),
path('data/sync/wait', data.sync_wait, name='data_sync_wait'),
path('api/import/', api.AppImportView.as_view(), name='view_import'),
path('data/import/url', data.import_url, name='data_import_url'),
path('api/get_external_file_link/<int:recipe_id>/', api.get_external_file_link, name='api_get_external_file_link'),
path('api/get_recipe_file/<int:recipe_id>/', api.get_recipe_file, name='api_get_recipe_file'),
@@ -121,7 +122,6 @@ urlpatterns = [
path('api/reset-food-inheritance/', api.reset_food_inheritance, name='api_reset_food_inheritance'),
path('api/switch-active-space/<int:space_id>/', api.switch_active_space, name='api_switch_active_space'),
path('api/download-file/<int:file_id>/', api.download_file, name='api_download_file'),
path('api/image-to-recipe', api.ImageToRecipeView.as_view(), name='api_image_to_recipe'),
path('telegram/setup/<int:pk>', telegram.setup_bot, name='telegram_setup'),
path('telegram/remove/<int:pk>', telegram.remove_bot, name='telegram_remove'),
path('telegram/hook/<slug:token>/', telegram.hook, name='telegram_hook'),
@@ -175,4 +175,4 @@ urlpatterns += [
re_path(r'^v3/.*', views.vue3, name='vue_3'),
path('', views.index, name='index'),
path('<path:resource>', views.index, name='tandoor_frontend'),
]
]

View File

@@ -1895,6 +1895,37 @@ class ImageToRecipeView(APIView):
return Response({'msg': serializer.errors})
class AppImportView(APIView):
parser_classes = [MultiPartParser]
throttle_classes = [RecipeImportThrottle]
permission_classes = [CustomIsUser & CustomTokenHasReadWriteScope]
@extend_schema(request=ImportImageSerializer(many=False), responses=RecipeFromSourceResponseSerializer(many=False))
def post(self, request, *args, **kwargs):
limit, msg = above_space_limit(request.space)
if limit:
return Response({'error': True, 'msg': _('File is above space limit')}, status=status.HTTP_400_BAD_REQUEST)
form = ImportForm(request.POST, request.FILES)
if form.is_valid() and request.FILES != {}:
try:
integration = get_integration(request, form.cleaned_data['type'])
il = ImportLog.objects.create(type=form.cleaned_data['type'], created_by=request.user, space=request.space)
files = []
for f in request.FILES.getlist('files'):
files.append({'file': io.BytesIO(f.read()), 'name': f.name})
t = threading.Thread(target=integration.do_import, args=[files, il, form.cleaned_data['duplicates']])
t.setDaemon(True)
t.start()
return Response({'import_id': il.pk}, status=status.HTTP_200_OK)
except NotImplementedError:
return Response({'error': True, 'msg': _('Importing is not implemented for this provider')},
status=status.HTTP_400_BAD_REQUEST)
else:
return Response({'error': True, 'msg': form.errors}, status=status.HTTP_400_BAD_REQUEST)
@extend_schema(
request=None,
responses=None,

View File

@@ -0,0 +1,47 @@
(function(){
var v = "1.3.2";
if (window.jQuery === undefined || window.jQuery.fn.jquery < v) {
var done = false;
var script = document.createElement("script");
script.src = "https://ajax.googleapis.com/ajax/libs/jquery/" + v + "/jquery.min.js";
script.onload = script.onreadystatechange = function(){
if (!done && (!this.readyState || this.readyState == "loaded" || this.readyState == "complete")) {
done = true;
initBookmarklet();
}
};
document.getElementsByTagName("head")[0].appendChild(script);
} else {
initBookmarklet();
}
function initBookmarklet() {
(window.bookmarkletTandoor = function() {
let recipe = document.documentElement.outerHTML
let windowName = "ImportRecipe"
let url = localStorage.getItem('importURL')
let redirect = localStorage.getItem('redirectURL')
let token = localStorage.getItem('token')
let params = { 'url': window.location.protocol + '//' + window.location.host + window.location.pathname, 'html' : recipe};
const xhr = new XMLHttpRequest();
xhr.open('POST', url, true);
xhr.setRequestHeader('Content-Type', 'application/json');
xhr.setRequestHeader('Authorization', 'Bearer ' + token);
// listen for `onload` event
xhr.onload = () => {
// process response
if (xhr.readyState == 4 && xhr.status == 201) {
// parse JSON data
window.open(redirect.concat('?bookmarklet_import=', JSON.parse(xhr.response).id) )
} else {
console.error('Error!');
}
};
xhr.send(JSON.stringify(params));
}
)();
}
})();

View File

@@ -0,0 +1,34 @@
<template>
<v-row v-if=" props.importLog.importedRecipes != undefined && props.importLog.totalRecipes != undefined">
<v-col>
<v-progress-linear :model-value="(props.importLog.importedRecipes/props.importLog.totalRecipes)*100" height="24" color="primary">
{{ props.importLog.importedRecipes }} / {{ props.importLog.totalRecipes }}
</v-progress-linear>
</v-col>
</v-row>
<v-row>
<v-col>
<v-textarea :model-value="importLog.msg"></v-textarea>
</v-col>
</v-row>
</template>
<script setup lang="ts">
import {ImportLog} from "@/openapi";
import {PropType} from "vue";
const props = defineProps({
importLog: {type: {} as PropType<ImportLog>, required: true}
})
</script>
<style scoped>
</style>

View File

@@ -1,8 +1,8 @@
import {useDjangoUrls} from "@/composables/useDjangoUrls";
import {ref} from "vue";
import {getCookie} from "@/utils/cookie";
import {RecipeFromJSON, RecipeFromSourceFromJSON, RecipeFromSourceResponseFromJSON, RecipeImageFromJSON, UserFile, UserFileFromJSON} from "@/openapi";
import {ErrorMessageType, PreparedMessage, useMessageStore} from "@/stores/MessageStore";
import {RecipeFromSourceResponseFromJSON, RecipeImageFromJSON, UserFile, UserFileFromJSON} from "@/openapi";
/**
* function to upload files to the multipart endpoints accepting file uploads
@@ -101,25 +101,27 @@ export function useFileApi() {
/**
* uploads the given files to the app import endpoint
* @param file array to import
* @param files array to import
* @param app app to import
* @param includeDuplicates if recipes that were found as duplicates should be imported as well
* @returns Promise resolving to the import ID of the app import
*/
function doAppImport(file: File, app: string, includeDuplicates: boolean) {
function doAppImport(files: File[], app: string, includeDuplicates: boolean) {
let formData = new FormData()
formData.append('type', app);
formData.append('duplicates', includeDuplicates ? 'true' : 'false')
// files.forEach(file => {
// formData.append('files', file)
// })
formData.append('files', file)
files.forEach(file => {
formData.append('files', file)
})
return fetch(getDjangoUrl(`api/import/`), {
method: 'POST',
headers: {'X-CSRFToken': getCookie('csrftoken')},
body: formData
}).then(r => {
console.log(r)
return r.json().then(r => {
return r.import_id
})
}).finally(() => {
fileApiLoading.value = false
})

View File

@@ -32,6 +32,9 @@
"BaseUnitHelp": "",
"Book": "",
"Bookmarklet": "",
"BookmarkletHelp1": "",
"BookmarkletHelp2": "",
"BookmarkletHelp3": "",
"BookmarkletImportSubtitle": "",
"Books": "",
"Calories": "",
@@ -143,6 +146,7 @@
"IgnoredFood": "",
"Image": "",
"Import": "",
"ImportIntoTandoor": "",
"Import_Error": "",
"Import_Not_Yet_Supported": "",
"Import_Result_Info": "",

View File

@@ -32,6 +32,9 @@
"BaseUnitHelp": "",
"Book": "",
"Bookmarklet": "Книжен пазар",
"BookmarkletHelp1": "",
"BookmarkletHelp2": "",
"BookmarkletHelp3": "",
"BookmarkletImportSubtitle": "",
"Books": "Книги",
"Calories": "Калории",
@@ -140,6 +143,7 @@
"IgnoredFood": "{food} е настроен да игнорира пазаруването.",
"Image": "Изображение",
"Import": "Импортиране",
"ImportIntoTandoor": "",
"Import_Error": "Възникна грешка по време на импортирането ви. Моля, разгънете подробностите в долната част на страницата, за да ги видите.",
"Import_Not_Yet_Supported": "Импортирането все още не се поддържа",
"Import_Result_Info": "Импортирани са {imported} от {total} рецепти",

View File

@@ -39,6 +39,9 @@
"BaseUnitHelp": "",
"Book": "",
"Bookmarklet": "",
"BookmarkletHelp1": "",
"BookmarkletHelp2": "",
"BookmarkletHelp3": "",
"BookmarkletImportSubtitle": "",
"Books": "",
"Calculator": "",
@@ -186,6 +189,7 @@
"Image": "",
"Import": "",
"Import Recipe": "",
"ImportIntoTandoor": "",
"Import_Error": "",
"Import_Not_Yet_Supported": "",
"Import_Result_Info": "",

View File

@@ -39,6 +39,9 @@
"BaseUnitHelp": "",
"Book": "",
"Bookmarklet": "Skript v záložce",
"BookmarkletHelp1": "",
"BookmarkletHelp2": "",
"BookmarkletHelp3": "",
"BookmarkletImportSubtitle": "",
"Books": "Kuchařky",
"Calculator": "Kalkulačka",
@@ -186,6 +189,7 @@
"Image": "Obrázek",
"Import": "Import",
"Import Recipe": "Importovat recept",
"ImportIntoTandoor": "",
"Import_Error": "Během importu došlo k chybě. Pro více informací rozbalte Detaily na konci stránky.",
"Import_Not_Yet_Supported": "Import není zatím podporován",
"Import_Result_Info": "{imported} z {total} receptů naimportováno",

View File

@@ -39,6 +39,9 @@
"BaseUnitHelp": "",
"Book": "",
"Bookmarklet": "Bogmærke",
"BookmarkletHelp1": "",
"BookmarkletHelp2": "",
"BookmarkletHelp3": "",
"BookmarkletImportSubtitle": "",
"Books": "Bøger",
"Calories": "Kalorier",
@@ -174,6 +177,7 @@
"Image": "Billede",
"Import": "Importer",
"Import Recipe": "Importer opskrift",
"ImportIntoTandoor": "",
"Import_Error": "Der opstod en fejl under din importering. Udvid detaljerne i bunden af siden for at se fejlen.",
"Import_Not_Yet_Supported": "Import endnu ikke understøttet",
"Import_Result_Info": "{imported} af {total} opskrifter blev importeret",

View File

@@ -41,6 +41,9 @@
"BaseUnitHelp": "Optionale Standardeinheit zur automatischen Umrechnung",
"Book": "Buch",
"Bookmarklet": "Lesezeichen",
"BookmarkletHelp1": "Schiebe den Knopf in deine Lesezeichenleiste",
"BookmarkletHelp2": "Öffne die Seite von der importiert werden soll.",
"BookmarkletHelp3": "Klicke auf das Lesezeichen um den Import durchzuführen",
"BookmarkletImportSubtitle": "Verwende ein Bookmarklet um von nicht öffentlichen Seiten zu importieren.",
"Books": "Kochbücher",
"Calculator": "Rechner",
@@ -189,6 +192,7 @@
"Image": "Bild",
"Import": "Importieren",
"Import Recipe": "Rezept importieren",
"ImportIntoTandoor": "In Tandoor importieren",
"Import_Error": "Es ist ein Fehler beim Importieren aufgetreten. Bitte sieh dir die ausgeklappten Details unten auf der Seite an.",
"Import_Not_Yet_Supported": "Importieren wird noch nicht unterstützt",
"Import_Result_Info": "{imported} von insgesamt {total} Rezepten wurden importiert",

View File

@@ -38,6 +38,9 @@
"BaseUnitHelp": "",
"Book": "",
"Bookmarklet": "Bookmarklet",
"BookmarkletHelp1": "",
"BookmarkletHelp2": "",
"BookmarkletHelp3": "",
"BookmarkletImportSubtitle": "",
"Books": "Βιβλία",
"Calories": "Θερμίδες",
@@ -169,6 +172,7 @@
"Image": "Εικόνα",
"Import": "Εισαγωγή",
"Import Recipe": "Εισαγωγή συνταγής",
"ImportIntoTandoor": "",
"Import_Error": "Συνέβη ένα σφάλμα κατά την εισαγωγή. Για να το δείτε, εμφανίστε τις λεπτομέρειες στο κάτω μέρος της σελίδας.",
"Import_Not_Yet_Supported": "Η εισαγωγή δεν υποστηρίζεται ακόμη",
"Import_Result_Info": "Έγινε εισαγωγή {imported} από τις {total} συνταγές",

View File

@@ -39,6 +39,9 @@
"BaseUnitHelp": "Standard unit for automatic unit conversion",
"Book": "Book",
"Bookmarklet": "Bookmarklet",
"BookmarkletHelp1": "Drag the following button to your bookmarks bar",
"BookmarkletHelp2": "Open the page you want to import from",
"BookmarkletHelp3": "Click on the bookmark to perform the import.",
"BookmarkletImportSubtitle": "Use a bookmarklet to import from non public pages.",
"Books": "Books",
"Calculator": "Calculator",
@@ -187,6 +190,7 @@
"Image": "Image",
"Import": "Import",
"Import Recipe": "Import Recipe",
"ImportIntoTandoor": "Import into Tandoor",
"Import_Error": "An Error occurred during your import. Please expand the Details at the bottom of the page to view it.",
"Import_Not_Yet_Supported": "Import not yet supported",
"Import_Result_Info": "{imported} of {total} recipes were imported",

View File

@@ -39,6 +39,9 @@
"BaseUnitHelp": "",
"Book": "",
"Bookmarklet": "Marcadores",
"BookmarkletHelp1": "",
"BookmarkletHelp2": "",
"BookmarkletHelp3": "",
"BookmarkletImportSubtitle": "",
"Books": "Libros",
"Calculator": "Calculadora",
@@ -187,6 +190,7 @@
"Image": "Imagen",
"Import": "Importar",
"Import Recipe": "Importar Receta",
"ImportIntoTandoor": "",
"Import_Error": "Ocurrió un Error ocurrió durante la importación. Por favor, expanda los Detalles al final de la página para verlo.",
"Import_Not_Yet_Supported": "Importación no soportada todavía",
"Import_Result_Info": "{imported} de {total} recetas fueron importadas",

View File

@@ -24,6 +24,9 @@
"BaseUnit": "",
"BaseUnitHelp": "",
"Book": "",
"BookmarkletHelp1": "",
"BookmarkletHelp2": "",
"BookmarkletHelp3": "",
"BookmarkletImportSubtitle": "",
"Books": "Kirjat",
"Calories": "Kalorit",
@@ -110,6 +113,7 @@
"Ignore_Shopping": "Ohita Ostokset",
"Image": "Kuva",
"Import": "Tuo",
"ImportIntoTandoor": "",
"Import_finished": "Tuonti valmistui",
"Information": "Tiedot",
"Ingredient": "",

View File

@@ -40,6 +40,9 @@
"BaseUnitHelp": "",
"Book": "",
"Bookmarklet": "Signet",
"BookmarkletHelp1": "",
"BookmarkletHelp2": "",
"BookmarkletHelp3": "",
"BookmarkletImportSubtitle": "",
"Books": "Livres",
"Calculator": "Calculatrice",
@@ -186,6 +189,7 @@
"Image": "Image",
"Import": "Importer",
"Import Recipe": "Importer une recette",
"ImportIntoTandoor": "",
"Import_Error": "Une erreur est survenue pendant votre importation. Veuillez développer les détails au bas de la page pour la consulter.",
"Import_Not_Yet_Supported": "Importation pas encore prise en charge",
"Import_Result_Info": "{imported} sur {total} recettes ont été importées",

View File

@@ -39,6 +39,9 @@
"BaseUnitHelp": "",
"Book": "",
"Bookmarklet": "סימניה",
"BookmarkletHelp1": "",
"BookmarkletHelp2": "",
"BookmarkletHelp3": "",
"BookmarkletImportSubtitle": "",
"Books": "ספרים",
"Calculator": "מחשבון",
@@ -187,6 +190,7 @@
"Image": "תמונה",
"Import": "ייבוא",
"Import Recipe": "ייבא מתכון",
"ImportIntoTandoor": "",
"Import_Error": "שגיאה בעת ייבוא. הרחב את הפירוט בסוף עמוד זה לראות מידע נוסף.",
"Import_Not_Yet_Supported": "ייבוא לא נתמך עדיין",
"Import_Result_Info": "{imported} מתוך {total} מתכונים יובאו",

View File

@@ -39,6 +39,9 @@
"BaseUnitHelp": "",
"Book": "",
"Bookmarklet": "Könyvjelző",
"BookmarkletHelp1": "",
"BookmarkletHelp2": "",
"BookmarkletHelp3": "",
"BookmarkletImportSubtitle": "",
"Books": "Könyvek",
"Calories": "Kalóriák",
@@ -170,6 +173,7 @@
"Image": "Kép",
"Import": "Import",
"Import Recipe": "Recept importálása",
"ImportIntoTandoor": "",
"Import_Error": "Hiba történt az importálás során. Kérjük, a megtekintéshez bontsa ki az oldal alján található Részletek menüpontot.",
"Import_Not_Yet_Supported": "",
"Import_Result_Info": "{total}-ból/ből {imported} recept importálva",

View File

@@ -21,6 +21,9 @@
"BaseUnit": "",
"BaseUnitHelp": "",
"Book": "",
"BookmarkletHelp1": "",
"BookmarkletHelp2": "",
"BookmarkletHelp3": "",
"BookmarkletImportSubtitle": "",
"Books": "",
"Calories": "",
@@ -85,6 +88,7 @@
"History": "",
"HostedFreeVersion": "",
"Import": "Ներմուծել",
"ImportIntoTandoor": "",
"Import_finished": "Ներմուծումն ավարտված է",
"Information": "Տեղեկություն",
"Ingredient": "",

View File

@@ -34,6 +34,9 @@
"BaseUnitHelp": "",
"Book": "",
"Bookmarklet": "",
"BookmarkletHelp1": "",
"BookmarkletHelp2": "",
"BookmarkletHelp3": "",
"BookmarkletImportSubtitle": "",
"Books": "Buku",
"Calories": "Kalori",
@@ -157,6 +160,7 @@
"IgnoredFood": "",
"Image": "Gambar",
"Import": "Impor",
"ImportIntoTandoor": "",
"Import_Error": "",
"Import_Not_Yet_Supported": "",
"Import_Result_Info": "",

View File

@@ -39,6 +39,9 @@
"BaseUnitHelp": "",
"Book": "",
"Bookmarklet": "",
"BookmarkletHelp1": "",
"BookmarkletHelp2": "",
"BookmarkletHelp3": "",
"BookmarkletImportSubtitle": "",
"Books": "",
"Calculator": "",
@@ -186,6 +189,7 @@
"Image": "",
"Import": "",
"Import Recipe": "",
"ImportIntoTandoor": "",
"Import_Error": "",
"Import_Not_Yet_Supported": "",
"Import_Result_Info": "",

View File

@@ -38,6 +38,9 @@
"BaseUnitHelp": "",
"Book": "",
"Bookmarklet": "Segnalibro",
"BookmarkletHelp1": "",
"BookmarkletHelp2": "",
"BookmarkletHelp3": "",
"BookmarkletImportSubtitle": "",
"Books": "Libri",
"Calories": "Calorie",
@@ -162,6 +165,7 @@
"IgnoredFood": "{food} è impostato per ignorare la spesa.",
"Image": "Immagine",
"Import": "Importa",
"ImportIntoTandoor": "",
"Import_Error": "Si è verificato un errore durante l'importazione. Per avere maggiori informazioni, espandi la sezione dettagli in fondo alla pagina.",
"Import_Not_Yet_Supported": "Importazione non ancora supportata",
"Import_Result_Info": "{imported} di {total} ricette sono state importate",

View File

@@ -39,6 +39,9 @@
"BaseUnitHelp": "",
"Book": "",
"Bookmarklet": "",
"BookmarkletHelp1": "",
"BookmarkletHelp2": "",
"BookmarkletHelp3": "",
"BookmarkletImportSubtitle": "",
"Books": "",
"Calories": "",
@@ -172,6 +175,7 @@
"Image": "",
"Import": "",
"Import Recipe": "",
"ImportIntoTandoor": "",
"Import_Error": "",
"Import_Not_Yet_Supported": "",
"Import_Result_Info": "",

View File

@@ -38,6 +38,9 @@
"BaseUnitHelp": "",
"Book": "",
"Bookmarklet": "",
"BookmarkletHelp1": "",
"BookmarkletHelp2": "",
"BookmarkletHelp3": "",
"BookmarkletImportSubtitle": "",
"Books": "Bøker",
"Calories": "Kalorier",
@@ -167,6 +170,7 @@
"Image": "Bilde",
"Import": "Importer",
"Import Recipe": "Importer oppskrift",
"ImportIntoTandoor": "",
"Import_Error": "",
"Import_Not_Yet_Supported": "",
"Import_Result_Info": "",

View File

@@ -40,6 +40,9 @@
"BaseUnitHelp": "",
"Book": "",
"Bookmarklet": "Bladwijzer",
"BookmarkletHelp1": "",
"BookmarkletHelp2": "",
"BookmarkletHelp3": "",
"BookmarkletImportSubtitle": "",
"Books": "Boeken",
"Calories": "Calorieën",
@@ -171,6 +174,7 @@
"Image": "Afbeelding",
"Import": "Importeer",
"Import Recipe": "Recept Importeren",
"ImportIntoTandoor": "",
"Import_Error": "Er is een fout opgetreden tijdens je import. Breid de details aan de onderzijde van de pagina uit om ze te bekijken.",
"Import_Not_Yet_Supported": "Import nog niet ondersteund",
"Import_Result_Info": "{imported} van {total} recepten zijn geïmporteerd",

View File

@@ -40,6 +40,9 @@
"BaseUnitHelp": "",
"Book": "",
"Bookmarklet": "Skryptozakładka",
"BookmarkletHelp1": "",
"BookmarkletHelp2": "",
"BookmarkletHelp3": "",
"BookmarkletImportSubtitle": "",
"Books": "Książki",
"Calculator": "Kalkulator",
@@ -188,6 +191,7 @@
"Image": "Obraz",
"Import": "Importuj",
"Import Recipe": "Importuj przepis",
"ImportIntoTandoor": "",
"Import_Error": "Podczas importowania wystąpił błąd. Rozwiń Szczegóły na dole strony, aby go wyświetlić.",
"Import_Not_Yet_Supported": "Importowanie jeszcze nie wspierane",
"Import_Result_Info": "{imported} z {total} przepisów zostało zaimportowanych",

View File

@@ -32,6 +32,9 @@
"BaseUnit": "",
"BaseUnitHelp": "",
"Book": "",
"BookmarkletHelp1": "",
"BookmarkletHelp2": "",
"BookmarkletHelp3": "",
"BookmarkletImportSubtitle": "",
"Books": "Livros",
"Calories": "Calorias",
@@ -142,6 +145,7 @@
"IgnoredFood": "{food} está definida para ignorar compras.",
"Image": "Image",
"Import": "Importar",
"ImportIntoTandoor": "",
"Import_finished": "Importação terminada",
"Information": "Informação",
"Ingredient": "",

View File

@@ -38,6 +38,9 @@
"BaseUnit": "",
"BaseUnitHelp": "",
"Book": "",
"BookmarkletHelp1": "",
"BookmarkletHelp2": "",
"BookmarkletHelp3": "",
"BookmarkletImportSubtitle": "",
"Books": "Livros",
"Calculator": "Calculadora",
@@ -182,6 +185,7 @@
"Image": "Imagem",
"Import": "Importar",
"Import Recipe": "Importar Receita",
"ImportIntoTandoor": "",
"Import_Not_Yet_Supported": "Importação ainda não suportada",
"Import_Result_Info": "{imported} de {total} receitas foram importadas",
"Import_Supported": "Importação suportada",

View File

@@ -38,6 +38,9 @@
"BaseUnitHelp": "",
"Book": "",
"Bookmarklet": "Marcaj",
"BookmarkletHelp1": "",
"BookmarkletHelp2": "",
"BookmarkletHelp3": "",
"BookmarkletImportSubtitle": "",
"Books": "Cărți",
"Calories": "Calorii",
@@ -165,6 +168,7 @@
"Image": "Imagine",
"Import": "Importă",
"Import Recipe": "Importă rețeta",
"ImportIntoTandoor": "",
"Import_Error": "A apărut o eroare în timpul importului. Vă rugăm să extindeți detaliile din partea de jos a paginii pentru a le vizualiza.",
"Import_Not_Yet_Supported": "Importul încă nu este compatibil",
"Import_Result_Info": "{imported} din {total} rețete au fost importate",

View File

@@ -31,6 +31,9 @@
"BaseUnit": "",
"BaseUnitHelp": "",
"Book": "",
"BookmarkletHelp1": "",
"BookmarkletHelp2": "",
"BookmarkletHelp3": "",
"BookmarkletImportSubtitle": "",
"Books": "Книги",
"Calories": "Каллории",
@@ -131,6 +134,7 @@
"IgnoredFood": "{food} будет исключён из списка покупок.",
"Image": "Изображение",
"Import": "Импорт",
"ImportIntoTandoor": "",
"Import_Error": "Во время импорта произошла ошибка. Для просмотра разверните \"Подробности\" в нижней части страницы.",
"Import_finished": "Импорт завершен",
"Imported": "Импортировано",

View File

@@ -30,6 +30,9 @@
"BaseUnit": "",
"BaseUnitHelp": "",
"Book": "",
"BookmarkletHelp1": "",
"BookmarkletHelp2": "",
"BookmarkletHelp3": "",
"BookmarkletImportSubtitle": "",
"Books": "Knjige",
"Calories": "Kalorije",
@@ -130,6 +133,7 @@
"Ignore_Shopping": "Prezri nakup",
"Image": "Slika",
"Import": "Uvozi",
"ImportIntoTandoor": "",
"Import_finished": "Uvoz je končan",
"Information": "Informacija",
"Ingredient": "",

View File

@@ -40,6 +40,9 @@
"BaseUnitHelp": "",
"Book": "",
"Bookmarklet": "Bokmärke",
"BookmarkletHelp1": "",
"BookmarkletHelp2": "",
"BookmarkletHelp3": "",
"BookmarkletImportSubtitle": "",
"Books": "Böcker",
"Calculator": "Räknare",
@@ -188,6 +191,7 @@
"Image": "Bild",
"Import": "Importera",
"Import Recipe": "Importera recept",
"ImportIntoTandoor": "",
"Import_Error": "Ett fel uppstod under din import. Expandera informationen längst ner på sidan för att se den.",
"Import_Not_Yet_Supported": "Import stöds inte ännu",
"Import_Result_Info": "{imported} av totalt {total} recept blev importerat",

View File

@@ -39,6 +39,9 @@
"BaseUnitHelp": "",
"Book": "",
"Bookmarklet": "Yer İmi",
"BookmarkletHelp1": "",
"BookmarkletHelp2": "",
"BookmarkletHelp3": "",
"BookmarkletImportSubtitle": "",
"Books": "Kitaplar",
"Calculator": "Hesap Makinesi",
@@ -187,6 +190,7 @@
"Image": "Resim",
"Import": "İçeriye Aktar",
"Import Recipe": "Tarif İçe Aktar",
"ImportIntoTandoor": "",
"Import_Error": "İçeri aktarma sırasında bir hata oluştu. Görüntülemek için lütfen sayfanın altındaki Ayrıntıları genişletin.",
"Import_Not_Yet_Supported": "İçe aktarma henüz desteklenmiyor",
"Import_Result_Info": "{total} tariften {imported} tanesi içe aktarıldı",

View File

@@ -35,6 +35,9 @@
"BaseUnitHelp": "",
"Book": "",
"Bookmarklet": "",
"BookmarkletHelp1": "",
"BookmarkletHelp2": "",
"BookmarkletHelp3": "",
"BookmarkletImportSubtitle": "",
"Books": "Книжки",
"Calories": "Калорії",
@@ -149,6 +152,7 @@
"IgnoredFood": "{food} ігнорується в покупках.",
"Image": "Зображення",
"Import": "Імпорт",
"ImportIntoTandoor": "",
"Import_Error": "",
"Import_Not_Yet_Supported": "",
"Import_Result_Info": "",

View File

@@ -39,6 +39,9 @@
"BaseUnitHelp": "",
"Book": "",
"Bookmarklet": "书签",
"BookmarkletHelp1": "",
"BookmarkletHelp2": "",
"BookmarkletHelp3": "",
"BookmarkletImportSubtitle": "",
"Books": "书籍",
"Calories": "卡路里",
@@ -183,6 +186,7 @@
"Image": "图片",
"Import": "导入",
"Import Recipe": "导入食谱",
"ImportIntoTandoor": "",
"Import_Error": "导入时发生错误。 请跳转至页面底部的详细信息进行查看。",
"Import_Not_Yet_Supported": "导入尚未支持",
"Import_Result_Info": "导入 {imported} 个,共 {total} 个食谱已导入",

View File

@@ -18,6 +18,9 @@
"BaseUnit": "",
"BaseUnitHelp": "",
"Book": "",
"BookmarkletHelp1": "",
"BookmarkletHelp2": "",
"BookmarkletHelp3": "",
"BookmarkletImportSubtitle": "",
"Books": "",
"Calories": "",
@@ -70,6 +73,7 @@
"History": "",
"HostedFreeVersion": "",
"Import": "",
"ImportIntoTandoor": "",
"Import_finished": "匯入完成",
"Information": "",
"Ingredient": "",

View File

@@ -33,6 +33,10 @@
<v-stepper-item :title="$t('Import')" value="import_log" icon=" "></v-stepper-item>
</template>
<template v-if="importType == 'bookmarklet'">
<v-stepper-item :title="$t('Bookmarklet')" value="bookmarklet" icon=" "></v-stepper-item>
</template>
</v-stepper-header>
<v-stepper-window>
@@ -90,6 +94,7 @@
<template #next>
<v-btn @click="stepper = 'url'" v-if="['url','ai'].includes(importType)">{{ $t('Next') }}</v-btn>
<v-btn @click="stepper = 'app'" v-if="importType == 'app'">{{ $t('Next') }}</v-btn>
<v-btn @click="stepper = 'bookmarklet'" v-if="importType == 'bookmarklet'">{{ $t('Next') }}</v-btn>
</template>
</v-stepper-actions>
</v-stepper-window-item>
@@ -97,6 +102,7 @@
<!-- ------------ -->
<!-- ULR/AI Items -->
<!-- ------------ -->
<v-stepper-window-item value="url">
<v-text-field :label="$t('Website') + ' (https://...)'" v-model="importUrl" v-if="importType == 'url'" :loading="loading"></v-text-field>
@@ -107,7 +113,7 @@
<v-btn @click="stepper = 'type'">{{ $t('Back') }}</v-btn>
</template>
<template #next>
<v-btn @click="loadRecipeFromUrl()" v-if="importType == 'url'" :disabled="importUrl == ''" :loading="loading">{{ $t('Load') }}</v-btn>
<v-btn @click="loadRecipeFromUrl({url: importUrl})" v-if="importType == 'url'" :disabled="importUrl == ''" :loading="loading">{{ $t('Load') }}</v-btn>
<v-btn @click="uploadAndConvertImage()" v-if="importType == 'ai'" :disabled="image == null" :loading="loading">{{ $t('Load') }}</v-btn>
</template>
</v-stepper-actions>
@@ -302,9 +308,11 @@
</template>
</v-stepper-actions>
</v-stepper-window-item>
<!-- ------------ -->
<!-- ---------------- -->
<!-- App Import Items -->
<!-- ------------ -->
<!-- ---------------- -->
<v-stepper-window-item value="app">
<v-row>
@@ -328,23 +336,55 @@
</v-stepper-actions>
</v-stepper-window-item>
<v-stepper-window-item value="file">
<v-file-upload v-model="appImportFile"></v-file-upload>
<v-file-upload v-model="appImportFiles" multiple></v-file-upload>
<v-card variant="outlined" elevation="1" density="compact" :title="$t('Duplicate')" :subtitle="$t('import_duplicates')" class="mt-2">
<template #prepend>
<v-checkbox v-model="appImportDuplicates"></v-checkbox>
</template>
</v-card>
<v-stepper-actions>
<template #prev>
<v-btn @click="stepper = 'app'">{{ $t('Back') }}</v-btn>
</template>
<template #next>
<v-btn @click="appImport()" :disabled="appImportFile == null" :loading="fileApiLoading">{{ $t('Import') }}</v-btn>
<v-btn @click="appImport()" :disabled="appImportFiles.length == 0" :loading="fileApiLoading">{{ $t('Import') }}</v-btn>
</template>
</v-stepper-actions>
</v-stepper-window-item>
<v-stepper-window-item value="import_log">
<import-log-viewer :import-log="appImportLog" v-if="appImportLog"></import-log-viewer>
<v-stepper-actions>
<template #prev>
<v-btn @click="stepper = 'app'">{{ $t('Back') }}</v-btn>
<v-btn @click="stepper = 'file'">{{ $t('Back') }}</v-btn>
</template>
<template #next>
<v-btn :to="{name: 'SearchPage', query: {keywords: appImportLog.keyword.id}}" v-if="appImportLog && !appImportLog.running"
:disabled="false">{{ $t('View_Recipes') }}
</v-btn>
</template>
</v-stepper-actions>
</v-stepper-window-item>
<!-- ------------ -->
<!-- Bookmarklet -->
<!-- ------------ -->
<v-stepper-window-item value="bookmarklet">
{{$t('BookmarkletImportSubtitle')}}
<ol>
<li>1. {{$t('BookmarkletHelp1')}}</li>
<li> <v-btn :href="bookmarkletContent" color="primary">{{$t('ImportIntoTandoor')}}</v-btn></li>
<li>2. {{$t('BookmarkletHelp2')}}</li>
<li>3. {{$t('BookmarkletHelp3')}}</li>
</ol>
<v-stepper-actions>
<template #prev>
<v-btn @click="stepper = 'type'">{{ $t('Back') }}</v-btn>
</template>
<template #next>
@@ -354,13 +394,8 @@
</v-stepper-window>
</template>
</v-stepper>
</v-col>
</v-row>
</v-container>
@@ -368,14 +403,13 @@
<script lang="ts" setup>
import {nextTick, onMounted, ref} from "vue";
import {ApiApi, Keyword, RecipeFromSourceResponse, type SourceImportIngredient, SourceImportKeyword, SourceImportStep} from "@/openapi";
import {computed, onMounted, ref} from "vue";
import {AccessToken, ApiApi, ImportLog, type RecipeFromSource, RecipeFromSourceResponse, type SourceImportIngredient, SourceImportKeyword, SourceImportStep} from "@/openapi";
import {ErrorMessageType, MessageType, useMessageStore} from "@/stores/MessageStore";
import {useRouter} from "vue-router";
import {useUserPreferenceStore} from "@/stores/UserPreferenceStore";
import {VueDraggable} from "vue-draggable-plus";
import VClosableCardTitle from "@/components/dialogs/VClosableCardTitle.vue";
import KeywordsBar from "@/components/display/KeywordsBar.vue";
import {VNumberInput} from 'vuetify/labs/VNumberInput'
import {useFileApi} from "@/composables/useFileApi";
import ModelSelect from "@/components/inputs/ModelSelect.vue";
@@ -383,11 +417,28 @@ import {useDisplay} from "vuetify";
import {useUrlSearchParams} from "@vueuse/core";
import {INTEGRATIONS} from "@/utils/integration_utils";
import {VFileUpload} from 'vuetify/labs/VFileUpload'
import ImportLogViewer from "@/components/display/ImportLogViewer.vue";
import {DateTime} from "luxon";
import {useDjangoUrls} from "@/composables/useDjangoUrls";
import bookmarkletJs from '@/assets/bookmarklet_v3?url'
const params = useUrlSearchParams('history', {})
const {mobile} = useDisplay()
const router = useRouter()
const {updateRecipeImage, convertImageToRecipe, doAppImport, fileApiLoading} = useFileApi()
const {getDjangoUrl} = useDjangoUrls()
const bookmarkletContent = computed(() => {
return 'javascript:(function(){' +
'if(window.bookmarkletTandoor!==undefined){' +
'bookmarkletTandoor();' +
'} else {' +
`localStorage.setItem("importURL", "${getDjangoUrl('/api/bookmarklet-import/')}");` +
`localStorage.setItem("redirectURL", "${getDjangoUrl('/recipe/import/')}");` +
`localStorage.setItem("token", "${bookmarkletToken.value}");` +
`document.body.appendChild(document.createElement("script")).src="${bookmarkletJs}?r="+Math.floor(Math.random()*999999999)}` +
`})()`
})
const importType = ref<'url' | 'ai' | 'app' | 'bookmarklet' | 'source'>("url")
const importApp = ref('DEFAULT')
@@ -396,33 +447,43 @@ const dialog = ref(false)
const loading = ref(false)
const importUrl = ref("")
const appImportFile = ref<null | File>(null)
const appImportFiles = ref<File[]>([])
const appImportDuplicates = ref(false)
const appImportLog = ref<null | ImportLog>(null)
const image = ref<null | File>(null)
const bookmarkletToken = ref("")
const importResponse = ref({} as RecipeFromSourceResponse)
const keywordSelect = ref<null | SourceImportKeyword>(null)
const editingIngredient = ref({} as SourceImportIngredient)
onMounted(() => {
loadOrCreateBookmarkletToken()
// handle manifest share intend passing url to import page
if (params.url && typeof params.url === "string") {
importUrl.value = params.url
loadRecipeFromUrl()
loadRecipeFromUrl({url: importUrl.value})
}
if (params.text && typeof params.text === "string") {
importUrl.value = params.text
loadRecipeFromUrl()
loadRecipeFromUrl({url: importUrl.value})
}
if (params.bookmarklet_import && typeof params.bookmarklet_import === "string" && !isNaN(parseInt(params.bookmarklet_import))) {
importType.value = 'url'
loadRecipeFromUrl({bookmarklet: parseInt(params.bookmarklet_import)})
}
})
/**
* call server to load recipe from a given URl
*/
function loadRecipeFromUrl() {
function loadRecipeFromUrl(recipeFromSourceRequest: RecipeFromSource) {
let api = new ApiApi()
loading.value = true
api.apiRecipeFromSourceCreate({recipeFromSource: {url: importUrl.value}}).then(r => {
api.apiRecipeFromSourceCreate({recipeFromSource: recipeFromSourceRequest}).then(r => {
importResponse.value = r
if (importResponse.value.duplicates && importResponse.value.duplicates.length > 0) {
@@ -451,8 +512,24 @@ function uploadAndConvertImage() {
}
function appImport() {
doAppImport(appImportFile.value, importApp.value, true).then(r => {
doAppImport(appImportFiles.value, importApp.value, appImportDuplicates.value).then(r => {
stepper.value = 'import_log'
recLoadImportLog(r)
})
}
function recLoadImportLog(importLogId: number) {
let api = new ApiApi()
api.apiImportLogRetrieve({id: importLogId}).then(r => {
appImportLog.value = r
if (r.running) {
setTimeout(() => {
recLoadImportLog(importLogId)
}, 1000)
}
}).catch(err => {
useMessageStore().addError(ErrorMessageType.FETCH_ERROR, err)
})
}
@@ -639,6 +716,27 @@ function addStep() {
importResponse.value.recipe?.steps.push({} as SourceImportStep)
}
/**
* load or create an AccessToken with the bookmarklet scope for use in the bookmarklet code
*/
function loadOrCreateBookmarkletToken() {
let api = new ApiApi()
api.apiAccessTokenList().then(r => {
r.forEach(token => {
if (token.scope == 'bookmarklet') {
bookmarkletToken.value = token.token
}
})
if (bookmarkletToken.value == '') {
api.apiAccessTokenCreate({accessToken: {scope: 'bookmarklet', expires: DateTime.now().plus({year: 100}).toJSDate()} as AccessToken}).then(r => {
bookmarkletToken.value = r.token
})
}
})
}
</script>
<style scoped>