1
0
Fork 0
mirror of https://github.com/mealie-recipes/mealie.git synced 2025-07-24 15:49:42 +02:00

feat: add reports to bulk recipe import (url) (#1294)

* remove unused docker and caddy configs

* add experimental nested configs

* switch to nest under docker-compose

* remove v-card

* bulk parser backend re-implementation

* refactor UI for bulk importer

* remove migration specific report text
This commit is contained in:
Hayden 2022-05-25 19:33:58 -08:00 committed by GitHub
parent d66d6c55ae
commit 010aafa69b
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
15 changed files with 240 additions and 139 deletions

View file

@ -4,9 +4,7 @@
<template #header>
<v-img max-height="200" max-width="200" class="mb-2" :src="require('~/static/svgs/data-reports.svg')"></v-img>
</template>
<template #title> Recipe Data Migrations</template>
Recipes can be migrated from another supported application to Mealie. This is a great way to get started with
Mealie.
<template #title> Report </template>
</BasePageTitle>
<v-container v-if="report">
<BaseCardSectionTitle :title="report.name"> </BaseCardSectionTitle>
@ -31,8 +29,9 @@
</template>
<script lang="ts">
import { defineComponent, useRoute, reactive, toRefs, onMounted } from "@nuxtjs/composition-api";
import { defineComponent, useRoute, ref, onMounted } from "@nuxtjs/composition-api";
import { useUserApi } from "~/composables/api";
import { ReportOut } from "~/types/api-types/reports";
export default defineComponent({
setup() {
@ -41,16 +40,11 @@ export default defineComponent({
const api = useUserApi();
const state = reactive({
report: {},
});
const report = ref<ReportOut | null>(null);
async function getReport() {
const { data } = await api.groupReports.getOne(id);
if (data) {
state.report = data;
}
report.value = data ?? null;
}
onMounted(async () => {
@ -64,7 +58,7 @@ export default defineComponent({
];
return {
...toRefs(state),
report,
id,
itemHeaders,
};
@ -72,5 +66,4 @@ export default defineComponent({
});
</script>
<style lang="scss" scoped>
</style>
<style lang="scss" scoped></style>

View file

@ -1,15 +1,15 @@
<template>
<div>
<v-card flat>
<div flat>
<v-card-title class="headline"> Recipe Bulk Importer </v-card-title>
<v-card-text>
The Bulk recipe importer allows you to import multiple recipes at once by queing the sites on the backend and
running the task in the background. This can be useful when initially migrating to Mealie, or when you want to
import a large number of recipes.
</v-card-text>
</v-card>
</div>
<section class="mt-2">
<v-row v-for="(bulkUrl, idx) in bulkUrls" :key="'bulk-url' + idx" class="my-1" dense>
<v-row v-for="(_, idx) in bulkUrls" :key="'bulk-url' + idx" class="my-1" dense>
<v-col cols="12" xs="12" sm="12" md="12">
<v-text-field
v-model="bulkUrls[idx].url"
@ -26,7 +26,7 @@
class="rounded-lg"
>
<template #append>
<v-btn color="error" icon x-small @click="bulkUrls.splice(idx, 1)">
<v-btn style="margin-top: -2px" icon small @click="bulkUrls.splice(idx, 1)">
<v-icon>
{{ $globals.icons.delete }}
</v-icon>
@ -34,38 +34,40 @@
</template>
</v-text-field>
</v-col>
<v-col cols="12" xs="12" sm="6">
<RecipeOrganizerSelector
v-model="bulkUrls[idx].categories"
:items="allCategories || []"
selector-type="category"
:input-attrs="{
filled: true,
singleLine: true,
dense: true,
rounded: true,
class: 'rounded-lg',
hideDetails: true,
clearable: true,
}"
/>
</v-col>
<v-col cols="12" xs="12" sm="6">
<RecipeOrganizerSelector
v-model="bulkUrls[idx].tags"
:items="allTags || []"
selector-type="tag"
:input-attrs="{
filled: true,
singleLine: true,
dense: true,
rounded: true,
class: 'rounded-lg',
hideDetails: true,
clearable: true,
}"
/>
</v-col>
<template v-if="showCatTags">
<v-col cols="12" xs="12" sm="6">
<RecipeOrganizerSelector
v-model="bulkUrls[idx].categories"
:items="allCategories || []"
selector-type="category"
:input-attrs="{
filled: true,
singleLine: true,
dense: true,
rounded: true,
class: 'rounded-lg',
hideDetails: true,
clearable: true,
}"
/>
</v-col>
<v-col cols="12" xs="12" sm="6">
<RecipeOrganizerSelector
v-model="bulkUrls[idx].tags"
:items="allTags || []"
selector-type="tag"
:input-attrs="{
filled: true,
singleLine: true,
dense: true,
rounded: true,
class: 'rounded-lg',
hideDetails: true,
clearable: true,
}"
/>
</v-col>
</template>
</v-row>
<v-card-actions class="justify-end">
<BaseButton
@ -78,32 +80,56 @@
Clear
</BaseButton>
<v-spacer></v-spacer>
<BaseButton color="info" @click="bulkUrls.push({ url: '', categories: [], tags: [] })">
<template #icon> {{ $globals.icons.createAlt }} </template> New
<BaseButton class="mr-1" color="info" @click="bulkUrls.push({ url: '', categories: [], tags: [] })">
<template #icon> {{ $globals.icons.createAlt }} </template>
New
</BaseButton>
<RecipeDialogBulkAdd v-model="bulkDialog" @bulk-data="assignUrls" />
</v-card-actions>
<div class="px-1">
<v-checkbox v-model="showCatTags" hide-details label="Set Categories and Tags " />
</div>
<v-card-actions class="justify-end">
<BaseButton :disabled="bulkUrls.length === 0 || lockBulkImport" @click="bulkCreate">
<template #icon> {{ $globals.icons.check }} </template> Submit
<template #icon> {{ $globals.icons.check }} </template>
Submit
</BaseButton>
</v-card-actions>
</section>
<section class="mt-12">
<BaseCardSectionTitle title="Bulk Imports"> </BaseCardSectionTitle>
<ReportTable :items="reports" @delete="deleteReport" />
</section>
</div>
</template>
<script lang="ts">
import { defineComponent, reactive, toRefs, ref } from "@nuxtjs/composition-api";
import { whenever } from "@vueuse/shared";
import { useUserApi } from "~/composables/api";
import { alert } from "~/composables/use-toast";
import RecipeOrganizerSelector from "~/components/Domain/Recipe/RecipeOrganizerSelector.vue";
import { useCategories, useTags } from "~/composables/recipes";
import { ReportSummary } from "~/types/api-types/reports";
import RecipeDialogBulkAdd from "~/components/Domain/Recipe/RecipeDialogBulkAdd.vue";
export default defineComponent({
components: { RecipeOrganizerSelector },
components: { RecipeOrganizerSelector, RecipeDialogBulkAdd },
setup() {
const state = reactive({
error: false,
loading: false,
showCatTags: false,
bulkDialog: false,
});
whenever(
() => !state.showCatTags,
() => {
console.log("showCatTags changed");
}
);
const api = useUserApi();
const bulkUrls = ref([{ url: "", categories: [], tags: [] }]);
@ -122,6 +148,8 @@ export default defineComponent({
} else {
alert.error("Bulk import process has failed");
}
fetchReports();
}
const { allTags, useAsyncGetAll: getAllTags } = useTags();
@ -130,7 +158,37 @@ export default defineComponent({
getAllTags();
getAllCategories();
// =========================================================
// Reports
const reports = ref<ReportSummary[]>([]);
async function fetchReports() {
const { data } = await api.groupReports.getAll("bulk_import");
reports.value = data ?? [];
}
async function deleteReport(id: string) {
console.log(id);
const { response } = await api.groupReports.deleteOne(id);
if (response?.status === 200) {
fetchReports();
} else {
alert.error("Report deletion failed");
}
}
fetchReports();
function assignUrls(urls: string[]) {
bulkUrls.value = urls.map((url) => ({ url, categories: [], tags: [] }));
}
return {
assignUrls,
reports,
deleteReport,
allTags,
allCategories,
bulkCreate,

View file

@ -1,7 +1,7 @@
<template>
<div>
<v-form ref="domUrlForm" @submit.prevent="debugUrl(recipeUrl)">
<v-card flat>
<div>
<v-card-title class="headline"> Recipe Debugger </v-card-title>
<v-card-text>
Grab the URL of the recipe you want to debug and paste it here. The URL will be scraped by the recipe scraper
@ -32,7 +32,7 @@
</BaseButton>
</div>
</v-card-actions>
</v-card>
</div>
</v-form>
<section v-if="debugData">
<v-checkbox v-model="debugTreeView" label="Tree View"></v-checkbox>

View file

@ -1,5 +1,5 @@
<template>
<v-card flat>
<div>
<v-card-title class="headline"> Create Recipe </v-card-title>
<v-card-text>
Create a recipe by providing the name. All recipes must have unique names.
@ -31,7 +31,7 @@
/>
</div>
</v-card-actions>
</v-card>
</div>
</template>
<script lang="ts">
import { defineComponent, reactive, toRefs, ref, useRouter } from "@nuxtjs/composition-api";

View file

@ -1,7 +1,7 @@
<template>
<div>
<v-form ref="domUrlForm" @submit.prevent="createByUrl(recipeUrl, importKeywordsAsTags)">
<v-card flat>
<div>
<v-card-title class="headline"> Scrape Recipe </v-card-title>
<v-card-text>
Scrape a recipe by url. Provide the url for the site you want to scrape, and Mealie will attempt to scrape the
@ -27,7 +27,7 @@
<BaseButton :disabled="recipeUrl === null" rounded block type="submit" :loading="loading" />
</div>
</v-card-actions>
</v-card>
</div>
</v-form>
<v-expand-transition>
<v-alert v-show="error" color="error" class="mt-6 white--text">

View file

@ -1,6 +1,6 @@
<template>
<v-form>
<v-card flat>
<div>
<v-card-title class="headline"> Import from Zip </v-card-title>
<v-card-text>
Import a single recipe that was exported from another Mealie instance.
@ -25,7 +25,7 @@
<BaseButton :disabled="newRecipeZip === null" large rounded block :loading="loading" @click="createByZip" />
</div>
</v-card-actions>
</v-card>
</div>
</v-form>
</template>

View file

@ -5,7 +5,7 @@
/* Do not modify it by hand - just update the pydantic models and then re-run the script
*/
export type ReportCategory = "backup" | "restore" | "migration";
export type ReportCategory = "backup" | "restore" | "migration" | "bulk_import";
export type ReportSummaryStatus = "in-progress" | "success" | "failure" | "partial";
export interface ReportCreate {