New docs for v0.4.0 (#234)

* spacing

* fix parser bug

* update for v0.4.0

* demo link

* remove gifs

* add organize diagram

* demo code

* remove large gifs

* v0.4.0 changelog

Co-authored-by: hay-kot <hay-kot@pm.me>
This commit is contained in:
Hayden 2021-03-30 22:05:52 -08:00 committed by GitHub
commit 629aae49c9
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
14 changed files with 84 additions and 61 deletions

View file

@ -23,7 +23,7 @@
<a href="https://github.com/hay-kot/mealie"> <a href="https://github.com/hay-kot/mealie">
</a> </a>
<br /> <br />
<a href="https://github.com/hay-kot/mealie"><s>View Demo</s></a> <a href="https://mealie-demo.hay-kot.dev/">View Demo</a>
· ·
<a href="https://github.com/hay-kot/mealie/issues">Report Bug</a> <a href="https://github.com/hay-kot/mealie/issues">Report Bug</a>
· ·
@ -39,41 +39,51 @@
<!-- ABOUT THE PROJECT -->
## About The Project
[![Product Name Screen Shot][product-screenshot]](https://example.com) [![Product Name Screen Shot][product-screenshot]](https://example.com)
**Mealie** is a self hosted recipe manager and meal planner with a RestAPI backend and a reactive frontend application built in Vue for a pleasant user experience for the whole family. Easily add recipes into your database by providing the url and mealie will automatically import the relevant data or add a family recipe with the UI editor. # About The Project
Mealie also provides a secure API for interactions from 3rd party applications. **Why does my recipe manager need an API?** An API allows integration into applications like [Home Assistant]() that can act as notification engines to provide custom notifications based of Meal Plan data to remind you to defrost the chicken, marinade the steak, or start the CrockPot. See the section on [Meal Plan hooks](#hooks) for more information. Additionally, you can access any available API from the backend server. To explore the API spin up your server and navigate to http://yourserver.com/docs for interactive API documentation. Mealie is a self hosted recipe manager and meal planner with a RestAPI backend and a reactive frontend application built in Vue for a pleasant user experience for the whole family. Easily add recipes into your database by providing the url and Mealie will automatically import the relevant data or add a family recipe with the UI editor. Mealie also provides an API for interactions from 3rd party applications.
[Remember to join the Discord](https://discord.gg/R6QDyJgbD2)!
### Main Features ## Key Features
#### Recipes - 🔍 Fuzzy search
- Automatic web scrapping for common recipe platforms - 🏷️ Tag recipes with categories or tags to flexible sorting
- Interactive API Documentation thanks to [FastAPI](https://fastapi.tiangolo.com/) and [Swagger](https://petstore.swagger.io/) - 🕸 Import recipes from around the web by URL
- UI Recipe Editor - 📱 Beautiful Mobile Views
- JSON Recipe Editor in browser - 📆 Create Meal Plans
- Custom tags and categories - 🛒 Generate shopping lists
- Rate recipes - 🐳 Easy setup with Docker
- Add notes to recipes - 🎨 Customize your interface with color themes layouts
#### Meal Planner - 💾 Export all your data in any format with Jinja2 Templates, with easy data restoration from the user interface.
- Random Meal plan generation based off categories - 🌍 localized in many languages
- Expose notes in the API to allow external applications to access relevant information for meal plans - Plus tons more!
- Shopping Lists - Flexible API
#### Database Import / Export - Custom key/value pairs for recipes
- Easily Import / Export your recipes from the UI - Webhook support
- Export recipes in into custom files using Jinja2 templates - Interactive API Documentation thanks to [FastAPI](https://fastapi.tiangolo.com/) and [Swagger](https://petstore.swagger.io/)
- Raw JSON Recipe Editor
- Migration from other platforms
- Chowdown
- Nextcloud Cookbook
- Random meal plan generation
### Built With ## FAQ
* [Vue.js](https://vuejs.org/) ### Why An API?
* [Vuetify](https://vuetifyjs.com/en/) An API allows integration into applications like [Home Assistant](https://www.home-assistant.io/) that can act as notification engines to provide custom notifications based of Meal Plan data to remind you to defrost the chicken, marinade the steak, or start the CrockPot. Additionally, you can access nearly any backend service via the API giving you total control to extend the application. To explore the API spin up your server and navigate to http://yourserver.com/docs for interactive API documentation.
* [FastAPI](https://fastapi.tiangolo.com/)
* [Docker](https://www.docker.com/) ### Why a Database?
Some users of static-site generator applications like ChowDown have expressed concerns about their data being stuck in a database. Considering this is a new project it is a valid concern to be worried about your data. Mealie specifically addresses this concern by provided automatic daily backups that export your data in json, plain-text markdown files, and/or custom Jinja2 templates. **This puts you in controls of how your data is represented** when exported from Mealie, which means you can easily migrate to any other service provided Mealie doesn't work for you.
As to why we need a database?
- **Developer Experience:** Without a database a lot of the work to maintain your data is taken on by the developer instead of a battle tested platform for storing data.
- **Multi User Support:** With a solid database as backend storage for your data Mealie can better support multi-user sites and avoid read/write access errors when multiple actions are taken at the same time.
<!-- CONTRIBUTING --> <!-- CONTRIBUTING -->
@ -87,7 +97,6 @@ If you are not a coder, you can still contribute financially. financial contribu
<!-- LICENSE --> <!-- LICENSE -->
## License ## License
Distributed under the MIT License. See `LICENSE` for more information. Distributed under the MIT License. See `LICENSE` for more information.
@ -97,16 +106,6 @@ Project Link: [https://github.com/hay-kot/mealie](https://github.com/hay-kot/mea
<!-- ACKNOWLEDGEMENTS -->
## Acknowledgements
* [Talk Python Training for helping me learn python](https://training.talkpython.fm/)
* [Academind for helping me learn Javascript and Vue.js](https://academind.com/)
<!-- MARKDOWN LINKS & IMAGES --> <!-- MARKDOWN LINKS & IMAGES -->
<!-- https://www.markdownguide.org/basic-syntax/#reference-style-links --> <!-- https://www.markdownguide.org/basic-syntax/#reference-style-links -->
[contributors-shield]: https://img.shields.io/github/contributors/hay-kot/mealie.svg?style=flat-square [contributors-shield]: https://img.shields.io/github/contributors/hay-kot/mealie.svg?style=flat-square

Binary file not shown.

Before

Width:  |  Height:  |  Size: 30 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 35 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 58 KiB

View file

@ -12,14 +12,14 @@
A new database will be created. You must export your data and then import it after upgrading. A new database will be created. You must export your data and then import it after upgrading.
#### Site Settings #### Site Settings
With the addition of group settings and a re-write of the database layer of the application backend, there is no migration path for your current site settings. Webhooks Settings, Meal Plan Categories are now managed by groups. Site settings, mainly homepage settings, are now site specific and managed by administrators. With the addition of group settings and a re-write of the database layer of the application backend, there is no migration path for your current site settings. Webhooks Settings, Meal Plan Categories are now managed by groups. Site settings, mainly homepage settings, are now site specific and managed by administrators. When upgrading be sure to uncheck the settings when importing your old data.
#### ENV Variables #### ENV Variables
Names have been changed to be more consistent with industry standards. See the [Installation Page](/getting-started/install/) for new parameters. Names have been changed to be more consistent with industry standards. See the [Installation Page](/getting-started/install/) for new parameters.
## Bug Fixes ## Bug Fixes
- Fixed Search Results Limited to 100 - #198 - Fixed Search Results Limited to 100 - #198
- Fixed Recette from marmiton.org not fully scrapped - #196 - Fixed recipes from marmiton.org not fully scrapped - #196
- Fixed Unable to get a page to load - #194 - Fixed Unable to get a page to load - #194
- Fixed Recipe's from Epicurious don't upload. - #193 - Fixed Recipe's from Epicurious don't upload. - #193
- Fixed Edited blank recipe in meal plan is not saved - #184 - Fixed Edited blank recipe in meal plan is not saved - #184
@ -31,6 +31,7 @@
### General ### General
- Documentation Rewrite - Documentation Rewrite
- [New Demo Site!](https://mealie-demo.hay-kot.dev/)
- New Documentation - New Documentation
- Landing Page - Landing Page
- Custom Caddy Configuration - Custom Caddy Configuration
@ -77,8 +78,9 @@
### Behind the Scenes ### Behind the Scenes
- Removed CDN dependencies - Removed CDN dependencies
- Database Model Refactoring - Database Model Refactoring
- Import/Export refactoring
- File/Folder Name Refactoring - File/Folder Name Refactoring
- Development is now easier with a makefile! - Development is now easier with a makefile
- Mealie is now a proper package using poetry - Mealie is now a proper package using poetry
- Test refactoring - Test refactoring
- Test Coverage 75% - Test Coverage 83% up from 75%!

View file

@ -2,3 +2,7 @@
!!! tip !!! tip
Below is a suggestion of guidelines my wife and I use for organizing our recipes within Mealie. Mealie is fairly flexible, so feel free to utilize how you'd like! 👍 Below is a suggestion of guidelines my wife and I use for organizing our recipes within Mealie. Mealie is fairly flexible, so feel free to utilize how you'd like! 👍
In the diagram below you will see what we came up with using the new custom pages feature. The large circles indicate pages, and the rectangles indicate categories. We've grouped several 'like' categories with each other as a way to quickly find similar items.
![Mealie Diagram](../../assets/img/MealieDiagram.png)

View file

@ -4,16 +4,11 @@
Adding a recipe can be as easy as copying the recipe URL into mealie and letting the web scrapper try to pull down the information. Currently this scraper is implemented with [scrape-schema-recipe package](https://pypi.org/project/scrape-schema-recipe/). You may have mixed results on some websites, especially with blogs or non specific recipe websites. See the bulk import Option below for another a convenient way to add blog style recipes into Mealie. Adding a recipe can be as easy as copying the recipe URL into mealie and letting the web scrapper try to pull down the information. Currently this scraper is implemented with [scrape-schema-recipe package](https://pypi.org/project/scrape-schema-recipe/). You may have mixed results on some websites, especially with blogs or non specific recipe websites. See the bulk import Option below for another a convenient way to add blog style recipes into Mealie.
![](../assets/gifs/url-demo.gif)
## Recipe Editor ## Recipe Editor
Recipes can be edited and created via the UI. This is done with both a form based approach where you have a UI to work with as well as with a in browser JSON Editor. The JSON editor allows you to easily copy and paste data from other sources. Recipes can be edited and created via the UI. This is done with both a form based approach where you have a UI to work with as well as with a in browser JSON Editor. The JSON editor allows you to easily copy and paste data from other sources.
You can also add a custom recipe with the UI editor built into the web view. You can also add a custom recipe with the UI editor built into the web view.
![](../assets/gifs/editor-demo.gif)
## Bulk Import ## Bulk Import
Mealie also supports bulk import of recipe instructions and ingredients. Select "Bulk Add" in the editor and paste in your plain text data to be parsed. Each line is treated as one entry and will be appended to the existing ingredients or instructions if they exist. Empty lines will be stripped from the text. Mealie also supports bulk import of recipe instructions and ingredients. Select "Bulk Add" in the editor and paste in your plain text data to be parsed. Each line is treated as one entry and will be appended to the existing ingredients or instructions if they exist. Empty lines will be stripped from the text.

View file

@ -239,8 +239,8 @@
<a href="{{ page.next_page.url | url }}" title="{{ page.next_page.title | striptags }}" class="md-button md-button--primary"> <a href="{{ page.next_page.url | url }}" title="{{ page.next_page.title | striptags }}" class="md-button md-button--primary">
Get started Get started
</a> </a>
<a href="{{ config.repo_url }}" title="{{ lang.t('source.link.title') }}" class="md-button"> <a href="{{ config.demo_url }}" title="{{ lang.t('source.link.title') }}" target="_blank" class="md-button">
Go to GitHub View the Demo
</a> </a>
</div> </div>
</div> </div>

View file

@ -1,5 +1,5 @@
site_name: Mealie site_name: Mealie
demo_url: https://mealie-demo.hay-kot.dev/
theme: theme:
custom_dir: docs/overrides custom_dir: docs/overrides
features: features:

View file

@ -31,6 +31,12 @@
<LanguageMenu /> <LanguageMenu />
</v-app-bar> </v-app-bar>
<v-main> <v-main>
<v-banner v-if="demo" sticky
><div class="text-center">
<b> This is a Demo</b> | Username: changeme@email.com | Password: demo
</div></v-banner
>
<v-slide-x-reverse-transition> <v-slide-x-reverse-transition>
<AddRecipeFab v-if="loggedIn" /> <AddRecipeFab v-if="loggedIn" />
</v-slide-x-reverse-transition> </v-slide-x-reverse-transition>
@ -47,6 +53,7 @@ import AddRecipeFab from "@/components/UI/AddRecipeFab";
import LanguageMenu from "@/components/UI/LanguageMenu"; import LanguageMenu from "@/components/UI/LanguageMenu";
import Vuetify from "./plugins/vuetify"; import Vuetify from "./plugins/vuetify";
import { user } from "@/mixins/user"; import { user } from "@/mixins/user";
import { api } from "./api";
export default { export default {
name: "App", name: "App",
@ -80,7 +87,7 @@ export default {
this.$store.dispatch("initLang", { currentVueComponent: this }); this.$store.dispatch("initLang", { currentVueComponent: this });
}, },
mounted() { async mounted() {
this.$store.dispatch("initTheme"); this.$store.dispatch("initTheme");
this.$store.dispatch("requestRecentRecipes"); this.$store.dispatch("requestRecentRecipes");
this.$store.dispatch("refreshToken"); this.$store.dispatch("refreshToken");
@ -89,10 +96,14 @@ export default {
this.$store.dispatch("requestTags"); this.$store.dispatch("requestTags");
this.darkModeSystemCheck(); this.darkModeSystemCheck();
this.darkModeAddEventListener(); this.darkModeAddEventListener();
const api_status = await api.meta.getIsDemo();
this.demo = api_status.demoStatus;
}, },
data: () => ({ data: () => ({
search: false, search: false,
demo: false,
}), }),
methods: { methods: {
// For Later! // For Later!

View file

@ -6,9 +6,10 @@ const prefix = baseURL + "debug";
const debugURLs = { const debugURLs = {
version: `${prefix}/version`, version: `${prefix}/version`,
lastRecipe: `${prefix}/last-recipe-json`, lastRecipe: `${prefix}/last-recipe-json`,
demo: `${prefix}/is-demo`,
}; };
export const metaAPI = { export const metaAPI = {
async get_version() { async get_version() {
let response = await apiReq.get(debugURLs.version); let response = await apiReq.get(debugURLs.version);
return response.data; return response.data;
@ -17,4 +18,10 @@ export const metaAPI = {
let response = await apiReq.get(debugURLs.lastRecipe); let response = await apiReq.get(debugURLs.lastRecipe);
return response.data; return response.data;
}, },
async getIsDemo() {
let response = await apiReq.get(debugURLs.demo);
console.log(response);
return response.data;
},
}; };

View file

@ -78,6 +78,7 @@ class AppSettings:
def __init__(self, app_dirs: AppDirectories) -> None: def __init__(self, app_dirs: AppDirectories) -> None:
global DB_VERSION global DB_VERSION
self.PRODUCTION = bool(os.environ.get("ENV")) self.PRODUCTION = bool(os.environ.get("ENV"))
self.IS_DEMO = os.getenv("DEMO", "False") == "True"
self.API_PORT = int(os.getenv("API_PORT", 9000)) self.API_PORT = int(os.getenv("API_PORT", 9000))
self.API = os.getenv("API_DOCS", "False") == "True" self.API = os.getenv("API_DOCS", "False") == "True"
self.DOCS_URL = "/docs" if self.API else None self.DOCS_URL = "/docs" if self.API else None

View file

@ -1,20 +1,26 @@
import json import json
from fastapi import APIRouter, Depends from fastapi import APIRouter, Depends
from mealie.core.config import APP_VERSION, LOGGER_FILE, app_dirs from mealie.core.config import APP_VERSION, LOGGER_FILE, app_dirs, settings
from mealie.routes.deps import get_current_user from mealie.routes.deps import get_current_user
router = APIRouter(prefix="/api/debug", tags=["Debug"], dependencies=[Depends(get_current_user)]) router = APIRouter(prefix="/api/debug", tags=["Debug"])
@router.get("/version") @router.get("/version")
async def get_mealie_version(): async def get_mealie_version(current_user=Depends(get_current_user)):
""" Returns the current version of mealie""" """ Returns the current version of mealie"""
return {"version": APP_VERSION} return {"version": APP_VERSION}
@router.get("/is-demo")
async def get_demo_status():
print(settings.IS_DEMO)
return {"demoStatus": settings.IS_DEMO}
@router.get("/last-recipe-json") @router.get("/last-recipe-json")
async def get_last_recipe_json(): async def get_last_recipe_json(current_user=Depends(get_current_user)):
""" Doc Str """ """ Doc Str """
with open(app_dirs.DEBUG_DIR.joinpath("last_recipe.json"), "r") as f: with open(app_dirs.DEBUG_DIR.joinpath("last_recipe.json"), "r") as f:
@ -22,7 +28,7 @@ async def get_last_recipe_json():
@router.get("/log/{num}") @router.get("/log/{num}")
async def get_log(num: int): async def get_log(num: int, current_user=Depends(get_current_user)):
""" Doc Str """ """ Doc Str """
with open(LOGGER_FILE, "rb") as f: with open(LOGGER_FILE, "rb") as f:
log_text = tail(f, num) log_text = tail(f, num)

View file

@ -146,7 +146,5 @@ class Cleaner:
return None return None
elif isinstance(time_entry, datetime): elif isinstance(time_entry, datetime):
print(time_entry) print(time_entry)
elif isinstance(time_entry, str):
return str(time_entry)
else: else:
return time_entry return str(time_entry)