Improved import summary dialog

This commit is contained in:
hay-kot 2021-02-20 11:48:15 -09:00
commit 4de7bd8016
7 changed files with 281 additions and 22 deletions

View file

@ -8,9 +8,9 @@ const backupURLs = {
// Backup
available: `${backupBase}available`,
createBackup: `${backupBase}export/database`,
importBackup: (fileName) => `${backupBase}${fileName}/import`,
deleteBackup: (fileName) => `${backupBase}${fileName}/delete`,
downloadBackup: (fileName) => `${backupBase}${fileName}/download`,
importBackup: fileName => `${backupBase}${fileName}/import`,
deleteBackup: fileName => `${backupBase}${fileName}/delete`,
downloadBackup: fileName => `${backupBase}${fileName}/download`,
};
export default {

View file

@ -69,10 +69,9 @@ export default {
this.$emit("loading");
let response = await api.backups.import(data.name, data);
let failed = response.data.failed;
let succesful = response.data.successful;
let importData = response.data;
this.$emit("finished", succesful, failed);
this.$emit("finished", importData);
},
deleteBackup(data) {
this.$emit("loading");

View file

@ -0,0 +1,47 @@
<template>
<div>
<v-data-table
dense
:headers="dataHeaders"
:items="dataSet"
item-key="name"
class="elevation-1 mt-2"
show-expand
:expanded.sync="expanded"
:footer-props="{
'items-per-page-options': [100, 200, 300, 400, -1],
}"
:items-per-page="100"
>
<template v-slot:item.status="{ item }">
<div :class="item.status ? 'success--text' : 'error--text'">
{{ item.status ? "Imported" : "Failed" }}
</div>
</template>
<template v-slot:expanded-item="{ headers, item }">
<td :colspan="headers.length">
<div class="ma-2">
{{ item.exception }}
</div>
</td>
</template>
</v-data-table>
</div>
</template>
<script>
export default {
props: {
dataSet: Array,
dataHeaders: Array,
},
data: () => ({
singleExpand: false,
expanded: [],
}),
};
</script>
<style>
</style>

View file

@ -0,0 +1,152 @@
<template>
<div class="text-center">
<v-dialog v-model="dialog" width="70%">
<v-card>
<v-card-title> Import Summary </v-card-title>
<v-card-text>
<v-row class="mb-n9">
<v-card flat>
<v-card-text>
<div>
<h3>Recipes</h3>
</div>
<div class="success--text">
Success: {{ recipeNumbers.success }}
</div>
<div class="error--text">
Failed: {{ recipeNumbers.failure }}
</div>
</v-card-text>
</v-card>
<v-card flat>
<v-card-text>
<div>
<h3>Themes</h3>
</div>
<div class="success--text">
Success: {{ themeNumbers.success }}
</div>
<div class="error--text">
Failed: {{ themeNumbers.failure }}
</div>
</v-card-text>
</v-card>
<v-card flat>
<v-card-text>
<div>
<h3>Settings</h3>
</div>
<div class="success--text">
Success: {{ settingsNumbers.success }}
</div>
<div class="error--text">
Failed: {{ settingsNumbers.failure }}
</div>
</v-card-text>
</v-card>
</v-row>
</v-card-text>
<v-tabs v-model="tab">
<v-tab>Recipes</v-tab>
<v-tab>Themes</v-tab>
<v-tab>Settings</v-tab>
</v-tabs>
<v-tabs-items v-model="tab">
<v-tab-item>
<v-card flat>
<DataTable :data-headers="recipeHeaders" :data-set="recipeData" />
</v-card>
</v-tab-item>
<v-tab-item>
<v-card>
<DataTable
:data-headers="recipeHeaders"
:data-set="themeData"
/> </v-card
></v-tab-item>
<v-tab-item>
<v-card
><DataTable
:data-headers="recipeHeaders"
:data-set="settingsData"
/>
</v-card>
</v-tab-item>
</v-tabs-items>
</v-card>
</v-dialog>
</div>
</template>
<script>
import DataTable from "./DataTable";
export default {
components: {
DataTable,
},
data: () => ({
tab: null,
dialog: false,
recipeData: [],
themeData: [],
settingsData: [],
recipeHeaders: [
{
text: "Status",
value: "status",
},
{
text: "Name",
align: "start",
sortable: true,
value: "name",
},
{ text: "Exception", value: "data-table-expand", align: "center" },
],
allDataTables: [],
}),
computed: {
recipeNumbers() {
let numbers = { success: 0, failure: 0 };
this.recipeData.forEach(element => {
if (element.status) {
numbers.success++;
} else numbers.failure++;
});
return numbers;
},
settingsNumbers() {
let numbers = { success: 0, failure: 0 };
this.settingsData.forEach(element => {
if (element.status) {
numbers.success++;
} else numbers.failure++;
});
return numbers;
},
themeNumbers() {
let numbers = { success: 0, failure: 0 };
this.themeData.forEach(element => {
if (element.status) {
numbers.success++;
} else numbers.failure++;
});
return numbers;
},
},
methods: {
open(importData) {
this.recipeData = importData.recipeImports;
this.themeData = importData.themeReport;
this.settingsData = importData.settingsReport;
this.dialog = true;
},
},
};
</script>
<style>
</style>

View file

@ -41,6 +41,7 @@
:failed-header="$t('settings.backup.failed-imports')"
:failed="failedImports"
/>
<ImportSummaryDialog ref="report" :import-data="importData" />
</v-card-text>
</v-card>
</template>
@ -48,6 +49,7 @@
<script>
import api from "@/api";
import SuccessFailureAlert from "../../UI/SuccessFailureAlert";
import ImportSummaryDialog from "./ImportSummaryDialog";
import UploadBtn from "../../UI/UploadBtn";
import AvailableBackupCard from "./AvailableBackupCard";
import NewBackupCard from "./NewBackupCard";
@ -58,6 +60,7 @@ export default {
UploadBtn,
AvailableBackupCard,
NewBackupCard,
ImportSummaryDialog,
},
data() {
return {
@ -65,6 +68,7 @@ export default {
successfulImports: [],
backupLoading: false,
availableBackups: [],
importData: [],
};
},
mounted() {
@ -87,12 +91,10 @@ export default {
this.backupLoading = false;
}
},
processFinished(successful = null, failed = null) {
processFinished(data) {
this.getAvailableBackups();
this.backupLoading = false;
this.successfulImports = successful;
this.failedImports = failed;
this.$refs.report.open();
this.$refs.report.open(data);
},
},
};

View file

@ -0,0 +1,20 @@
from typing import Optional
from pydantic.main import BaseModel
class RecipeImport(BaseModel):
name: Optional[str]
slug: str
status: bool
exception: Optional[str]
class ThemeImport(BaseModel):
name: str
status: bool
exception: Optional[str]
class SettingsImport(BaseModel):
name: str
status: bool
exception: Optional[str]

View file

@ -1,12 +1,13 @@
import json
import shutil
import zipfile
from logging import error
from logging import error, exception
from pathlib import Path
from typing import List
from app_config import BACKUP_DIR, IMG_DIR, TEMP_DIR
from db.database import db
from models.import_models import RecipeImport, SettingsImport, ThemeImport
from models.theme_models import SiteTheme
from services.recipe_services import Recipe
from services.settings_services import SiteSettings
@ -57,23 +58,29 @@ class ImportDatabase:
raise Exception("Import file does not exist")
def run(self):
report = {}
recipe_report = []
settings_report = []
theme_report = []
if self.imp_recipes:
report = self.import_recipes()
recipe_report = self.import_recipes()
if self.imp_settings:
self.import_settings()
settings_report = self.import_settings()
if self.imp_themes:
self.import_themes()
theme_report = self.import_themes()
self.clean_up()
return report if report else None
return {
"recipeImports": recipe_report,
"settingsReport": settings_report,
"themeReport": theme_report,
}
def import_recipes(self):
recipe_dir: Path = self.import_dir.joinpath("recipes")
imports = []
successful_imports = []
failed_imports = []
for recipe in recipe_dir.glob("*.json"):
with open(recipe, "r") as f:
@ -82,16 +89,27 @@ class ImportDatabase:
try:
recipe_obj = Recipe(**recipe_dict)
recipe_obj.save_to_db(self.session)
import_status = RecipeImport(
name=recipe_obj.name, slug=recipe_obj.slug, status=True
)
imports.append(import_status)
successful_imports.append(recipe.stem)
logger.info(f"Imported: {recipe.stem}")
except Exception as inst:
logger.error(inst)
logger.info(f"Failed Import: {recipe.stem}")
failed_imports.append(recipe.stem)
import_status = RecipeImport(
name=recipe.stem,
slug=recipe.stem,
status=False,
exception=str(inst),
)
imports.append(import_status)
self._import_images(successful_imports)
return {"successful": successful_imports, "failed": failed_imports}
return imports
@staticmethod
def _recipe_migration(recipe_dict: dict) -> dict:
@ -130,7 +148,7 @@ class ImportDatabase:
def import_themes(self):
themes_file = self.import_dir.joinpath("themes", "themes.json")
theme_imports = []
with open(themes_file, "r") as f:
themes: list[dict] = json.loads(f.read())
for theme in themes:
@ -138,17 +156,38 @@ class ImportDatabase:
continue
new_theme = SiteTheme(**theme)
try:
db.themes.create(self.session, new_theme.dict())
except:
theme_imports.append(ThemeImport(name=new_theme.name, status=True))
except Exception as inst:
logger.info(f"Unable Import Theme {new_theme.name}")
theme_imports.append(
ThemeImport(name=new_theme.name, status=False, exception=str(inst))
)
return theme_imports
def import_settings(self):
settings_file = self.import_dir.joinpath("settings", "settings.json")
settings_imports = []
with open(settings_file, "r") as f:
settings: dict = json.loads(f.read())
db.settings.update(self.session, settings.get("name"), settings)
name = settings.get("name")
try:
db.settings.update(self.session, name, settings)
import_status = SettingsImport(name=name, status=True)
except Exception as inst:
import_status = SettingsImport(
name=name, status=False, exception=str(inst)
)
settings_imports.append(import_status)
return settings_imports
def clean_up(self):
shutil.rmtree(TEMP_DIR)