all types bascially working (lacking bookmark)

This commit is contained in:
vabene1111
2022-02-22 17:00:30 +01:00
parent 0d98c77301
commit 52c16ab7dd
4 changed files with 107 additions and 52 deletions

View File

@@ -99,7 +99,7 @@ def import_recipe(request):
t.setDaemon(True) t.setDaemon(True)
t.start() t.start()
return JsonResponse({'import_id': [il.pk]}) return JsonResponse({'import_id': il.pk})
except NotImplementedError: except NotImplementedError:
return JsonResponse( return JsonResponse(
{ {

View File

@@ -9,14 +9,16 @@
<div class="row"> <div class="row">
<div class="col col-md-12"> <div class="col col-md-12">
<b-tabs content-class="mt-3"> <b-tabs content-class="mt-3" v-model="tab_index">
<b-tab v-bind:title="$t('Website')" active> <!-- URL Tab -->
<b-tab v-bind:title="$t('Website')" id="id_tab_url">
<b-card no-body> <b-card no-body>
<b-card-header header-tag="header" class="p-1" role="tab"> <b-card-header header-tag="header" class="p-1" role="tab">
<b-button block v-b-toggle.id_accordion_url variant="info">Website</b-button> <b-button block v-b-toggle.id_accordion_url variant="info">Website</b-button>
</b-card-header> </b-card-header>
<b-collapse id="id_accordion_url" visible accordion="url_import_accordion" role="tabpanel"> <b-collapse id="id_accordion_url" visible accordion="url_import_accordion"
role="tabpanel">
<b-card-body> <b-card-body>
<b-input-group> <b-input-group>
<b-input v-model="website_url" placeholder="Website URL" <b-input v-model="website_url" placeholder="Website URL"
@@ -40,10 +42,14 @@
<b-card no-body> <b-card no-body>
<b-card-header header-tag="header" class="p-1" role="tab"> <b-card-header header-tag="header" class="p-1" role="tab">
<b-button block v-b-toggle.id_accordion_add_options variant="info">Additional Options</b-button> <b-button block v-b-toggle.id_accordion_add_options variant="info">Additional
Options
</b-button>
</b-card-header> </b-card-header>
<b-collapse id="id_accordion_add_options" accordion="url_import_accordion" role="tabpanel"> <b-collapse id="id_accordion_add_options" accordion="url_import_accordion"
<b-card-body v-if="recipe_json !== undefined"> <!-- TODO disable/show message if not imported yet --> role="tabpanel">
<b-card-body v-if="recipe_json !== undefined">
<!-- TODO disable/show message if not imported yet -->
<div class="row"> <div class="row">
<div class="col col-md-12 text-center"> <div class="col col-md-12 text-center">
@@ -119,7 +125,8 @@
<b-card-header header-tag="header" class="p-1" role="tab"> <b-card-header header-tag="header" class="p-1" role="tab">
<b-button block v-b-toggle.id_accordion_import variant="info">Import</b-button> <b-button block v-b-toggle.id_accordion_import variant="info">Import</b-button>
</b-card-header> </b-card-header>
<b-collapse id="id_accordion_import" visible accordion="url_import_accordion" role="tabpanel"> <b-collapse id="id_accordion_import" visible accordion="url_import_accordion"
role="tabpanel">
<b-card-body> <b-card-body>
<b-button-group> <b-button-group>
@@ -134,16 +141,50 @@
</b-tab> </b-tab>
<b-tab v-bind:title="$t('App')"> <!-- App Tab -->
<!-- TODO implement app import --> <b-tab v-bind:title="$t('App')" active>
<select class="form-control" v-model="recipe_app">
<option v-for="i in INTEGRATIONS" :value="i.id" v-bind:key="i.id">{{ i.name }}</option>
</select>
<b-form-checkbox v-model="import_duplicates" name="check-button" switch
style="margin-top: 1vh">
{{ $t('import_duplicates') }}
</b-form-checkbox>
<b-form-file
class="my-2"
multiple
v-model="recipe_files"
:placeholder="$t('Select_File')"
drop-placeholder="Drop recipe files here...">
</b-form-file>
<button @click="importAppRecipe()" class="btn btn-primary shadow-none" type="button"
id="id_btn_app"><i class="fas fa-file-archive"></i> {{ $t('Import') }}
</button>
</b-tab> </b-tab>
<!-- Source Tab -->
<b-tab v-bind:title="$t('Source')"> <b-tab v-bind:title="$t('Source')">
<!-- TODO implement source import -->
<div class="input-group mt-4">
<b-textarea class="form-control input-group-append" v-model="source_data" rows=10
:placeholder="$t('paste_json')" style="font-size: 12px">
</b-textarea>
</div>
<b-button @click="loadRecipe()" variant="primary"><i class="fas fa-code"></i> {{ $t('Import') }} </b-button>
</b-tab> </b-tab>
<!-- Bookmarklet Tab -->
<b-tab v-bind:title="$t('Bookmarklet')"> <b-tab v-bind:title="$t('Bookmarklet')">
<!-- TODO get code for bookmarklet here and provide some instructions --> <!-- TODO get code for bookmarklet -->
<a class="btn btn-outline-info btn-sm" href="#"> <!-- TODO localize -->
Bookmark Text </a> Some pages cannot be imported from their URL, the Bookmarklet can be used to import from
some of them anyway.
1. Drag the following button to your bookmarks bar <a class="btn btn-outline-info btn-sm"
href="#"> Bookmark Text </a>
2. Open the page you want to import from
3. Click on the bookmark to perform the import
</b-tab> </b-tab>
</b-tabs> </b-tabs>
@@ -167,6 +208,7 @@ import {resolveDjangoUrl, ResolveUrlMixin, StandardToasts, ToastMixin} from "@/u
import axios from "axios"; import axios from "axios";
import {ApiApiFactory} from "@/utils/openapi/api"; import {ApiApiFactory} from "@/utils/openapi/api";
import draggable from "vuedraggable"; import draggable from "vuedraggable";
import {INTEGRATIONS} from "@/utils/integration";
Vue.use(BootstrapVue) Vue.use(BootstrapVue)
@@ -181,6 +223,8 @@ export default {
}, },
data() { data() {
return { return {
tab_index: 0,
// URL import
LS_IMPORT_RECENT: 'import_recent_urls', //TODO use central helper to manage all local storage keys (and maybe even access) LS_IMPORT_RECENT: 'import_recent_urls', //TODO use central helper to manage all local storage keys (and maybe even access)
website_url: '', website_url: '',
recent_urls: [], recent_urls: [],
@@ -189,10 +233,12 @@ export default {
recipe_data: undefined, recipe_data: undefined,
recipe_tree: undefined, recipe_tree: undefined,
recipe_images: [], recipe_images: [],
automatic: true, // App Import
error: undefined, INTEGRATIONS: INTEGRATIONS,
loading: false, recipe_app: undefined,
preview: false, import_duplicates: false,
recipe_files: [],
} }
}, },
mounted() { mounted() {
@@ -238,9 +284,7 @@ export default {
this.recipe_json = undefined this.recipe_json = undefined
this.recipe_tree = undefined this.recipe_tree = undefined
this.recipe_images = [] this.recipe_images = []
this.error = undefined
this.loading = true
this.preview = false
axios.post(resolveDjangoUrl('api_recipe_from_source'), { axios.post(resolveDjangoUrl('api_recipe_from_source'), {
'url': this.website_url, 'url': this.website_url,
'data': this.source_data, 'data': this.source_data,
@@ -254,21 +298,30 @@ export default {
this.recipe_tree = response.data['recipe_tree']; this.recipe_tree = response.data['recipe_tree'];
this.recipe_html = response.data['recipe_html']; this.recipe_html = response.data['recipe_html'];
this.recipe_images = response.data['recipe_images']; //todo change on backend as well after old view is deprecated this.recipe_images = response.data['recipe_images'];
if (this.automatic) {
this.recipe_data = this.recipe_json; this.tab_index = 0
this.preview = false
} else {
this.preview = true
}
this.loading = false
}).catch((err) => { }).catch((err) => {
this.error = err.data
this.loading = false
console.log(err.response)
StandardToasts.makeStandardToast(StandardToasts.FAIL_FETCH, err.response.data.msg) StandardToasts.makeStandardToast(StandardToasts.FAIL_FETCH, err.response.data.msg)
}) })
}, },
/**
* Import recipes with uploaded files and app integration
*/
importAppRecipe: function () {
let formData = new FormData();
formData.append('type', this.recipe_app);
formData.append('duplicates', this.import_duplicates)
for (let i = 0; i < this.recipe_files.length; i++) {
formData.append('files', this.recipe_files[i]);
}
axios.post(resolveDjangoUrl('view_import'), formData, {headers: {'Content-Type': 'multipart/form-data'}}).then((response) => {
window.location.href = resolveDjangoUrl('view_import_response', response.data['import_id'])
}).catch((err) => {
console.log(err)
StandardToasts.makeStandardToast(StandardToasts.FAIL_CREATE)
})
},
/** /**
* Splits the steps of a given recipe at the split character (e.g. \n or \n\n) * Splits the steps of a given recipe at the split character (e.g. \n or \n\n)
* @param split_character: character to split steps at * @param split_character: character to split steps at

View File

@@ -336,6 +336,8 @@
"Website": "Website", "Website": "Website",
"App": "App", "App": "App",
"Bookmarklet": "Bookmarklet", "Bookmarklet": "Bookmarklet",
"import_duplicates": "To prevent duplicates recipes with the same name as existing ones are ignored. Check this box to import everything.",
"paste_json": "Paste json or html source here to load recipe.",
"search_no_recipes": "Could not find any recipes!", "search_no_recipes": "Could not find any recipes!",
"search_import_help_text": "Import a recipe from an external website or application.", "search_import_help_text": "Import a recipe from an external website or application.",
"search_create_help_text": "Create a new recipe directly in Tandoor.", "search_create_help_text": "Create a new recipe directly in Tandoor.",

View File

@@ -1,22 +1,22 @@
// containing all data and functions regarding the different integrations // containing all data and functions regarding the different integrations
export const INTEGRATIONS = { export const INTEGRATIONS = [
DEFAULT: {name: "Tandoor", import: true, export: true}, {id: 'DEFAULT', name: "Tandoor", import: true, export: true},
CHEFTAP: {name: "Cheftap", import: true, export: false}, {id: 'CHEFTAP', name: "Cheftap", import: true, export: false},
CHOWDOWN: {name: "Chowdown", import: true, export: false}, {id: 'CHOWDOWN', name: "Chowdown", import: true, export: false},
COOKBOOKAPP: {name: "CookBookApp", import: true, export: false}, {id: 'COOKBOOKAPP', name: "CookBookApp", import: true, export: false},
COPYMETHAT: {name: "CopyMeThat", import: true, export: false}, {id: 'COPYMETHAT', name: "CopyMeThat", import: true, export: false},
DOMESTICA: {name: "Domestica", import: true, export: false}, {id: 'DOMESTICA', name: "Domestica", import: true, export: false},
MEALIE: {name: "Mealie", import: true, export: false}, {id: 'MEALIE', name: "Mealie", import: true, export: false},
MEALMASTER: {name: "Mealmaster", import: true, export: false}, {id: 'MEALMASTER', name: "Mealmaster", import: true, export: false},
NEXTCLOUD: {name: "Nextcloud Cookbook", import: true, export: false}, {id: 'NEXTCLOUD', name: "Nextcloud Cookbook", import: true, export: false},
OPENEATS: {name: "Openeats", import: true, export: false}, {id: 'OPENEATS', name: "Openeats", import: true, export: false},
PAPRIKA: {name: "Paprika", import: true, export: false}, {id: 'PAPRIKA', name: "Paprika", import: true, export: false},
PEPPERPLATE: {name: "Pepperplate", import: true, export: false}, {id: 'PEPPERPLATE', name: "Pepperplate", import: true, export: false},
PLANTOEAT: {name: "Plantoeat", import: true, export: false}, {id: 'PLANTOEAT', name: "Plantoeat", import: true, export: false},
RECETTETEK: {name: "RecetteTek", import: true, export: false}, {id: 'RECETTETEK', name: "RecetteTek", import: true, export: false},
RECIPEKEEPER: {name: "Recipekeeper", import: true, export: false}, {id: 'RECIPEKEEPER', name: "Recipekeeper", import: true, export: false},
RECIPESAGE: {name: "Recipesage", import: true, export: true}, {id: 'RECIPESAGE', name: "Recipesage", import: true, export: true},
REZKONV: {name: "Rezkonv", import: true, export: false}, {id: 'REZKONV', name: "Rezkonv", import: true, export: false},
SAFRON: {name: "Safron", import: true, export: true}, {id: 'SAFRON', name: "Safron", import: true, export: true},
} ]