Feature/submit on enter key (#224)

* general cleanup

* submit on enter

* fix signup form

* fix duplicate slugs when testing

Co-authored-by: hay-kot <hay-kot@pm.me>
This commit is contained in:
Hayden 2021-03-27 13:23:08 -08:00 committed by GitHub
commit 894d6b9c9b
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
23 changed files with 216 additions and 210 deletions

View file

@ -24,7 +24,7 @@
v-bind="attrs" v-bind="attrs"
v-on="on" v-on="on"
> >
{{ $t('user.create-group') }} {{ $t("user.create-group") }}
</v-btn> </v-btn>
</template> </template>
<v-card> <v-card>
@ -34,31 +34,30 @@
</v-icon> </v-icon>
<v-toolbar-title class="headline"> <v-toolbar-title class="headline">
{{ $t('user.create-group') }} {{ $t("user.create-group") }}
</v-toolbar-title> </v-toolbar-title>
<v-spacer></v-spacer> <v-spacer></v-spacer>
</v-app-bar> </v-app-bar>
<v-form ref="newGroup" @submit="createGroup">
<v-card-text> <v-card-text>
<v-form ref="newGroup">
<v-text-field <v-text-field
v-model="newGroupName" v-model="newGroupName"
:label="$t('user.group-name')" :label="$t('user.group-name')"
:rules="[existsRule]" :rules="[existsRule]"
></v-text-field> ></v-text-field>
</v-form> </v-card-text>
</v-card-text>
<v-card-actions> <v-card-actions>
<v-spacer></v-spacer> <v-spacer></v-spacer>
<v-btn color="grey" text @click="groupDialog = false"> <v-btn color="grey" text @click="groupDialog = false">
{{ $t('general.cancel') }} {{ $t("general.cancel") }}
</v-btn> </v-btn>
<v-btn color="primary" @click="createGroup"> <v-btn color="primary" type="submit" @click.prevent="createGroup">
{{ $t('general.create') }} {{ $t("general.create") }}
</v-btn> </v-btn>
</v-card-actions> </v-card-actions>
</v-form>
</v-card> </v-card>
</v-dialog> </v-dialog>
</v-card-actions> </v-card-actions>

View file

@ -3,7 +3,11 @@
<Confirmation <Confirmation
ref="deleteUserDialog" ref="deleteUserDialog"
:title="$t('user.confirm-link-deletion')" :title="$t('user.confirm-link-deletion')"
:message="$t('user.are-you-sure-you-want-to-delete-the-link', {link: activeName })" :message="
$t('user.are-you-sure-you-want-to-delete-the-link', {
link: activeName,
})
"
icon="mdi-alert" icon="mdi-alert"
@confirm="deleteUser" @confirm="deleteUser"
:width="450" :width="450"
@ -14,14 +18,14 @@
mdi-link-variant mdi-link-variant
</v-icon> </v-icon>
<v-toolbar-title class="headine"> <v-toolbar-title class="headine">
{{ $t('user.sign-up-links') }} {{ $t("user.sign-up-links") }}
</v-toolbar-title> </v-toolbar-title>
<v-spacer> </v-spacer> <v-spacer> </v-spacer>
<v-dialog v-model="dialog" max-width="600px"> <v-dialog v-model="dialog" max-width="600px">
<template v-slot:activator="{ on, attrs }"> <template v-slot:activator="{ on, attrs }">
<v-btn small color="success" dark v-bind="attrs" v-on="on"> <v-btn small color="success" dark v-bind="attrs" v-on="on">
{{ $t('user.create-link') }} {{ $t("user.create-link") }}
</v-btn> </v-btn>
</template> </template>
<v-card> <v-card>
@ -31,14 +35,13 @@
</v-icon> </v-icon>
<v-toolbar-title class="headline"> <v-toolbar-title class="headline">
{{ $t('user.create-link') }} {{ $t("user.create-link") }}
</v-toolbar-title> </v-toolbar-title>
<v-spacer></v-spacer> <v-spacer></v-spacer>
</v-app-bar> </v-app-bar>
<v-form ref="newUser" @submit="save">
<v-card-text> <v-card-text>
<v-form ref="newUser">
<v-row class="justify-center mt-3"> <v-row class="justify-center mt-3">
<v-text-field <v-text-field
class="mr-2" class="mr-2"
@ -52,18 +55,18 @@
:label="$t('user.admin')" :label="$t('user.admin')"
></v-checkbox> ></v-checkbox>
</v-row> </v-row>
</v-form> </v-card-text>
</v-card-text>
<v-card-actions> <v-card-actions>
<v-spacer></v-spacer> <v-spacer></v-spacer>
<v-btn color="grey" text @click="close"> <v-btn color="grey" text @click="close">
{{ $t('general.cancel') }} {{ $t("general.cancel") }}
</v-btn> </v-btn>
<v-btn color="primary" @click="save"> <v-btn color="primary" type="submit" @click.prevent="save">
{{ $t('general.save') }} {{ $t("general.save") }}
</v-btn> </v-btn>
</v-card-actions> </v-card-actions>
</v-form>
</v-card> </v-card>
</v-dialog> </v-dialog>
</v-toolbar> </v-toolbar>
@ -90,7 +93,7 @@
<v-icon small left> <v-icon small left>
mdi-account-cog mdi-account-cog
</v-icon> </v-icon>
{{ item.admin ? $t('general.yes') : $t('general.no') }} {{ item.admin ? $t("general.yes") : $t("general.no") }}
</v-btn> </v-btn>
</template> </template>
<template v-slot:item.actions="{ item }"> <template v-slot:item.actions="{ item }">
@ -98,7 +101,7 @@
<v-icon small left> <v-icon small left>
mdi-delete mdi-delete
</v-icon> </v-icon>
{{ $t('general.delete') }} {{ $t("general.delete") }}
</v-btn> </v-btn>
</template> </template>
</v-data-table> </v-data-table>
@ -113,21 +116,21 @@ import { validators } from "@/mixins/validators";
export default { export default {
components: { Confirmation }, components: { Confirmation },
mixins: [validators], mixins: [validators],
data() { data() {
return { return {
dialog: false, dialog: false,
activeId: null, activeId: null,
activeName: null, activeName: null,
headers: [ headers: [
{ {
text: this.$t('user.link-id'), text: this.$t("user.link-id"),
align: "start", align: "start",
sortable: false, sortable: false,
value: "id", value: "id",
}, },
{ text: this.$t('general.name'), value: "name" }, { text: this.$t("general.name"), value: "name" },
{ text: this.$t('general.token'), value: "token" }, { text: this.$t("general.token"), value: "token" },
{ text: this.$t('user.admin'), value: "admin", align: "center" }, { text: this.$t("user.admin"), value: "admin", align: "center" },
{ text: "", value: "actions", sortable: false, align: "center" }, { text: "", value: "actions", sortable: false, align: "center" },
], ],
links: [], links: [],
@ -144,7 +147,7 @@ export default {
admin: false, admin: false,
id: 0, id: 0,
}, },
} };
}, },
computed: { computed: {

View file

@ -3,7 +3,12 @@
<Confirmation <Confirmation
ref="deleteUserDialog" ref="deleteUserDialog"
:title="$t('user.confirm-user-deletion')" :title="$t('user.confirm-user-deletion')"
:message="$t('user.are-you-sure-you-want-to-delete-the-user', { activeName, activeId })" :message="
$t('user.are-you-sure-you-want-to-delete-the-user', {
activeName,
activeId,
})
"
icon="mdi-alert" icon="mdi-alert"
@confirm="deleteUser" @confirm="deleteUser"
:width="450" :width="450"
@ -25,7 +30,7 @@
<v-dialog v-model="dialog" max-width="600px"> <v-dialog v-model="dialog" max-width="600px">
<template v-slot:activator="{ on, attrs }"> <template v-slot:activator="{ on, attrs }">
<v-btn small color="success" dark v-bind="attrs" v-on="on"> <v-btn small color="success" dark v-bind="attrs" v-on="on">
{{$t('user.create-user')}} {{ $t("user.create-user") }}
</v-btn> </v-btn>
</template> </template>
<v-card> <v-card>
@ -40,12 +45,11 @@
<v-spacer></v-spacer> <v-spacer></v-spacer>
<v-toolbar-title class="headline"> <v-toolbar-title class="headline">
{{$t('user.user-id-with-value', {id: editedItem.id }) }} {{ $t("user.user-id-with-value", { id: editedItem.id }) }}
</v-toolbar-title> </v-toolbar-title>
</v-app-bar> </v-app-bar>
<v-form ref="newUser" @submit="save">
<v-card-text> <v-card-text>
<v-form ref="newUser">
<v-row> <v-row>
<v-col cols="12" sm="12" md="6"> <v-col cols="12" sm="12" md="6">
<v-text-field <v-text-field
@ -80,21 +84,24 @@
></v-text-field> ></v-text-field>
</v-col> </v-col>
<v-col cols="12" sm="12" md="3"> <v-col cols="12" sm="12" md="3">
<v-switch v-model="editedItem.admin" :label="$t('user.admin')"></v-switch> <v-switch
v-model="editedItem.admin"
:label="$t('user.admin')"
></v-switch>
</v-col> </v-col>
</v-row> </v-row>
</v-form> </v-card-text>
</v-card-text>
<v-card-actions> <v-card-actions>
<v-spacer></v-spacer> <v-spacer></v-spacer>
<v-btn color="grey" text @click="close"> <v-btn color="grey" text @click="close">
{{$t('general.cancel')}} {{ $t("general.cancel") }}
</v-btn> </v-btn>
<v-btn color="primary" @click="save"> <v-btn color="primary" type="submit" @click.prevent="save">
{{$t('general.save')}} {{ $t("general.save") }}
</v-btn> </v-btn>
</v-card-actions> </v-card-actions>
</v-form>
</v-card> </v-card>
</v-dialog> </v-dialog>
</v-toolbar> </v-toolbar>
@ -111,13 +118,13 @@
<v-icon small left> <v-icon small left>
mdi-delete mdi-delete
</v-icon> </v-icon>
{{$t('general.delete')}} {{ $t("general.delete") }}
</v-btn> </v-btn>
<v-btn small color="success" @click="editItem(item)"> <v-btn small color="success" @click="editItem(item)">
<v-icon small left class="mr-2"> <v-icon small left class="mr-2">
mdi-pencil mdi-pencil
</v-icon> </v-icon>
{{$t('general.edit')}} {{ $t("general.edit") }}
</v-btn> </v-btn>
</template> </template>
<template v-slot:item.admin="{ item }"> <template v-slot:item.admin="{ item }">
@ -125,7 +132,7 @@
</template> </template>
<template v-slot:no-data> <template v-slot:no-data>
<v-btn color="primary" @click="initialize"> <v-btn color="primary" @click="initialize">
{{$t('general.reset')}} {{ $t("general.reset") }}
</v-btn> </v-btn>
</template> </template>
</v-data-table> </v-data-table>
@ -140,7 +147,7 @@ import { validators } from "@/mixins/validators";
export default { export default {
components: { Confirmation }, components: { Confirmation },
mixins: [validators], mixins: [validators],
data() { data() {
return { return {
search: "", search: "",
dialog: false, dialog: false,
@ -153,10 +160,10 @@ export default {
sortable: false, sortable: false,
value: "id", value: "id",
}, },
{ text: this.$t('user.full-name'), value: "fullName" }, { text: this.$t("user.full-name"), value: "fullName" },
{ text: this.$t('user.email'), value: "email" }, { text: this.$t("user.email"), value: "email" },
{ text: this.$t('user.group'), value: "group" }, { text: this.$t("user.group"), value: "group" },
{ text: this.$t('user.admin'), value: "admin" }, { text: this.$t("user.admin"), value: "admin" },
{ text: "", value: "actions", sortable: false, align: "center" }, { text: "", value: "actions", sortable: false, align: "center" },
], ],
users: [], users: [],
@ -177,12 +184,14 @@ export default {
group: "", group: "",
admin: false, admin: false,
}, },
} };
}, },
computed: { computed: {
formTitle() { formTitle() {
return this.editedIndex === -1 ? this.$t('user.new-user') : this.$t('user.edit-user'); return this.editedIndex === -1
? this.$t("user.new-user")
: this.$t("user.edit-user");
}, },
showPassword() { showPassword() {
return this.editedIndex === -1 ? true : false; return this.editedIndex === -1 ? true : false;

View file

@ -17,22 +17,24 @@
<v-spacer></v-spacer> <v-spacer></v-spacer>
</v-app-bar> </v-app-bar>
<v-card-title> </v-card-title> <v-card-title> </v-card-title>
<v-card-text> <v-form @submit="select">
<v-text-field <v-card-text>
:label="$t('settings.theme.theme-name')" <v-text-field
v-model="themeName" :label="$t('settings.theme.theme-name')"
:rules="[rules.required]" v-model="themeName"
></v-text-field> :rules="[rules.required]"
</v-card-text> ></v-text-field>
<v-card-actions> </v-card-text>
<v-spacer></v-spacer> <v-card-actions>
<v-btn color="grey" text @click="dialog = false"> <v-spacer></v-spacer>
{{ $t("general.cancel") }} <v-btn color="grey" text @click="dialog = false">
</v-btn> {{ $t("general.cancel") }}
<v-btn color="success" text @click="Select" :disabled="!themeName"> </v-btn>
{{ $t("general.create") }} <v-btn color="success" text type="submit" @click.prevent="select" :disabled="!themeName">
</v-btn> {{ $t("general.create") }}
</v-card-actions> </v-btn>
</v-card-actions>
</v-form>
</v-card> </v-card>
</v-dialog> </v-dialog>
</div> </div>
@ -64,7 +66,7 @@ export default {
randomColor() { randomColor() {
return "#" + Math.floor(Math.random() * 16777215).toString(16); return "#" + Math.floor(Math.random() * 16777215).toString(16);
}, },
Select() { select() {
const newTheme = { const newTheme = {
name: this.themeName, name: this.themeName,
colors: { colors: {

View file

@ -13,11 +13,12 @@
class="mr-2" class="mr-2"
> >
</v-progress-circular> </v-progress-circular>
<v-toolbar-title class="headline">{{$t('user.login')}}</v-toolbar-title> <v-toolbar-title class="headline">{{ $t("user.login") }}</v-toolbar-title>
<v-spacer></v-spacer> <v-spacer></v-spacer>
</v-app-bar> </v-app-bar>
<v-card-text>
<v-form> <v-form @submit="login">
<v-card-text>
<v-text-field <v-text-field
v-if="!options.isLoggingIn" v-if="!options.isLoggingIn"
v-model="user.name" v-model="user.name"
@ -43,22 +44,24 @@
:append-icon="showPassword ? 'mdi-eye' : 'mdi-eye-off'" :append-icon="showPassword ? 'mdi-eye' : 'mdi-eye-off'"
@click:append="showPassword = !showPassword" @click:append="showPassword = !showPassword"
></v-text-field> ></v-text-field>
</v-form> <v-card-actions>
<v-card-actions> <v-btn
<v-btn v-if="options.isLoggingIn"
v-if="options.isLoggingIn" @click.prevent="login"
@click.prevent="login" dark
dark color="primary"
color="primary" block="block"
block="block" type="submit"
type="submit" >{{ $t("user.sign-in") }}
>{{ $t("user.sign-in") }}</v-btn </v-btn
> >
</v-card-actions> </v-card-actions>
<v-alert v-if="error" outlined class="mt-3 mb-0" type="error">
{{$t('user.could-not-validate-credentials')}} <v-alert v-if="error" outlined class="mt-3 mb-0" type="error">
</v-alert> {{ $t("user.could-not-validate-credentials") }}
</v-card-text> </v-alert>
</v-card-text>
</v-form>
</v-card> </v-card>
</template> </template>

View file

@ -21,7 +21,7 @@
have a valid invitation link. If you haven't recieved an invitation you have a valid invitation link. If you haven't recieved an invitation you
are unable to sign-up. To recieve a link, contact the sites administrator. are unable to sign-up. To recieve a link, contact the sites administrator.
<v-divider class="mt-3"></v-divider> <v-divider class="mt-3"></v-divider>
<v-form ref="signUpForm"> <v-form ref="signUpForm" @submit="signUp">
<v-text-field <v-text-field
v-model="user.name" v-model="user.name"
light="light" light="light"
@ -63,22 +63,22 @@
]" ]"
@click:append="showPassword = !showPassword" @click:append="showPassword = !showPassword"
></v-text-field> ></v-text-field>
<v-card-actions>
<v-btn
v-if="options.isLoggingIn"
@click.prevent="signUp"
dark
color="primary"
block="block"
type="submit"
>
Sign Up
</v-btn>
</v-card-actions>
<v-alert dense v-if="error" outlined class="mt-3 mb-0" type="error">
Error Signing Up
</v-alert>
</v-form> </v-form>
<v-card-actions>
<v-btn
v-if="options.isLoggingIn"
@click.prevent="signUp"
dark
color="primary"
block="block"
type="submit"
>
Sign Up
</v-btn>
</v-card-actions>
<v-alert dense v-if="error" outlined class="mt-3 mb-0" type="error">
Error Signing Up
</v-alert>
</v-card-text> </v-card-text>
</v-card> </v-card>
</template> </template>

View file

@ -21,9 +21,8 @@
<v-spacer></v-spacer> <v-spacer></v-spacer>
</v-app-bar> </v-app-bar>
<v-form ref="urlForm" @submit="createRecipe">
<v-card-text> <v-card-text>
<v-form ref="urlForm">
<v-text-field <v-text-field
v-model="recipeURL" v-model="recipeURL"
:label="$t('new-recipe.recipe-url')" :label="$t('new-recipe.recipe-url')"
@ -35,29 +34,29 @@
:hint="$t('new-recipe.url-form-hint')" :hint="$t('new-recipe.url-form-hint')"
persistent-hint persistent-hint
></v-text-field> ></v-text-field>
</v-form>
<v-alert v-if="error" color="red" outlined type="success"> <v-alert v-if="error" color="red" outlined type="success">
{{ $t("new-recipe.error-message") }} {{ $t("new-recipe.error-message") }}
</v-alert> </v-alert>
</v-card-text> </v-card-text>
<v-divider></v-divider> <v-divider></v-divider>
<v-card-actions> <v-card-actions>
<v-spacer></v-spacer> <v-spacer></v-spacer>
<v-btn color="grey" text @click="reset"> <v-btn color="grey" text @click="reset">
{{ $t("general.close") }} {{ $t("general.close") }}
</v-btn> </v-btn>
<v-btn <v-btn
color="success" color="success"
text text
@click="createRecipe" @click.prevent="createRecipe"
:loading="processing" :loading="processing"
> >
{{ $t("general.submit") }} {{ $t("general.submit") }}
</v-btn> </v-btn>
</v-card-actions> </v-card-actions>
</v-form>
</v-card> </v-card>
</v-dialog> </v-dialog>
<v-speed-dial v-model="fab" fixed right bottom open-on-hover> <v-speed-dial v-model="fab" fixed right bottom open-on-hover>

View file

@ -42,7 +42,6 @@ def update_generics(func):
elif attribute: elif attribute:
setattr(class_object, key, value) setattr(class_object, key, value)
print("Complex", complex_attributed)
func(class_object, complex_attributed) func(class_object, complex_attributed)
return wrapper return wrapper

View file

@ -23,7 +23,6 @@ async def get_current_user(token: str = Depends(oauth2_scheme), session=Depends(
if username is None: if username is None:
raise credentials_exception raise credentials_exception
token_data = TokenData(username=username) token_data = TokenData(username=username)
print("Login Payload", token_data)
except JWTError: except JWTError:
raise credentials_exception raise credentials_exception
user = db.users.get(session, token_data.username, "email") user = db.users.get(session, token_data.username, "email")

View file

@ -77,6 +77,5 @@ def get_today(
group_in_db: GroupInDB = db.groups.get(session, current_user.group, "name") group_in_db: GroupInDB = db.groups.get(session, current_user.group, "name")
recipe = get_todays_meal(session, group_in_db) recipe = get_todays_meal(session, group_in_db)
print(datetime.date.today())
return recipe.slug return recipe.slug

View file

@ -68,14 +68,11 @@ async def update_user(
token = None token = None
if current_user.id == id or current_user.admin: if current_user.id == id or current_user.admin:
print("Current User")
db.users.update(session, id, new_data.dict()) db.users.update(session, id, new_data.dict())
if current_user.id == id: if current_user.id == id:
print(new_data.email) access_token = security.create_access_token(data=dict(sub=new_data.email), expires_delta=timedelta(hours=2))
access_token = security.create_access_token(data=dict(sub=new_data.email), expires_delta=timedelta(hours=2)) token = {"access_token": access_token, "token_type": "bearer"}
token = {"access_token": access_token, "token_type": "bearer"}
print(SnackResponse.success("User Updated", token))
return SnackResponse.success("User Updated", token) return SnackResponse.success("User Updated", token)

View file

@ -33,10 +33,10 @@ class ImportJob(BackupOptions):
"example": { "example": {
"name": "my_local_backup.zip", "name": "my_local_backup.zip",
"recipes": True, "recipes": True,
"force": False, "settings": True,
"rebase": False, "themes": True,
"themes": False, "groups": True,
"settings": False, "users": True,
} }
} }

View file

@ -89,13 +89,16 @@ class ImportDatabase:
# Migration from list to Object Type Data # Migration from list to Object Type Data
try: try:
if "" in recipe_dict["tags"]: if "" in recipe_dict["tags"]:
recipe_dict["tags"] = [tag for tag in recipe_dict["tags"] if not tag == ""] recipe_dict["tags"] = [tag for tag in recipe_dict["tags"] if tag != ""]
except: except:
pass pass
try: try:
if "" in recipe_dict["categories"]: if "" in recipe_dict["categories"]:
recipe_dict["categories"] = [cat for cat in recipe_dict["categories"] if not cat == ""] recipe_dict["categories"] = [
cat for cat in recipe_dict["categories"] if cat != ""
]
except: except:
pass pass
@ -239,16 +242,15 @@ class ImportDatabase:
item = db_table.get(self.session, search_value, search_key) item = db_table.get(self.session, search_value, search_key)
if item: if item:
if self.force_imports: if not self.force_imports:
primary_key = getattr(item, db_table.primary_key)
db_table.delete(self.session, primary_key)
else:
return return_model( return return_model(
name=model_name, name=model_name,
status=False, status=False,
exception=f"Table entry with matching '{search_key}': '{search_value}' exists", exception=f"Table entry with matching '{search_key}': '{search_value}' exists",
) )
primary_key = getattr(item, db_table.primary_key)
db_table.delete(self.session, primary_key)
try: try:
db_table.create(self.session, model.dict()) db_table.create(self.session, model.dict())
import_status = return_model(name=model_name, status=True) import_status = return_model(name=model_name, status=True)
@ -301,12 +303,10 @@ def import_database(
import_session.clean_up() import_session.clean_up()
data = { return {
"recipeImports": recipe_report, "recipeImports": recipe_report,
"settingsImports": settings_report, "settingsImports": settings_report,
"themeImports": theme_report, "themeImports": theme_report,
"groupImports": group_report, "groupImports": group_report,
"userImports": user_report, "userImports": user_report,
} }
return data

View file

@ -4,26 +4,16 @@ from typing import Union
import pytz import pytz
from mealie.db.database import db from mealie.db.database import db
from mealie.db.db_setup import create_session from mealie.db.db_setup import create_session
from pydantic.tools import T from mealie.schema.meal import MealIn, MealOut, MealPlanIn, MealPlanInDB, MealPlanProcessed
from mealie.schema.meal import (
MealIn,
MealOut,
MealPlanIn,
MealPlanInDB,
MealPlanProcessed,
)
from mealie.schema.recipe import Recipe from mealie.schema.recipe import Recipe
from mealie.schema.user import GroupInDB from mealie.schema.user import GroupInDB
from pydantic.tools import T
from sqlalchemy.orm.session import Session from sqlalchemy.orm.session import Session
def process_meals(session: Session, meal_plan_base: MealPlanIn) -> MealPlanProcessed: def process_meals(session: Session, meal_plan_base: MealPlanIn) -> MealPlanProcessed:
meals = [] meals = []
for x, meal in enumerate(meal_plan_base.meals): for x, meal in enumerate(meal_plan_base.meals):
# europe = pytz.timezone("America/Anchorage")
# d = europe.localize(meal_plan_base.startDate)
# print(d)
meal: MealIn meal: MealIn
try: try:
recipe: Recipe = db.recipes.get(session, meal.slug) recipe: Recipe = db.recipes.get(session, meal.slug)

View file

@ -2,11 +2,12 @@ import shutil
from pathlib import Path from pathlib import Path
import yaml import yaml
from fastapi.logger import logger
from mealie.core.config import IMG_DIR, TEMP_DIR from mealie.core.config import IMG_DIR, TEMP_DIR
from mealie.db.database import db from mealie.db.database import db
from mealie.schema.recipe import Recipe from mealie.schema.recipe import Recipe
from sqlalchemy.orm.session import Session
from mealie.utils.unzip import unpack_zip from mealie.utils.unzip import unpack_zip
from sqlalchemy.orm.session import Session
try: try:
from yaml import CLoader as Loader from yaml import CLoader as Loader
@ -50,11 +51,8 @@ def read_chowdown_file(recipe_file: Path) -> Recipe:
"tags": recipe_data.get("tags").split(","), "tags": recipe_data.get("tags").split(","),
} }
print(reformat_data) reformated_list = [{"text": instruction} for instruction in reformat_data["recipeInstructions"]]
reformated_list = []
for instruction in reformat_data["recipeInstructions"]:
reformated_list.append({"text": instruction})
reformat_data["recipeInstructions"] = reformated_list reformat_data["recipeInstructions"] = reformated_list
return Recipe(**reformat_data) return Recipe(**reformat_data)
@ -63,21 +61,12 @@ def read_chowdown_file(recipe_file: Path) -> Recipe:
def chowdown_migrate(session: Session, zip_file: Path): def chowdown_migrate(session: Session, zip_file: Path):
temp_dir = unpack_zip(zip_file) temp_dir = unpack_zip(zip_file)
print(temp_dir.name)
path = Path(temp_dir.name)
for p in path.iterdir():
print("ItterDir", p)
for p in p.iterdir():
print("Sub Itter", p)
with temp_dir as dir: with temp_dir as dir:
chow_dir = next(Path(dir).iterdir()) chow_dir = next(Path(dir).iterdir())
image_dir = TEMP_DIR.joinpath(chow_dir, "images") image_dir = TEMP_DIR.joinpath(chow_dir, "images")
recipe_dir = TEMP_DIR.joinpath(chow_dir, "_recipes") recipe_dir = TEMP_DIR.joinpath(chow_dir, "_recipes")
print(image_dir.exists())
print(recipe_dir.exists())
failed_recipes = [] failed_recipes = []
successful_recipes = [] successful_recipes = []
for recipe in recipe_dir.glob("*.md"): for recipe in recipe_dir.glob("*.md"):
@ -86,17 +75,18 @@ def chowdown_migrate(session: Session, zip_file: Path):
db.recipes.create(session, new_recipe.dict()) db.recipes.create(session, new_recipe.dict())
successful_recipes.append(new_recipe.name) successful_recipes.append(new_recipe.name)
except Exception as inst: except Exception as inst:
session.rollback()
logger.error(inst)
failed_recipes.append(recipe.stem) failed_recipes.append(recipe.stem)
failed_images = [] failed_images = []
for image in image_dir.iterdir(): for image in image_dir.iterdir():
try: try:
if not image.stem in failed_recipes: if image.stem not in failed_recipes:
shutil.copy(image, IMG_DIR.joinpath(image.name)) shutil.copy(image, IMG_DIR.joinpath(image.name))
except Exception as inst: except Exception as inst:
print(inst) logger.error(inst)
failed_images.append(image.name) failed_images.append(image.name)
report = {"successful": successful_recipes, "failed": failed_recipes} report = {"successful": successful_recipes, "failed": failed_recipes}
return report return report

View file

@ -52,8 +52,7 @@ class Cleaner:
@staticmethod @staticmethod
def html(raw_html): def html(raw_html):
cleanr = re.compile("<.*?>") cleanr = re.compile("<.*?>")
cleantext = re.sub(cleanr, "", raw_html) return re.sub(cleanr, "", raw_html)
return cleantext
@staticmethod @staticmethod
def image(image=None) -> str: def image(image=None) -> str:
@ -142,12 +141,11 @@ class Cleaner:
@staticmethod @staticmethod
def time(time_entry): def time(time_entry):
print(time_entry, type(time_entry)) if time_entry is None:
if time_entry == None:
return None return None
elif type(time_entry) == datetime: elif type(time_entry) == datetime:
print(time_entry) print(time_entry)
elif type(time_entry) != str: elif type(time_entry) != str:
return str(time_entry) return str(time_entry)
elif time_entry != None: else:
return time_entry return time_entry

View file

@ -25,13 +25,10 @@ def create_from_url(url: str) -> Recipe:
""" """
r = requests.get(url) r = requests.get(url)
new_recipe = extract_recipe_from_html(r.text, url) new_recipe = extract_recipe_from_html(r.text, url)
print(new_recipe)
new_recipe = Cleaner.clean(new_recipe, url) new_recipe = Cleaner.clean(new_recipe, url)
new_recipe = download_image_for_recipe(new_recipe) new_recipe = download_image_for_recipe(new_recipe)
recipe = Recipe(**new_recipe) return Recipe(**new_recipe)
return recipe
def extract_recipe_from_html(html: str, url: str) -> dict: def extract_recipe_from_html(html: str, url: str) -> dict:

View file

@ -4,13 +4,9 @@ import requests
from fastapi.testclient import TestClient from fastapi.testclient import TestClient
from mealie.app import app from mealie.app import app
from mealie.core.config import SQLITE_DIR from mealie.core.config import SQLITE_DIR
from mealie.db.database import db
from mealie.db.db_setup import generate_session, sql_global_init from mealie.db.db_setup import generate_session, sql_global_init
from mealie.db.init_db import init_db from mealie.db.init_db import init_db
from mealie.routes.deps import get_current_user
from mealie.schema.user import UserInDB
from pytest import fixture from pytest import fixture
from sqlalchemy.orm.session import Session
from tests.test_config import TEST_DATA from tests.test_config import TEST_DATA

View file

@ -0,0 +1,25 @@
import json
import pytest
@pytest.fixture
def backup_data():
return {
"name": "dev_sample_data_2021-Feb-13.zip",
"force": False,
"recipes": True,
"settings": False, #! Broken
"themes": True,
"groups": True,
"users": True,
}
def test_import(api_client, backup_data):
response = api_client.post("/api/backups/dev_sample_data_2021-Feb-13.zip/import", json=backup_data)
assert response.status_code == 200
for key, value in json.loads(response.content).items():
for v in value:
assert v["status"] == True

View file

@ -34,6 +34,7 @@ def test_upload_chowdown_zip(api_client, chowdown_zip):
def test_import_chowdown_directory(api_client, chowdown_zip): def test_import_chowdown_directory(api_client, chowdown_zip):
api_client.delete(f"{RECIPES_PREFIX}/roasted-okra") # TODO: Manage Test Data better
selection = chowdown_zip.name selection = chowdown_zip.name
response = api_client.post(f"{MIGRATIONS_PREFIX}/chowdown/{selection}/import") response = api_client.post(f"{MIGRATIONS_PREFIX}/chowdown/{selection}/import")

View file

@ -14,6 +14,7 @@ def test_create_by_url(api_client, recipe_data: RecipeTestData):
def test_create_by_json(api_client): def test_create_by_json(api_client):
api_client.delete(f"{RECIPES_PREFIX}/banana-bread")
response = api_client.post(RECIPES_CREATE, json=raw_recipe) response = api_client.post(RECIPES_CREATE, json=raw_recipe)
assert response.status_code == 201 assert response.status_code == 201

View file

@ -66,7 +66,6 @@ def test_update_user(api_client: requests, token):
response = api_client.put(f"{BASE}/1", headers=token, json=update_data) response = api_client.put(f"{BASE}/1", headers=token, json=update_data)
assert response.status_code == 200 assert response.status_code == 200
print(response.text)
assert json.loads(response.text).get("access_token") assert json.loads(response.text).get("access_token")

View file