simplified url import
This commit is contained in:
parent
c6739ba8e0
commit
efe4c4043d
@ -58,6 +58,7 @@ def get_recipe_from_source(text, url, space):
|
|||||||
|
|
||||||
recipe_json = {
|
recipe_json = {
|
||||||
'name': '',
|
'name': '',
|
||||||
|
'url': '',
|
||||||
'description': '',
|
'description': '',
|
||||||
'image': '',
|
'image': '',
|
||||||
'keywords': [],
|
'keywords': [],
|
||||||
|
@ -102,6 +102,7 @@ def get_from_scraper(scrape, space):
|
|||||||
recipe_json['recipeInstructions'] = ""
|
recipe_json['recipeInstructions'] = ""
|
||||||
|
|
||||||
if scrape.url:
|
if scrape.url:
|
||||||
|
recipe_json['url'] = scrape.url
|
||||||
recipe_json['recipeInstructions'] += "\n\nImported from " + scrape.url
|
recipe_json['recipeInstructions'] += "\n\nImported from " + scrape.url
|
||||||
return recipe_json
|
return recipe_json
|
||||||
|
|
||||||
|
@ -22,20 +22,13 @@
|
|||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<div id="app">
|
<div id="app">
|
||||||
<div class="row px-3" style="justify-content:space-between;">
|
<h2> {% trans 'Import' %} </h2>
|
||||||
<h2> {% trans 'Import' %}</h2>
|
<nav class="nav nav-pills flex-sm-row" style="margin-bottom:10px">
|
||||||
<a class="btn btn-outline-info btn-sm"
|
<a class="nav-link active" href="#nav-url" data-toggle="tab" role="tab" aria-controls="nav-url" aria-selected="true" @click="mode='url'">URL</a>
|
||||||
style="height:50%"
|
<a class="nav-link" href="#nav-app" data-toggle="tab" role="tab" aria-controls="nav-app" @click="mode='app'">App</a>
|
||||||
href="{% bookmarklet request %}"
|
<a class="nav-link" href="#nav-source" data-toggle="tab" role="tab" aria-controls="nav-source" @click="mode='source'">Source</a>
|
||||||
title="{% trans 'Drag me to your bookmarks to import recipes from anywhere' %}">
|
<a class="nav-link disabled" href="#nav-text" data-toggle="tab" role="tab" aria-controls="nav-text" @click="mode='text'">Text</a>
|
||||||
<img src="{% static 'assets/favicon-16x16.png' %}">{% trans 'Bookmark Me!' %} </a>
|
<a class="nav-link disabled" href="#nav-file" data-toggle="tab" role="tab" aria-controls="nav-file" @click="mode='file'">File</a>
|
||||||
</div>
|
|
||||||
<nav class="nav nav-pills flex-sm-row" id="nav-tabs" style="margin-bottom:10px">
|
|
||||||
<a class="nav-link active" href="#nav-url" data-toggle="tab" role="tab" >URL</a>
|
|
||||||
<a class="nav-link" href="#nav-app" data-toggle="tab" role="tab" >App</a>
|
|
||||||
<a class="nav-link" href="#nav-source" data-toggle="tab" role="tab" >Source</a>
|
|
||||||
<a class="nav-link disabled" href="#nav-text" data-toggle="tab" role="tab" >Text</a>
|
|
||||||
<a class="nav-link disabled" href="#nav-file" data-toggle="tab" role="tab" >File</a>
|
|
||||||
</nav>
|
</nav>
|
||||||
|
|
||||||
|
|
||||||
@ -111,7 +104,7 @@
|
|||||||
<textarea class="form-control input-group-append" v-model="source_data" rows=10 placeholder="{% trans 'Paste json or html source here to load recipe.' %}" style="font-size: 12px">
|
<textarea class="form-control input-group-append" v-model="source_data" rows=10 placeholder="{% trans 'Paste json or html source here to load recipe.' %}" style="font-size: 12px">
|
||||||
</textarea>
|
</textarea>
|
||||||
</div>
|
</div>
|
||||||
<button @click="loadSource()" class="btn btn-primary shadow-none" type="button"
|
<button @click="loadRecipe()" class="btn btn-primary shadow-none" type="button"
|
||||||
id="id_btn_app"><i class="fas fa-code"></i> {% trans 'Import' %}
|
id="id_btn_app"><i class="fas fa-code"></i> {% trans 'Import' %}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
@ -177,7 +170,7 @@
|
|||||||
<b-collapse id="collapse-kw" visible class="mt-2">
|
<b-collapse id="collapse-kw" visible class="mt-2">
|
||||||
<div class="card-body drop-zone" @drop="replacePreview('keywords', $event)" @dragover.prevent @dragenter.prevent>
|
<div class="card-body drop-zone" @drop="replacePreview('keywords', $event)" @dragover.prevent @dragenter.prevent>
|
||||||
<div v-for="kw in recipe_json.keywords">
|
<div v-for="kw in recipe_json.keywords">
|
||||||
<div class="card-text">[[kw]] </div>
|
<div class="card-text">[[kw.text]] </div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</b-collapse>
|
</b-collapse>
|
||||||
@ -273,7 +266,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<br/>
|
<br/>
|
||||||
<!-- end of preview card -->
|
<!-- end of preview card -->
|
||||||
<button @click="loadRecipeManual()" class="btn btn-primary shadow-none" type="button"
|
<button @click="showRecipe()" class="btn btn-primary shadow-none" type="button"
|
||||||
id="id_btn_json"><i class="fas fa-code"></i> {% trans 'Import' %}
|
id="id_btn_json"><i class="fas fa-code"></i> {% trans 'Import' %}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
@ -638,6 +631,7 @@
|
|||||||
recipe_app: 'DEFAULT',
|
recipe_app: 'DEFAULT',
|
||||||
recipe_files: [],
|
recipe_files: [],
|
||||||
images: [],
|
images: [],
|
||||||
|
mode: 'url'
|
||||||
},
|
},
|
||||||
directives: {
|
directives: {
|
||||||
tabindex: {
|
tabindex: {
|
||||||
@ -675,51 +669,20 @@
|
|||||||
this.error = undefined
|
this.error = undefined
|
||||||
this.loading = true
|
this.loading = true
|
||||||
this.preview = false
|
this.preview = false
|
||||||
if (this.automatic) {
|
this.$http.post("{% url 'api_recipe_from_source' %}", {
|
||||||
console.log('true')
|
'url': this.remote_url,
|
||||||
}
|
'data': this.source_data,
|
||||||
this.$http.post("{% url 'api_recipe_from_url' %}", {'url': this.remote_url, 'auto':this.automatic}, {emulateJSON: true}).then((response) => {
|
'auto':this.automatic,
|
||||||
|
'mode':this.mode}, {emulateJSON: true}).then((response) => {
|
||||||
console.log(response.data)
|
console.log(response.data)
|
||||||
|
this.recipe_json = response.data['recipe_json'];
|
||||||
|
this.recipe_tree = response.data['recipe_tree'];
|
||||||
|
this.recipe_html = response.data['recipe_html'];
|
||||||
|
this.images = response.data['images'];
|
||||||
if (this.automatic) {
|
if (this.automatic) {
|
||||||
this.recipe_data = response.data;
|
this.recipe_data = this.recipe_json;
|
||||||
} else {
|
|
||||||
this.recipe_json = response.data['recipe_json'];
|
|
||||||
this.recipe_tree = response.data['recipe_tree'];
|
|
||||||
this.recipe_html = response.data['recipe_html'];
|
|
||||||
this.images = response.data['images'];
|
|
||||||
this.preview = true
|
|
||||||
}
|
|
||||||
this.loading = false
|
|
||||||
}).catch((err) => {
|
|
||||||
this.error = err.data
|
|
||||||
this.loading = false
|
|
||||||
console.log(err)
|
|
||||||
let msg = gettext('There was an error loading a resource!')
|
|
||||||
if (err.bodyText.length < 300) {
|
|
||||||
msg += err.bodyText
|
|
||||||
} else {
|
|
||||||
msg += ' ' + err.status + ' ' + err.statusText
|
|
||||||
}
|
|
||||||
this.makeToast(gettext('Error'), msg, 'danger')
|
|
||||||
})
|
|
||||||
},
|
|
||||||
loadSource: function() {
|
|
||||||
this.recipe_data = undefined
|
|
||||||
this.recipe_json = undefined
|
|
||||||
this.recipe_tree = undefined
|
|
||||||
this.images = []
|
|
||||||
this.error = undefined
|
|
||||||
this.loading = true
|
|
||||||
this.preview = false
|
|
||||||
this.$http.post("{% url 'api_recipe_from_source' %}", {'data': this.source_data, 'url': this.remote_url, 'auto':this.automatic}, {emulateJSON: true}).then((response) => {
|
|
||||||
console.log(response.data)
|
|
||||||
if (this.automatic) {
|
|
||||||
this.recipe_data = response.data['recipe_json'];
|
|
||||||
this.preview = false
|
this.preview = false
|
||||||
} else {
|
} else {
|
||||||
this.recipe_json = response.data['recipe_json'];
|
|
||||||
this.recipe_tree = response.data['recipe_tree'];
|
|
||||||
this.recipe_html = response.data['recipe_html'];
|
|
||||||
this.preview = true
|
this.preview = true
|
||||||
}
|
}
|
||||||
this.loading = false
|
this.loading = false
|
||||||
@ -736,51 +699,9 @@
|
|||||||
this.makeToast(gettext('Error'), msg, 'danger')
|
this.makeToast(gettext('Error'), msg, 'danger')
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
loadBookmarklet: function(id_bkmk) {
|
showRecipe: function() {
|
||||||
let uri = window.location.search.substring(1);
|
|
||||||
let params = new URLSearchParams(uri);
|
|
||||||
q = params.get("id")
|
|
||||||
console.log(q)
|
|
||||||
this.error = undefined
|
|
||||||
this.loading = true
|
|
||||||
this.$http.get("{% url 'api:bookmarkletimport-list' %}?id=" + id_bkmk ).then((response) => {
|
|
||||||
console.log(response.data)
|
|
||||||
this.automatic = false
|
|
||||||
this.source_data = response.data[0]['html']
|
|
||||||
this.remote_url = response.data[0]['url']
|
|
||||||
this.loadSource()
|
|
||||||
}).catch((err) => {
|
|
||||||
this.error = err.data
|
|
||||||
this.loading = false
|
|
||||||
console.log(err)
|
|
||||||
this.makeToast(gettext('Error'), gettext('Bookmarklet not found!') , 'danger')
|
|
||||||
})
|
|
||||||
},
|
|
||||||
deleteBookmarklet: function(id_bkmk) {
|
|
||||||
this.error = undefined
|
|
||||||
this.$http.delete("{% url 'api:bookmarkletimport-list' %}" + id_bkmk +"/").then((response) => {
|
|
||||||
}).catch((err) => {
|
|
||||||
this.error = err.data
|
|
||||||
console.log(err)
|
|
||||||
this.makeToast(gettext('Error'), gettext('There was an error deleting bookmarklet!') + err.bodyText, 'danger')
|
|
||||||
})
|
|
||||||
},
|
|
||||||
loadRecipeManual: function () {
|
|
||||||
this.error = undefined
|
|
||||||
this.preview = false
|
this.preview = false
|
||||||
this.loading = true
|
this.recipe_data = this.recipe_json
|
||||||
this.recipe_json['@type'] = "Recipe"
|
|
||||||
this.$http.post("{% url 'api_recipe_from_source' %}", {'data': JSON.stringify(this.recipe_json), 'auto':'true'}, {emulateJSON: true}).then((response) => {
|
|
||||||
console.log(response.data)
|
|
||||||
this.recipe_data = response.data['recipe_json'];
|
|
||||||
this.loading = false
|
|
||||||
this.preview = false
|
|
||||||
}).catch((err) => {
|
|
||||||
this.error = err.data
|
|
||||||
this.loading = false
|
|
||||||
console.log(err)
|
|
||||||
this.makeToast(gettext('Error'), gettext('There was an error loading a resource!') + err.bodyText, 'danger')
|
|
||||||
})
|
|
||||||
},
|
},
|
||||||
importRecipe: function () {
|
importRecipe: function () {
|
||||||
if (this.recipe_data.name.length > 128) {
|
if (this.recipe_data.name.length > 128) {
|
||||||
@ -955,7 +876,8 @@
|
|||||||
this.recipe_json.image=v
|
this.recipe_json.image=v
|
||||||
break;
|
break;
|
||||||
case 'keywords':
|
case 'keywords':
|
||||||
this.recipe_json.keywords.push(v)
|
let new_keyword = {'text': v, 'id': null}
|
||||||
|
this.recipe_json.keywords.push(new_keyword)
|
||||||
break;
|
break;
|
||||||
case 'servings':
|
case 'servings':
|
||||||
this.recipe_json.servings=v
|
this.recipe_json.servings=v
|
||||||
|
@ -93,7 +93,6 @@ urlpatterns = [
|
|||||||
path('api/sync_all/', api.sync_all, name='api_sync'),
|
path('api/sync_all/', api.sync_all, name='api_sync'),
|
||||||
path('api/log_cooking/<int:recipe_id>/', api.log_cooking, name='api_log_cooking'),
|
path('api/log_cooking/<int:recipe_id>/', api.log_cooking, name='api_log_cooking'),
|
||||||
path('api/plan-ical/<slug:from_date>/<slug:to_date>/', api.get_plan_ical, name='api_get_plan_ical'),
|
path('api/plan-ical/<slug:from_date>/<slug:to_date>/', api.get_plan_ical, name='api_get_plan_ical'),
|
||||||
path('api/recipe-from-url/', api.recipe_from_url, name='api_recipe_from_url'),
|
|
||||||
path('api/recipe-from-source/', api.recipe_from_source, name='api_recipe_from_source'),
|
path('api/recipe-from-source/', api.recipe_from_source, name='api_recipe_from_source'),
|
||||||
path('api/backup/', api.get_backup, name='api_backup'),
|
path('api/backup/', api.get_backup, name='api_backup'),
|
||||||
path('api/ingredient-from-string/', api.ingredient_from_string, name='api_ingredient_from_string'),
|
path('api/ingredient-from-string/', api.ingredient_from_string, name='api_ingredient_from_string'),
|
||||||
|
@ -548,100 +548,87 @@ def get_plan_ical(request, from_date, to_date):
|
|||||||
|
|
||||||
|
|
||||||
@group_required('user')
|
@group_required('user')
|
||||||
def recipe_from_url(request):
|
def recipe_from_source(request):
|
||||||
url = request.POST['url']
|
url = request.POST.get('url', None)
|
||||||
if 'auto' in request.POST:
|
data = request.POST.get('data', None)
|
||||||
auto = request.POST['auto']
|
mode = request.POST.get('mode', None)
|
||||||
else:
|
auto = request.POST.get('auto', 'true')
|
||||||
auto = 'true'
|
|
||||||
|
|
||||||
if auto == 'false':
|
if (not url and not data) or (mode == 'url' and not url) or (mode == 'source' and not data):
|
||||||
headers = {
|
|
||||||
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.106 Safari/537.36' # noqa: E501
|
|
||||||
}
|
|
||||||
try:
|
|
||||||
response = requests.get(url, headers=headers)
|
|
||||||
except requests.exceptions.ConnectionError:
|
|
||||||
return JsonResponse(
|
|
||||||
{
|
|
||||||
'error': True,
|
|
||||||
'msg': _('The requested page could not be found.')
|
|
||||||
},
|
|
||||||
status=400
|
|
||||||
)
|
|
||||||
|
|
||||||
if response.status_code == 403:
|
|
||||||
return JsonResponse(
|
|
||||||
{
|
|
||||||
'error': True,
|
|
||||||
'msg': _('The requested page refused to provide any information (Status Code 403).') # noqa: E501
|
|
||||||
},
|
|
||||||
status=400
|
|
||||||
)
|
|
||||||
return recipe_from_source(request, url=url, url_text=response.text)
|
|
||||||
|
|
||||||
try:
|
|
||||||
scrape = scrape_me(url)
|
|
||||||
except WebsiteNotImplementedError:
|
|
||||||
try:
|
|
||||||
scrape = scrape_me(url, wild_mode=True)
|
|
||||||
except NoSchemaFoundInWildMode:
|
|
||||||
return JsonResponse(
|
|
||||||
{
|
|
||||||
'error': True,
|
|
||||||
'msg': _('The requested site provided malformed data and cannot be read.') # noqa: E501
|
|
||||||
},
|
|
||||||
status=400)
|
|
||||||
except ConnectionError:
|
|
||||||
return JsonResponse(
|
return JsonResponse(
|
||||||
{
|
{
|
||||||
'error': True,
|
'error': True,
|
||||||
'msg': _('The requested page could not be found.')
|
'msg': _('Nothing to do.')
|
||||||
},
|
},
|
||||||
status=400
|
status=400
|
||||||
)
|
)
|
||||||
if len(scrape.schema.data) == 0:
|
|
||||||
return JsonResponse(
|
|
||||||
{
|
|
||||||
'error': True,
|
|
||||||
'msg': _('The requested site does not provide any recognized data format to import the recipe from.') # noqa: E501
|
|
||||||
},
|
|
||||||
status=400)
|
|
||||||
else:
|
|
||||||
return JsonResponse(get_from_scraper(scrape, request.space))
|
|
||||||
|
|
||||||
|
if mode == 'url':
|
||||||
@group_required('user')
|
if auto == 'true':
|
||||||
def recipe_from_source(request, url=None, url_text=None):
|
try:
|
||||||
if url_text:
|
scrape = scrape_me(url)
|
||||||
json_data = url_text
|
except WebsiteNotImplementedError:
|
||||||
else:
|
try:
|
||||||
json_data = request.POST['data']
|
scrape = scrape_me(url, wild_mode=True)
|
||||||
if 'auto' in request.POST:
|
except NoSchemaFoundInWildMode:
|
||||||
auto = request.POST['auto']
|
return JsonResponse(
|
||||||
else:
|
{
|
||||||
auto = 'true'
|
'error': True,
|
||||||
if 'url' in request.POST:
|
'msg': _('The requested site provided malformed data and cannot be read.') # noqa: E501
|
||||||
url = request.POST['url']
|
},
|
||||||
|
status=400)
|
||||||
recipe_json, recipe_tree, recipe_html, images = get_recipe_from_source(json_data, url, request.space)
|
except ConnectionError:
|
||||||
if len(recipe_tree) == 0 and len(recipe_json) == 0:
|
return JsonResponse(
|
||||||
return JsonResponse(
|
{
|
||||||
{
|
'error': True,
|
||||||
'error': True,
|
'msg': _('The requested page could not be found.')
|
||||||
'msg': _('No useable data could be found.') # noqa: E501
|
},
|
||||||
},
|
status=400
|
||||||
status=400
|
)
|
||||||
)
|
if len(scrape.schema.data) == 0:
|
||||||
else:
|
return JsonResponse(
|
||||||
if auto == "true":
|
{
|
||||||
return JsonResponse({'recipe_json': recipe_json})
|
'error': True,
|
||||||
|
'msg': _('The requested site does not provide any recognized data format to import the recipe from.') # noqa: E501
|
||||||
|
},
|
||||||
|
status=400)
|
||||||
|
else:
|
||||||
|
return JsonResponse(get_from_scraper(scrape, request.space))
|
||||||
|
else:
|
||||||
|
headers = {
|
||||||
|
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.106 Safari/537.36' # noqa: E501
|
||||||
|
}
|
||||||
|
try:
|
||||||
|
response = requests.get(url, headers=headers)
|
||||||
|
except requests.exceptions.ConnectionError:
|
||||||
|
return JsonResponse(
|
||||||
|
{
|
||||||
|
'error': True,
|
||||||
|
'msg': _('The requested page could not be found.')
|
||||||
|
},
|
||||||
|
status=400
|
||||||
|
)
|
||||||
|
|
||||||
|
if response.status_code == 403:
|
||||||
|
return JsonResponse(
|
||||||
|
{
|
||||||
|
'error': True,
|
||||||
|
'msg': _('The requested page refused to provide any information (Status Code 403).')
|
||||||
|
},
|
||||||
|
status=400
|
||||||
|
)
|
||||||
|
data = response.text
|
||||||
|
if (mode == 'source') or (mode == 'url' and auto == 'false'):
|
||||||
|
recipe_json, recipe_tree, recipe_html, images = get_recipe_from_source(data, url, request.space)
|
||||||
|
if len(recipe_tree) == 0 and len(recipe_json) == 0:
|
||||||
|
return JsonResponse(
|
||||||
|
{
|
||||||
|
'error': True,
|
||||||
|
'msg': _('No useable data could be found.')
|
||||||
|
},
|
||||||
|
status=400
|
||||||
|
)
|
||||||
else:
|
else:
|
||||||
# overide keyword structure from dict to list
|
|
||||||
kws = []
|
|
||||||
for kw in recipe_json['keywords']:
|
|
||||||
kws.append(kw['text'])
|
|
||||||
recipe_json['keywords'] = kws
|
|
||||||
return JsonResponse({
|
return JsonResponse({
|
||||||
'recipe_tree': recipe_tree,
|
'recipe_tree': recipe_tree,
|
||||||
'recipe_json': recipe_json,
|
'recipe_json': recipe_json,
|
||||||
@ -649,6 +636,14 @@ def recipe_from_source(request, url=None, url_text=None):
|
|||||||
'images': images,
|
'images': images,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
return JsonResponse(
|
||||||
|
{
|
||||||
|
'error': True,
|
||||||
|
'msg': _('I couldn\'t find anything to do.')
|
||||||
|
},
|
||||||
|
status=400
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
@group_required('admin')
|
@group_required('admin')
|
||||||
def get_backup(request):
|
def get_backup(request):
|
||||||
|
Loading…
Reference in New Issue
Block a user