importer stuff

This commit is contained in:
vabene1111
2024-12-12 17:26:01 +01:00
parent 37761bf6e7
commit 325ee16d39
12 changed files with 205 additions and 1913 deletions

View File

@@ -106,14 +106,14 @@ def get_from_scraper(scrape, request):
# assign image # assign image
try: try:
recipe_json['image'] = parse_image(scrape.image()) or None recipe_json['image_url'] = parse_image(scrape.image()) or None
except Exception: except Exception:
recipe_json['image'] = None recipe_json['image_url'] = None
if not recipe_json['image']: if not recipe_json['image_url']:
try: try:
recipe_json['image'] = parse_image(scrape.schema.data.get('image')) or '' recipe_json['image_url'] = parse_image(scrape.schema.data.get('image')) or ''
except Exception: except Exception:
recipe_json['image'] = '' recipe_json['image_url'] = ''
# assign keywords # assign keywords
try: try:

View File

@@ -1609,7 +1609,7 @@ class SourceImportRecipeSerializer(serializers.Serializer):
servings_text = serializers.CharField() servings_text = serializers.CharField()
working_time = serializers.IntegerField() working_time = serializers.IntegerField()
waiting_time = serializers.IntegerField() waiting_time = serializers.IntegerField()
image = serializers.URLField() image_url = serializers.URLField()
keywords = SourceImportKeywordSerializer(many=True) keywords = SourceImportKeywordSerializer(many=True)
properties = serializers.ListField(child=SourceImportPropertySerializer()) properties = serializers.ListField(child=SourceImportPropertySerializer())

View File

@@ -7,8 +7,6 @@ models/AccessToken.ts
models/AuthToken.ts models/AuthToken.ts
models/AutoMealPlan.ts models/AutoMealPlan.ts
models/Automation.ts models/Automation.ts
models/AutomationTypeEnum.ts
models/BaseUnitEnum.ts
models/BookmarkletImport.ts models/BookmarkletImport.ts
models/BookmarkletImportList.ts models/BookmarkletImportList.ts
models/ConnectorConfigConfig.ts models/ConnectorConfigConfig.ts
@@ -34,16 +32,6 @@ models/MealPlan.ts
models/MealType.ts models/MealType.ts
models/MethodEnum.ts models/MethodEnum.ts
models/NutritionInformation.ts models/NutritionInformation.ts
models/OpenDataCategory.ts
models/OpenDataConversion.ts
models/OpenDataFood.ts
models/OpenDataFoodProperty.ts
models/OpenDataProperty.ts
models/OpenDataStore.ts
models/OpenDataStoreCategory.ts
models/OpenDataUnit.ts
models/OpenDataUnitTypeEnum.ts
models/OpenDataVersion.ts
models/PaginatedAutomationList.ts models/PaginatedAutomationList.ts
models/PaginatedBookmarkletImportListList.ts models/PaginatedBookmarkletImportListList.ts
models/PaginatedCookLogList.ts models/PaginatedCookLogList.ts
@@ -90,13 +78,6 @@ models/PatchedInviteLink.ts
models/PatchedKeyword.ts models/PatchedKeyword.ts
models/PatchedMealPlan.ts models/PatchedMealPlan.ts
models/PatchedMealType.ts models/PatchedMealType.ts
models/PatchedOpenDataCategory.ts
models/PatchedOpenDataConversion.ts
models/PatchedOpenDataFood.ts
models/PatchedOpenDataProperty.ts
models/PatchedOpenDataStore.ts
models/PatchedOpenDataUnit.ts
models/PatchedOpenDataVersion.ts
models/PatchedProperty.ts models/PatchedProperty.ts
models/PatchedPropertyType.ts models/PatchedPropertyType.ts
models/PatchedRecipe.ts models/PatchedRecipe.ts
@@ -154,6 +135,7 @@ models/SupermarketCategoryRelation.ts
models/Sync.ts models/Sync.ts
models/SyncLog.ts models/SyncLog.ts
models/ThemeEnum.ts models/ThemeEnum.ts
models/TypeEnum.ts
models/Unit.ts models/Unit.ts
models/UnitConversion.ts models/UnitConversion.ts
models/User.ts models/User.ts

File diff suppressed because it is too large Load Diff

View File

@@ -13,13 +13,13 @@
*/ */
import { mapValues } from '../runtime'; import { mapValues } from '../runtime';
import type { AutomationTypeEnum } from './AutomationTypeEnum'; import type { TypeEnum } from './TypeEnum';
import { import {
AutomationTypeEnumFromJSON, TypeEnumFromJSON,
AutomationTypeEnumFromJSONTyped, TypeEnumFromJSONTyped,
AutomationTypeEnumToJSON, TypeEnumToJSON,
AutomationTypeEnumToJSONTyped, TypeEnumToJSONTyped,
} from './AutomationTypeEnum'; } from './TypeEnum';
/** /**
* *
@@ -35,10 +35,10 @@ export interface Automation {
id?: number; id?: number;
/** /**
* *
* @type {AutomationTypeEnum} * @type {TypeEnum}
* @memberof Automation * @memberof Automation
*/ */
type: AutomationTypeEnum; type: TypeEnum;
/** /**
* *
* @type {string} * @type {string}
@@ -111,7 +111,7 @@ export function AutomationFromJSONTyped(json: any, ignoreDiscriminator: boolean)
return { return {
'id': json['id'] == null ? undefined : json['id'], 'id': json['id'] == null ? undefined : json['id'],
'type': AutomationTypeEnumFromJSON(json['type']), 'type': TypeEnumFromJSON(json['type']),
'name': json['name'] == null ? undefined : json['name'], 'name': json['name'] == null ? undefined : json['name'],
'description': json['description'] == null ? undefined : json['description'], 'description': json['description'] == null ? undefined : json['description'],
'param1': json['param_1'] == null ? undefined : json['param_1'], 'param1': json['param_1'] == null ? undefined : json['param_1'],
@@ -135,7 +135,7 @@ export function AutomationToJSONTyped(value?: Omit<Automation, 'created_by'> | n
return { return {
'id': value['id'], 'id': value['id'],
'type': AutomationTypeEnumToJSON(value['type']), 'type': TypeEnumToJSON(value['type']),
'name': value['name'], 'name': value['name'],
'description': value['description'], 'description': value['description'],
'param_1': value['param1'], 'param_1': value['param1'],

View File

@@ -13,13 +13,13 @@
*/ */
import { mapValues } from '../runtime'; import { mapValues } from '../runtime';
import type { AutomationTypeEnum } from './AutomationTypeEnum'; import type { TypeEnum } from './TypeEnum';
import { import {
AutomationTypeEnumFromJSON, TypeEnumFromJSON,
AutomationTypeEnumFromJSONTyped, TypeEnumFromJSONTyped,
AutomationTypeEnumToJSON, TypeEnumToJSON,
AutomationTypeEnumToJSONTyped, TypeEnumToJSONTyped,
} from './AutomationTypeEnum'; } from './TypeEnum';
/** /**
* *
@@ -35,10 +35,10 @@ export interface PatchedAutomation {
id?: number; id?: number;
/** /**
* *
* @type {AutomationTypeEnum} * @type {TypeEnum}
* @memberof PatchedAutomation * @memberof PatchedAutomation
*/ */
type?: AutomationTypeEnum; type?: TypeEnum;
/** /**
* *
* @type {string} * @type {string}
@@ -109,7 +109,7 @@ export function PatchedAutomationFromJSONTyped(json: any, ignoreDiscriminator: b
return { return {
'id': json['id'] == null ? undefined : json['id'], 'id': json['id'] == null ? undefined : json['id'],
'type': json['type'] == null ? undefined : AutomationTypeEnumFromJSON(json['type']), 'type': json['type'] == null ? undefined : TypeEnumFromJSON(json['type']),
'name': json['name'] == null ? undefined : json['name'], 'name': json['name'] == null ? undefined : json['name'],
'description': json['description'] == null ? undefined : json['description'], 'description': json['description'] == null ? undefined : json['description'],
'param1': json['param_1'] == null ? undefined : json['param_1'], 'param1': json['param_1'] == null ? undefined : json['param_1'],
@@ -133,7 +133,7 @@ export function PatchedAutomationToJSONTyped(value?: Omit<PatchedAutomation, 'cr
return { return {
'id': value['id'], 'id': value['id'],
'type': AutomationTypeEnumToJSON(value['type']), 'type': TypeEnumToJSON(value['type']),
'name': value['name'], 'name': value['name'],
'description': value['description'], 'description': value['description'],
'param_1': value['param1'], 'param_1': value['param1'],

View File

@@ -100,7 +100,7 @@ export interface SourceImportRecipe {
* @type {string} * @type {string}
* @memberof SourceImportRecipe * @memberof SourceImportRecipe
*/ */
image: string; imageUrl: string;
/** /**
* *
* @type {Array<SourceImportKeyword>} * @type {Array<SourceImportKeyword>}
@@ -128,7 +128,7 @@ export function instanceOfSourceImportRecipe(value: object): value is SourceImpo
if (!('servingsText' in value) || value['servingsText'] === undefined) return false; if (!('servingsText' in value) || value['servingsText'] === undefined) return false;
if (!('workingTime' in value) || value['workingTime'] === undefined) return false; if (!('workingTime' in value) || value['workingTime'] === undefined) return false;
if (!('waitingTime' in value) || value['waitingTime'] === undefined) return false; if (!('waitingTime' in value) || value['waitingTime'] === undefined) return false;
if (!('image' in value) || value['image'] === undefined) return false; if (!('imageUrl' in value) || value['imageUrl'] === undefined) return false;
if (!('keywords' in value) || value['keywords'] === undefined) return false; if (!('keywords' in value) || value['keywords'] === undefined) return false;
if (!('properties' in value) || value['properties'] === undefined) return false; if (!('properties' in value) || value['properties'] === undefined) return false;
return true; return true;
@@ -153,7 +153,7 @@ export function SourceImportRecipeFromJSONTyped(json: any, ignoreDiscriminator:
'servingsText': json['servings_text'], 'servingsText': json['servings_text'],
'workingTime': json['working_time'], 'workingTime': json['working_time'],
'waitingTime': json['waiting_time'], 'waitingTime': json['waiting_time'],
'image': json['image'], 'imageUrl': json['image_url'],
'keywords': ((json['keywords'] as Array<any>).map(SourceImportKeywordFromJSON)), 'keywords': ((json['keywords'] as Array<any>).map(SourceImportKeywordFromJSON)),
'properties': ((json['properties'] as Array<any>).map(SourceImportPropertyFromJSON)), 'properties': ((json['properties'] as Array<any>).map(SourceImportPropertyFromJSON)),
}; };
@@ -179,7 +179,7 @@ export function SourceImportRecipeToJSONTyped(value?: SourceImportRecipe | null,
'servings_text': value['servingsText'], 'servings_text': value['servingsText'],
'working_time': value['workingTime'], 'working_time': value['workingTime'],
'waiting_time': value['waitingTime'], 'waiting_time': value['waitingTime'],
'image': value['image'], 'image_url': value['imageUrl'],
'keywords': ((value['keywords'] as Array<any>).map(SourceImportKeywordToJSON)), 'keywords': ((value['keywords'] as Array<any>).map(SourceImportKeywordToJSON)),
'properties': ((value['properties'] as Array<any>).map(SourceImportPropertyToJSON)), 'properties': ((value['properties'] as Array<any>).map(SourceImportPropertyToJSON)),
}; };

View File

@@ -0,0 +1,70 @@
/* tslint:disable */
/* eslint-disable */
/**
* Tandoor
* Tandoor API Docs
*
* The version of the OpenAPI document: 0.0.0
*
*
* NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
* https://openapi-generator.tech
* Do not edit the class manually.
*/
/**
* * `FOOD_ALIAS` - Food Alias
* * `UNIT_ALIAS` - Unit Alias
* * `KEYWORD_ALIAS` - Keyword Alias
* * `DESCRIPTION_REPLACE` - Description Replace
* * `INSTRUCTION_REPLACE` - Instruction Replace
* * `NEVER_UNIT` - Never Unit
* * `TRANSPOSE_WORDS` - Transpose Words
* * `FOOD_REPLACE` - Food Replace
* * `UNIT_REPLACE` - Unit Replace
* * `NAME_REPLACE` - Name Replace
* @export
*/
export const TypeEnum = {
FoodAlias: 'FOOD_ALIAS',
UnitAlias: 'UNIT_ALIAS',
KeywordAlias: 'KEYWORD_ALIAS',
DescriptionReplace: 'DESCRIPTION_REPLACE',
InstructionReplace: 'INSTRUCTION_REPLACE',
NeverUnit: 'NEVER_UNIT',
TransposeWords: 'TRANSPOSE_WORDS',
FoodReplace: 'FOOD_REPLACE',
UnitReplace: 'UNIT_REPLACE',
NameReplace: 'NAME_REPLACE'
} as const;
export type TypeEnum = typeof TypeEnum[keyof typeof TypeEnum];
export function instanceOfTypeEnum(value: any): boolean {
for (const key in TypeEnum) {
if (Object.prototype.hasOwnProperty.call(TypeEnum, key)) {
if (TypeEnum[key as keyof typeof TypeEnum] === value) {
return true;
}
}
}
return false;
}
export function TypeEnumFromJSON(json: any): TypeEnum {
return TypeEnumFromJSONTyped(json, false);
}
export function TypeEnumFromJSONTyped(json: any, ignoreDiscriminator: boolean): TypeEnum {
return json as TypeEnum;
}
export function TypeEnumToJSON(value?: TypeEnum | null): any {
return value as any;
}
export function TypeEnumToJSONTyped(value: any, ignoreDiscriminator: boolean): TypeEnum {
return value as TypeEnum;
}

View File

@@ -4,8 +4,6 @@ export * from './AccessToken';
export * from './AuthToken'; export * from './AuthToken';
export * from './AutoMealPlan'; export * from './AutoMealPlan';
export * from './Automation'; export * from './Automation';
export * from './AutomationTypeEnum';
export * from './BaseUnitEnum';
export * from './BookmarkletImport'; export * from './BookmarkletImport';
export * from './BookmarkletImportList'; export * from './BookmarkletImportList';
export * from './ConnectorConfigConfig'; export * from './ConnectorConfigConfig';
@@ -31,16 +29,6 @@ export * from './MealPlan';
export * from './MealType'; export * from './MealType';
export * from './MethodEnum'; export * from './MethodEnum';
export * from './NutritionInformation'; export * from './NutritionInformation';
export * from './OpenDataCategory';
export * from './OpenDataConversion';
export * from './OpenDataFood';
export * from './OpenDataFoodProperty';
export * from './OpenDataProperty';
export * from './OpenDataStore';
export * from './OpenDataStoreCategory';
export * from './OpenDataUnit';
export * from './OpenDataUnitTypeEnum';
export * from './OpenDataVersion';
export * from './PaginatedAutomationList'; export * from './PaginatedAutomationList';
export * from './PaginatedBookmarkletImportListList'; export * from './PaginatedBookmarkletImportListList';
export * from './PaginatedCookLogList'; export * from './PaginatedCookLogList';
@@ -87,13 +75,6 @@ export * from './PatchedInviteLink';
export * from './PatchedKeyword'; export * from './PatchedKeyword';
export * from './PatchedMealPlan'; export * from './PatchedMealPlan';
export * from './PatchedMealType'; export * from './PatchedMealType';
export * from './PatchedOpenDataCategory';
export * from './PatchedOpenDataConversion';
export * from './PatchedOpenDataFood';
export * from './PatchedOpenDataProperty';
export * from './PatchedOpenDataStore';
export * from './PatchedOpenDataUnit';
export * from './PatchedOpenDataVersion';
export * from './PatchedProperty'; export * from './PatchedProperty';
export * from './PatchedPropertyType'; export * from './PatchedPropertyType';
export * from './PatchedRecipe'; export * from './PatchedRecipe';
@@ -151,6 +132,7 @@ export * from './SupermarketCategoryRelation';
export * from './Sync'; export * from './Sync';
export * from './SyncLog'; export * from './SyncLog';
export * from './ThemeEnum'; export * from './ThemeEnum';
export * from './TypeEnum';
export * from './Unit'; export * from './Unit';
export * from './UnitConversion'; export * from './UnitConversion';
export * from './User'; export * from './User';

View File

@@ -19,7 +19,7 @@
<v-stepper-window> <v-stepper-window>
<v-stepper-window-item value="1"> <v-stepper-window-item value="1">
<v-card> <v-card :loading="loading">
<v-card-text> <v-card-text>
<v-text-field :label="$t('Website') + ' (https://...)'" @paste="nextTick(loadRecipeFromUrl())" v-model="importUrl"> <v-text-field :label="$t('Website') + ' (https://...)'" @paste="nextTick(loadRecipeFromUrl())" v-model="importUrl">
<template #append> <template #append>
@@ -44,13 +44,13 @@
<v-row> <v-row>
<v-col cols="12" md="6"> <v-col cols="12" md="6">
<h2 class="text-h5">{{ $t('Selected') }}</h2> <h2 class="text-h5">{{ $t('Selected') }}</h2>
<v-img max-height="30vh" :src="importResponse.recipe.image"></v-img> <v-img max-height="30vh" :src="importResponse.recipe.imageUrl"></v-img>
</v-col> </v-col>
<v-col cols="12" md="6"> <v-col cols="12" md="6">
<h2 class="text-h5">{{ $t('Available') }}</h2> <h2 class="text-h5">{{ $t('Available') }}</h2>
<v-row dense> <v-row dense>
<v-col cols="4" v-for="i in importResponse.images"> <v-col cols="4" v-for="i in importResponse.images">
<v-img max-height="10vh" cover aspect-ratio="1" :src="i" @click="importResponse.recipe.image = i"></v-img> <v-img max-height="10vh" cover aspect-ratio="1" :src="i" @click="importResponse.recipe.imageUrl = i"></v-img>
</v-col> </v-col>
</v-row> </v-row>
</v-col> </v-col>
@@ -72,9 +72,9 @@
<v-row> <v-row>
<v-col class="text-center"> <v-col class="text-center">
<v-btn-group border divided> <v-btn-group border divided>
<v-btn prepend-icon="fa-solid fa-shuffle" @click="autoSortIngredients()">Auto Sort</v-btn> <v-btn prepend-icon="fa-solid fa-shuffle" @click="autoSortIngredients()">{{ $t('Auto_Sort') }}</v-btn>
<v-btn prepend-icon="fa-solid fa-maximize" @click="splitAllSteps('\n')">Split All</v-btn> <v-btn prepend-icon="fa-solid fa-maximize" @click="splitAllSteps('\n')">{{ $t('Split') }}</v-btn>
<v-btn prepend-icon="fa-solid fa-minimize" @click="mergeAllSteps()">Merge All</v-btn> <v-btn prepend-icon="fa-solid fa-minimize" @click="mergeAllSteps()">{{ $t('Merge') }}</v-btn>
</v-btn-group> </v-btn-group>
</v-col> </v-col>
</v-row> </v-row>
@@ -97,9 +97,12 @@
<v-row> <v-row>
<v-col> <v-col>
<v-list> <v-list>
<v-list-item v-for="i in s.ingredients"> <vue-draggable v-model="s.ingredients" group="ingredients" drag-class="drag-handle">
{{ i.amount }} {{ i.unit.name }} {{ i.food.name }} <v-list-item v-for="i in s.ingredients">
</v-list-item> <v-icon size="small" class="drag-handle cursor-grab" icon="$dragHandle"></v-icon>
{{ i.amount }} {{ i.unit.name }} {{ i.food.name }}
</v-list-item>
</vue-draggable>
</v-list> </v-list>
</v-col> </v-col>
<v-col> <v-col>
@@ -114,7 +117,10 @@
</v-stepper-window-item> </v-stepper-window-item>
<v-stepper-window-item value="5"> <v-stepper-window-item value="5">
<v-btn @click="createRecipeFromImport()">Import</v-btn> <v-card :loading="loading">
<v-card-title></v-card-title>
<v-btn @click="createRecipeFromImport()">{{ $t('Import') }}</v-btn>
</v-card>
</v-stepper-window-item> </v-stepper-window-item>
</v-stepper-window> </v-stepper-window>
@@ -139,10 +145,12 @@ import {ApiApi, RecipeFromSourceResponse, SourceImportStep} from "@/openapi";
import {ErrorMessageType, MessageType, useMessageStore} from "@/stores/MessageStore"; import {ErrorMessageType, MessageType, useMessageStore} from "@/stores/MessageStore";
import {useRouter} from "vue-router"; import {useRouter} from "vue-router";
import {useUserPreferenceStore} from "@/stores/UserPreferenceStore"; import {useUserPreferenceStore} from "@/stores/UserPreferenceStore";
import {VueDraggable} from "vue-draggable-plus";
const router = useRouter() const router = useRouter()
const stepper = ref("1") const stepper = ref("1")
const loading = ref(false)
const importUrl = ref("") const importUrl = ref("")
const importResponse = ref({} as RecipeFromSourceResponse) const importResponse = ref({} as RecipeFromSourceResponse)
@@ -152,6 +160,7 @@ const importResponse = ref({} as RecipeFromSourceResponse)
*/ */
function loadRecipeFromUrl() { function loadRecipeFromUrl() {
let api = new ApiApi() let api = new ApiApi()
loading.value = true
api.apiRecipeFromSourceCreate({recipeFromSource: {url: importUrl.value}}).then(r => { api.apiRecipeFromSourceCreate({recipeFromSource: {url: importUrl.value}}).then(r => {
importResponse.value = r importResponse.value = r
if (r.duplicates.length == 0) { if (r.duplicates.length == 0) {
@@ -160,6 +169,8 @@ function loadRecipeFromUrl() {
} }
}).catch(err => { }).catch(err => {
useMessageStore().addError(ErrorMessageType.FETCH_ERROR, err) useMessageStore().addError(ErrorMessageType.FETCH_ERROR, err)
}).finally(() => {
loading.value = false
}) })
} }
@@ -168,12 +179,21 @@ function loadRecipeFromUrl() {
*/ */
function createRecipeFromImport() { function createRecipeFromImport() {
let api = new ApiApi() let api = new ApiApi()
console.log(importResponse.value)
api.apiRecipeCreate({recipe: importResponse.value.recipe}).then(r => { if (importResponse.value.recipe) {
router.push({name: 'view_recipe', params: {id: r.id}}) loading.value = true
}).catch(err => { importResponse.value.recipe.keywords = importResponse.value.recipe.keywords.filter(k => k.importKeyword)
useMessageStore().addError(ErrorMessageType.CREATE_ERROR, err)
}) api.apiRecipeCreate({recipe: importResponse.value.recipe}).then(r => {
api.apiRecipeImageUpdate({id: r.id, imageUrl: importResponse.value.recipe?.imageUrl}).then(rI => {
router.push({name: 'view_recipe', params: {id: r.id}})
}).finally(() => {
loading.value = false
})
}).catch(err => {
useMessageStore().addError(ErrorMessageType.CREATE_ERROR, err)
})
}
} }
/** /**
@@ -181,8 +201,8 @@ function createRecipeFromImport() {
* @param step step to delete * @param step step to delete
*/ */
function deleteStep(step: SourceImportStep) { function deleteStep(step: SourceImportStep) {
if(importResponse.value.recipe){ if (importResponse.value.recipe) {
importResponse.value.recipe.steps.splice(importResponse.value.recipe.steps.findIndex(x => x === step),1) importResponse.value.recipe.steps.splice(importResponse.value.recipe.steps.findIndex(x => x === step), 1)
} }
} }
@@ -285,7 +305,7 @@ function autoSortIngredients() {
s.ingredients.push(i) s.ingredients.push(i)
} }
}) })
if(!found){ if (!found) {
importResponse.value.recipe!.steps[0].ingredients.push(i) importResponse.value.recipe!.steps[0].ingredients.push(i)
} }
// TODO implement a new "second try" algorithm if no exact match was found // TODO implement a new "second try" algorithm if no exact match was found

View File

@@ -1,43 +1,35 @@
<template> <template>
<v-container class="ps-0 pe-0 pt-0"> <v-container :class="{'ps-0 pe-0 pt-0': mobile}">
<RecipeView :recipe="recipe"></RecipeView> <RecipeView :recipe="recipe"></RecipeView>
</v-container> </v-container>
</template> </template>
<script lang="ts"> <script setup lang="ts">
import {defineComponent} from 'vue' import {defineComponent, onMounted, ref} from 'vue'
import {ApiApi, Recipe} from "@/openapi"; import {ApiApi, Recipe} from "@/openapi";
import RecipeView from "@/components/display/RecipeView.vue"; import RecipeView from "@/components/display/RecipeView.vue";
import {useDisplay} from "vuetify";
export default defineComponent({ const props = defineProps({
name: "RecipeSearchPage", id: {type: String, required: true}
components: {RecipeView},
watch: {
id: function (newValue) {
this.refreshData(newValue)
},
},
props: {
id: {type: String, required: true}
},
data() {
return {
recipe: {} as Recipe
}
},
mounted() {
this.refreshData(this.id)
},
methods: {
refreshData(recipeId: string) {
const api = new ApiApi()
api.apiRecipeRetrieve({id: Number(recipeId)}).then(r => {
this.recipe = r
})
}
}
}) })
const {mobile} = useDisplay()
const recipe = ref({} as Recipe)
onMounted(() => {
refreshData(props.id)
})
function refreshData(recipeId: string) {
const api = new ApiApi()
api.apiRecipeRetrieve({id: Number(recipeId)}).then(r => {
recipe.value = r
})
}
</script> </script>
<style scoped> <style scoped>

View File

@@ -1,3 +1,7 @@
import {getCookie} from "@/utils/cookie";
import {Recipe, RecipeFromJSON, RecipeImageFromJSON, UserFileFromJSON} from "@/openapi";
import {ErrorMessageType, PreparedMessage, useMessageStore} from "@/stores/MessageStore";
/** /**
* Gets a nested property of an object given a dot-notation path. * Gets a nested property of an object given a dot-notation path.
* *
@@ -6,13 +10,42 @@
* @returns The value of the nested property, or `undefined` if not found. * @returns The value of the nested property, or `undefined` if not found.
*/ */
export function getNestedProperty(object: any, path: string): any { export function getNestedProperty(object: any, path: string): any {
const pathParts = path.split('.'); const pathParts = path.split('.');
return pathParts.reduce((obj, key) => {
if (obj && typeof obj === 'object') {
return obj[key]
} else {
return undefined;
}
}, object);
}
//TODO just some partial code
/**
* I currently don't know how to do this properly through the API client so this
* helper function uploads files for now
*/
export function uploadRecipeImage(recipeId: number, file: File) {
let formData = new FormData()
formData.append('image', file)
//TODO proper URL finding (sub path setups)
// TODO maybe better use existing URL clients response functions for parsing
fetch('/api/recipe/' + recipeId + '/image/', {
method: 'PUT',
headers: {'X-CSRFToken': getCookie('csrftoken')},
body: formData
}).then(r => {
r.json().then(r => {
return RecipeImageFromJSON(r)
})
}).catch(err => {
useMessageStore().addError(ErrorMessageType.UPDATE_ERROR, err)
}).finally(() => {
})
return pathParts.reduce((obj, key) => {
if (obj && typeof obj === 'object') {
return obj[key]
} else {
return undefined;
}
}, object);
} }