combined json import and source import

This commit is contained in:
smilerz 2021-03-21 13:13:56 -05:00
parent 4015517c0a
commit 40a2f7ff90
5 changed files with 98 additions and 137 deletions

View File

@ -7,10 +7,7 @@ from bs4.element import Tag
from cookbook.helper import recipe_url_import as helper
# %%
# %%
def get_from_raw(text, space):
def get_recipe_from_source(text, space):
def build_node(k, v):
if isinstance(v, dict):
node = {
@ -113,17 +110,20 @@ def get_from_raw(text, space):
if '@graph' in el:
for x in el['@graph']:
if '@type' in x and x['@type'] == 'Recipe':
recipe_json = helper.find_recipe_json(x, None, space)
recipe_tree += [{'name': 'ld+json', 'children': temp_tree}]
el = x
if '@type' in el and el['@type'] == 'Recipe':
recipe_json = helper.find_recipe_json(el, None, space)
recipe_tree += [{'name': 'ld+json', 'children': temp_tree}]
else:
recipe_tree += [{'name': 'json', 'children': temp_tree}]
temp_tree = []
# overide keyword structure from dict to list
kws = []
for kw in recipe_json['keywords']:
kws.append(kw['text'])
recipe_json['keywords'] = kws
return recipe_json, recipe_tree
def get_from_html(text, space):
for s in soup.strings:
if ((s.parent.name not in INVISIBLE_ELEMS) and (len(s.strip()) > 0)):
print(s.parent.name, s, len(s))

View File

@ -15,7 +15,7 @@ from django.utils.translation import gettext as _
from recipe_scrapers import _utils
def get_from_html(html_text, url, space):
def get_from_html_old(html_text, url, space):
soup = BeautifulSoup(html_text, "html.parser")
# first try finding ld+json as its most common

View File

@ -25,72 +25,54 @@
<h2> {% trans 'Import' %} </h2>
<nav class="nav nav-pills flex-sm-row" style="margin-bottom:10px">
<a class="nav-link active" href="#nav-url" data-toggle="tab" role="tab" aria-controls="nav-url" aria-selected="true">URL</a>
<a class="nav-link" href="#nav-ldjson" data-toggle="tab" role="tab" aria-controls="nav-ldjson">ld+json</a>
<a class="nav-link" href="#nav-json" data-toggle="tab" role="tab" aria-controls="nav-json">json</a>
<a class="nav-link disabled" href="#nav-html" data-toggle="tab" role="tab" aria-controls="nav-html">HTML</a>
<a class="nav-link disabled" href="#nav-text" data-toggle="tab" role="tab" aria-controls="nav-text">text</a>
<a class="nav-link disabled" href="#nav-pdf" data-toggle="tab" role="tab" aria-controls="nav-pdf">PDF</a>
<a class="nav-link" href="#nav-source" data-toggle="tab" role="tab" aria-controls="nav-source">Source</a>
<a class="nav-link disabled" href="#nav-text" data-toggle="tab" role="tab" aria-controls="nav-text">Text</a>
<a class="nav-link disabled" href="#nav-file" data-toggle="tab" role="tab" aria-controls="nav-file">File</a>
</nav>
<div class="tab-content" id="nav-tabContent">
<!-- Import URL -->
<div class="row tab-pane fade show active" id="nav-url" role="tabpanel">
<div class="col-md-12">
<div class="input-group mb-3">
<input class="form-control" v-model="remote_url" placeholder="{% trans 'Enter website URL' %}">
<div class="input-group-append">
<button @click="loadRecipe()" class="btn btn-primary shadow-none" type="button"
id="id_btn_search"><i class="fas fa-search"></i>
</button>
</div>
</div>
</div>
</div>
<div class="tab-pane fade show active" id="nav-url" role="tabpanel">
<div class="btn-group btn-group-toggle" data-toggle="buttons">
<label class="btn btn-outline-info btn-sm active" @click="automatic=true">
<input type="radio" name="auto" id="auto" autocomplete="off" checked> Automatic
</label>
<!-- Automatically import LD+JSON -->
<div class="row tab-pane fade show" id="nav-ldjson" role="tabpanel">
<div class="col-md-12">
<div class="input-group input-group-lg">
<textarea class="form-control input-group-append" v-model="raw_data" rows=10 placeholder="{% trans 'Paste ld+json here to parse recipe automatically.' %}" style="font-size: 12px">
</textarea>
</div>
<br>
<button @click="loadRecipeJson()" class="btn btn-primary shadow-none" type="button"
id="id_btn_json"><i class="fas fa-code"></i> {% trans 'Import' %}
<label class="btn btn-outline-info btn-sm disabled" @click="automatic=false">
<input type="radio" name="auto" id="manual" autocomplete="off"> Manual
</label>
</div>
<div class="input-group mb-3">
<input class="form-control" v-model="remote_url" placeholder="{% trans 'Enter website URL' %}">
<div class="input-group-append">
<button @click="loadRecipe()" class="btn btn-primary shadow-none" type="button"
id="id_btn_search"><i class="fas fa-search"></i>
</button>
</div>
</div>
<!-- Manually import from JSON -->
<div class="row tab-pane fade show" id="nav-json" role="tabpanel">
<div class="col-md-12">
<div class="input-group input-group-lg">
<textarea class="form-control input-group-append" v-model="raw_data" rows=10
placeholder="{% trans 'To parse recipe manually: Paste JSON document here or a web page source that contains one or more JSON elements here.' %}" style="font-size: 12px">
</textarea>
</div>
<br>
<button @click="loadPreviewRaw()" class="btn btn-primary shadow-none" type="button"
id="id_btn_raw"><i class="fas fa-code"></i> {% trans 'Preview Import' %}
</button>
</div>
</div>
<!-- Manually import from HTML -->
<div class="row tab-pane fade show" id="nav-html" role="tabpanel">
<div class="col-md-12">
<div class="input-group input-group-lg">
<textarea class="form-control input-group-append" v-model="raw_data" rows=10 placeholder="{% trans 'Paste html source here to parse recipe manually.' %}" style="font-size: 12px">
</textarea>
</div>
<br>
<button @click="loadPreviewRaw()" class="btn btn-primary shadow-none" type="button"
id="id_btn_HTML"><i class="fas fa-code"></i> {% trans 'Preview Import' %}
</button>
</div>
</div>
<!-- Import JSON or HTML -->
<div class=" tab-pane fade show" id="nav-source" role="tabpanel">
<div class="btn-group btn-group-toggle" data-toggle="buttons">
<label class="btn btn-outline-info btn-sm active" @click="automatic=true">
<input type="radio" name="auto" id="auto" autocomplete="off" checked> Automatic
</label>
<label class="btn btn-outline-info btn-sm" @click="automatic=false">
<input type="radio" name="auto" id="manual" autocomplete="off"> Manual
</label>
</div>
<div class="input-group input-group-lg">
<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>
</div>
<br>
<button @click="loadSource()" class="btn btn-primary shadow-none" type="button"
id="id_btn_json"><i class="fas fa-code"></i> {% trans 'Import' %}
</button>
</div>
</div>
<br/>
@ -101,9 +83,7 @@
</div>
<!-- recipe preview before Import -->
<div class="container-fluid" v-if="parsed" id="manage_tree">
<h2></h2>
<div class="container-fluid" v-if="preview" id="manage_tree">
<div class="row">
<div class="col" style="max-width:50%">
<div class="card card-border-primary" >
@ -116,6 +96,7 @@
<div class="card mb-2">
<div class="card-header" style="display:flex; justify-content:space-between;">
{% trans 'Name' %}
<!-- this and subsequent delete commands should be fixed to identify which model attribute the element is referring to -->
<i class="fas fa-eraser" style="cursor:pointer;" @click="deletePreview('name')" title="{% trans 'Clear Contents'%}"></i>
</div>
@ -232,11 +213,13 @@
</div>
</div>
<br/>
<button @click="loadRecipeRaw()" class="btn btn-primary shadow-none" type="button"
<!-- end of preview card -->
<button @click="loadRecipeManual()" class="btn btn-primary shadow-none" type="button"
id="id_btn_json"><i class="fas fa-code"></i> {% trans 'Import' %}
</button>
</div>
<!-- start of json tree -->
<div class="col" style="max-width:50%">
<div class="card card-border-primary">
<div class="card-header">
@ -274,9 +257,9 @@
</div>
</div>
</div>
<!-- end of json tree -->
</div>
</div>
<!-- end of recipe preview before Import -->
<template v-if="recipe_data !== undefined">
@ -518,7 +501,7 @@
el: '#app',
data: {
remote_url: '',
raw_data: undefined,
source_data: undefined,
keywords: [],
keywords_loading: false,
units: [],
@ -528,11 +511,12 @@
recipe_data: undefined,
error: undefined,
loading: false,
parsed: false,
preview: false,
all_keywords: false,
importing_recipe: false,
recipe_json: undefined,
recipe_tree: undefined,
automatic: true
},
directives: {
tabindex: {
@ -561,6 +545,9 @@
this.recipe_data = undefined
this.error = undefined
this.loading = true
if (this.automatic) {
console.log('true')
}
this.$http.post("{% url 'api_recipe_from_url' %}", {'url': this.remote_url}, {emulateJSON: true}).then((response) => {
console.log(response.data)
this.recipe_data = response.data;
@ -578,13 +565,22 @@
this.makeToast(gettext('Error'), msg, 'danger')
})
},
loadRecipeJson: function () {
loadSource: function() {
this.recipe_data = undefined
this.recipe_json = undefined
this.recipe_tree = undefined
this.error = undefined
this.loading = true
this.$http.post("{% url 'api_recipe_from_json' %}", {'json': this.raw_data}, {emulateJSON: true}).then((response) => {
this.$http.post("{% url 'api_recipe_from_source' %}", {'data': this.source_data, 'auto':this.automatic}, {emulateJSON: true}).then((response) => {
console.log(response.data)
this.recipe_data = response.data;
if (this.automatic) {
this.recipe_data = response.data['recipe_json'];
this.preview = false
} else {
this.recipe_json = response.data['recipe_json'];
this.recipe_tree = response.data['recipe_tree'];
this.preview = true
}
this.loading = false
}).catch((err) => {
this.error = err.data
@ -599,15 +595,16 @@
this.makeToast(gettext('Error'), msg, 'danger')
})
},
loadRecipeRaw: function () {
loadRecipeManual: function () {
this.error = undefined
this.preview = false
this.loading = true
this.parsed = false
this.recipe_json['@type'] = "Recipe"
this.$http.post("{% url 'api_recipe_from_json' %}", {'json': JSON.stringify(this.recipe_json)}, {emulateJSON: true}).then((response) => {
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;
this.recipe_data = response.data['recipe_json'];
this.loading = false
this.preview = false
}).catch((err) => {
this.error = err.data
this.loading = false
@ -615,41 +612,6 @@
this.makeToast(gettext('Error'), gettext('There was an error loading a resource!') + err.bodyText, 'danger')
})
},
loadRecipeJson: function () {
this.recipe_data = undefined
this.error = undefined
this.loading = true
this.$http.post("{% url 'api_recipe_from_json' %}", {'json': this.json_data}, {emulateJSON: true}).then((response) => {
console.log(response.data)
this.recipe_data = response.data;
this.loading = 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')
})
},
loadPreviewRaw: function () {
this.recipe_json = undefined
this.recipe_tree = undefined
this.error = undefined
this.loading = true
this.$http.post("{% url 'api_manual_recipe_from_json' %}", {'data': this.raw_data}, {emulateJSON: true}).then((response) => {
console.log(response.data)
this.recipe_json = response.data['recipe_json'];
this.recipe_tree = response.data['recipe_tree'];
this.loading = false
this.parsed = true
}).catch((err) => {
this.error = err.data
this.loading = false
this.parsed = false
console.log(err)
this.makeToast(gettext('Error'), gettext('There was an error loading a resource!') + err.bodyText, 'danger')
})
},
importRecipe: function () {
if (this.recipe_data.name.length > 128) {
this.makeToast(gettext('Error'), gettext('Recipe name is longer than 128 characters'), 'danger')
@ -852,16 +814,6 @@
break;
}
},
parseIngredient: function(txt) {
this.$http.post('{% url 'api_ingredient_from_string' %}', {text: txt}, {emulateJSON: true}).then((response) => {
console.log(response)
let ing = [response.body.amount, response.body.unit, response.body.food, response.body.note]
return ing
}).catch((err) => {
console.log(err)
this.makeToast(gettext('Error'), gettext('Something went wrong.'), 'danger')
})
}
}
});
</script>

View File

@ -92,9 +92,8 @@ urlpatterns = [
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/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-html/', api.manual_recipe_from_json, name='api_manual_recipe_from_json'),
path('api/recipe-from-json/', api.recipe_from_json, name='api_recipe_from_json'),
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/backup/', api.get_backup, name='api_backup'),
path('api/ingredient-from-string/', api.ingredient_from_string, name='api_ingredient_from_string'),

View File

@ -28,8 +28,8 @@ from cookbook.helper.permission_helper import (CustomIsAdmin, CustomIsGuest,
CustomIsOwner, CustomIsShare,
CustomIsShared, CustomIsUser,
group_required)
from cookbook.helper.recipe_url_import import get_from_html, find_recipe_json
from cookbook.helper.recipe_html_import import get_from_raw
from cookbook.helper.recipe_html_import import get_recipe_from_source
from cookbook.helper.recipe_url_import import get_from_scraper, find_recipe_json
from cookbook.models import (CookLog, Food, Ingredient, Keyword, MealPlan,
MealType, Recipe, RecipeBook, ShoppingList,
ShoppingListEntry, ShoppingListRecipe, Step,
@ -616,9 +616,11 @@ def recipe_from_url_old(request):
@group_required('user')
def manual_recipe_from_json(request):
def recipe_from_source(request):
json_data = request.POST['data']
recipe_json, recipe_tree = get_from_raw(json_data, request.space)
auto = request.POST['auto']
recipe_json, recipe_tree = get_recipe_from_source(json_data, request.space)
if len(recipe_tree) == 0 and len(recipe_json) == 0:
return JsonResponse(
{
@ -628,10 +630,18 @@ def manual_recipe_from_json(request):
status=400
)
else:
return JsonResponse({
'recipe_tree': recipe_tree,
'recipe_json': recipe_json
})
if auto == "true":
return JsonResponse({'recipe_json': recipe_json})
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({
'recipe_tree': recipe_tree,
'recipe_json': recipe_json
})
@group_required('admin')