Merge pull request #24 from hay-kot/0.1.0
12
.gitignore
vendored
@ -6,6 +6,15 @@ __pycache__/
|
||||
frontend/.env.development
|
||||
docs/site/
|
||||
|
||||
mealie/data/backups/*
|
||||
mealie/data/debug/*
|
||||
mealie/data/img/*
|
||||
|
||||
#Exception to keep folders
|
||||
!mealie/data/backups/.gitkeep
|
||||
!mealie/data/backups/dev_sample_data*
|
||||
!mealie/data/debug/.gitkeep
|
||||
!mealie/data/img/.gitkeep
|
||||
|
||||
.DS_Store
|
||||
node_modules
|
||||
@ -24,7 +33,6 @@ pnpm-debug.log*
|
||||
|
||||
# Editor directories and files
|
||||
.idea
|
||||
.vscode
|
||||
*.suo
|
||||
*.ntvs*
|
||||
*.njsproj
|
||||
@ -136,4 +144,4 @@ ENV/
|
||||
# Node Modules
|
||||
node_modules/
|
||||
|
||||
/*.env.development
|
||||
/*.env.development*
|
16
.vscode/settings.json
vendored
Normal file
@ -0,0 +1,16 @@
|
||||
{
|
||||
"python.formatting.provider": "black",
|
||||
"python.pythonPath": "venv/bin/python",
|
||||
"python.linting.pylintEnabled": true,
|
||||
"python.linting.enabled": true,
|
||||
"python.autoComplete.extraPaths": ["mealie", "mealie/mealie"],
|
||||
"python.analysis.extraPaths": ["mealie", "mealie/mealie"],
|
||||
|
||||
"python.testing.unittestEnabled": false,
|
||||
"python.testing.nosetestsEnabled": false,
|
||||
"python.testing.pytestEnabled": false,
|
||||
"python.testing.promptToConfigure": false,
|
||||
"cSpell.enableFiletypes": [
|
||||
"!python"
|
||||
]
|
||||
}
|
27
.vscode/tasks.json
vendored
Normal file
@ -0,0 +1,27 @@
|
||||
{
|
||||
"version": "2.0.0",
|
||||
"tasks": [
|
||||
{
|
||||
"label": "DEV: Build and Start Docker Compose",
|
||||
"command": "./dev/scripts/docker-compose.dev.sh",
|
||||
"type": "shell",
|
||||
"args": [],
|
||||
"problemMatcher": ["$tsc"],
|
||||
"presentation": {
|
||||
"reveal": "always"
|
||||
},
|
||||
"group": "test"
|
||||
},
|
||||
{
|
||||
"label": "Production: Build and Start Docker Compose",
|
||||
"command": "./dev/scripts/docker-compose.sh",
|
||||
"type": "shell",
|
||||
"args": [],
|
||||
"problemMatcher": ["$tsc"],
|
||||
"presentation": {
|
||||
"reveal": "always"
|
||||
},
|
||||
"group": "test"
|
||||
}
|
||||
]
|
||||
}
|
@ -18,7 +18,7 @@ WORKDIR /app
|
||||
RUN pip install -r requirements.txt
|
||||
|
||||
COPY ./mealie /app
|
||||
COPY ./mealie/data/templates/recipes.md /app/data/templates/
|
||||
COPY --from=build-stage /app/dist /app/dist
|
||||
|
||||
|
||||
CMD ["uvicorn", "app:app", "--host", "0.0.0.0", "--port", "9000"]
|
@ -10,7 +10,9 @@
|
||||
<br />
|
||||
<p align="center">
|
||||
<a href="https://github.com/hay-kot/mealie">
|
||||
<img src="images/logo.png" alt="Logo" width="80" height="80">
|
||||
<svg style="width:100px;height:100px" viewBox="0 0 24 24">
|
||||
<path fill="currentColor" d="M8.1,13.34L3.91,9.16C2.35,7.59 2.35,5.06 3.91,3.5L10.93,10.5L8.1,13.34M13.41,13L20.29,19.88L18.88,21.29L12,14.41L5.12,21.29L3.71,19.88L13.36,10.22L13.16,10C12.38,9.23 12.38,7.97 13.16,7.19L17.5,2.82L18.43,3.74L15.19,7L16.15,7.94L19.39,4.69L20.31,5.61L17.06,8.85L18,9.81L21.26,6.56L22.18,7.5L17.81,11.84C17.03,12.62 15.77,12.62 15,11.84L14.78,11.64L13.41,13Z" />
|
||||
</svg>
|
||||
</a>
|
||||
|
||||
<h3 align="center">Mealie</h3>
|
||||
|
55
dev/dev-notes.md
Normal file
@ -0,0 +1,55 @@
|
||||
# Getting A Developer Instance Started
|
||||
For the best experience developing I recommend using docker. I've used both WSL2 and Ubuntu to develop mealie and have had no issues with cross compatibility with docker. 2 Scripts are available along ith docker-compose files to make development instances easier. After cloning the repo you can set the scripts in /dev/scripts/ as executable and then use VSCode tasks to execute the scripts or execute them from the CLI.
|
||||
|
||||
`docker-compose.dev.sh` Will spin up a development stack with hot-reloading enabled.
|
||||
`docker-compose.sh` Will spin up a production version of the stack.
|
||||
|
||||
After the stack is running navigate to the [admin page localhost:9090/settings/site](http://localhost:9090/settings/site). On the Backups and Exports section import the backup labeled dev_sample_data_{DATE}.zip. This will give you some recipe data to work with.
|
||||
|
||||
Once you're up and running you should be able to make changes and see them reflected on the frontend/backend. If you're not sure what to work on you can check:
|
||||
|
||||
- The Todo's below.
|
||||
- The [Development Road Map](https://hay-kot.github.io/mealie/2.0%20-%20roadmap/)
|
||||
- The [Current Open Issues](https://github.com/hay-kot/mealie/issues)
|
||||
|
||||
Don't forget to [join the Discord](https://discord.gg/R6QDyJgbD2)!
|
||||
|
||||
# Todo's
|
||||
|
||||
Frontend
|
||||
- [ ] .Vue file reorganized into something that makes sense
|
||||
- [ ] Recipe Print Page
|
||||
- [x] Catch 400 / bad response on create from URL
|
||||
- [ ] Recipe Editor Data Validation Client Side
|
||||
- [x] Favicon
|
||||
- [x] Rename Window
|
||||
- [ ] Add version indicator and notification for new version available
|
||||
- [ ] Enhanced Search Functionality
|
||||
- [ ] Organize Home Page my Category, ideally user selectable.
|
||||
|
||||
Backend
|
||||
- [x] Add Debug folder for writing the last pulled recipe data to.
|
||||
- [x] Recipe Editor Data Validation Server Side
|
||||
- [ ] Normalize Recipe data on scrape
|
||||
- [ ] Support how to Sections and how to steps
|
||||
- [ ] Export Markdown on Auto backups
|
||||
- [ ] Recipe request by category/tags
|
||||
- [ ] Add Additional Migrations, See mealie/services/migrations/chowdown.py for examples of how to do this.
|
||||
- [ ] Open Eats [See Issue #4](https://github.com/hay-kot/mealie/issues/4)
|
||||
- [ ] NextCloud [See Issue #14](https://github.com/hay-kot/mealie/issues/14)
|
||||
|
||||
# Draft Changelog
|
||||
## v0.0.2
|
||||
|
||||
General
|
||||
- Fixed opacity issues with marked steps - [mtoohey31](https://github.com/mtoohey31)
|
||||
- Updated Favicon
|
||||
- Renamed Frontend Window
|
||||
- Added Debug folder to dump scraper data prior to processing.
|
||||
- Improved documentation
|
||||
- Added version tag / relevant links, and new version notifier
|
||||
|
||||
Recipes
|
||||
- Added user feedback on bad URL.
|
||||
- Better backend data validation for updating recipes, avoid small syntax errors corrupting database entry. [Issue #8](https://github.com/hay-kot/mealie/issues/8)
|
||||
- Fixed spacing issue while editing new recipes in JSON
|
0
dev/scratch/scratch.json
Normal file
@ -1,6 +1,5 @@
|
||||
# Getting Started
|
||||
To deploy docker on your local network it is highly recommended to use docker to deploy the image straight from dockerhub. Using the docker-compose below you should be able to get a stack up and running easily by changing a few default values and deploying. Currently the only supported database is mongo.
|
||||
|
||||
To deploy docker on your local network it is highly recommended to use docker to deploy the image straight from dockerhub. Using the docker-compose below you should be able to get a stack up and running easily by changing a few default values and deploying. Currently the only supported database is Mongo. Mealie is looking for contributors to support additional databases.
|
||||
|
||||
|
||||
[Get Docker](https://docs.docker.com/get-docker/)
|
||||
@ -9,13 +8,15 @@ To deploy docker on your local network it is highly recommended to use docker to
|
||||
|
||||
## Env Variables
|
||||
|
||||
| Variables | default | description |
|
||||
| ----------- | ------- | ------------------------------------------------------------------------------------------------------------- |
|
||||
| db_username | root | The Mongodb username you specified in your mongo container |
|
||||
| db_password | example | The Mongodb password you specified in your mongo container |
|
||||
| db_host | mongo | The host address of MongoDB if you're in docker and using the same network you can use mongo as the host name |
|
||||
| db_port | 27017 | the port to access MongoDB 27017 is the default for mongo |
|
||||
| TZ | | You should set your time zone accordingly so the date/time features work correctly |
|
||||
| Variables | default | description |
|
||||
| -------------- | ------- | ------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| mealie_db_name | mealie | The name of the database to be created in Mongodb |
|
||||
| mealie_port | 9000 | The port exposed by mealie. **do not change this if you're running in docker** If you'd like to use another port, map 9000 to another port of the host. |
|
||||
| db_username | root | The Mongodb username you specified in your mongo container |
|
||||
| db_password | example | The Mongodb password you specified in your mongo container |
|
||||
| db_host | mongo | The host address of MongoDB if you're in docker and using the same network you can use mongo as the host name |
|
||||
| db_port | 27017 | the port to access MongoDB 27017 is the default for mongo |
|
||||
| TZ | | You should set your time zone accordingly so the date/time features work correctly |
|
||||
|
||||
|
||||
## Docker Compose
|
||||
|
@ -13,13 +13,85 @@ Color themes can be created and set from the UI in the settings page. You can se
|
||||
|
||||
|
||||
## Backup and Export
|
||||

|
||||
|
||||
All recipe data can be imported and exported as necessary from the UI. Under the admin page you'll find the section for using Backups and Exports.
|
||||
|
||||
To create an export simple add the tag and the markdown template and click Backup Recipes and your backup will be created on the server. The backup is a standard zipfile containing all the images, json files, and rendered markdown files for each recipe. Markdown files are rendered from jinja2 templates. Adding your own markdown file into the templates folder will automatically show up as an option to select when creating a backup. To view the availible variables, open a recipe in the json editor.
|
||||
|
||||
To import a backup it must be in your backups folder. If it is in the backup folder it will automatically show up as an source to restore from. Selected the desired backup and import the backup file.
|
||||
|
||||

|
||||
### Custom Templating
|
||||
On export you can select a template to use to render files using the jinja2 syntax. This can be done to export recipes in other formats besides regular .json.Look at this example for rendering a markdown recipe using the jinja2 syntax.
|
||||
|
||||
#### Input
|
||||
```jinja2
|
||||

|
||||
|
||||
# {{ recipe.name }}
|
||||
{{ recipe.description }}
|
||||
|
||||
## Ingredients
|
||||
{% for ingredient in recipe.recipeIngredient %}
|
||||
- [ ] {{ ingredient }}
|
||||
{% endfor %}
|
||||
|
||||
## Instructions
|
||||
{% for step in recipe.recipeInstructions %}
|
||||
- [ ] {{ step.text }}
|
||||
{% endfor %}
|
||||
|
||||
{% for note in recipe.notes %}
|
||||
**{{ note.title }}:** {{ note.text }}
|
||||
{% endfor %}
|
||||
|
||||
---
|
||||
|
||||
Tags: {{ recipe.tags }}
|
||||
Categories: {{ recipe.categories }}
|
||||
Original URL: {{ recipe.orgURL }}
|
||||
```
|
||||
|
||||
#### Output
|
||||
```markdown
|
||||

|
||||
|
||||
# Five Spice Popcorn Chicken
|
||||
It’s easy to rely on take-out for some of our favorite Chinese dishes. However, with the right pantry staples, dishes like this Five Spice Popcorn Chicken can become part of your go-to arsenal of recipes. This crispy chicken is coated in a creamy, tangy sauce, made zesty with The Spice Hunter Chinese Five Spice, a blend of star anise, cloves, cinnamon, fennel, and black pepper.
|
||||
|
||||
## Ingredients
|
||||
- [ ] 1 tablespoon soy sauce
|
||||
- [ ] 1 tablespoon sugar
|
||||
- [ ] 1 teaspoon The Spice Hunter® Chinese Five Spice Blend, plus more for serving
|
||||
- [ ] 1 clove garlic, finely grated
|
||||
- [ ] 1 1/2 pounds boneless skinless chicken thighs, cut into roughly 1-inch chunks
|
||||
- [ ] ⅓ cup cornstarch
|
||||
- [ ] 1 large egg, beaten
|
||||
- [ ] ¾ cup all-purpose flour
|
||||
- [ ] Canola or vegetable oil, for frying
|
||||
- [ ] Flaky sea salt
|
||||
- [ ] Scallion, thinly sliced, for serving
|
||||
- [ ] Sriracha mayonnaise, for serving, optional
|
||||
|
||||
|
||||
## Instructions
|
||||
- [ ] In a medium bowl, whisk the soy sauce with the sugar, Chinese Five Spice, and garlic. Add the chicken and toss to coat. Let marinate 15 minutes.
|
||||
- [ ] Drain any excess marinade off of the chicken and toss the chicken with the cornstarch to coat. Once fully coated, add the beaten egg and toss to coat.
|
||||
- [ ] In a large heavy bottomed pot, heat 1-inch of oil to 350.
|
||||
- [ ] Place the flour in a large ziploc bag. Working in batches, transfer a few chicken pieces into the bag with the flour and toss to coat, then remove, leaving excess flour in the bag.
|
||||
- [ ] Carefully place the breaded chicken in the hot oil and fry, turning occasionally, until golden and cooked through about 3 to 4 minutes.
|
||||
- [ ] Using a slotted spoon or spider, transfer the cooked chicken to a paper towel lined plate. Season with salt and additional Chinese Five Spice seasoning. Repeat the flouring and frying with remaining chicken.
|
||||
- [ ] Serve with scallions, more Chinese Five Spice Blend, and optional sriracha mayonnaise.
|
||||
|
||||
---
|
||||
|
||||
Tags: []
|
||||
Categories: []
|
||||
Original URL: https://www.bonappetit.com/recipe/five-spice-popcorn-chicken#intcid=_bon-appetit-recipe-bottom-recirc_3cad5ce9-734a-46f8-b503-78c33d2e7279_similar2-3
|
||||
```
|
||||
|
||||
If you decide you don't like mealie. This is a good way to export into a format that can be imported into another.
|
||||
|
||||
|
||||
## Meal Planner Webhooks
|
||||
Meal planner webhooks are post requests sent from Mealie to an external endpoint. The body of the message is the Recipe JSON of the scheduled meal. If no meal is schedule, no request is sent. The webhook functionality can be enabled or disabled as well as scheduled. Note that you must "Save Webhooks" prior to any changes taking affect server side.
|
||||
|
@ -1,5 +1,16 @@
|
||||
# Release Notes
|
||||
|
||||
## v0.0.1 - Pre-release Patch
|
||||
General
|
||||
- Updated Favicon
|
||||
- Renamed Frontend Window
|
||||
- Added Debug folder to dump scraper data prior to processing.
|
||||
|
||||
Recipes
|
||||
- Added user feedback on bad URL
|
||||
- Better backend data validation for updating recipes, avoid small syntax errors corrupting database entry. [Issue #8](https://github.com/hay-kot/mealie/issues/8)
|
||||
- Fixed spacing issue while editing new recipes in JSON
|
||||
|
||||
## v0.0.0 - Initial Pre-release
|
||||
The initial pre-release. It should be semi-functional but does not include a lot of user feedback You may notice errors that have no user feedback and have no idea what went wrong.
|
||||
|
||||
|
49
docs/docs/2.1 - Contributions.md
Normal file
@ -0,0 +1,49 @@
|
||||
# Contributing to Mealie
|
||||
We love your input! We want to make contributing to this project as easy and transparent as possible, whether it's:
|
||||
|
||||
- Reporting a bug
|
||||
- Discussing the current state of the code
|
||||
- Submitting a fix
|
||||
- Proposing new features
|
||||
- Becoming a maintainer
|
||||
|
||||
[Remember to join the Discord and stay in touch with other developers working on the project](https://discord.gg/R6QDyJgbD2)!
|
||||
|
||||
## We Develop with Github
|
||||
We use github to host code, to track issues and feature requests, as well as accept pull requests.
|
||||
|
||||
## We Use [Github Flow](https://guides.github.com/introduction/flow/index.html), So All Code Changes Happen Through Pull Requests
|
||||
Pull requests are the best way to propose changes to the codebase (we use [Github Flow](https://guides.github.com/introduction/flow/index.html)). We actively welcome your pull requests:
|
||||
|
||||
1. Fork the repo and create your branch from `master`.
|
||||
2. Read the page in in [dev/dev-notes.md](https://github.com/hay-kot/mealie/blob/0.1.0/dev/dev-notes.md) to get an idea on where the project is at.
|
||||
3. If you've changed APIs, update the documentation.
|
||||
4. Make sure your code lints.
|
||||
5. Issue that pull request!
|
||||
6. If you make changes to the dev/0.1.0 branch reflect those changes in the dev/dev-notes.md to keep track of changes.
|
||||
|
||||
## Any contributions you make will be under the MIT Software License
|
||||
In short, when you submit code changes, your submissions are understood to be under the same [MIT License](http://choosealicense.com/licenses/mit/) that covers the project. Feel free to contact the maintainers if that's a concern.
|
||||
|
||||
## Report bugs using Github's [issues](https://github.com/hay-kot/mealie/issues)
|
||||
We use GitHub issues to track public bugs. Report a bug by [opening a new issue](https://github.com/hay-kot/mealie/issues/new); it's that easy!
|
||||
|
||||
## Write bug reports with detail, background, and sample code
|
||||
**Great Bug Reports** tend to have:
|
||||
|
||||
- A quick summary and/or background
|
||||
- Steps to reproduce
|
||||
- Be specific!
|
||||
- Give sample code if you can. [This stackoverflow question](http://stackoverflow.com/q/12488905/180626) includes sample code that *anyone* with a base R setup can run to reproduce what I was seeing
|
||||
- What you expected would happen
|
||||
- What actually happens
|
||||
- Notes (possibly including why you think this might be happening, or stuff you tried that didn't work)
|
||||
|
||||
People *love* thorough bug reports. I'm not even kidding.
|
||||
|
||||
|
||||
## License
|
||||
By contributing, you agree that your contributions will be licensed under its MIT License.
|
||||
|
||||
## References
|
||||
This document was adapted from the open-source contribution guidelines for [Facebook's Draft](https://github.com/facebook/draft-js/blob/a9316a723f9e918afde44dea68b5f9f39b7d9b00/CONTRIBUTING.md)
|
BIN
docs/docs/img/favicon.png
Normal file
After Width: | Height: | Size: 7.0 KiB |
@ -18,6 +18,8 @@
|
||||
|
||||
Mealie also provides an API for interactions from 3rd party applications. **Why does my recipe manager need an API?** 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 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.
|
||||
|
||||
[Remember to join the Discord](https://discord.gg/R6QDyJgbD2)!
|
||||
|
||||
!!! note
|
||||
In some of the demo gifs the styling may be different than the finale application. demos were done during development prior to finale styling.
|
||||
|
||||
@ -93,17 +95,4 @@ Project Link: [https://github.com/hay-kot/mealie](https://github.com/hay-kot/mea
|
||||
|
||||
|
||||
<!-- MARKDOWN LINKS & IMAGES -->
|
||||
<!-- 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-url]: https://github.com/hay-kot/mealie/graphs/contributors
|
||||
[forks-shield]: https://img.shields.io/github/forks/hay-kot/mealie.svg?style=flat-square
|
||||
[forks-url]: https://github.com/hay-kot/mealie/network/members
|
||||
[stars-shield]: https://img.shields.io/github/stars/hay-kot/mealie.svg?style=flat-square
|
||||
[stars-url]: https://github.com/hay-kot/mealie/stargazers
|
||||
[issues-shield]: https://img.shields.io/github/issues/hay-kot/mealie.svg?style=flat-square
|
||||
[issues-url]: https://github.com/hay-kot/mealie/issues
|
||||
[license-shield]: https://img.shields.io/github/license/hay-kot/mealie.svg?style=flat-square
|
||||
[license-url]: https://github.com/hay-kot/mealie/blob/master/LICENSE.txt
|
||||
[linkedin-shield]: https://img.shields.io/badge/-LinkedIn-black.svg?style=flat-square&logo=linkedin&colorB=555
|
||||
[linkedin-url]: https://linkedin.com/in/hay-kot
|
||||
[product-screenshot]: img/home_screenshot.png
|
||||
|
@ -1,5 +1,6 @@
|
||||
site_name: Mealie
|
||||
site_name: Mealie Docs
|
||||
theme:
|
||||
favicon: img/favicon.png
|
||||
name: material
|
||||
icon:
|
||||
logo: material/silverware-variant
|
||||
|
Before Width: | Height: | Size: 4.2 KiB After Width: | Height: | Size: 7.0 KiB |
@ -5,7 +5,7 @@
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1.0">
|
||||
<link rel="icon" href="<%= BASE_URL %>favicon.ico">
|
||||
<title><%= htmlWebpackPlugin.options.title %></title>
|
||||
<title> Mealie </title>
|
||||
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Roboto:100,300,400,500,700,900">
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@mdi/font@latest/css/materialdesignicons.min.css">
|
||||
</head>
|
||||
|
@ -1,13 +1,13 @@
|
||||
<template>
|
||||
<v-app>
|
||||
<v-app-bar dense app color="primary" dark class="d-print-none">
|
||||
<div class="d-flex align-center">
|
||||
<v-icon size="40" @click="$router.push('/')">
|
||||
<v-btn @click="$router.push('/')" icon class="d-flex align-center">
|
||||
<v-icon size="40" >
|
||||
mdi-silverware-variant
|
||||
</v-icon>
|
||||
</div>
|
||||
<div btn class="pl-2" @click="$router.push('/')">
|
||||
<v-toolbar-title>Mealie</v-toolbar-title>
|
||||
</v-btn>
|
||||
<div btn class="pl-2">
|
||||
<v-toolbar-title @click="$router.push('/')">Mealie</v-toolbar-title>
|
||||
</div>
|
||||
|
||||
<v-spacer></v-spacer>
|
||||
|
@ -19,9 +19,8 @@ const apiReq = {
|
||||
post: async function(url, data) {
|
||||
let response = await axios.post(url, data).catch(function(error) {
|
||||
if (error.response) {
|
||||
console.log("Error");
|
||||
processResponse(error.response);
|
||||
return;
|
||||
return error.response;
|
||||
}
|
||||
});
|
||||
processResponse(response);
|
||||
@ -32,7 +31,7 @@ const apiReq = {
|
||||
let response = await axios.get(url, data).catch(function(error) {
|
||||
if (error.response) {
|
||||
processResponse(error.response);
|
||||
return;
|
||||
return response;
|
||||
} else return;
|
||||
});
|
||||
// processResponse(response);
|
||||
@ -43,7 +42,7 @@ const apiReq = {
|
||||
let response = await axios.delete(url, data).catch(function(error) {
|
||||
if (error.response) {
|
||||
processResponse(error.response);
|
||||
return;
|
||||
return response;
|
||||
}
|
||||
});
|
||||
processResponse(response);
|
||||
|
@ -23,10 +23,9 @@ export default {
|
||||
let response = await apiReq.post(recipeURLs.createByURL, {
|
||||
url: recipeURL,
|
||||
});
|
||||
console.log(response);
|
||||
let recipeSlug = response.data;
|
||||
|
||||
store.dispatch("requestRecentRecipes");
|
||||
router.push(`/recipe/${recipeSlug}`);
|
||||
return response;
|
||||
},
|
||||
|
||||
async create(recipeData) {
|
||||
|
Before Width: | Height: | Size: 6.7 KiB |
@ -1 +0,0 @@
|
||||
<svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 87.5 100"><defs><style>.cls-1{fill:#1697f6;}.cls-2{fill:#7bc6ff;}.cls-3{fill:#1867c0;}.cls-4{fill:#aeddff;}</style></defs><title>Artboard 46</title><polyline class="cls-1" points="43.75 0 23.31 0 43.75 48.32"/><polygon class="cls-2" points="43.75 62.5 43.75 100 0 14.58 22.92 14.58 43.75 62.5"/><polyline class="cls-3" points="43.75 0 64.19 0 43.75 48.32"/><polygon class="cls-4" points="64.58 14.58 87.5 14.58 43.75 100 43.75 62.5 64.58 14.58"/></svg>
|
Before Width: | Height: | Size: 539 B |
@ -8,6 +8,11 @@
|
||||
<v-form>
|
||||
<v-text-field v-model="recipeURL" label="Recipe URL"></v-text-field>
|
||||
</v-form>
|
||||
|
||||
<v-alert v-if="error" color="red" outlined type="success">
|
||||
Looks like there was an error parsing the URL. Check the log and
|
||||
debug/last_recipe.json to see what went wrong.
|
||||
</v-alert>
|
||||
</v-card-text>
|
||||
|
||||
<v-divider></v-divider>
|
||||
@ -37,6 +42,7 @@ import api from "../api";
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
error: false,
|
||||
fab: false,
|
||||
addRecipe: false,
|
||||
recipeURL: "",
|
||||
@ -47,9 +53,16 @@ export default {
|
||||
methods: {
|
||||
async createRecipe() {
|
||||
this.processing = true;
|
||||
await api.recipes.createByURL(this.recipeURL);
|
||||
let response = await api.recipes.createByURL(this.recipeURL);
|
||||
if (response.status !== 201) {
|
||||
this.error = true;
|
||||
this.processing = false;
|
||||
return;
|
||||
}
|
||||
|
||||
this.addRecipe = false;
|
||||
this.processing = false;
|
||||
this.$router.push(`/recipe/${response.data}`);
|
||||
},
|
||||
|
||||
navCreate() {
|
||||
|
@ -1,9 +1,28 @@
|
||||
<template>
|
||||
<v-container>
|
||||
<v-alert v-if="newVersion" color="green" type="success" outlined>
|
||||
A New Version of Mealie is Avaiable,
|
||||
<a href="https://github.com/hay-kot/mealie" class="green--text">
|
||||
Visit the Repo
|
||||
</a>
|
||||
</v-alert>
|
||||
<Theme />
|
||||
<Backup />
|
||||
<Webhooks />
|
||||
<Migration />
|
||||
<p class="text-center my-2">
|
||||
Version: {{ version }} | Latest: {{ latestVersion }} ·
|
||||
<a href="https://hay-kot.github.io/mealie/" target="_blank">
|
||||
Explore the Docs
|
||||
</a>
|
||||
·
|
||||
<a
|
||||
href="https://hay-kot.github.io/mealie/2.1%20-%20Contributions/"
|
||||
target="_blank"
|
||||
>
|
||||
Contribute
|
||||
</a>
|
||||
</p>
|
||||
</v-container>
|
||||
</template>
|
||||
|
||||
@ -12,6 +31,8 @@ import Backup from "./Backup";
|
||||
import Webhooks from "./Webhooks";
|
||||
import Theme from "./Theme";
|
||||
import Migration from "./Migration";
|
||||
import axios from "axios";
|
||||
|
||||
export default {
|
||||
components: {
|
||||
Backup,
|
||||
@ -19,6 +40,34 @@ export default {
|
||||
Theme,
|
||||
Migration,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
latestVersion: null,
|
||||
version: "v0.0.1",
|
||||
};
|
||||
},
|
||||
mounted() {
|
||||
this.getVersion();
|
||||
},
|
||||
computed: {
|
||||
newVersion() {
|
||||
if ((this.latestVersion != null) & (this.latestVersion != this.version)) {
|
||||
console.log("New Version Avaiable");
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
async getVersion() {
|
||||
let response = await axios.get(
|
||||
"https://api.github.com/repos/hay-kot/mealie/releases/latest"
|
||||
);
|
||||
console.log(response);
|
||||
this.latestVersion = response.data.tag_name;
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
|
@ -1,8 +1,9 @@
|
||||
<template>
|
||||
<v-card :loading="backupLoading" class="mt-3">
|
||||
<v-card :loading="backupLoading" class="mt-3" min-height="410px">
|
||||
<v-card-title class="secondary white--text">
|
||||
Backup and Exports
|
||||
</v-card-title>
|
||||
|
||||
<v-card-text>
|
||||
<p>
|
||||
Backups are exported in standard JSON format along with all the images
|
||||
|
@ -18,12 +18,16 @@
|
||||
@save="createRecipe"
|
||||
/>
|
||||
|
||||
<VJsoneditor
|
||||
v-if="jsonEditor"
|
||||
v-model="recipeDetails"
|
||||
height="1500px"
|
||||
:options="jsonEditorOptions"
|
||||
/>
|
||||
<div v-if="jsonEditor">
|
||||
<!-- Probably not the best way, but it works! -->
|
||||
<br />
|
||||
<br />
|
||||
<VJsoneditor
|
||||
v-model="recipeDetails"
|
||||
height="1500px"
|
||||
:options="jsonEditorOptions"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<EditRecipe v-else v-model="recipeDetails" @upload="getImage" />
|
||||
</v-card>
|
||||
@ -73,7 +77,6 @@ export default {
|
||||
|
||||
methods: {
|
||||
getImage(fileObject) {
|
||||
console.log(fileObject);
|
||||
this.fileObject = fileObject;
|
||||
this.onFileChange();
|
||||
},
|
||||
@ -83,11 +86,9 @@ export default {
|
||||
async createRecipe() {
|
||||
this.isLoading = true;
|
||||
this.recipeDetails.image = this.fileObject.name;
|
||||
console.log(this.recipeDetails);
|
||||
let slug = await api.recipes.create(this.recipeDetails);
|
||||
|
||||
let response = await api.recipes.updateImage(slug, this.fileObject);
|
||||
console.log(response);
|
||||
await api.recipes.updateImage(slug, this.fileObject);
|
||||
this.isLoading = false;
|
||||
|
||||
this.$router.push(`/recipe/${slug}`);
|
||||
|
@ -32,6 +32,7 @@
|
||||
:orgURL="recipeDetails.orgURL"
|
||||
/>
|
||||
<VJsoneditor
|
||||
@error="logError()"
|
||||
class="mt-10"
|
||||
v-else-if="showJsonEditor"
|
||||
v-model="recipeDetails"
|
||||
@ -152,6 +153,6 @@ export default {
|
||||
margin-top: -10px;
|
||||
}
|
||||
.disabled-card {
|
||||
opacity: 50%;
|
||||
opacity: 0.5;
|
||||
}
|
||||
</style>
|
@ -123,6 +123,6 @@ export default {
|
||||
|
||||
<style>
|
||||
.disabled-card {
|
||||
opacity: 50%;
|
||||
opacity: 0.5;
|
||||
}
|
||||
</style>
|
@ -20,10 +20,7 @@
|
||||
<div class="my-2"></div>
|
||||
<v-row dense disabled>
|
||||
<v-col sm="5">
|
||||
<v-text-field
|
||||
label="Servings"
|
||||
v-model="value.recipeYield"
|
||||
>
|
||||
<v-text-field label="Servings" v-model="value.recipeYield">
|
||||
</v-text-field>
|
||||
</v-col>
|
||||
<v-col></v-col>
|
||||
@ -74,7 +71,13 @@
|
||||
v-model="value.categories"
|
||||
>
|
||||
<template v-slot:selection="data">
|
||||
<v-chip :input-value="data.selected" close color="secondary" dark>
|
||||
<v-chip
|
||||
:input-value="data.selected"
|
||||
close
|
||||
@click:close="removeCategory(data.index)"
|
||||
color="secondary"
|
||||
dark
|
||||
>
|
||||
{{ data.item }}
|
||||
</v-chip>
|
||||
</template>
|
||||
@ -83,7 +86,13 @@
|
||||
<h2 class="mt-4">Tags</h2>
|
||||
<v-combobox dense multiple chips deletable-chips v-model="value.tags">
|
||||
<template v-slot:selection="data">
|
||||
<v-chip :input-value="data.selected" close color="secondary" dark>
|
||||
<v-chip
|
||||
:input-value="data.selected"
|
||||
close
|
||||
@click:close="removeTags(data.index)"
|
||||
color="secondary"
|
||||
dark
|
||||
>
|
||||
{{ data.item }}
|
||||
</v-chip>
|
||||
</template>
|
||||
@ -255,13 +264,19 @@ export default {
|
||||
removeNote(index) {
|
||||
this.value.notes.splice(index, 1);
|
||||
},
|
||||
removeCategory(index) {
|
||||
this.value.categories.splice(index, 1);
|
||||
},
|
||||
removeTags(index) {
|
||||
this.value.tags.splice(index, 1);
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.disabled-card {
|
||||
opacity: 50%;
|
||||
opacity: 0.5;
|
||||
}
|
||||
.my-divider {
|
||||
margin: 0 -1px;
|
||||
|
0
mealie/data/backups/.gitkeep
Normal file
0
mealie/data/debug/.gitkeep
Normal file
128
mealie/data/debug/last_recipe.json
Normal file
@ -0,0 +1,128 @@
|
||||
{
|
||||
"@context": "http://schema.org",
|
||||
"@type": "Recipe",
|
||||
"articleBody": "Leftover rice is ideal for this dish (and a great way to use up any takeout that\u2019s hanging around), since fully chilled rice tends to be drier and will become crispier and browner in the skillet. To get the best texture, evenly distribute the rice in your pan and gently press down to flatten it out like a pancake. Don\u2019t touch until you hear it crackle! Finish with a sunny-side-up egg\u2014or poach it if you don't mind the stovetop fuss. This recipe is part of the 2021\u00a0Feel Good Food Plan, our eight-day dinner plan for starting the year off right.",
|
||||
"alternativeHeadline": "To get the best texture, evenly distribute the rice in your pan and gently press down to flatten it. Don\u2019t touch until you hear it crackle!",
|
||||
"dateModified": "2021-01-03 03:40:32.190000",
|
||||
"datePublished": "2021-01-01 06:00:00",
|
||||
"keywords": [
|
||||
"recipes",
|
||||
"healthyish",
|
||||
"salad",
|
||||
"ginger",
|
||||
"garlic",
|
||||
"orange",
|
||||
"oil",
|
||||
"soy sauce",
|
||||
"lemon juice",
|
||||
"sesame oil",
|
||||
"kosher salt",
|
||||
"broccoli",
|
||||
"brown rice",
|
||||
"egg",
|
||||
"celery",
|
||||
"cilantro",
|
||||
"mint",
|
||||
"feel good food plan 2021",
|
||||
"feel good food plan",
|
||||
"web"
|
||||
],
|
||||
"thumbnailUrl": "https://assets.bonappetit.com/photos/5fdbe70a84d333dd1dcc7900/1:1/w_1698,h_1698,c_limit/BA1220feelgoodalt.jpg",
|
||||
"publisher": {
|
||||
"@context": "https://schema.org",
|
||||
"@type": "Organization",
|
||||
"name": "Bon App\u00e9tit",
|
||||
"logo": {
|
||||
"@type": "ImageObject",
|
||||
"url": "https://www.bonappetit.com/verso/static/bon-appetit/assets/logo-seo.328de564b950e3d5d1fbe3e42f065290ca1d3844.png",
|
||||
"width": "479px",
|
||||
"height": "100px"
|
||||
},
|
||||
"url": "https://www.bonappetit.com"
|
||||
},
|
||||
"isPartOf": {
|
||||
"@type": [
|
||||
"CreativeWork",
|
||||
"Product"
|
||||
],
|
||||
"name": "Bon App\u00e9tit"
|
||||
},
|
||||
"isAccessibleForFree": true,
|
||||
"author": [
|
||||
{
|
||||
"@type": "Person",
|
||||
"name": "Devonn Francis",
|
||||
"sameAs": "https://bon-appetit.com/contributor/devonn-francis/"
|
||||
}
|
||||
],
|
||||
"aggregateRating": {
|
||||
"@type": "AggregateRating",
|
||||
"ratingValue": 4,
|
||||
"ratingCount": 2
|
||||
},
|
||||
"description": "To get the best texture, evenly distribute the rice in your pan and gently press down to flatten it. Don\u2019t touch until you hear it crackle! ",
|
||||
"image": "crispy-rice-with-ginger-citrus-celery-salad.jpg",
|
||||
"name": "Crispy Rice With Ginger-Citrus Celery Salad",
|
||||
"recipeIngredient": [
|
||||
"1 2\" piece ginger, peeled, finely grated",
|
||||
"1 small garlic clove, finely grated",
|
||||
"Juice of 1 orange",
|
||||
"2 tbsp. vegetable oil",
|
||||
"1Tbsp. coconut aminos or low-sodium soy sauce",
|
||||
"1 Tbsp. fresh lemon juice",
|
||||
"\u00bc tsp. toasted sesame oil",
|
||||
"Kosher salt",
|
||||
"1 medium head of broccoli",
|
||||
"6 Tbsp. (or more) vegetable oil, divided",
|
||||
"Kosher salt",
|
||||
"2 cups chilled cooked brown rice",
|
||||
"4 large eggs",
|
||||
"3 celery stalks, thinly sliced on a steep diagonal",
|
||||
"\u00bd cup cilantro leaves with tender stems",
|
||||
"\u00bd cup mint leaves",
|
||||
"Crushed red pepper flakes (for serving)"
|
||||
],
|
||||
"recipeInstructions": [
|
||||
{
|
||||
"@type": "HowToStep",
|
||||
"text": "Whisk ginger, garlic, orange juice, vegetable oil, coconut aminos, lemon juice, and sesame oil in a small bowl; season with salt and set aside."
|
||||
},
|
||||
{
|
||||
"@type": "HowToStep",
|
||||
"text": "Trim about \u00bd\" from woody end of broccoli stem. Peel tough outer layer from stem. Cut florets from stems and thinly slice stems about \u00bd\" thick. Break florets apart with your hands into 1\"\u20131\u00bd\" pieces."
|
||||
},
|
||||
{
|
||||
"@type": "HowToStep",
|
||||
"text": "Heat 2 Tbsp. oil in a large nonstick skillet over medium. Working in 2 batches if needed, arrange broccoli in a single layer and cook, tossing occasionally, until broccoli is bright green and lightly charred around the edges, about\u00a03 minutes. Transfer to a large plate."
|
||||
},
|
||||
{
|
||||
"@type": "HowToStep",
|
||||
"text": "Pour 2 Tbsp. oil into same pan and heat over medium-high. Once you see the first wisp of smoke, add rice and season lightly with salt. Using a spatula or spoon, press rice evenly into pan like a pancake. Rice will begin to crackle, but don\u2019t fuss with it. When the crackling has died down almost completely, about\u00a03 minutes, break rice into large pieces and turn over."
|
||||
},
|
||||
{
|
||||
"@type": "HowToStep",
|
||||
"text": "Add broccoli back to pan and give everything a toss to combine. Cook, tossing occasionally and adding another\u00a01 Tbsp. oil if pan looks dry, until broccoli is tender and rice is warmed through and very crisp, about 5 minutes. Transfer mixture to a platter or divide among plates and set aside."
|
||||
},
|
||||
{
|
||||
"@type": "HowToStep",
|
||||
"text": "Wipe out skillet; heat remaining\u00a02 Tbsp. oil over medium-high. Crack eggs into skillet; season with salt. Oil should bubble around eggs right away. Cook, rotating skillet occasionally, until whites are golden brown and crisp at the edges and set around the yolk (which should be runny), about 2 minutes."
|
||||
},
|
||||
{
|
||||
"@type": "HowToStep",
|
||||
"text": "Toss celery, cilantro, and mint with\u00a03 Tbsp. reserved dressing and a pinch of salt in a medium bowl to combine."
|
||||
},
|
||||
{
|
||||
"@type": "HowToStep",
|
||||
"text": "Scatter celery salad over fried rice; top with fried eggs and sprinkle with red pepper flakes. Serve extra dressing alongside."
|
||||
}
|
||||
],
|
||||
"recipeYield": "4 servings",
|
||||
"url": "https://www.bonappetit.com/recipe/crispy-rice-with-ginger-citrus-celery-salad",
|
||||
"slug": "crispy-rice-with-ginger-citrus-celery-salad",
|
||||
"orgURL": "https://www.bonappetit.com/recipe/crispy-rice-with-ginger-citrus-celery-salad",
|
||||
"categories": [],
|
||||
"tags": [],
|
||||
"dateAdded": null,
|
||||
"notes": [],
|
||||
"extras": []
|
||||
}
|
0
mealie/data/img/.gitkeep
Normal file
Before Width: | Height: | Size: 1.1 MiB |
Before Width: | Height: | Size: 997 KiB |
Before Width: | Height: | Size: 812 KiB |
Before Width: | Height: | Size: 889 KiB |
Before Width: | Height: | Size: 137 KiB |
Before Width: | Height: | Size: 1.1 MiB |
Before Width: | Height: | Size: 312 KiB |
Before Width: | Height: | Size: 1.0 MiB |
Before Width: | Height: | Size: 324 KiB |
Before Width: | Height: | Size: 1.7 MiB |
@ -3,6 +3,7 @@
|
||||

|
||||
|
||||
# {{ recipe.name }}
|
||||
{{ recipe.description }}
|
||||
|
||||
## Ingredients
|
||||
{% for ingredient in recipe.recipeIngredient %}
|
||||
|
@ -1,15 +1,14 @@
|
||||
import mongoengine
|
||||
from settings import DB_HOST, DB_PASSWORD, DB_PORT, DB_USERNAME
|
||||
from settings import DB_HOST, DB_PASSWORD, DB_PORT, DB_USERNAME, MEALIE_DB_NAME
|
||||
|
||||
|
||||
def global_init():
|
||||
mongoengine.register_connection(
|
||||
alias="core",
|
||||
name="mealie",
|
||||
name=MEALIE_DB_NAME,
|
||||
host=DB_HOST,
|
||||
port=int(DB_PORT),
|
||||
username=DB_USERNAME,
|
||||
password=DB_PASSWORD,
|
||||
authentication_source="admin",
|
||||
)
|
||||
|
||||
|
@ -24,7 +24,7 @@ async def get_all_recipes(
|
||||
async def get_recipe(recipe_slug: str):
|
||||
""" Takes in a recipe slug, returns all data for a recipe """
|
||||
recipe = Recipe.get_by_slug(recipe_slug)
|
||||
|
||||
|
||||
return recipe
|
||||
|
||||
|
||||
@ -37,18 +37,19 @@ async def get_recipe_img(recipe_slug: str):
|
||||
|
||||
|
||||
# Recipe Creations
|
||||
@router.post("/api/recipe/create-url/", tags=["Recipes"])
|
||||
@router.post("/api/recipe/create-url/", tags=["Recipes"], status_code=201)
|
||||
async def get_recipe_url(url: dict):
|
||||
""" Takes in a URL and Attempts to scrape data and load it into the database """
|
||||
|
||||
url = url.get("url")
|
||||
slug = create_from_url(url)
|
||||
|
||||
try:
|
||||
slug = create_from_url(url)
|
||||
except:
|
||||
raise HTTPException(
|
||||
status_code=400, detail=SnackResponse.error("Unable to Parse URL")
|
||||
)
|
||||
# try:
|
||||
# slug = create_from_url(url)
|
||||
# except:
|
||||
# raise HTTPException(
|
||||
# status_code=400, detail=SnackResponse.error("Unable to Parse URL")
|
||||
# )
|
||||
|
||||
return slug
|
||||
|
||||
@ -72,9 +73,11 @@ def update_image(
|
||||
|
||||
|
||||
@router.post("/api/recipe/{recipe_slug}/update/", tags=["Recipes"])
|
||||
async def update(recipe_slug: str, data: dict):
|
||||
async def update(recipe_slug: str, data: Recipe):
|
||||
""" Updates a recipe by existing slug and data. Data should containt """
|
||||
Recipe.update(recipe_slug, data)
|
||||
|
||||
data.update(recipe_slug)
|
||||
|
||||
return {"message": "PLACEHOLDER"}
|
||||
|
||||
|
||||
|
@ -1,106 +0,0 @@
|
||||
import shutil
|
||||
from os.path import join
|
||||
from pathlib import Path
|
||||
from pprint import pprint
|
||||
|
||||
import git
|
||||
import yaml
|
||||
from git.util import join_path
|
||||
|
||||
from services.image_services import IMG_DIR
|
||||
from services.recipe_services import Recipe
|
||||
|
||||
try:
|
||||
from yaml import CDumper as Dumper
|
||||
from yaml import CLoader as Loader
|
||||
except ImportError:
|
||||
from yaml import Dumper, Loader
|
||||
|
||||
CWD = Path(__file__).parent
|
||||
|
||||
file = f"/home/hayden/Projects/mealie-fastAPI/mealie/chowdown.md"
|
||||
|
||||
repo = "https://github.com/clarklab/chowdown"
|
||||
|
||||
|
||||
def pull_repo(repo):
|
||||
dest_dir = CWD.joinpath("data/temp/migration/git_pull")
|
||||
if dest_dir.exists():
|
||||
shutil.rmtree(dest_dir)
|
||||
dest_dir.mkdir(parents=True, exist_ok=True)
|
||||
git.Git(dest_dir).clone(repo)
|
||||
|
||||
repo_name = repo.split("/")[-1]
|
||||
recipe_dir = dest_dir.joinpath(repo_name, "_recipes")
|
||||
image_dir = dest_dir.joinpath(repo_name, "images")
|
||||
|
||||
return recipe_dir, image_dir
|
||||
|
||||
|
||||
def read_chowdown_file(recipe_file: Path) -> Recipe:
|
||||
with open(recipe_file, "r") as stream:
|
||||
recipe_description: str = str
|
||||
recipe_data: dict = {}
|
||||
try:
|
||||
for x, item in enumerate(yaml.load_all(stream, Loader=Loader)):
|
||||
print(item)
|
||||
if x == 0:
|
||||
recipe_data = item
|
||||
|
||||
elif x == 1:
|
||||
recipe_description = str(item)
|
||||
|
||||
except yaml.YAMLError as exc:
|
||||
print(exc)
|
||||
return
|
||||
|
||||
reformat_data = {
|
||||
"name": recipe_data.get("title"),
|
||||
"description": recipe_description,
|
||||
"image": recipe_data.get("image", ""),
|
||||
"recipeIngredient": recipe_data.get("ingredients"),
|
||||
"recipeInstructions": recipe_data.get("directions"),
|
||||
"tags": recipe_data.get("tags").split(","),
|
||||
}
|
||||
|
||||
pprint(reformat_data)
|
||||
new_recipe = Recipe(**reformat_data)
|
||||
|
||||
reformated_list = []
|
||||
for instruction in new_recipe.recipeInstructions:
|
||||
reformated_list.append({"text": instruction})
|
||||
|
||||
new_recipe.recipeInstructions = reformated_list
|
||||
|
||||
return new_recipe
|
||||
|
||||
|
||||
def main():
|
||||
from db.mongo_setup import global_init
|
||||
|
||||
global_init()
|
||||
recipe_dir, image_dir = pull_repo(repo)
|
||||
|
||||
failed_images = []
|
||||
for image in image_dir.iterdir():
|
||||
try:
|
||||
image.rename(IMG_DIR.joinpath(image.name))
|
||||
except:
|
||||
failed_images.append(image.name)
|
||||
|
||||
failed_recipes = []
|
||||
for recipe in recipe_dir.glob("*.md"):
|
||||
print(recipe.name)
|
||||
try:
|
||||
new_recipe = read_chowdown_file(recipe)
|
||||
new_recipe.save_to_db()
|
||||
|
||||
except:
|
||||
failed_recipes.append(recipe.name)
|
||||
|
||||
report = {"failedImages": failed_images, "failedRecipes": failed_recipes}
|
||||
|
||||
print(report)
|
||||
|
||||
|
||||
main()
|
@ -20,7 +20,11 @@ def auto_backup_job():
|
||||
for backup in BACKUP_DIR.glob("Auto*.zip"):
|
||||
backup.unlink()
|
||||
|
||||
export_db(tag="Auto", template=None)
|
||||
templates = []
|
||||
for template in TEMPLATE_DIR.iterdir():
|
||||
templates.append(template)
|
||||
|
||||
export_db(tag="Auto", templates=templates)
|
||||
logger.info("Auto Backup Called")
|
||||
|
||||
|
||||
@ -56,7 +60,7 @@ def import_from_archive(file_name: str) -> list:
|
||||
return successful_imports
|
||||
|
||||
|
||||
def export_db(tag=None, template=None) -> str:
|
||||
def export_db(tag=None, templates=None):
|
||||
if tag:
|
||||
export_tag = tag + "_" + datetime.now().strftime("%Y-%b-%d")
|
||||
else:
|
||||
@ -72,7 +76,12 @@ def export_db(tag=None, template=None) -> str:
|
||||
recipe_folder.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
export_images(img_folder)
|
||||
export_recipes(recipe_folder, template)
|
||||
|
||||
if type(templates) == list:
|
||||
for template in templates:
|
||||
export_recipes(recipe_folder, template)
|
||||
elif type(templates) == str:
|
||||
export_recipes(recipe_folder, templates)
|
||||
|
||||
zip_path = BACKUP_DIR.joinpath(f"{export_tag}")
|
||||
shutil.make_archive(zip_path, "zip", backup_folder)
|
||||
@ -95,7 +104,7 @@ def export_recipes(dest_dir: Path, template=None) -> Path:
|
||||
json_recipe = recipe.to_json(indent=4)
|
||||
|
||||
if template:
|
||||
md_dest = dest_dir.parent.joinpath("markdown")
|
||||
md_dest = dest_dir.parent.joinpath("templates")
|
||||
md_dest.mkdir(parents=True, exist_ok=True)
|
||||
template = TEMPLATE_DIR.joinpath(template)
|
||||
export_markdown(md_dest, json_recipe, template)
|
||||
|
@ -29,6 +29,18 @@ def pull_repo(repo):
|
||||
|
||||
|
||||
def read_chowdown_file(recipe_file: Path) -> Recipe:
|
||||
"""Parse through the yaml file to try and pull out the relavent information.
|
||||
Some issues occur when ":" are used in the text. I have no put a lot of effort
|
||||
into this so there may be better ways of going about it. Currently, I get about 80-90%
|
||||
of recipes from repos I've tried.
|
||||
|
||||
Args:
|
||||
recipe_file (Path): Path to the .yml file
|
||||
|
||||
Returns:
|
||||
Recipe: Recipe class object
|
||||
"""
|
||||
|
||||
with open(recipe_file, "r") as stream:
|
||||
recipe_description: str = str
|
||||
recipe_data: dict = {}
|
||||
@ -85,7 +97,6 @@ def chowdown_migrate(repo):
|
||||
except:
|
||||
failed_recipes.append(recipe.name)
|
||||
|
||||
|
||||
report = {"failedImages": failed_images, "failedRecipes": failed_recipes}
|
||||
|
||||
return report
|
||||
|
@ -125,26 +125,25 @@ class Recipe(BaseModel):
|
||||
document.delete()
|
||||
return "Document Deleted"
|
||||
|
||||
@staticmethod
|
||||
def update(recipe_slug: str, data: dict) -> dict:
|
||||
""" Updates the recipe from the database by slug """
|
||||
def update(self, recipe_slug: str):
|
||||
""" Updates the recipe from the database by slug"""
|
||||
document = RecipeDocument.objects.get(slug=recipe_slug)
|
||||
|
||||
if document:
|
||||
document.update(set__name=data.get("name"))
|
||||
document.update(set__description=data.get("description"))
|
||||
document.update(set__image=data.get("image"))
|
||||
document.update(set__recipeYield=data.get("recipeYield"))
|
||||
document.update(set__recipeIngredient=data.get("recipeIngredient"))
|
||||
document.update(set__recipeInstructions=data.get("recipeInstructions"))
|
||||
document.update(set__totalTime=data.get("totalTime"))
|
||||
document.update(set__name=self.name)
|
||||
document.update(set__description=self.description)
|
||||
document.update(set__image=self.image)
|
||||
document.update(set__recipeYield=self.recipeYield)
|
||||
document.update(set__recipeIngredient=self.recipeIngredient)
|
||||
document.update(set__recipeInstructions=self.recipeInstructions)
|
||||
document.update(set__totalTime=self.totalTime)
|
||||
|
||||
document.update(set__categories=data.get("categories"))
|
||||
document.update(set__tags=data.get("tags"))
|
||||
document.update(set__notes=data.get("notes"))
|
||||
document.update(set__orgURL=data.get("orgURL"))
|
||||
document.update(set__rating=data.get("rating"))
|
||||
document.update(set__extras=data.get("extras"))
|
||||
document.update(set__categories=self.categories)
|
||||
document.update(set__tags=self.tags)
|
||||
document.update(set__notes=self.notes)
|
||||
document.update(set__orgURL=self.orgURL)
|
||||
document.update(set__rating=self.rating)
|
||||
document.update(set__extras=self.extras)
|
||||
document.save()
|
||||
|
||||
|
||||
@ -174,5 +173,3 @@ def read_requested_values(keys: list, max_results: int = 0) -> List[dict]:
|
||||
recipe_list.append(recipe_details)
|
||||
|
||||
return recipe_list
|
||||
|
||||
|
||||
|
@ -1,3 +1,6 @@
|
||||
import json
|
||||
from pathlib import Path
|
||||
|
||||
from scrape_schema_recipe import scrape_url
|
||||
from slugify import slugify
|
||||
from utils.logger import logger
|
||||
@ -5,9 +8,24 @@ from utils.logger import logger
|
||||
from services.image_services import scrape_image
|
||||
from services.recipe_services import Recipe
|
||||
|
||||
CWD = Path(__file__).parent
|
||||
TEMP_FILE = CWD.parent.joinpath("data", "debug", "last_recipe.json")
|
||||
|
||||
|
||||
def normalize_data(recipe_data: dict) -> dict:
|
||||
if type(recipe_data["recipeYield"]) == list:
|
||||
recipe_data["recipeYield"] = recipe_data["recipeYield"][0]
|
||||
|
||||
return recipe_data
|
||||
|
||||
|
||||
def create_from_url(url: str) -> dict:
|
||||
recipe_data = process_recipe_url(url)
|
||||
|
||||
with open(TEMP_FILE, "w") as f:
|
||||
f.write(json.dumps(recipe_data, indent=4, default=str))
|
||||
|
||||
recipe_data = normalize_data(recipe_data)
|
||||
recipe = Recipe(**recipe_data)
|
||||
|
||||
return recipe.save_to_db()
|
||||
|
@ -6,10 +6,12 @@ import dotenv
|
||||
CWD = Path(__file__).parent
|
||||
ENV = CWD.joinpath(".env")
|
||||
dotenv.load_dotenv(ENV)
|
||||
PORT = 9000
|
||||
|
||||
# General
|
||||
PORT = int(os.getenv("mealie_port", 9000))
|
||||
|
||||
# Mongo Database
|
||||
MEALIE_DB_NAME = os.getenv("mealie_db_name", "mealie")
|
||||
DB_USERNAME = os.getenv("db_username", "root")
|
||||
DB_PASSWORD = os.getenv("db_password", "example")
|
||||
DB_HOST = os.getenv("db_host", "mongo")
|
||||
|
@ -13,6 +13,7 @@ def ensure_dirs():
|
||||
DATA_DIR.joinpath("img").mkdir(parents=True, exist_ok=True)
|
||||
DATA_DIR.joinpath("backups").mkdir(parents=True, exist_ok=True)
|
||||
DATA_DIR.joinpath("templates").mkdir(parents=True, exist_ok=True)
|
||||
DATA_DIR.joinpath("debug").mkdir(parents=True, exist_ok=True)
|
||||
|
||||
|
||||
def generate_default_theme():
|
||||
@ -21,7 +22,7 @@ def generate_default_theme():
|
||||
"accent": "#00457A",
|
||||
"secondary": "#973542",
|
||||
"success": "#5AB1BB",
|
||||
"info": "#FFFD99",
|
||||
"info": "#4990BA",
|
||||
"warning": "#FF4081",
|
||||
"error": "#EF5350",
|
||||
}
|
||||
|
@ -1,4 +1 @@
|
||||
{'name': 'Broccoli Beer Cheese Soup', 'description': "This recipe is inspired by one of my favorites, Gourmand's Beer Cheese Soup, which uses Shiner Bock. Feel free to use whatever you want, then go to [Gourmand's](http://lovethysandwich.com) to have the real thing.", 'image': 'broccoli-beer-cheese-soup.jpg', 'recipeYield': None, 'recipeIngredient': ['4 tablespoons butter', '1 cup diced onion', '1/2 cup shredded carrot', '1/2 cup diced celery', '1 tablespoon garlic', '1/4 cup flour', '1 quart chicken broth', '1 cup heavy cream', '10 ounces muenster cheese', '1 cup white white wine', '1 cup pale beer', '1 teaspoon Worcestershire sauce', '1/2 teaspoon hot sauce'
|
||||
], 'recipeInstructions': ['Start with butter, onions, carrots, celery, garlic until cooked down', 'Add flour, stir well, cook for 4-5 mins', 'Add chicken broth, bring to a boil', 'Add wine and reduce to a simmer', 'Add cream, cheese, Worcestershire, and hot sauce', 'Serve with croutons'
|
||||
], 'totalTime': None, 'slug': 'broccoli-beer-cheese-soup', 'categories': None, 'tags': None, 'dateAdded': None, 'notes': None, 'rating': None, 'orgURL': None, 'extras': None
|
||||
}
|
||||
// Test Notify
|