Add a form to manually add a recipe
This commit is contained in:
parent
e128171ddc
commit
f60b59d789
24
cuizin/db.py
24
cuizin/db.py
@ -56,21 +56,31 @@ class Recipe(Model):
|
|||||||
class Meta:
|
class Meta:
|
||||||
database = database
|
database = database
|
||||||
|
|
||||||
def update_from_weboob(self, weboob_obj):
|
def update_from_dict(self, d):
|
||||||
"""
|
"""
|
||||||
Update fields taking values from the Weboob object.
|
Update field taking values from a dict of values.
|
||||||
"""
|
"""
|
||||||
# Set fields
|
# Set fields
|
||||||
for field in ['title', 'url', 'author', 'picture_url',
|
for field in ['title', 'url', 'author', 'picture_url',
|
||||||
'short_description', 'preparation_time', 'cooking_time',
|
'short_description', 'preparation_time', 'cooking_time',
|
||||||
'ingredients', 'instructions']:
|
'ingredients', 'instructions', 'nb_person']:
|
||||||
value = getattr(weboob_obj, field)
|
value = d.get(field, None)
|
||||||
if value:
|
if value:
|
||||||
setattr(self, field, value)
|
setattr(self, field, value)
|
||||||
# Serialize number of person
|
|
||||||
self.nb_person = '-'.join(str(num) for num in weboob_obj.nb_person)
|
|
||||||
# Download picture and save it as a blob
|
# Download picture and save it as a blob
|
||||||
self.picture = requests.get(weboob_obj.picture_url).content
|
if d.get('picture_url', None):
|
||||||
|
self.picture = requests.get(d['picture_url']).content
|
||||||
|
|
||||||
|
def update_from_weboob(self, weboob_obj):
|
||||||
|
"""
|
||||||
|
Update fields taking values from the Weboob object.
|
||||||
|
"""
|
||||||
|
weboob_dict = dict(weboob_obj.iter_fields())
|
||||||
|
if weboob_dict.get('nb_person', None):
|
||||||
|
weboob_dict['nb_person'] = '-'.join(
|
||||||
|
str(num) for num in weboob_dict['nb_person']
|
||||||
|
)
|
||||||
|
self.update_from_dict(weboob_dict)
|
||||||
|
|
||||||
def to_dict(self):
|
def to_dict(self):
|
||||||
"""
|
"""
|
||||||
|
@ -28,4 +28,9 @@ export default {
|
|||||||
color: rgba(0,0,0,.87);
|
color: rgba(0,0,0,.87);
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.panel {
|
||||||
|
max-width: 600px;
|
||||||
|
}
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
|
@ -16,7 +16,7 @@ function _postProcessRecipes(response) {
|
|||||||
parsed.recipes = parsed.recipes.map(item => Object.assign(
|
parsed.recipes = parsed.recipes.map(item => Object.assign(
|
||||||
item,
|
item,
|
||||||
{
|
{
|
||||||
instructions: item.instructions.split(/\r\n/).map(
|
instructions: item.instructions.split(/[\r\n]\n/).map(
|
||||||
line => line.trim(),
|
line => line.trim(),
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
@ -48,8 +48,17 @@ export function refetchRecipe(id) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
export function postRecipe(recipe) {
|
export function postRecipeByUrl(recipe) {
|
||||||
return fetch(`${constants.API_URL}api/v1/recipes`, {
|
return fetch(`${constants.API_URL}api/v1/recipes/by_url`, {
|
||||||
|
method: 'POST',
|
||||||
|
body: JSON.stringify(recipe),
|
||||||
|
})
|
||||||
|
.then(_postProcessRecipes);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export function postRecipeManually(recipe) {
|
||||||
|
return fetch(`${constants.API_URL}api/v1/recipes/manually`, {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
body: JSON.stringify(recipe),
|
body: JSON.stringify(recipe),
|
||||||
})
|
})
|
||||||
|
@ -12,7 +12,7 @@
|
|||||||
:key="recipe.title"
|
:key="recipe.title"
|
||||||
xs12 sm6 md3 lg2>
|
xs12 sm6 md3 lg2>
|
||||||
<v-card :to="{name: 'Recipe', params: { recipeId: recipe.id }}">
|
<v-card :to="{name: 'Recipe', params: { recipeId: recipe.id }}">
|
||||||
<v-card-media :src="recipe.picture" height="200px"></v-card-media>
|
<v-card-media :src="recipe.picture" height="200px" class="grey"></v-card-media>
|
||||||
<v-card-title primary-title>
|
<v-card-title primary-title>
|
||||||
<div>
|
<div>
|
||||||
<h3 class="headline mb-0">{{ recipe.title }}</h3>
|
<h3 class="headline mb-0">{{ recipe.title }}</h3>
|
||||||
@ -21,10 +21,10 @@
|
|||||||
<p>{{ recipe.short_description }}</p>
|
<p>{{ recipe.short_description }}</p>
|
||||||
<v-layout row text-xs-center>
|
<v-layout row text-xs-center>
|
||||||
<v-flex xs6>
|
<v-flex xs6>
|
||||||
<p><v-icon>timelapse</v-icon> {{ $tc('misc.Nmins', recipe.preparation_time, { count: recipe.preparation_time }) }}</p>
|
<p><v-icon>timelapse</v-icon> {{ $tc('misc.Nmins', recipe.preparation_time, { count: recipe.preparation_time ? recipe.preparation_time : '?' }) }}</p>
|
||||||
</v-flex>
|
</v-flex>
|
||||||
<v-flex xs6>
|
<v-flex xs6>
|
||||||
<p><v-icon>whatshot</v-icon> {{ $tc('misc.Nmins', recipe.cooking_time, { count: recipe.cooking_time }) }}</p>
|
<p><v-icon>whatshot</v-icon> {{ $tc('misc.Nmins', recipe.cooking_time, { count: recipe.cooking_time ? recipe.cooking_time : '?' }) }}</p>
|
||||||
</v-flex>
|
</v-flex>
|
||||||
</v-layout>
|
</v-layout>
|
||||||
</v-card>
|
</v-card>
|
||||||
|
@ -8,55 +8,50 @@
|
|||||||
</v-flex>
|
</v-flex>
|
||||||
</v-layout>
|
</v-layout>
|
||||||
</v-container>
|
</v-container>
|
||||||
<v-container text-xs-center v-else>
|
<v-container text-xs-center v-else class="panel">
|
||||||
<v-layout row wrap>
|
<v-layout row wrap>
|
||||||
<ErrorDialog v-model="error" :description="$t('error.unable_import_recipe')" />
|
<ErrorDialog v-model="error" :description="$t('error.unable_import_recipe')" />
|
||||||
|
|
||||||
<v-flex xs12>
|
<v-flex xs12>
|
||||||
<h2>{{ $t('new.import_from_url') }}</h2>
|
<h2>{{ $t('new.import_from_url') }}</h2>
|
||||||
<v-form v-model="validImport">
|
<v-form v-model="isValidImport">
|
||||||
<v-text-field
|
<v-text-field
|
||||||
label="URL"
|
label="URL"
|
||||||
v-model="url"
|
v-model="url"
|
||||||
required
|
required
|
||||||
:rules="urlRules"
|
:rules="requiredUrlRules"
|
||||||
></v-text-field>
|
></v-text-field>
|
||||||
<v-btn
|
<v-btn
|
||||||
@click="submitImport"
|
@click="submitImport"
|
||||||
:disabled="!validImport || isImporting"
|
:disabled="!isValidImport || isImporting"
|
||||||
>
|
>
|
||||||
{{ $t('new.import') }}
|
{{ $t('new.import') }}
|
||||||
</v-btn>
|
</v-btn>
|
||||||
</v-form>
|
</v-form>
|
||||||
</v-flex>
|
</v-flex>
|
||||||
</v-layout>
|
</v-layout>
|
||||||
<v-layout row wrap mt-5 v-if="featureAddManually">
|
<v-layout row wrap mt-5>
|
||||||
<v-flex xs12>
|
<v-flex xs12>
|
||||||
<h2>{{ $t('new.add_manually') }}</h2>
|
<h2>{{ $t('new.add_manually') }}</h2>
|
||||||
<v-form v-model="validAdd">
|
<v-form v-model="isValidAdd">
|
||||||
<v-text-field
|
<v-text-field
|
||||||
:label="$t('new.title')"
|
:label="$t('new.title')"
|
||||||
v-model="title"
|
v-model="title"
|
||||||
required
|
required
|
||||||
|
:rules="[v => !!v || $t('new.title_is_required')]"
|
||||||
></v-text-field>
|
></v-text-field>
|
||||||
<v-text-field
|
<v-text-field
|
||||||
:label="$t('new.picture')"
|
:label="$t('new.picture_url')"
|
||||||
v-model="picture"
|
v-model="picture_url"
|
||||||
|
:rules="urlRules"
|
||||||
></v-text-field>
|
></v-text-field>
|
||||||
<v-text-field
|
<v-text-field
|
||||||
:label="$t('new.short_description')"
|
:label="$t('new.short_description')"
|
||||||
v-model="short_description"
|
v-model="short_description"
|
||||||
textarea
|
textarea
|
||||||
></v-text-field>
|
></v-text-field>
|
||||||
<v-layout row>
|
<v-layout row wrap>
|
||||||
<v-flex xs4 mr-3>
|
<v-flex xs12 md5>
|
||||||
<v-text-field
|
|
||||||
:label="$t('new.nb_persons')"
|
|
||||||
v-model="nb_person"
|
|
||||||
type="number"
|
|
||||||
></v-text-field>
|
|
||||||
</v-flex>
|
|
||||||
<v-flex xs4 mx-3>
|
|
||||||
<v-text-field
|
<v-text-field
|
||||||
:label="$t('new.preparation_time')"
|
:label="$t('new.preparation_time')"
|
||||||
v-model="preparation_time"
|
v-model="preparation_time"
|
||||||
@ -64,7 +59,7 @@
|
|||||||
:suffix="$t('new.mins')"
|
:suffix="$t('new.mins')"
|
||||||
></v-text-field>
|
></v-text-field>
|
||||||
</v-flex>
|
</v-flex>
|
||||||
<v-flex xs4 ml-3>
|
<v-flex xs12 md5 offset-md2>
|
||||||
<v-text-field
|
<v-text-field
|
||||||
:label="$t('new.cooking_time')"
|
:label="$t('new.cooking_time')"
|
||||||
v-model="cooking_time"
|
v-model="cooking_time"
|
||||||
@ -74,20 +69,43 @@
|
|||||||
</v-flex>
|
</v-flex>
|
||||||
</v-layout>
|
</v-layout>
|
||||||
<v-text-field
|
<v-text-field
|
||||||
:label="$t('new.ingredients')"
|
:label="$t('new.nb_persons')"
|
||||||
v-model="ingredients"
|
v-model="nb_person"
|
||||||
textarea
|
|
||||||
></v-text-field>
|
></v-text-field>
|
||||||
|
<v-layout row>
|
||||||
|
<v-flex xs12 class="text-xs-left">
|
||||||
|
<h3>{{ $t('new.ingredients') }}</h3>
|
||||||
|
<v-list v-if="ingredients.length" class="transparent">
|
||||||
|
<v-list-tile v-for="ingredient in ingredients" :key="ingredient">
|
||||||
|
<v-list-tile-action>
|
||||||
|
<v-btn flat icon color="red" v-on:click="() => removeIngredient(ingredient)">
|
||||||
|
<v-icon>delete</v-icon>
|
||||||
|
</v-btn>
|
||||||
|
</v-list-tile-action>
|
||||||
|
<v-list-tile-content>
|
||||||
|
<v-list-tile-title>{{ ingredient }}</v-list-tile-title>
|
||||||
|
</v-list-tile-content>
|
||||||
|
</v-list-tile>
|
||||||
|
</v-list>
|
||||||
|
<p class="ml-5 my-3" v-else>{{ $t('new.none') }}</p>
|
||||||
|
<v-text-field
|
||||||
|
:label="$t('new.add_ingredient')"
|
||||||
|
v-model="new_ingredient"
|
||||||
|
@keyup.enter.native="addIngredient"
|
||||||
|
></v-text-field>
|
||||||
|
</v-flex>
|
||||||
|
</v-layout>
|
||||||
<v-text-field
|
<v-text-field
|
||||||
:label="$t('new.instructions')"
|
:label="$t('new.instructions')"
|
||||||
v-model="instructions"
|
v-model="instructions"
|
||||||
|
:rules="[v => !!v || $t('new.instructions_are_required')]"
|
||||||
textarea
|
textarea
|
||||||
required
|
required
|
||||||
></v-text-field>
|
></v-text-field>
|
||||||
|
|
||||||
<v-btn
|
<v-btn
|
||||||
@click="submitAdd"
|
@click="submitAdd"
|
||||||
:disabled="!validAdd"
|
:disabled="!isValidAdd || isImporting"
|
||||||
>
|
>
|
||||||
{{ $t('new.add') }}
|
{{ $t('new.add') }}
|
||||||
</v-btn>
|
</v-btn>
|
||||||
@ -110,38 +128,56 @@ export default {
|
|||||||
Loader,
|
Loader,
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
|
const urlRules = [
|
||||||
|
(v) => {
|
||||||
|
if (!v) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
new URL(v); // eslint-disable-line no-new
|
||||||
|
return true;
|
||||||
|
} catch (e) {
|
||||||
|
return $t('new.url_must_be_valid');
|
||||||
|
}
|
||||||
|
},
|
||||||
|
];
|
||||||
return {
|
return {
|
||||||
error: null,
|
error: null,
|
||||||
url: null,
|
url: null,
|
||||||
validImport: false,
|
isValidImport: false,
|
||||||
isImporting: false,
|
isImporting: false,
|
||||||
validAdd: false,
|
isValidAdd: false,
|
||||||
title: null,
|
title: null,
|
||||||
picture: null,
|
picture_url: null,
|
||||||
short_description: null,
|
short_description: null,
|
||||||
nb_person: null,
|
nb_person: null,
|
||||||
preparation_time: null,
|
preparation_time: null,
|
||||||
cooking_time: null,
|
cooking_time: null,
|
||||||
ingredients: null,
|
new_ingredient: null,
|
||||||
|
ingredients: [],
|
||||||
instructions: null,
|
instructions: null,
|
||||||
urlRules: [
|
requiredUrlRules: Array.concat(
|
||||||
v => !!v || 'URL is required',
|
[],
|
||||||
(v) => {
|
[v => !!v || $t('new.url_is_required')],
|
||||||
try {
|
urlRules,
|
||||||
new URL(v); // eslint-disable-line no-new
|
),
|
||||||
return true;
|
urlRules,
|
||||||
} catch (e) {
|
|
||||||
return 'URL must be valid';
|
|
||||||
}
|
|
||||||
},
|
|
||||||
],
|
|
||||||
featureAddManually: false,
|
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
addIngredient() {
|
||||||
|
this.ingredients.push(this.new_ingredient);
|
||||||
|
this.new_ingredient = null;
|
||||||
|
},
|
||||||
|
removeIngredient(ingredient) {
|
||||||
|
const index = this.ingredients.indexOf(ingredient);
|
||||||
|
if (index !== -1) {
|
||||||
|
this.ingredients.splice(index, 1);
|
||||||
|
}
|
||||||
|
},
|
||||||
submitImport() {
|
submitImport() {
|
||||||
this.isImporting = true;
|
this.isImporting = true;
|
||||||
api.postRecipe({ url: this.url })
|
api.postRecipeByUrl({ url: this.url })
|
||||||
.then(response => this.$router.push({
|
.then(response => this.$router.push({
|
||||||
name: 'Recipe',
|
name: 'Recipe',
|
||||||
params: {
|
params: {
|
||||||
@ -154,8 +190,34 @@ export default {
|
|||||||
});
|
});
|
||||||
},
|
},
|
||||||
submitAdd() {
|
submitAdd() {
|
||||||
// TODO
|
this.isImporting = true;
|
||||||
|
api.postRecipeManually({
|
||||||
|
title: this.title,
|
||||||
|
picture_url: this.picture_url,
|
||||||
|
short_description: this.short_description,
|
||||||
|
preparation_time: this.preparation_time,
|
||||||
|
cooking_time: this.cooking_time,
|
||||||
|
nb_person: this.nb_person,
|
||||||
|
ingredients: this.ingredients,
|
||||||
|
instructions: this.instructions,
|
||||||
|
})
|
||||||
|
.then(response => this.$router.push({
|
||||||
|
name: 'Recipe',
|
||||||
|
params: {
|
||||||
|
recipeId: response.recipes[0].id,
|
||||||
|
},
|
||||||
|
}))
|
||||||
|
.catch((error) => {
|
||||||
|
this.isImporting = false;
|
||||||
|
this.error = error;
|
||||||
|
});
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.transparent {
|
||||||
|
background: transparent;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
@ -46,23 +46,26 @@
|
|||||||
<p>{{ recipe.nb_person }}</p>
|
<p>{{ recipe.nb_person }}</p>
|
||||||
</v-flex>
|
</v-flex>
|
||||||
<v-flex xs6>
|
<v-flex xs6>
|
||||||
<p><v-icon>timelapse</v-icon> {{ $t('recipe.preparation') }} {{ $tc('misc.Nmins', recipe.preparation_time, { count: recipe.preparation_time }) }}</p>
|
<p><v-icon>timelapse</v-icon> {{ $t('recipe.preparation') }} {{ $tc('misc.Nmins', recipe.preparation_time, { count: recipe.preparation_time ? recipe.preparation_time : '?' }) }}</p>
|
||||||
<p><v-icon>whatshot</v-icon> {{ $t('recipe.cooking') }} {{ $tc('misc.Nmins', recipe.cooking_time, { count: recipe.cooking_time }) }}</p>
|
<p><v-icon>whatshot</v-icon> {{ $t('recipe.cooking') }} {{ $tc('misc.Nmins', recipe.cooking_time, { count: recipe.cooking_time ? recipe.cooking_time : '?' }) }}</p>
|
||||||
</v-flex>
|
</v-flex>
|
||||||
</v-layout>
|
</v-layout>
|
||||||
<p>{{ recipe.short_description }}</p>
|
<p>{{ recipe.short_description }}</p>
|
||||||
|
|
||||||
<h2>{{ $t('recipe.ingredients') }}</h2>
|
<h2>{{ $t('recipe.ingredients') }}</h2>
|
||||||
<ul class="ml-5">
|
<ul class="ml-5" v-if="recipe.ingredients && recipe.ingredients.length">
|
||||||
<li v-for="ingredient in recipe.ingredients">
|
<li v-for="ingredient in recipe.ingredients">
|
||||||
{{ ingredient }}
|
{{ ingredient }}
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
<p class="ml-5 my-3" v-else>{{ $t('new.none') }}</p>
|
||||||
|
|
||||||
<h2 class="mt-3">{{ $t('recipe.instructions') }}</h2>
|
<h2 class="mt-3">{{ $t('recipe.instructions') }}</h2>
|
||||||
<p v-for="item in recipe.instructions">
|
<p v-for="item in recipe.instructions">
|
||||||
{{ item }}
|
{{ item }}
|
||||||
</p>
|
</p>
|
||||||
<p v-if="recipe.url" class="text-xs-center">
|
<p class="text-xs-center">
|
||||||
<v-btn :href="recipe.url" :title="$t('recipe.website')">
|
<v-btn :href="recipe.url" :title="$t('recipe.website')" v-if="recipe.url">
|
||||||
<v-icon class="fa-icon">fa-external-link</v-icon>
|
<v-icon class="fa-icon">fa-external-link</v-icon>
|
||||||
</v-btn>
|
</v-btn>
|
||||||
<v-btn @click.stop="deleteConfirm = true" :title="$t('recipe.delete')">
|
<v-btn @click.stop="deleteConfirm = true" :title="$t('recipe.delete')">
|
||||||
@ -156,10 +159,6 @@ img {
|
|||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.panel {
|
|
||||||
max-width: 600px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.fa-icon {
|
.fa-icon {
|
||||||
font-size: 20px;
|
font-size: 20px;
|
||||||
}
|
}
|
||||||
|
@ -13,19 +13,25 @@ export default {
|
|||||||
},
|
},
|
||||||
new: {
|
new: {
|
||||||
add: 'Add',
|
add: 'Add',
|
||||||
|
add_ingredient: 'Add ingredient',
|
||||||
add_manually: 'Add manually',
|
add_manually: 'Add manually',
|
||||||
cooking_time: 'Cooking time',
|
cooking_time: 'Cooking time',
|
||||||
import: 'Import',
|
import: 'Import',
|
||||||
import_from_url: 'Import from URL',
|
import_from_url: 'Import from URL',
|
||||||
importing: 'Importing…',
|
importing: 'Importing…',
|
||||||
ingredients: 'Ingredients',
|
ingredients: 'Ingredients:',
|
||||||
instructions: 'Instructions',
|
instructions: 'Instructions',
|
||||||
|
instructions_are_required: 'Instructions are required',
|
||||||
mins: 'mins',
|
mins: 'mins',
|
||||||
nb_opersons: 'Number of persons',
|
nb_persons: 'Serves',
|
||||||
picture: 'Picture',
|
none: 'None',
|
||||||
|
picture_url: 'Picture URL',
|
||||||
preparation_time: 'Preparation time',
|
preparation_time: 'Preparation time',
|
||||||
short_description: 'Short description',
|
short_description: 'Short description',
|
||||||
title: 'Title',
|
title: 'Title',
|
||||||
|
title_is_required: 'Title is required',
|
||||||
|
url_is_required: 'URL is required',
|
||||||
|
url_must_be_valid: 'URL must be a valid one',
|
||||||
},
|
},
|
||||||
recipe: {
|
recipe: {
|
||||||
cooking: 'Cooking:',
|
cooking: 'Cooking:',
|
||||||
@ -34,6 +40,7 @@ export default {
|
|||||||
delete_recipe_description: 'This will delete this recipe. Are you sure?',
|
delete_recipe_description: 'This will delete this recipe. Are you sure?',
|
||||||
ingredients: 'Ingredients',
|
ingredients: 'Ingredients',
|
||||||
instructions: 'Instructions',
|
instructions: 'Instructions',
|
||||||
|
none: 'Aucun',
|
||||||
preparation: 'Preparation:',
|
preparation: 'Preparation:',
|
||||||
refetch: 'Refetch',
|
refetch: 'Refetch',
|
||||||
refetch_recipe: 'Refetch recipe',
|
refetch_recipe: 'Refetch recipe',
|
||||||
|
@ -13,19 +13,25 @@ export default {
|
|||||||
},
|
},
|
||||||
new: {
|
new: {
|
||||||
add: 'Ajouter',
|
add: 'Ajouter',
|
||||||
|
add_ingredient: 'Ajouter un ingrédient',
|
||||||
add_manually: 'Ajouter manuellement',
|
add_manually: 'Ajouter manuellement',
|
||||||
cooking_time: 'Temps de cuisson',
|
cooking_time: 'Temps de cuisson',
|
||||||
import: 'Importer',
|
import: 'Importer',
|
||||||
import_from_url: 'Importer depuis l\'URL',
|
import_from_url: 'Importer depuis l\'URL',
|
||||||
importing: 'En cours d\'import…',
|
importing: 'En cours d\'import…',
|
||||||
ingredients: 'Ingrédients',
|
ingredients: 'Ingrédients :',
|
||||||
instructions: 'Instructions',
|
instructions: 'Instructions',
|
||||||
|
instructions_are_required: 'Des instructions sont requises',
|
||||||
mins: 'mins',
|
mins: 'mins',
|
||||||
nb_opersons: 'Nombre de personnes',
|
nb_persons: 'Nombre de personnes / parts',
|
||||||
picture: 'Photo',
|
none: 'Aucun',
|
||||||
|
picture_url: 'URL d\'une photo',
|
||||||
preparation_time: 'Temps de préparation',
|
preparation_time: 'Temps de préparation',
|
||||||
short_description: 'Description brève',
|
short_description: 'Description brève',
|
||||||
title: 'Titre',
|
title: 'Titre',
|
||||||
|
title_is_required: 'Un titre est requis',
|
||||||
|
url_is_required: 'Une URL est requise',
|
||||||
|
url_must_be_valid: 'L\'URL est invalide',
|
||||||
},
|
},
|
||||||
recipe: {
|
recipe: {
|
||||||
cooking: 'Cuisson :',
|
cooking: 'Cuisson :',
|
||||||
@ -34,6 +40,7 @@ export default {
|
|||||||
delete_recipe_description: 'Vous allez supprimer cette recette. En êtes-vous sûr ?',
|
delete_recipe_description: 'Vous allez supprimer cette recette. En êtes-vous sûr ?',
|
||||||
ingredients: 'Ingrédients',
|
ingredients: 'Ingrédients',
|
||||||
instructions: 'Instructions',
|
instructions: 'Instructions',
|
||||||
|
none: 'Aucun',
|
||||||
preparation: 'Préparation :',
|
preparation: 'Préparation :',
|
||||||
refetch: 'Réactualiser',
|
refetch: 'Réactualiser',
|
||||||
refetch_recipe: 'Réactualiser la recette',
|
refetch_recipe: 'Réactualiser la recette',
|
||||||
|
@ -56,8 +56,8 @@ def api_v1_recipes():
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@app.post('/api/v1/recipes')
|
@app.post('/api/v1/recipes/by_url')
|
||||||
def api_v1_recipes_post():
|
def api_v1_recipes_post_by_url():
|
||||||
"""
|
"""
|
||||||
Create a new recipe from URL
|
Create a new recipe from URL
|
||||||
"""
|
"""
|
||||||
@ -80,6 +80,27 @@ def api_v1_recipes_post():
|
|||||||
bottle.redirect('/api/v1/recipe/%s' % recipe.id, 301)
|
bottle.redirect('/api/v1/recipe/%s' % recipe.id, 301)
|
||||||
|
|
||||||
|
|
||||||
|
@app.post('/api/v1/recipes/manually')
|
||||||
|
def api_v1_recipes_post_manual():
|
||||||
|
"""
|
||||||
|
Create a new recipe manually
|
||||||
|
"""
|
||||||
|
data = json.loads(bottle.request.body.read().decode('utf-8'))
|
||||||
|
|
||||||
|
try:
|
||||||
|
# Try to add
|
||||||
|
recipe = db.Recipe()
|
||||||
|
recipe.update_from_dict(data)
|
||||||
|
recipe.save()
|
||||||
|
return {
|
||||||
|
'recipes': [recipe.to_dict()]
|
||||||
|
}
|
||||||
|
except peewee.IntegrityError:
|
||||||
|
return {
|
||||||
|
'error': 'Duplicate recipe.'
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@app.route('/api/v1/recipe/:id', ['GET', 'OPTIONS'])
|
@app.route('/api/v1/recipe/:id', ['GET', 'OPTIONS'])
|
||||||
def api_v1_recipe(id):
|
def api_v1_recipe(id):
|
||||||
"""
|
"""
|
||||||
|
Loading…
Reference in New Issue
Block a user