diff --git a/.dockerignore b/.dockerignore index 6316f30fa..cc5da2130 100644 --- a/.dockerignore +++ b/.dockerignore @@ -1,2 +1,3 @@ */node_modules -*/dist \ No newline at end of file +*/dist +## \ No newline at end of file diff --git a/.github/workflows/build-docs.yml b/.github/workflows/build-docs.yml new file mode 100644 index 000000000..3a7a0116e --- /dev/null +++ b/.github/workflows/build-docs.yml @@ -0,0 +1,20 @@ +name: Publish docs via GitHub Pages +on: + push: + branches: + - main + +jobs: + build: + name: Deploy docs + runs-on: ubuntu-latest + steps: + - name: Checkout main + uses: actions/checkout@v1 + + - name: Deploy docs + uses: mhausenblas/mkdocs-deploy-gh-pages@master + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + CONFIG_FILE: docs/mkdocs.yml + EXTRA_PACKAGES: build-base \ No newline at end of file diff --git a/.gitignore b/.gitignore index 486f3fec7..7230489b4 100644 --- a/.gitignore +++ b/.gitignore @@ -3,14 +3,19 @@ __pycache__/ *.py[cod] *$py.class -frontend/.env.development +# frontend/.env.development docs/site/ +mealie/temp/* +mealie/temp/api.html + mealie/data/backups/* mealie/data/debug/* mealie/data/img/* +!mealie/dist/* #Exception to keep folders +!mealie/dist/.gitkeep !mealie/data/backups/.gitkeep !mealie/data/backups/dev_sample_data* !mealie/data/debug/.gitkeep @@ -18,12 +23,12 @@ mealie/data/img/* .DS_Store node_modules -/dist + # local env files .env.local .env.*.local -.env.development + # Log files npm-debug.log* @@ -48,7 +53,7 @@ pnpm-debug.log* env/ build/ develop-eggs/ -dist/ + downloads/ eggs/ .eggs/ @@ -143,5 +148,3 @@ ENV/ # Node Modules node_modules/ - -/*.env.development* \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json index 94282cdd5..9c2025d70 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -8,9 +8,12 @@ "python.testing.unittestEnabled": false, "python.testing.nosetestsEnabled": false, - "python.testing.pytestEnabled": false, - "python.testing.promptToConfigure": false, + "python.discoverTest": true, + "python.testing.pytestEnabled": true, "cSpell.enableFiletypes": [ "!python" + ], + "python.testing.pytestArgs": [ + "mealie" ] } diff --git a/Dockerfile b/Dockerfile index c79a16c3a..4a803d026 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ -FROM node:alpine as build-stage +FROM node:lts-alpine as build-stage WORKDIR /app COPY ./frontend/package*.json ./ RUN npm install @@ -18,7 +18,12 @@ WORKDIR /app RUN pip install -r requirements.txt COPY ./mealie /app -COPY ./mealie/data/templates/recipes.md /app/data/templates/ +COPY ./mealie/data/templates/recipes.md /app/data/templates/recipes.md COPY --from=build-stage /app/dist /app/dist +RUN rm -rf /app/test /app/temp + +ENV ENV prod + +VOLUME [ "/app/data" ] CMD ["uvicorn", "app:app", "--host", "0.0.0.0", "--port", "9000"] \ No newline at end of file diff --git a/Dockerfile.dev b/Dockerfile.dev index 7e55d6008..de37b9cc1 100644 --- a/Dockerfile.dev +++ b/Dockerfile.dev @@ -3,18 +3,17 @@ FROM python:3 RUN apt-get update -y && \ apt-get install -y python-pip python-dev -# We copy just the requirements.txt first to leverage Docker cache COPY ./requirements.txt /app/requirements.txt WORKDIR /app RUN pip install -r requirements.txt +RUN pip install pytest -# COPY ./mealie /app +COPY ./mealie /app ENTRYPOINT [ "python" ] -# TODO Reconfigure Command to start a Gunicorn Server that managed the Uvicorn Server. Also Learn how to do that :-/ -CMD [ "app.py" ] \ No newline at end of file +CMD [ "app.py" ] \ No newline at end of file diff --git a/README.md b/README.md index eb4cf0d58..ddf6db3d2 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,6 @@ [![Stargazers][stars-shield]][stars-url] [![Issues][issues-shield]][issues-url] [![MIT License][license-shield]][license-url] -[![LinkedIn][linkedin-shield]][linkedin-url] [![Docker Pulls][docker-pull]][docker-pull] @@ -21,14 +20,21 @@ A Place for All Your Recipes
Explore the docs » -
+ +
View Demo · - Report Bug + Report Bug · - Request Feature -

+ API + · + + Request Feature + + · + Docker Hub +

@@ -39,7 +45,7 @@ [![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 relavent data or add a family recipe with the UI editor. +**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 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. @@ -56,7 +62,7 @@ Mealie also provides a secure API for interactions from 3rd party applications. - Add notes to recipes #### Meal Planner - Random Meal plan generation based off categories - - Expose notes in the API to allow external applications to access relavent information for meal plans + - Expose notes in the API to allow external applications to access relevant information for meal plans #### Database Import / Export - Easily Import / Export your recipes from the UI - Export recipes in into custom files using Jinja2 templates @@ -73,7 +79,9 @@ Mealie also provides a secure API for interactions from 3rd party applications. ## Contributing -Contributions are what make the open source community such an amazing place to be learn, inspire, and create. Any contributions you make are **greatly appreciated**. +Contributions are what make the open source community such an amazing place to be learn, inspire, and create. Any contributions you make are **greatly appreciated**. Especially test. Literally any tests. See the [Contributors Guide](https://hay-kot.github.io/mealie/contributors/developers-guide/code-contributions/) for help getting started. + +If you are not a coder, you can still contribute financially. financial contributions help me prioritize working on this project over others and helps me know that there is a real demand for project development. Buy Me A Coffee diff --git a/dev/dev-notes.md b/dev/dev-notes.md index 86d47ce21..a1b0d7108 100644 --- a/dev/dev-notes.md +++ b/dev/dev-notes.md @@ -17,13 +17,13 @@ Don't forget to [join the Discord](https://discord.gg/R6QDyJgbD2)! # Todo's Frontend -- [ ] .Vue file reorganized into something that makes sense +- [x] .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 +- [x] Add version indicator and notification for new version available - [ ] Enhanced Search Functionality - [ ] Organize Home Page my Category, ideally user selectable. @@ -41,15 +41,21 @@ Backend # Draft Changelog ## v0.0.2 -General +Bug Fixes - 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 +- Fixed hot-reloading development environment - [grssmnn](https://github.com/grssmnn) +- Fixed recipe not saving without image +- Fixed parsing error on image property null -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 +General Improvements +- Added Confirmation component to deleting recipes - [zackbcom](https://github.com/zackbcom) +- Updated Theme backend - [zackbcom](https://github.com/zackbcom) +- Added Persistent storage to vuex - [zackbcom](https://github.com/zackbcom) +- General Color/Theme Improvements + - More consistent UI + - More minimalist coloring +- Added API Key Extras to Recipe Data + - Users can now add custom json key/value pairs to all recipes via the editor for access in 3rd part applications. For example users can add a "message" field in the extras that can be accessed on API calls to play a message over google home. +- Improved image rendering (nearly x2 speed) +- Improved documentation + API Documentation +- Improved recipe parsing diff --git a/dev/scripts/docker-compose.dev.sh b/dev/scripts/docker-compose.dev.sh index 3a4741345..485e52f66 100755 --- a/dev/scripts/docker-compose.dev.sh +++ b/dev/scripts/docker-compose.dev.sh @@ -1 +1 @@ -docker-compose -f docker-compose.dev.yml build && docker-compose -f docker-compose.dev.yml -p dev-mealie up -d \ No newline at end of file +docker-compose -f docker-compose.dev.yml -p dev-mealie up --build \ No newline at end of file diff --git a/dev/scripts/docker-compose.sh b/dev/scripts/docker-compose.sh index 3336c7bc5..e2fd9edcd 100755 --- a/dev/scripts/docker-compose.sh +++ b/dev/scripts/docker-compose.sh @@ -1 +1 @@ -docker-compose build && docker-compose -p mealie up -d \ No newline at end of file +docker-compose -p mealie up --build \ No newline at end of file diff --git a/dev/scripts/scrape_recipe.py b/dev/scripts/scrape_recipe.py new file mode 100644 index 000000000..de18e64b2 --- /dev/null +++ b/dev/scripts/scrape_recipe.py @@ -0,0 +1,18 @@ +""" +Helper script to download raw recipe data from a URL and dump it to disk. +The resulting files can be used as test input data. +""" + +import sys, json +from scrape_schema_recipe import scrape_url + +for url in sys.argv[1:]: + try: + data = scrape_url(url)[0] + slug = list(filter(None, url.split("/")))[-1] + filename = f"{slug}.json" + with open(filename, "w") as f: + json.dump(data, f, indent=4, default=str) + print(f"Saved {filename}") + except Exception as e: + print(f"Error for {url}: {e}") diff --git a/docker-compose.dev.yml b/docker-compose.dev.yml index d9b9d4879..72b5f9a84 100644 --- a/docker-compose.dev.yml +++ b/docker-compose.dev.yml @@ -2,23 +2,26 @@ version: "3.1" services: # Vue Frontend - mealie: + mealie-frontend: + image: mealie-frontend:dev build: context: ./frontend dockerfile: frontend.Dockerfile - container_name: mealie_frontend restart: always ports: - 9920:8080 + environment: + VUE_APP_API_BASE_URL: "http://mealie-api:9000" volumes: - - ./frontend:/app + - ./frontend/:/app + - /app/node_modules # Fast API mealie-api: + image: mealie-api:dev build: context: ./ dockerfile: Dockerfile.dev - container_name: mealie-api restart: always ports: - 9921:9000 diff --git a/docker-compose.yml b/docker-compose.yml index 77a722672..1b044e1dc 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -16,8 +16,7 @@ services: db_host: mongo db_port: 27017 volumes: - - ./mealie/data/img:/app/data/img - - ./mealie/data/backups:/app/data/backups + - ./mealie/data/:/app/data mongo: image: mongo restart: always diff --git a/docs/docs/1.9 - release-notes.md b/docs/docs/1.9 - release-notes.md deleted file mode 100644 index 9e19854fb..000000000 --- a/docs/docs/1.9 - release-notes.md +++ /dev/null @@ -1,34 +0,0 @@ -# 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. - -### Recipes - - Automatic web scrapping for common recipe platforms - - Interactive API Documentation thanks to [FastAPI](https://fastapi.tiangolo.com/) and [Swagger](https://petstore.swagger.io/) - - UI Recipe Editor - - JSON Recipe Editor in browser - - Custom tags and categories - - Rate recipes - - Add notes to recipes - - Migration From Other Platforms - - Chowdown -### Meal Planner - - Random Meal plan generation based off categories - - Expose notes in the API to allow external applications to access relevant information for meal plans - -### Database Import / Export - - Easily Import / Export your recipes from the UI - - Export recipes in markdown format for universal access - - Use the default or a custom jinja2 template \ No newline at end of file diff --git a/docs/docs/api/api-usage.md b/docs/docs/api/api-usage.md new file mode 100644 index 000000000..911e11347 --- /dev/null +++ b/docs/docs/api/api-usage.md @@ -0,0 +1,14 @@ +# Usage + +## Key Components +### Recipe Extras +Recipes extras are a key feature of the Mealie API. They allow you to create custom json key/value pairs within a recipe to reference from 3rd part applications. You can use these keys to contain information to trigger automation or custom messages to relay to your desired device. + +For example you could add `{"message": "Remember to thaw the chicken"}` to a recipe and use the webhooks built into mealie to send that message payload to a destination to be processed. + +![api-extras-gif](/gifs/api-extras.gif) + + +## Examples + +Have Ideas? Submit a PR! \ No newline at end of file diff --git a/docs/docs/api/docs/index.html b/docs/docs/api/docs/index.html new file mode 100644 index 000000000..0f19965fb --- /dev/null +++ b/docs/docs/api/docs/index.html @@ -0,0 +1,26 @@ + + + + + My Project - ReDoc + + + + + + + +
+ + + + + diff --git a/docs/docs/changelog.md b/docs/docs/changelog.md new file mode 100644 index 000000000..61e4afb60 --- /dev/null +++ b/docs/docs/changelog.md @@ -0,0 +1,57 @@ +# Release Notes + +## v0.0.2 - Pre-release Second Patch +A quality update with major props to [zackbcom](https://github.com/zackbcom) for working hard on making the theming just that much better! +### Bug Fixes + - Fixed empty backup failure without markdown template + - Fixed opacity issues with marked steps - [mtoohey31](https://github.com/mtoohey31) + - Fixed hot-reloading development environment - [grssmnn](https://github.com/grssmnn) + - Fixed recipe not saving without image + - Fixed parsing error on image property null + +### General Improvements + - Added Confirmation component to deleting recipes - [zackbcom](https://github.com/zackbcom) + - Updated Theme backend - [zackbcom](https://github.com/zackbcom) + - Added Persistent storage to vuex - [zackbcom](https://github.com/zackbcom) + - General Color/Theme Improvements + - More consistent UI + - More minimalist coloring + - Added API key extras to Recipe Data - [See Documentation](/api/api-usage/) + - Users can now add custom json key/value pairs to all recipes via the editor for access in 3rd part applications. For example users can add a "message" field in the extras that can be accessed on API calls to play a message over google home. + - Improved image rendering (nearly x2 speed) + - Improved documentation + API Documentation + - Improved recipe parsing + - User feedback on backup importing + +## 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. + +### Recipes + - Automatic web scrapping for common recipe platforms + - Interactive API Documentation thanks to [FastAPI](https://fastapi.tiangolo.com/) and [Swagger](https://petstore.swagger.io/) + - UI Recipe Editor + - JSON Recipe Editor in browser + - Custom tags and categories + - Rate recipes + - Add notes to recipes + - Migration From Other Platforms + - Chowdown +### Meal Planner + - Random Meal plan generation based off categories + - Expose notes in the API to allow external applications to access relevant information for meal plans + +### Database Import / Export + - Easily Import / Export your recipes from the UI + - Export recipes in markdown format for universal access + - Use the default or a custom jinja2 template \ No newline at end of file diff --git a/docs/docs/2.1 - Contributions.md b/docs/docs/contributors/developers-guide/code-contributions.md similarity index 67% rename from docs/docs/2.1 - Contributions.md rename to docs/docs/contributors/developers-guide/code-contributions.md index 98abce87c..6b12973b1 100644 --- a/docs/docs/2.1 - Contributions.md +++ b/docs/docs/contributors/developers-guide/code-contributions.md @@ -1,26 +1,22 @@ # 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 +!!! Warning + It should be known going into this that this is my first open source project, and my first public github repo I'm actively managing. If something does not make sense, or is not best practice. PLEASE feel free to reach out and let me know. I'm all about improving workflow and making it easier for contributors. -[Remember to join the Discord and stay in touch with other developers working on the project](https://discord.gg/R6QDyJgbD2)! +[Please Join the Discord](https://discord.gg/R6QDyJgbD2). We are building a community of developers working on the project. ## 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: +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`. +1. Fork the repo and create your branch from `dev`. 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. +3. If you're interested on working on major changes please get in touch on discord and coordinate with other developers. No sense in doubling up on work if someones already on it. +4. If you've changed APIs, update the documentation. 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. +6. If you make changes to the dev branch reflect those changes in the dev/dev-notes.md to keep track of changes. Don't forget to add your name/handle/identifier! ## 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. diff --git a/docs/docs/contributors/developers-guide/general-guidelines.md b/docs/docs/contributors/developers-guide/general-guidelines.md new file mode 100644 index 000000000..c8743852d --- /dev/null +++ b/docs/docs/contributors/developers-guide/general-guidelines.md @@ -0,0 +1,3 @@ +# Guidelines + +TODO \ No newline at end of file diff --git a/docs/docs/contributors/developers-guide/starting-dev-server.md b/docs/docs/contributors/developers-guide/starting-dev-server.md new file mode 100644 index 000000000..9a3ee56a2 --- /dev/null +++ b/docs/docs/contributors/developers-guide/starting-dev-server.md @@ -0,0 +1,31 @@ +# Development: Getting Started + +After reading through the [Code Contributions Guide](https://hay-kot.github.io/mealie/contributors/developers-guide/code-contributions/) and forking the repo you can start working. This project is developed with :whale: docker and as such you will be greatly aided by using docker for development. It's not necessary but it is helpful. + +## With Docker +`cd` into frontend directory and run `npm install` to install the node modules. + +There are 2 scripts to help set up the docker containers in dev/scripts/. + +`docker-compose.dev.sh` - Will spin up a docker development server +`docker-compose.sh` - Will spin up a docker production server + +There are VSCode tasks created in the .vscode folder. You can use these to quickly execute the scripts above using the command palette. + + +## Without Docker +?? TODO + +## Trouble Shooting + +!!! Error "Symptom: Vue Development Server Wont Start" + **Error:** `TypeError: Cannot read property 'upgrade' of undefined` + + **Solution:** You may be missing the `/frontend/.env.development.` The contents should be `VUE_APP_API_BASE_URL=http://127.0.0.1:9921`. This is a reference to proxy the the API requests from Vue to 127.0.0.1 at port 9921 where FastAPI should be running. + +!!! Error "Symptom: FastAPI Development Server Wont Start" + **Error:** `RuntimeError: Directory '/app/dist' does not exist` + + **Solution:** Create an empty /mealie/dist directory. This directory is served as static content by FastAPI. It is provided during the build process and may be missing in development. + +Run into another issue? [Ask for help on discord](https://discord.gg/R6QDyJgbD2) \ No newline at end of file diff --git a/docs/docs/contributors/non-coders.md b/docs/docs/contributors/non-coders.md new file mode 100644 index 000000000..cb0480b28 --- /dev/null +++ b/docs/docs/contributors/non-coders.md @@ -0,0 +1,15 @@ +# Non-Code Contributions + +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)! + +Additionally, you can buy me a coffee and support the project. When I get financial support it helps me know that there's real interest in the project and that it's worth the time to keep developing. + +Buy Me A Coffee \ No newline at end of file diff --git a/docs/docs/1.3 - admin-panel.md b/docs/docs/getting-started/backups-and-exports.md similarity index 66% rename from docs/docs/1.3 - admin-panel.md rename to docs/docs/getting-started/backups-and-exports.md index fde088e51..f20e0c32d 100644 --- a/docs/docs/1.3 - admin-panel.md +++ b/docs/docs/getting-started/backups-and-exports.md @@ -1,19 +1,5 @@ -# Site Settings Panel -!!! danger - As this is still a **BETA** It is recommended that you backup your data often and store in more than one place. Ad-hear to backup best practices with the [3-2-1 Backup Rule](https://en.wikipedia.org/wiki/Backup) - - -## Theme Settings -Color themes can be created and set from the UI in the settings page. You can select an existing color theme or create a new one. On creation of a new color theme random colors will first be generated, then you can select and save as you'd like. By default the "default" theme will be loaded for all new users visiting the site. All created color themes are available to all users of the site. Separate color themes can be set for both Light and Dark modes. - -![](gifs/theme-demo.gif) - -!!! note - Theme data is stored in cookies in the browser. Calling "Save Theme" will refresh the cookie with the selected theme as well save the theme to the database. - - -## Backup and Export -![](img/admin-backup.png) +# Backup and Export +![](../img/admin-backup.png) 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. @@ -21,10 +7,10 @@ To create an export simple add the tag and the markdown template and click Backu 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 +## 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 +### Input ```jinja2 ![Recipe Image](../images/{{ recipe.image }}) @@ -52,7 +38,7 @@ Categories: {{ recipe.categories }} Original URL: {{ recipe.orgURL }} ``` -#### Output +### Output ```markdown ![Recipe Image](../images/five-spice-popcorn-chicken.jpg) @@ -90,14 +76,4 @@ 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. - -## Migration - -### Chowdown - -In the Admin page on the in the Migration section you can provide a URL for a repo hosting a Chowdown site and Mealie will pull the images and recipes from the instance and automatically import them into the database. Due to the nature of the yaml format you may have mixed results but you should get an error report of the recipes that had errors and will need to be manually added. Note that you can only import the repo as a whole. You cannot import individual recipes. +If you decide you don't like mealie. This is a good way to export into a format that can be imported into another. \ No newline at end of file diff --git a/docs/docs/1.0 - getting-started.md b/docs/docs/getting-started/install.md similarity index 94% rename from docs/docs/1.0 - getting-started.md rename to docs/docs/getting-started/install.md index 0c4346239..b5196d2b3 100644 --- a/docs/docs/1.0 - getting-started.md +++ b/docs/docs/getting-started/install.md @@ -1,4 +1,4 @@ -# Getting Started +# Installation 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. @@ -16,6 +16,7 @@ To deploy docker on your local network it is highly recommended to use docker to | 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 | +| api_docs | True | Turns on/off access to the API documentation locally. | | TZ | | You should set your time zone accordingly so the date/time features work correctly | @@ -38,12 +39,13 @@ services: db_port: 27017 # The Default port for Mongo DB TZ: America/Anchorage volumes: - - ./data/img:/app/data/img - - ./data/backups:/app/data/backups + - ./mealie/data/:/app/data/ mongo: image: mongo restart: always + volumes: + - ./mongo:/data/db environment: MONGO_INITDB_ROOT_USERNAME: root # Change! MONGO_INITDB_ROOT_PASSWORD: example # Change! @@ -56,6 +58,7 @@ services: environment: ME_CONFIG_MONGODB_ADMINUSERNAME: root ME_CONFIG_MONGODB_ADMINPASSWORD: example + ``` ## Ansible Tasks Template diff --git a/docs/docs/1.2 - meal-planner.md b/docs/docs/getting-started/meal-planner.md similarity index 96% rename from docs/docs/1.2 - meal-planner.md rename to docs/docs/getting-started/meal-planner.md index fbc388801..3e71a549e 100644 --- a/docs/docs/1.2 - meal-planner.md +++ b/docs/docs/getting-started/meal-planner.md @@ -8,4 +8,4 @@ To edit the meal in a meal plan simply select the edit button on the card in the !!! warning In coming a future release recipes for meals will be restricted to specific categories. -![](gifs/meal-plan-demo.gif) +![](../gifs/meal-plan-demo.gif) diff --git a/docs/docs/getting-started/migration-imports.md b/docs/docs/getting-started/migration-imports.md new file mode 100644 index 000000000..1326b5d00 --- /dev/null +++ b/docs/docs/getting-started/migration-imports.md @@ -0,0 +1,12 @@ +# Migration + +### Chowdown + +In the Admin page on the in the Migration section you can provide a URL for a repo hosting a [Chowdown](https://github.com/clarklab/chowdown) repository and Mealie will pull the images and recipes from the instance and automatically import them into the database. Due to the nature of the yaml format you may have mixed results but you should get an error report of the recipes that had errors and will need to be manually added. Note that you can only import the repo as a whole. You cannot import individual recipes. + +We'd like to support additional migration paths. [See open issues.](https://github.com/hay-kot/mealie/issues) + +**Currently Proposed Are:** + +- NextCloud Recipes +- Open Eats \ No newline at end of file diff --git a/docs/docs/1.1 - recipes.md b/docs/docs/getting-started/recipes.md similarity index 98% rename from docs/docs/1.1 - recipes.md rename to docs/docs/getting-started/recipes.md index a4cf6e412..7dab655b9 100644 --- a/docs/docs/1.1 - recipes.md +++ b/docs/docs/getting-started/recipes.md @@ -4,7 +4,7 @@ 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. -![](gifs/url-demo.gif) +![](../gifs/url-demo.gif) ## Recipe Editor @@ -12,12 +12,12 @@ Recipes can be edited and created via the UI. This is done with both a form base You can also add a custom recipe with the UI editor built into the web view. After logging in as a user you'll have access to the editor to make changes to all the content in the recipe. -![](gifs/editor-demo.gif) +![](../gifs/editor-demo.gif) ## 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. -![](gifs/bulk-add-demo.gif) +![](../gifs/bulk-add-demo.gif) ## Schema Recipes are stored in the json-like format in mongoDB and then sent and edited in json format on the frontend. Each recipes uses [Recipe Schema](https://schema.org/Recipe) as a general guide with some additional properties specific to Mealie. diff --git a/docs/docs/getting-started/site-settings.md b/docs/docs/getting-started/site-settings.md new file mode 100644 index 000000000..7dc6266cd --- /dev/null +++ b/docs/docs/getting-started/site-settings.md @@ -0,0 +1,21 @@ +# Site Settings Panel +!!! danger + As this is still a **BETA** It is recommended that you backup your data often and store in more than one place. Ad-hear to backup best practices with the [3-2-1 Backup Rule](https://en.wikipedia.org/wiki/Backup) + + +## Theme Settings +Color themes can be created and set from the UI in the settings page. You can select an existing color theme or create a new one. On creation of a new color theme, the default colors will be used, then you can select and save as you'd like. By default the "default" theme will be loaded for all new users visiting the site. All created color themes are available to all users of the site. Theme Colors will be set for both light and dark modes. + +![](../gifs/theme-demo.gif) + +!!! note + Theme data is stored in localstorage in the browser. Calling "Save colors and apply theme will refresh the localstorage with the selected theme as well save the theme to the database. + + + + + +## 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. + + diff --git a/docs/docs/gifs/api-extras.gif b/docs/docs/gifs/api-extras.gif new file mode 100644 index 000000000..d86f165c3 Binary files /dev/null and b/docs/docs/gifs/api-extras.gif differ diff --git a/docs/docs/html/api.html b/docs/docs/html/api.html new file mode 100644 index 000000000..7a1046cc7 --- /dev/null +++ b/docs/docs/html/api.html @@ -0,0 +1,26 @@ + + + + + My Project - ReDoc + + + + + + + +
+ + + + + diff --git a/docs/docs/index.md b/docs/docs/index.md index d43b21511..7c5886d46 100644 --- a/docs/docs/index.md +++ b/docs/docs/index.md @@ -4,9 +4,20 @@

- Report Bug + A Place for All Your Recipes +
+ View Demo · - Request Feature + Report Bug + · + API + · + + Request Feature + + · + Docker Hub +

@@ -62,16 +73,16 @@ Mealie also provides an API for interactions from 3rd party applications. **Why ## Road Map -[See Roadmap](2.0 - roadmap) +[See Roadmap](roadmap.md) ## Contributing -Contributions are what make the open source community such an amazing place to be learn, inspire, and create. Any contributions you make are **greatly appreciated**. Especially test. Literally any tests. +Contributions are what make the open source community such an amazing place to be learn, inspire, and create. Any contributions you make are **greatly appreciated**. Especially test. Literally any tests. See the [Contributors Guide](https://hay-kot.github.io/mealie/contributors/developers-guide/code-contributions/) for help getting started. -If you are not a coder, you can still contribute financially. financial contributions help me prioritize working on this project over others and helps me know that there is a real demand for the project. +If you are not a coder, you can still contribute financially. financial contributions help me prioritize working on this project over others and helps me know that there is a real demand for project development. Buy Me A Coffee diff --git a/docs/docs/2.0 - roadmap.md b/docs/docs/roadmap.md similarity index 97% rename from docs/docs/2.0 - roadmap.md rename to docs/docs/roadmap.md index 788a242f1..f459a8126 100644 --- a/docs/docs/2.0 - roadmap.md +++ b/docs/docs/roadmap.md @@ -28,7 +28,6 @@ Feature placement is not set in stone. This is much more of a guideline than any * [ ] Basic Form Validation - [ ] Recipe Viewer * [ ] Print Page View - Like King Arthur Website - * [ ] Notes Hidden/Not Hidden * [ ] Total Time Indicator * [ ] Bake Time diff --git a/docs/mkdocs.yml b/docs/mkdocs.yml index 99008e637..d7f9170ab 100644 --- a/docs/mkdocs.yml +++ b/docs/mkdocs.yml @@ -1,19 +1,46 @@ site_name: Mealie Docs + theme: + features: + - navigation.expand favicon: img/favicon.png name: material icon: logo: material/silverware-variant - features: - - navigation.instant + markdown_extensions: + - pymdownx.emoji: + emoji_index: !!python/name:materialx.emoji.twemoji + emoji_generator: !!python/name:materialx.emoji.to_svg - def_list - pymdownx.highlight - pymdownx.superfences - pymdownx.tasklist: custom_checkbox: true - admonition + extra_css: - stylesheets/custom.css repo_url: https://github.com/hay-kot/mealie repo_name: hay-kot/mealie + +nav: + - About The Project: "index.md" + - Getting Started: + - Installation: "getting-started/install.md" + - Working With Recipes: "getting-started/recipes.md" + - Planning Meals: "getting-started/meal-planner.md" + - Site Settings: "getting-started/site-settings.md" + - Backups and Exports: "getting-started/backups-and-exports.md" + - Recipe Migration: "getting-started/migration-imports.md" + - API Reference: + - API Usage: "api/api-usage.md" + - API Documentation: "api/docs/index.html" + - Contributors Guide: + - Non-Code: "contributors/non-coders.md" + - Developers Guide: + - Code Contributions: "contributors/developers-guide/code-contributions.md" + - Dev Getting Started: "contributors/developers-guide/starting-dev-server.md" + - Guidelines: "contributors/developers-guide/general-guidelines.md" + - Development Road Map: "roadmap.md" + - Change Log: "changelog.md" diff --git a/frontend/.env.development b/frontend/.env.development new file mode 100644 index 000000000..bd3a290a5 --- /dev/null +++ b/frontend/.env.development @@ -0,0 +1 @@ +VUE_APP_API_BASE_URL=http://10.10.10.12:9921 \ No newline at end of file diff --git a/frontend/.vscode/settings.json b/frontend/.vscode/settings.json new file mode 100644 index 000000000..b0b5a72c6 --- /dev/null +++ b/frontend/.vscode/settings.json @@ -0,0 +1,5 @@ +{ + "cSpell.enableFiletypes": [ + "!javascript" + ] +} \ No newline at end of file diff --git a/frontend/frontend.Dockerfile b/frontend/frontend.Dockerfile index 2cdab5876..92f6eed1b 100644 --- a/frontend/frontend.Dockerfile +++ b/frontend/frontend.Dockerfile @@ -13,7 +13,7 @@ COPY package*.json ./ RUN npm install # copy project files and folders to the current working directory (i.e. 'app' folder) -# COPY . . +COPY . . # build app for production with minification # RUN npm run build diff --git a/frontend/package-lock.json b/frontend/package-lock.json index b18d4febd..6aa5bbd0c 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -1738,6 +1738,16 @@ "integrity": "sha1-/q7SVZc9LndVW4PbwIhRpsY1IPo=", "dev": true }, + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "optional": true, + "requires": { + "color-convert": "^2.0.1" + } + }, "cacache": { "version": "13.0.1", "resolved": "https://registry.npm.taobao.org/cacache/download/cacache-13.0.1.tgz?cache=0&sync_timestamp=1594428402513&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fcacache%2Fdownload%2Fcacache-13.0.1.tgz", @@ -1764,6 +1774,34 @@ "unique-filename": "^1.1.1" } }, + "chalk": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", + "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", + "dev": true, + "optional": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "optional": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true, + "optional": true + }, "find-cache-dir": { "version": "3.3.1", "resolved": "https://registry.npm.taobao.org/find-cache-dir/download/find-cache-dir-3.3.1.tgz?cache=0&sync_timestamp=1583735626956&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Ffind-cache-dir%2Fdownload%2Ffind-cache-dir-3.3.1.tgz", @@ -1785,6 +1823,25 @@ "path-exists": "^4.0.0" } }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "optional": true + }, + "loader-utils": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.0.tgz", + "integrity": "sha512-rP4F0h2RaWSvPEkD7BLDFQnvSf+nK+wr3ESUjNTyAGobqrijmW92zc+SO6d4p4B1wh7+B/Jg1mkQe5NYUEHtHQ==", + "dev": true, + "optional": true, + "requires": { + "big.js": "^5.2.2", + "emojis-list": "^3.0.0", + "json5": "^2.1.2" + } + }, "locate-path": { "version": "5.0.0", "resolved": "https://registry.npm.taobao.org/locate-path/download/locate-path-5.0.0.tgz?cache=0&sync_timestamp=1597081764621&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Flocate-path%2Fdownload%2Flocate-path-5.0.0.tgz", @@ -1849,6 +1906,16 @@ "minipass": "^3.1.1" } }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "optional": true, + "requires": { + "has-flag": "^4.0.0" + } + }, "terser-webpack-plugin": { "version": "2.3.8", "resolved": "https://registry.npm.taobao.org/terser-webpack-plugin/download/terser-webpack-plugin-2.3.8.tgz?cache=0&sync_timestamp=1603882075288&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fterser-webpack-plugin%2Fdownload%2Fterser-webpack-plugin-2.3.8.tgz", @@ -1865,6 +1932,18 @@ "terser": "^4.6.12", "webpack-sources": "^1.4.3" } + }, + "vue-loader-v16": { + "version": "npm:vue-loader@16.1.2", + "resolved": "https://registry.npmjs.org/vue-loader/-/vue-loader-16.1.2.tgz", + "integrity": "sha512-8QTxh+Fd+HB6fiL52iEVLKqE9N1JSlMXLR92Ijm6g8PZrwIxckgpqjPDWRP5TWxdiPaHR+alUWsnu1ShQOwt+Q==", + "dev": true, + "optional": true, + "requires": { + "chalk": "^4.1.0", + "hash-sum": "^2.0.0", + "loader-utils": "^2.0.0" + } } } }, @@ -2481,9 +2560,9 @@ "dev": true }, "axios": { - "version": "0.21.0", - "resolved": "https://registry.npmjs.org/axios/-/axios-0.21.0.tgz", - "integrity": "sha512-fmkJBknJKoZwem3/IKSSLpkdNXZeBu5Q7GA/aRsr2btgrptmSCxi2oFjZHqGdK9DoTil9PIHlPIZw2EcRJXRvw==", + "version": "0.21.1", + "resolved": "https://registry.npmjs.org/axios/-/axios-0.21.1.tgz", + "integrity": "sha512-dKQiRHxGD9PPRIUNIWvZhPTPpl1rf/OxTYKsqKUDjBwYylTvV7SjSHJb9ratfyzM6wCdLCOYLzs73qpg5c4iGA==", "requires": { "follow-redirects": "^1.10.0" } @@ -3740,9 +3819,9 @@ } }, "core-js": { - "version": "3.8.1", - "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.8.1.tgz", - "integrity": "sha512-9Id2xHY1W7m8hCl8NkhQn5CufmF/WuR30BTRewvCXc1aZd3kMECwNZ69ndLbekKfakw9Rf2Xyc+QR6E7Gg+obg==" + "version": "3.8.2", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.8.2.tgz", + "integrity": "sha512-FfApuSRgrR6G5s58casCBd9M2k+4ikuu4wbW6pJyYU7bd9zvFc9qf7vr5xmrZOhT9nn+8uwlH1oRR9jTnFoA3A==" }, "core-js-compat": { "version": "3.7.0", @@ -9448,9 +9527,9 @@ "dev": true }, "sass": { - "version": "1.30.0", - "resolved": "https://registry.npmjs.org/sass/-/sass-1.30.0.tgz", - "integrity": "sha512-26EUhOXRLaUY7+mWuRFqGeGGNmhB1vblpTENO1Z7mAzzIZeVxZr9EZoaY1kyGLFWdSOZxRMAufiN2mkbO6dAlw==", + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/sass/-/sass-1.32.0.tgz", + "integrity": "sha512-fhyqEbMIycQA4blrz/C0pYhv2o4x2y6FYYAH0CshBw3DXh5D5wyERgxw0ptdau1orc/GhNrhF7DFN2etyOCEng==", "dev": true, "requires": { "chokidar": ">=2.0.0 <4.0.0" @@ -9736,6 +9815,11 @@ "rechoir": "^0.6.2" } }, + "shvl": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/shvl/-/shvl-2.0.1.tgz", + "integrity": "sha512-VU7R5Uxp38LKHooGuZe0TcX2EPK95nn8DvclAvTPyD9/qHmXvt3dR2pJ4JLZ8uLjxQNQ3zNLFJCreteIj3cvpw==" + }, "signal-exit": { "version": "3.0.3", "resolved": "https://registry.npm.taobao.org/signal-exit/download/signal-exit-3.0.3.tgz?cache=0&sync_timestamp=1585253323149&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fsignal-exit%2Fdownload%2Fsignal-exit-3.0.3.tgz", @@ -11029,9 +11113,9 @@ "integrity": "sha1-9evU+mvShpQD4pqJau1JBEVskSM=" }, "vue-cli-plugin-vuetify": { - "version": "2.0.8", - "resolved": "https://registry.npmjs.org/vue-cli-plugin-vuetify/-/vue-cli-plugin-vuetify-2.0.8.tgz", - "integrity": "sha512-BHn9wwj/+B9v25mhZq2dV8NafM2LbogymjluPP+CjDnIdcwR3hW38r3nyKsZNPB1jXfWXsvVszipS3b8FqOBCg==", + "version": "2.0.9", + "resolved": "https://registry.npmjs.org/vue-cli-plugin-vuetify/-/vue-cli-plugin-vuetify-2.0.9.tgz", + "integrity": "sha512-J4fzpz27OmCCAA3CI56ulYsUrZ859dQAh58Z9XZilY03kd/M+svLlPkK45cBIrGGfjSqQ40oyWezA3NiPBEG8g==", "dev": true, "requires": { "null-loader": "^3.0.0", @@ -11065,11 +11149,6 @@ } } }, - "vue-cookies": { - "version": "1.7.4", - "resolved": "https://registry.npmjs.org/vue-cookies/-/vue-cookies-1.7.4.tgz", - "integrity": "sha512-mOS5Btr8V9zvAtkmQ7/TfqJIropOx7etDAgBywPCmHjvfJl2gFbH2XgoMghleLoyyMTi5eaJss0mPN7arMoslA==" - }, "vue-eslint-parser": { "version": "7.1.1", "resolved": "https://registry.npm.taobao.org/vue-eslint-parser/download/vue-eslint-parser-7.1.1.tgz", @@ -11102,11 +11181,6 @@ "integrity": "sha1-UylVzB6yCKPZkLOp+acFdGV+CPI=", "dev": true }, - "vue-html-to-paper": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/vue-html-to-paper/-/vue-html-to-paper-1.3.1.tgz", - "integrity": "sha512-5IdAPUgStfpVHfcG6nXD0FbUB1onWpvwVD+OZ00jJpy3qaRPkaGD7fFIvYgBB9YPkr0VK065LayEvmGmkkfhaQ==" - }, "vue-loader": { "version": "15.9.5", "resolved": "https://registry.npm.taobao.org/vue-loader/download/vue-loader-15.9.5.tgz?cache=0&sync_timestamp=1605670886675&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fvue-loader%2Fdownload%2Fvue-loader-15.9.5.tgz", @@ -11128,87 +11202,6 @@ } } }, - "vue-loader-v16": { - "version": "npm:vue-loader@16.1.2", - "resolved": "https://registry.npmjs.org/vue-loader/-/vue-loader-16.1.2.tgz", - "integrity": "sha512-8QTxh+Fd+HB6fiL52iEVLKqE9N1JSlMXLR92Ijm6g8PZrwIxckgpqjPDWRP5TWxdiPaHR+alUWsnu1ShQOwt+Q==", - "dev": true, - "optional": true, - "requires": { - "chalk": "^4.1.0", - "hash-sum": "^2.0.0", - "loader-utils": "^2.0.0" - }, - "dependencies": { - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "optional": true, - "requires": { - "color-convert": "^2.0.1" - } - }, - "chalk": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", - "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", - "dev": true, - "optional": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "optional": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true, - "optional": true - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, - "optional": true - }, - "loader-utils": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.0.tgz", - "integrity": "sha512-rP4F0h2RaWSvPEkD7BLDFQnvSf+nK+wr3ESUjNTyAGobqrijmW92zc+SO6d4p4B1wh7+B/Jg1mkQe5NYUEHtHQ==", - "dev": true, - "optional": true, - "requires": { - "big.js": "^5.2.2", - "emojis-list": "^3.0.0", - "json5": "^2.1.2" - } - }, - "supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "optional": true, - "requires": { - "has-flag": "^4.0.0" - } - } - } - }, "vue-router": { "version": "3.4.9", "resolved": "https://registry.npmjs.org/vue-router/-/vue-router-3.4.9.tgz", @@ -11249,9 +11242,9 @@ "dev": true }, "vuetify": { - "version": "2.3.21", - "resolved": "https://registry.npmjs.org/vuetify/-/vuetify-2.3.21.tgz", - "integrity": "sha512-c9FOjkpVPDoIim88wbfqSIuCsH3jtgQQBC1iMW+ZFxf/Bj+d73HySL2LhEnZwAQT7XTAUGfad4aLPfcNZzK5YQ==" + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/vuetify/-/vuetify-2.4.2.tgz", + "integrity": "sha512-8W1928Fv6GKwLiOThutYf2wtD5C9+vcCavlI8NT0YxNOVvluoL8xrep8mGGwDsCkay+4LzaAX92owKeNi3kpWg==" }, "vuetify-loader": { "version": "1.6.0", @@ -11268,6 +11261,22 @@ "resolved": "https://registry.npmjs.org/vuex/-/vuex-3.6.0.tgz", "integrity": "sha512-W74OO2vCJPs9/YjNjW8lLbj+jzT24waTo2KShI8jLvJW8OaIkgb3wuAMA7D+ZiUxDOx3ubwSZTaJBip9G8a3aQ==" }, + "vuex-persistedstate": { + "version": "4.0.0-beta.2", + "resolved": "https://registry.npmjs.org/vuex-persistedstate/-/vuex-persistedstate-4.0.0-beta.2.tgz", + "integrity": "sha512-JeiweafcU+9d4+/nRvQwK2PyHS9xCRcGIlL2cn0ny/afTw2RP+5M6SdsjkcYoGNICTGPi5i+K3J46ioWEyVgvg==", + "requires": { + "deepmerge": "^4.2.2", + "shvl": "^2.0.0" + }, + "dependencies": { + "deepmerge": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.2.2.tgz", + "integrity": "sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg==" + } + } + }, "watchpack": { "version": "1.7.5", "resolved": "https://registry.npm.taobao.org/watchpack/download/watchpack-1.7.5.tgz?cache=0&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fwatchpack%2Fdownload%2Fwatchpack-1.7.5.tgz", diff --git a/frontend/package.json b/frontend/package.json index 9e33391e2..7baea6b04 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -8,16 +8,15 @@ "lint": "vue-cli-service lint" }, "dependencies": { - "axios": "^0.21.0", - "core-js": "^3.8.1", + "axios": "^0.21.1", + "core-js": "^3.8.2", "qs": "^6.9.4", "v-jsoneditor": "^1.4.2", "vue": "^2.6.11", - "vue-cookies": "^1.7.4", - "vue-html-to-paper": "^1.3.1", "vue-router": "^3.4.9", - "vuetify": "^2.3.21", - "vuex": "^3.6.0" + "vuetify": "^2.4.2", + "vuex": "^3.6.0", + "vuex-persistedstate": "^4.0.0-beta.2" }, "devDependencies": { "@vue/cli-plugin-babel": "~4.5.0", @@ -26,9 +25,9 @@ "babel-eslint": "^10.1.0", "eslint": "^6.7.2", "eslint-plugin-vue": "^6.2.2", - "sass": "^1.30.0", + "sass": "^1.32.0", "sass-loader": "^8.0.0", - "vue-cli-plugin-vuetify": "^2.0.8", + "vue-cli-plugin-vuetify": "^2.0.9", "vue-template-compiler": "^2.6.11", "vuetify-loader": "^1.3.0" }, diff --git a/frontend/src/App.vue b/frontend/src/App.vue index a5c69bd11..0e33a66d5 100644 --- a/frontend/src/App.vue +++ b/frontend/src/App.vue @@ -2,7 +2,7 @@ - + mdi-silverware-variant @@ -20,7 +20,7 @@ - + @@ -35,41 +35,63 @@ diff --git a/frontend/src/api/backup.js b/frontend/src/api/backup.js index e6971151e..1ec026a83 100644 --- a/frontend/src/api/backup.js +++ b/frontend/src/api/backup.js @@ -19,8 +19,9 @@ export default { }, async import(fileName) { - apiReq.post(backupURLs.importBackup(fileName)); + let response = await apiReq.post(backupURLs.importBackup(fileName)); store.dispatch("requestRecentRecipes"); + return response; }, async delete(fileName) { diff --git a/frontend/src/api/migration.js b/frontend/src/api/migration.js index 9c4d4da91..7b8ba1610 100644 --- a/frontend/src/api/migration.js +++ b/frontend/src/api/migration.js @@ -12,7 +12,6 @@ export default { async migrateChowdown(repoURL) { let postBody = { url: repoURL }; let response = await apiReq.post(migrationURLs.chowdownURL, postBody); - console.log(response); store.dispatch("requestRecentRecipes"); return response.data; }, diff --git a/frontend/src/api/themes.js b/frontend/src/api/themes.js index 866848325..04ae7eb75 100644 --- a/frontend/src/api/themes.js +++ b/frontend/src/api/themes.js @@ -19,7 +19,6 @@ export default { async requestByName(name) { let response = await apiReq.get(settingsURLs.specificTheme(name)); - console.log(response); return response.data; }, diff --git a/frontend/src/components/Admin/SFTP.vue b/frontend/src/components/Admin/SFTP.vue deleted file mode 100644 index bc90342e1..000000000 --- a/frontend/src/components/Admin/SFTP.vue +++ /dev/null @@ -1,12 +0,0 @@ - - - - - \ No newline at end of file diff --git a/frontend/src/components/Admin/Theme.vue b/frontend/src/components/Admin/Theme.vue deleted file mode 100644 index 392f58a9a..000000000 --- a/frontend/src/components/Admin/Theme.vue +++ /dev/null @@ -1,159 +0,0 @@ - - - - - - diff --git a/frontend/src/components/Admin/Users.vue b/frontend/src/components/Admin/Users.vue deleted file mode 100644 index f8d0653fe..000000000 --- a/frontend/src/components/Admin/Users.vue +++ /dev/null @@ -1,12 +0,0 @@ - - - - - \ No newline at end of file diff --git a/frontend/src/components/MealPlan/EditPlan.vue b/frontend/src/components/MealPlan/MealPlanEditor.vue similarity index 88% rename from frontend/src/components/MealPlan/EditPlan.vue rename to frontend/src/components/MealPlan/MealPlanEditor.vue index 61cb58f7e..e1164fce2 100644 --- a/frontend/src/components/MealPlan/EditPlan.vue +++ b/frontend/src/components/MealPlan/MealPlanEditor.vue @@ -1,8 +1,7 @@ - - - - \ No newline at end of file diff --git a/frontend/src/components/Admin/Backup.vue b/frontend/src/components/Settings/Backup/index.vue similarity index 80% rename from frontend/src/components/Admin/Backup.vue rename to frontend/src/components/Settings/Backup/index.vue index e986a4385..7162ec5b2 100644 --- a/frontend/src/components/Admin/Backup.vue +++ b/frontend/src/components/Settings/Backup/index.vue @@ -1,8 +1,7 @@ + + + diff --git a/frontend/src/components/Admin/Webhooks/TimePicker.vue b/frontend/src/components/Settings/Webhook/TimePickerDialog.vue similarity index 100% rename from frontend/src/components/Admin/Webhooks/TimePicker.vue rename to frontend/src/components/Settings/Webhook/TimePickerDialog.vue diff --git a/frontend/src/components/Admin/Webhooks.vue b/frontend/src/components/Settings/Webhook/index.vue similarity index 93% rename from frontend/src/components/Admin/Webhooks.vue rename to frontend/src/components/Settings/Webhook/index.vue index 5f433e6a8..672780997 100644 --- a/frontend/src/components/Admin/Webhooks.vue +++ b/frontend/src/components/Settings/Webhook/index.vue @@ -1,6 +1,6 @@ diff --git a/frontend/src/components/UI/Confirmation.vue b/frontend/src/components/UI/Confirmation.vue new file mode 100644 index 000000000..e7720b50d --- /dev/null +++ b/frontend/src/components/UI/Confirmation.vue @@ -0,0 +1,129 @@ + + + + + + \ No newline at end of file diff --git a/frontend/src/components/RecentRecipes.vue b/frontend/src/components/UI/RecentRecipes.vue similarity index 93% rename from frontend/src/components/RecentRecipes.vue rename to frontend/src/components/UI/RecentRecipes.vue index 2804ff779..684d482bd 100644 --- a/frontend/src/components/RecentRecipes.vue +++ b/frontend/src/components/UI/RecentRecipes.vue @@ -20,7 +20,7 @@ + + \ No newline at end of file diff --git a/frontend/src/main.js b/frontend/src/main.js index 3e9f4f4b0..f5a1434d1 100644 --- a/frontend/src/main.js +++ b/frontend/src/main.js @@ -4,11 +4,9 @@ import vuetify from "./plugins/vuetify"; import store from "./store/store"; import VueRouter from "vue-router"; import { routes } from "./routes"; -import VueCookies from "vue-cookies"; Vue.config.productionTip = false; Vue.use(VueRouter); -Vue.use(VueCookies); const router = new VueRouter({ routes, @@ -23,7 +21,7 @@ new Vue({ }).$mount("#app"); // Truncate -let filter = function(text, length, clamp) { +let filter = function (text, length, clamp) { clamp = clamp || "..."; let node = document.createElement("div"); node.innerHTML = text; diff --git a/frontend/src/components/Page404.vue b/frontend/src/pages/404Page.vue similarity index 100% rename from frontend/src/components/Page404.vue rename to frontend/src/pages/404Page.vue diff --git a/frontend/src/components/Home.vue b/frontend/src/pages/HomePage.vue similarity index 72% rename from frontend/src/components/Home.vue rename to frontend/src/pages/HomePage.vue index 10aa93e0a..db14c5ee5 100644 --- a/frontend/src/components/Home.vue +++ b/frontend/src/pages/HomePage.vue @@ -5,7 +5,7 @@ + + + +""" + +CWD = Path(__file__).parent +out_path = CWD.joinpath("temp", "index.html") + + +def generate_api_docs(app): + with open(out_path, "w") as fd: + out_path.parent.mkdir(exist_ok=True) + print(HTML_TEMPLATE % json.dumps(app.openapi()), file=fd) + + if __name__ == "__main__": pass diff --git a/mealie/test/__init__.py b/mealie/test/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/mealie/test/data/recipes-raw/Pizza-Knoblauch-Champignon-Paprika-vegan.html.json b/mealie/test/data/recipes-raw/Pizza-Knoblauch-Champignon-Paprika-vegan.html.json new file mode 100644 index 000000000..420f8ac4f --- /dev/null +++ b/mealie/test/data/recipes-raw/Pizza-Knoblauch-Champignon-Paprika-vegan.html.json @@ -0,0 +1,103 @@ +{ + "@context": "http://schema.org", + "@type": "Recipe", + "image": "https://img.chefkoch-cdn.de/rezepte/2235331358009600/bilder/864648/crop-960x540/pizza-knoblauch-champignon-paprika-vegan.jpg", + "recipeCategory": "Gem\u00fcse", + "recipeIngredient": [ + "300 g Weizenmehl (Type 550)", + "1 Pck. Trockenhefe", + "1 TL Salz", + "170 ml Wasser", + "6 EL \u00d6l (Knoblauch\u00f6l oder Oliven\u00f6l)", + "1 EL Tomatenmark", + "n. B. Knoblauch , gew\u00fcrfelt", + "2 Spitzpaprika oder Gem\u00fcsepaprika, rot", + "250 g Champignons", + "1 Zwiebel(n)", + " Salz und Pfeffer", + " Kr\u00e4uter , italienische, frisch oder getrocknet", + " Harissa" + ], + "name": "Pizza Knoblauch Champignon Paprika - vegan", + "description": "Pizza Knoblauch Champignon Paprika - vegan - f\u00fcr Nicht-Veganer nat\u00fcrlich mit K\u00e4se zu belegen. \u00dcber 51 Bewertungen und f\u00fcr raffiniert befunden. Mit \u25ba Portionsrechner \u25ba Kochbuch \u25ba Video-Tipps!", + "recipeInstructions": "Die Zutaten f\u00fcr den Teig verkneten und ca. 40 Minuten an einem warmen Ort gehen lassen. In der Zwischenzeit eine beliebige Anzahl Knoblauchzehen fein w\u00fcrfeln (ich bedecke die Pizza nahezu fl\u00e4chendeckend), die Zwiebel ebenfalls w\u00fcrfeln, Paprika und Champignons klein schneiden. Das \u00d6l mit Tomatenmark, Salz und Pfeffer vermischen. \r\n\r\nDen fertigen Teig ausrollen und auf ein Blech legen (ich benutze eine Pflaumenkuchen-Backform). Die \u00d6lmischung mit einem Backpinsel gleichm\u00e4\u00dfig auf dem Teig verteilen, danach mit dem Knoblauch, den Champignons, der Paprika und den Zwiebeln belegen. \r\n\r\nNun die Pizza mit Salz, Pfeffer, Kr\u00e4utern und Harissa kr\u00e4ftig w\u00fcrzen und bei 250\u00b0C ca. 10 - 15 Minuten backen. Der Teig ist als Grundteig zu betrachten und l\u00e4sst sich nat\u00fcrlich mit allem M\u00f6glichen an Gem\u00fcse belegen.", + "author": { + "@type": "Person", + "name": "healing21" + }, + "publisher": { + "@type": "Organization", + "name": "Chefkoch.de" + }, + "datePublished": "2013-01-14", + "prepTime": "P0DT0H20M", + "cookTime": "P0DT0H15M", + "totalTime": "P0DT1H15M", + "recipeYield": "2 Portion(en)", + "aggregateRating": { + "@type": "AggregateRating", + "ratingCount": 51, + "ratingValue": 4.57, + "reviewCount": 34, + "worstRating": 0, + "bestRating": 5 + }, + "keywords": [ + "Gem\u00fcse", + "Hauptspeise", + "Backen", + "Vegetarisch", + "einfach", + "Vegan", + "Pizza", + "Pilze" + ], + "reviews": [ + { + "@type": "Review", + "reviewBody": " Sehr gutes Basis Rezept!\n\nHab noch Salami, Kochschinken und K\u00e4se dazu gemacht, sonst schmeckt es ja nach nichts. \n\nErgebnis: 1. Klasse! Sehr fein! ", + "datePublished": "2020-04-21", + "author": { + "@type": "Person", + "name": "eierkopp1824" + } + }, + { + "@type": "Review", + "reviewBody": "Hallo,\r\nhabe den Teig gut zwei Stunden gehen lassen und dann wie im Rezept angegeben weiter verarbeitet. Da ich noch einige Schinkenw\u00fcrfel und etwas Fetak\u00e4se im K\u00fchlschrank hatte, wurden diese ebenfalls auf dem Belag verteilt. Ich habe die Pizza auf der untersten Schiene im Backofen gebacken. Der Boden ist nach dem Backen sch\u00f6n knusprig. Es hat mir und meinem Mitesser sehr gut geschmeckt.\r\nLG von Sternek\u00f6chin2011", + "datePublished": "2020-03-10", + "author": { + "@type": "Person", + "name": "Sternek\u00f6chin2011" + } + }, + { + "@type": "Review", + "reviewBody": "Echt f\u00fcr mich die leckerste Pizza auf der Welt! Auch bei meiner Familie kommt sie super an und \u00fcberlebt nicht lange. :)\nDen Belag kann man ja variieren wie man will. ", + "datePublished": "2020-02-20", + "author": { + "@type": "Person", + "name": "Leo090800" + } + }, + { + "@type": "Review", + "reviewBody": "Beste Pizza, die ich je gegessen habe! Sooo lecker! Habe f\u00fcr den Teig Dinkelvollkornmehl genommen und den Belag noch mit ein paar Chiliflocken verfeinert. ", + "datePublished": "2018-04-15", + "author": { + "@type": "Person", + "name": "Sunny_Eyes" + } + }, + { + "@type": "Review", + "reviewBody": "Der Teig ist super, ebenso wie die Sauce! Habe anstelle von normalem Salz Basilikumsalz in den Teig gegeben, das gibt dem ganzen einen besonderen Geschmack.Statt Paprika und Knobi habe ich K\u00e4se und Rucola hinzuef\u00fcgt, den Salat erst nach dem Backen. Die wird sicherlich nochmal gemacht! Da ich nur eine Pizza gemacht habe, habe ich den restlichen Teig eingefroren. Foto ist unterwegs!", + "datePublished": "2018-02-14", + "author": { + "@type": "Person", + "name": "Chiqryn" + } + } + ], + "url": "https://www.chefkoch.de/rezepte/2235331358009600/Pizza-Knoblauch-Champignon-Paprika-vegan.html" +} \ No newline at end of file diff --git a/mealie/test/data/recipes-raw/best-homemade-salsa-recipe.json b/mealie/test/data/recipes-raw/best-homemade-salsa-recipe.json new file mode 100644 index 000000000..0f2671e2c --- /dev/null +++ b/mealie/test/data/recipes-raw/best-homemade-salsa-recipe.json @@ -0,0 +1,86 @@ +{ + "@context": "http://schema.org/", + "@type": "Recipe", + "name": "The Best Homemade Salsa Recipe", + "author": { + "@type": "Person", + "name": "Sommer Collier" + }, + "description": "How To Make Delicious Salsa: Secrets of making the Best Homemade Salsa Recipe! This restaurant style salsa recipe is loaded with flavor, has an amazing texture, and a secret ingredient.", + "datePublished": "2020-02-01T00:00:30+00:00", + "image": [ + "https://www.aspicyperspective.com/wp-content/uploads/2019/02/the-best-homemade-salsa-recipe-100.jpg", + "https://www.aspicyperspective.com/wp-content/uploads/2019/02/the-best-homemade-salsa-recipe-100-500x500.jpg", + "https://www.aspicyperspective.com/wp-content/uploads/2019/02/the-best-homemade-salsa-recipe-100-500x375.jpg", + "https://www.aspicyperspective.com/wp-content/uploads/2019/02/the-best-homemade-salsa-recipe-100-480x270.jpg" + ], + "video": { + "name": "The Best Homemade Salsa Recipe", + "description": "We\u2019re sharing our secrets for making The Best Homemade Salsa Recipe we\u2019ve ever tried. Healthy, fresh, and easy to adjust!", + "thumbnailUrl": "https://content.jwplatform.com/thumbs/rPi8NdK6-720.jpg", + "contentUrl": "https://content.jwplatform.com/videos/rPi8NdK6.mp4", + "uploadDate": "2017-03-22T16:24:09.000Z", + "@type": "VideoObject" + }, + "recipeYield": [ + "20", + "20 (5 cups)" + ], + "prepTime": "PT5M", + "totalTime": "PT5M", + "recipeIngredient": [ + "4 ripe tomatoes, (cored and quartered)", + "1 red onion, (peeled and quartered)", + "3 garlic cloves, (peeled)", + "3 jalapenos, (stemmed and seeded (you can\u00a0substitute 1-2 habanero or serrano peppers.))", + "1/3 cup fresh cilantro", + "3 tablespoons fresh lime juice", + "2-3 teaspoons ground cumin", + "2-3 teaspoons sugar ((optional))", + "1 1/2 teaspoons salt", + "15 ounces crushed San Marzano tomatoes ((1 can))", + "4.5 ounces diced green chiles, (mild, medium, or hot (1 can))" + ], + "recipeInstructions": [ + { + "@type": "HowToStep", + "text": "Place the fresh tomatoes, onion, garlic, peppers, cilantro, lime juice, 2 teaspoons cumin, 2 teaspoons sugar (if using), and salt in a food processor. Pulse until the contents are fine and well blended.", + "name": "Place the fresh tomatoes, onion, garlic, peppers, cilantro, lime juice, 2 teaspoons cumin, 2 teaspoons sugar (if using), and salt in a food processor. Pulse until the contents are fine and well blended.", + "url": "https://www.aspicyperspective.com/best-homemade-salsa-recipe/#wprm-recipe-61842-step-0-0" + }, + { + "@type": "HowToStep", + "text": "Pour in the crushed tomatoes and green chiles. Puree until mostly smooth. Taste, then add more cumin and sugar if desired. Refrigerate until ready to serve.", + "name": "Pour in the crushed tomatoes and green chiles. Puree until mostly smooth. Taste, then add more cumin and sugar if desired. Refrigerate until ready to serve.", + "url": "https://www.aspicyperspective.com/best-homemade-salsa-recipe/#wprm-recipe-61842-step-0-1" + } + ], + "aggregateRating": { + "@type": "AggregateRating", + "ratingValue": "4.98", + "ratingCount": "201" + }, + "recipeCategory": [ + "Appetizer", + "Snack" + ], + "recipeCuisine": [ + "American", + "Mexican" + ], + "keywords": "Homemade Salsa, Salsa, The Best Salsa Recipe", + "nutrition": { + "@type": "NutritionInformation", + "servingSize": "0.25 cup", + "calories": "19 kcal", + "carbohydrateContent": "4 g", + "sodiumContent": "230 mg", + "sugarContent": "2 g" + }, + "@id": "https://www.aspicyperspective.com/best-homemade-salsa-recipe/#recipe", + "isPartOf": { + "@id": "https://www.aspicyperspective.com/best-homemade-salsa-recipe/#article" + }, + "mainEntityOfPage": "https://www.aspicyperspective.com/best-homemade-salsa-recipe/#webpage", + "url": "https://www.aspicyperspective.com/best-homemade-salsa-recipe/" +} \ No newline at end of file diff --git a/mealie/test/data/recipes-raw/blue-cheese-stuffed-turkey-meatballs-with-raspberry-balsamic-glaze-2.json b/mealie/test/data/recipes-raw/blue-cheese-stuffed-turkey-meatballs-with-raspberry-balsamic-glaze-2.json new file mode 100644 index 000000000..32fc65485 --- /dev/null +++ b/mealie/test/data/recipes-raw/blue-cheese-stuffed-turkey-meatballs-with-raspberry-balsamic-glaze-2.json @@ -0,0 +1,36 @@ +{ + "image": "https://www.cookingforkeeps.com/wp-content/uploads/2013/02/done-1.jpg", + "aggregateRating": { + "properties": { + "ratingValue": "4.0", + "ratingCount": "2" + }, + "@type": "AggregateRating" + }, + "name": "Blue Cheese Stuffed Turkey Meatballs with Raspberry Balsamic Glaze", + "author": "Nicole-Cooking for Keeps", + "recipeYield": "Makes 18-22 meatballs depending on size", + "recipeInstructions": [ + "For the meatballs: Roll the blue cheese in small balls about the diameter of a dime. Freeze for 30 minutes. Preheat oven to 375 degrees. Mix the remaining ingredients together, until just combined. Roll sausage mixture into small balls. Place the blue cheese in the middle, enclosing with meat. Bake on a silt pad until golden brown and cooked through, about 25 min, turning halfway through to ensure even browning.", + "For the Dipping Sauce:", + "Combine all ingredients together in small sauce pan over medium high heat. Bring to a boil and then reduce heat and simmer about five minutes. Coat meatballs in sauce. Serve." + ], + "@context": "http://schema.org", + "@type": "Recipe", + "url": "https://www.cookingforkeeps.com/blue-cheese-stuffed-turkey-meatballs-with-raspberry-balsamic-glaze-2/", + "recipeIngredient": [ + "Sausage Bites", + "3 oz creamy gorgonzola cheese", + "1 lb turkey Italian sausage (schmicas) with fennel seed", + "\u00bd cup Italian style bread crumbs", + "\u00bd onion grated", + "1 egg white", + "Salt to taste", + "Dipping Sauce:", + "\u00bd cup raspberry preserves", + "\u215b cup balsamic vinegar", + "3 teaspoons Dijon mustard", + "Pinch of red pepper", + "Pinch of Salt" + ] +} \ No newline at end of file diff --git a/mealie/test/data/recipes-raw/bon_appetit.json b/mealie/test/data/recipes-raw/bon_appetit.json new file mode 100644 index 000000000..1cdb927c8 --- /dev/null +++ b/mealie/test/data/recipes-raw/bon_appetit.json @@ -0,0 +1,132 @@ +{ + "@context": "http://schema.org", + "@type": "Recipe", + "articleBody": "Atlanta pastry chef Claudia Martinez\u2019s family has been making what Martinez describes as meaty Venezuelan tamales around the holidays for generations. In the Martinez household, every family member has a task: Claudia\u2019s dad or grandmother always prepares the guiso, the tender shredded chicken and beef stew that comprises the bulk of the filling. One person slicks scoops of vibrant orange achiote-stained masa dough onto banana leaves, then passes them around the table to get filled. Claudia\u2019s grandma adds a spoonful of guiso; Claudia adds olives and capers; her sister adds a few raisins. Finally, each hallaca gets wrapped up in the fragrant leaves and tied with twine like a tiny present, ready to boil for a late Christmas Eve dinner. The Martinez family usually makes 100 at a time; this scaled-down version of their recipe makes just under 20, enough for a big dinner plus leftovers you can freeze for another day. If you find yourself with leftover masa and stew, do as the Martinezes do: Make arepas with guiso and fried eggs for breakfast on Christmas Day. (If you\u2019re in Atlanta in the days leading up to Christmas Eve, pick up hallacas at Caf\u00e9 Claudia, the pop-up Martinez runs out of the Hotel Clermont.)\nBanana leaves give a floral and grassy flavor to the hallacas, you can buy them either fresh or frozen at Latin and Asian markets. You can use parchment paper instead, but the outcome won\u2019t be as complex.", + "alternativeHeadline": "The Venezuelan holiday dish that Atlanta pastry chef Claudia Martinez\u2019s family has been making for generations.", + "dateModified": "2021-01-02 12:09:30.443000", + "datePublished": "2020-12-01 07:00:00", + "keywords": [ + "recipes", + "holiday 2020", + "new years eve", + "olive oil", + "beef", + "chicken recipes", + "kosher salt", + "tomato", + "garlic", + "tomato paste", + "onion", + "bell pepper", + "green onion scallion", + "cilantro", + "brown sugar", + "cornmeal", + "capers", + "olive", + "raisin", + "web" + ], + "thumbnailUrl": "https://assets.bonappetit.com/photos/5fb4407993a08c9bf97163f7/1:1/w_1125,h_1125,c_limit/1220-Hallacas.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": "Claudia Martinez", + "sameAs": "https://bon-appetit.com/contributor/claudia-martinez/" + } + ], + "aggregateRating": { + "@type": "AggregateRating", + "ratingValue": 5, + "ratingCount": 22 + }, + "description": "The Venezuelan holiday dish that Atlanta pastry chef Claudia Martinez\u2019s family has been making for generations.", + "image": "hallacas.jpg", + "name": "Hallacas", + "recipeIngredient": [ + "1\u00bd cups extra-virgin olive oil", + "3 Tbsp. plus 1\u00bd tsp. achiote (annatto) seeds", + "2\u00bd lb. boneless beef chuck roast", + "2\u00bd lb. skinless, boneless chicken breasts", + "1 Tbsp. Diamond Crystal or 1\u00be tsp. Morton kosher salt, plus more", + "3 medium tomatoes, coarsely chopped", + "3 garlic cloves", + "1 6-oz. can tomato paste", + "1 medium onion, chopped", + "1 large red bell pepper, seeds and ribs removed, coarsely chopped", + "1 large green bell pepper, seeds and ribs removed, coarsely chopped", + "1 bunch scallions, coarsely chopped", + "1 bunch cilantro, coarsely chopped", + "\u00bc cup (packed) light brown sugar", + "1 1-kg package P.A.N. precooked cornmeal", + "2 Tbsp. Diamond Crystal or 1 Tbsp. plus \u00bd tsp. kosher salt", + "3 1-lb. packages fresh or frozen, thawed banana or plantain leaves", + "\u00bc cup extra-virgin olive oil", + "\u00bd cup drained capers", + "\u00bd cup pitted green olives", + "\u00bd cup raisins" + ], + "recipeInstructions": [ + { + "@type": "HowToStep", + "text": "Cook oil and achiote seeds in a small saucepan over medium-low heat until oil turns deep orange, about 10 minutes. Strain into a heatproof jar and let cool. Measure out \u00bd cup achiote oil for making filling; set remaining 1 cup oil aside for making dough." + }, + { + "@type": "HowToStep", + "text": "Bring beef, chicken, 1 Tbsp. Diamond Crystal or 1\u00be tsp. Morton kosher salt, and 12 cups water to a boil in a large pot over medium-high heat. Reduce heat to medium-low and let simmer until cooked through, about 30 minutes. Transfer beef and chicken to a cutting board and let sit until cool enough to handle. Pour 8 cups cooking liquid into a heatproof pitcher or large measuring glass; set aside. Discard any extra liquid." + }, + { + "@type": "HowToStep", + "text": "Cut beef and chicken into \u2153\" cubes; place back into pot (cooking the meat before you chop it means that you can cut the pieces finer and more evenly). Blend tomatoes, garlic, and tomato paste in a blender until smooth; scrape pur\u00e9e into pot with meat. Blend onion, red and green bell peppers, scallions, cilantro, and \u00bd cup reserved cooking liquid in blender until smooth and add to pot. Add brown sugar and \u00bd cup reserved achiote oil. Pour in remaining 7\u00bd cups reserved cooking liquid. Bring to a boil, then reduce heat to medium-low and simmer until meat is tender and liquid is slightly reduced, about 40 minutes. Drain meat in a colander, season lightly with salt, and let cool." + }, + { + "@type": "HowToStep", + "text": "Meanwhile, mix cornmeal, salt, reserved 1 cup achiote oil, and 8 cups water in a large bowl with your hands until dough is smooth, spreadable, and no large lumps remain, 5\u20137 minutes. Press a sheet of plastic wrap or parchment paper directly onto surface of dough; let rest at least 30 minutes or up to 1 hour." + }, + { + "@type": "HowToStep", + "text": "Wash and pat banana leaves dry. Carefully remove any center stems with kitchen shears, avoiding breaking through the leaf, then cut into 14x10\" rectangles. Mix oil and 1 cup water in a medium bowl (it needs to be big enough to dip your hands into). This will help to keep the dough from sticking to your hands. Working one at a time, place a banana leaf on a surface so the veins in the leaves run horizontally. Dipping your hands in oil mixture as you work, place \u00be cup dough in center of leaf and spread out with your fingers into a \u215b\"-thick rectangle, leaving a 1\" border near the vertical edges and a space on both horizontal edges. Place \u00be cup guiso into center of dough. Top with 5 capers, 2 olives, and 8 raisins." + }, + { + "@type": "HowToStep", + "text": "Take top and bottom edges of leaf and bring up toward each other so edges of dough meet and enclose filling. Pull both sides of banana leaf together snugly toward the upper edge of hallaca to seal and fold over toward you to make a tube. Fold remaining 2 side ends toward the center to make a small package." + }, + { + "@type": "HowToStep", + "text": "Place package, fold side down, on another banana leaf and wrap up again. Wrap once more in a third leaf to hold everything together, then tie closed with kitchen twine. (Make sure package is compact, the leaves are not ripped, and hallaca is not leaking.) Repeat with remaining dough, filling, and banana leaves." + }, + { + "@type": "HowToStep", + "text": "Place as many hallacas as will fit into a clean large pot, pour in water to cover, and bring to a boil. Reduce heat and simmer, turning hallacas halfway through, until plumped and firm, about 35 minutes. Repeat with remaining hallacas.\nDo ahead: Hallacas can be made 1 week ahead. Let cool, then cover and chill, or freeze up to 3 months. To reheat, cook in a pot of simmering water (make sure hallacas are submerged), partially covered, until warmed through, 10\u201315 minutes if chilled, 25\u201330 minutes if frozen." + } + ], + "recipeYield": "Makes about 18", + "url": "https://www.bonappetit.com/recipe/hallacas", + "slug": "hallacas", + "orgURL": "https://www.bonappetit.com/recipe/hallacas", + "categories": [], + "tags": [], + "dateAdded": null, + "notes": [], + "extras": [] +} \ No newline at end of file diff --git a/mealie/test/data/recipes-raw/chunky-apple-cake.json b/mealie/test/data/recipes-raw/chunky-apple-cake.json new file mode 100644 index 000000000..7a20a2cab --- /dev/null +++ b/mealie/test/data/recipes-raw/chunky-apple-cake.json @@ -0,0 +1,33 @@ +{ + "url": "https://www.deliaonline.com/recipes/seasons/what-should-you-be-cooking-in-november/chunky-apple-cake", + "author": "Delia Smith", + "image": "https://www.deliaonline.com/sites/default/files/quick_media/cakes-chunky-apple-cake.jpg", + "name": "Chunky Apple Cake", + "description": "Apples are superb in cakes, so in the autumn when there are lots of windfalls around, why not make a few of these and freeze them.", + "recipeCuisine": "General", + "recipeCategory": [ + "Apples", + "Afternoon Tea", + "Cake Recipes", + "Autumn", + "Life in the Freezer" + ], + "keywords": "Apples, Afternoon Tea, Cake Recipes, Autumn, Life in the Freezer, Delia, Delia Smith", + "recipeInstructions": "Begin by sifting the flour, baking powder and spices into a roomy mixing bowl, lifting the sieve quite high to give the flour a good airing as it goes down.\n\nNext chop the apples into small dice (with or without peel, just as you like). Then place them in a bowl and toss them with one tablespoon of the sieved flour mixture. Then add the eggs, butter and sugar to the rest of the flour, and using an electric hand whisk, combine them for about 1 minute until you have a smooth creamy consistency. After that fold in the grated orange zest, mixed peel and diced apple. If the mixture seems a little dry, add a tablespoon of milk. Now spoon the cake mix into the prepared tin and level it off with the back of a spoon.\n\nThen bake near the centre of the oven for about one hour or until the cake feels springy in the centre when lightly pressed with a fingertip and just shows signs of shrinking away from the edge of the tin. Cool in the tin for 10 minutes before turning out onto a wire rack. This looks nice dusted with sifted icing sugar just before serving. Store in an airtight tin.\n\nYou can watch more of Delia's cake recipes being made in our Cookery School Videos on the right.", + "recipeIngredient": [ + "225g self-raising flour", + "1 rounded teaspoon baking powder", + "1 level teaspoon mixed spice", + "\u00bd level teaspoon ground cinnamon", + "3 Bramley apples (about 550g)", + "2 large eggs, beaten", + "75g spreadable butter", + "175g light brown soft sugar", + "grated zest of 1 large orange", + "1 tablespoon chopped mixed peel", + "1 tablespoon milk (if needed)", + "little icing sugar" + ], + "@context": "http://schema.org", + "@type": "Recipe" +} \ No newline at end of file diff --git a/mealie/test/data/recipes-raw/dairy-free-impossible-pumpkin-pie.json b/mealie/test/data/recipes-raw/dairy-free-impossible-pumpkin-pie.json new file mode 100644 index 000000000..a029bc6e6 --- /dev/null +++ b/mealie/test/data/recipes-raw/dairy-free-impossible-pumpkin-pie.json @@ -0,0 +1,102 @@ +{ + "@context": "http://schema.org/", + "@type": "Recipe", + "name": "Dairy-Free Impossible Pumpkin Pie", + "author": { + "@type": "Person", + "name": "Kare for Kitchen Treaty" + }, + "description": "This crustless pumpkin pie might just be the\u00a0easiest\u00a0you'll ever make. Simply blend the ingredients together, pour into your pie pan, and bake!", + "datePublished": "2017-11-10T16:12:06+00:00", + "image": [ + "https://www.kitchentreaty.com/wp-content/uploads/2017/11/dairy-free-impossible-pumpkin-pie-8.jpg" + ], + "recipeYield": [ + "8" + ], + "prepTime": "PT10M", + "cookTime": "PT45M", + "totalTime": "PT55M", + "recipeIngredient": [ + "1 (15-ounce) can coconut milk (I recommend full-fat for a richer pie, but lite also works)", + "1 cup pumpkin puree", + "4 large eggs", + "1/2 cup granulated sugar", + "1 tablespoon pure vanilla extract", + "1/2 cup all-purpose flour (or your favorite cup-for-cup gluten-free flour blend*)", + "1 teaspoon baking powder", + "1 tablespoon pumpkin pie spice", + "1/2 teaspoon fine-grain sea salt or table salt", + "Coconut whipped cream (for serving**)" + ], + "recipeInstructions": [ + { + "@type": "HowToStep", + "text": "Preheat oven to 375 degrees Fahrenheit and position rack in the middle of the oven. Spray a 9- or 10-inch pie pan with baking spray or oil the pan with coconut oil or vegan butter.", + "name": "Preheat oven to 375 degrees Fahrenheit and position rack in the middle of the oven. Spray a 9- or 10-inch pie pan with baking spray or oil the pan with coconut oil or vegan butter.", + "url": "https://www.kitchentreaty.com/dairy-free-impossible-pumpkin-pie/#wprm-recipe-32856-step-0-0" + }, + { + "@type": "HowToStep", + "text": "Add the coconut milk, pumpkin, eggs, sugar, and vanilla to the pitcher of a blender. Blend until combined, about 20 seconds. Add the flour, baking powder, pumpkin pie spice, and salt. Blend again until well-combined, another 20\u00a0seconds.", + "name": "Add the coconut milk, pumpkin, eggs, sugar, and vanilla to the pitcher of a blender. Blend until combined, about 20 seconds. Add the flour, baking powder, pumpkin pie spice, and salt. Blend again until well-combined, another 20\u00a0seconds.", + "url": "https://www.kitchentreaty.com/dairy-free-impossible-pumpkin-pie/#wprm-recipe-32856-step-0-1" + }, + { + "@type": "HowToStep", + "text": "Pour filling into the pie plate. The mixture will be fairly runny. Carefully transfer to the preheated oven.", + "name": "Pour filling into the pie plate. The mixture will be fairly runny. Carefully transfer to the preheated oven.", + "url": "https://www.kitchentreaty.com/dairy-free-impossible-pumpkin-pie/#wprm-recipe-32856-step-0-2" + }, + { + "@type": "HowToStep", + "text": "Bake\u00a0until the middle just barely jiggles, 40-50 minutes. I like to check the middle by giving the pie pan a little nudge, and if it seems like it's no longer liquid, I'll pull the pie out and insert a butter knife about halfway between the center and the edge. If the knife comes out relatively clean - no runny pie filling - it's\u00a0done!", + "name": "Bake\u00a0until the middle just barely jiggles, 40-50 minutes. I like to check the middle by giving the pie pan a little nudge, and if it seems like it's no longer liquid, I'll pull the pie out and insert a butter knife about halfway between the center and the edge. If the knife comes out relatively clean - no runny pie filling - it's\u00a0done!", + "url": "https://www.kitchentreaty.com/dairy-free-impossible-pumpkin-pie/#wprm-recipe-32856-step-0-3" + }, + { + "@type": "HowToStep", + "text": "Place on a cooling rack and let cool, about 1 hour. Transfer to refrigerator to completely\u00a0cool, at least one more hour (or up to 3 days in advance).", + "name": "Place on a cooling rack and let cool, about 1 hour. Transfer to refrigerator to completely\u00a0cool, at least one more hour (or up to 3 days in advance).", + "url": "https://www.kitchentreaty.com/dairy-free-impossible-pumpkin-pie/#wprm-recipe-32856-step-0-4" + }, + { + "@type": "HowToStep", + "text": "If desired, top pie with dollops of coconut whipped cream. Or simply cut slices, transfer to a plate, and top\u00a0individual servings with the whipped cream.", + "name": "If desired, top pie with dollops of coconut whipped cream. Or simply cut slices, transfer to a plate, and top\u00a0individual servings with the whipped cream.", + "url": "https://www.kitchentreaty.com/dairy-free-impossible-pumpkin-pie/#wprm-recipe-32856-step-0-5" + }, + { + "@type": "HowToStep", + "text": "Keeps in the refrigerator for 3-4 days. I suggest covering the completely cooled pie with plastic wrap if not serving right away.", + "name": "Keeps in the refrigerator for 3-4 days. I suggest covering the completely cooled pie with plastic wrap if not serving right away.", + "url": "https://www.kitchentreaty.com/dairy-free-impossible-pumpkin-pie/#wprm-recipe-32856-step-0-6" + } + ], + "aggregateRating": { + "@type": "AggregateRating", + "ratingValue": "5", + "ratingCount": "4" + }, + "recipeCategory": [ + "Dessert" + ], + "recipeCuisine": [ + "American" + ], + "keywords": "pie", + "nutrition": { + "@type": "NutritionInformation", + "calories": "231 kcal", + "sugarContent": "14 g", + "sodiumContent": "239 mg", + "fatContent": "14 g", + "saturatedFatContent": "11 g", + "carbohydrateContent": "23 g", + "fiberContent": "1 g", + "proteinContent": "5 g", + "cholesterolContent": "82 mg", + "servingSize": "1 serving" + }, + "url": "https://www.kitchentreaty.com/dairy-free-impossible-pumpkin-pie/" +} \ No newline at end of file diff --git a/mealie/test/data/recipes-raw/how-to-make-instant-pot-spaghetti.json b/mealie/test/data/recipes-raw/how-to-make-instant-pot-spaghetti.json new file mode 100644 index 000000000..eb9837a5a --- /dev/null +++ b/mealie/test/data/recipes-raw/how-to-make-instant-pot-spaghetti.json @@ -0,0 +1,121 @@ +{ + "@context": "http://schema.org/", + "@type": "Recipe", + "name": "How to Make Instant Pot Spaghetti", + "author": { + "@type": "Person", + "name": "Karlynn Johnston" + }, + "description": "This Instant Pot Spaghetti recipe is literally the best one out there and it's thanks to one ( or two!) secret ingredients!", + "datePublished": "2020-09-15T13:00:52+00:00", + "image": [ + "https://www.thekitchenmagpie.com/wp-content/uploads/images/2018/02/instantpotspaghetti.jpg", + "https://www.thekitchenmagpie.com/wp-content/uploads/images/2018/02/instantpotspaghetti-500x500.jpg", + "https://www.thekitchenmagpie.com/wp-content/uploads/images/2018/02/instantpotspaghetti-500x375.jpg", + "https://www.thekitchenmagpie.com/wp-content/uploads/images/2018/02/instantpotspaghetti-480x270.jpg" + ], + "recipeYield": [ + "4" + ], + "prepTime": "PT15M", + "cookTime": "PT7M", + "totalTime": "PT22M", + "recipeIngredient": [ + "1 tablespoon olive oil", + "1 cup white onion (diced)", + "1 tablespoon fresh minced garlic", + "1 pound lean ground beef", + "2 teaspoons Italian seasoning mix", + "one 16 ounce package uncooked white flour spaghetti noodles (cooking time for al dente needs to be 9-10 minutes! )", + "one 750 millilitre jar of 4 cheese spaghetti sauce", + "one 15 ounce can diced tomatoes", + "3 cups weak beef broth (divided)", + "1/2 teaspoon salt (( to taste))", + "1/2 teaspoon black pepper", + "1/2 teaspoon white sugar (to cut the acidity of the tomatoes )" + ], + "recipeInstructions": [ + { + "@type": "HowToStep", + "text": "Press the \"saute\" button on your Instant Pot. Add in the olive oil and heat. Once it's heated, add in the white onion. Saute until the onion is soft and translucent. Add in the garlic and fry for 2-3 minutes.", + "name": "Press the \"saute\" button on your Instant Pot. Add in the olive oil and heat. Once it's heated, add in the white onion. Saute until the onion is soft and translucent. Add in the garlic and fry for 2-3 minutes.", + "url": "https://www.thekitchenmagpie.com/how-to-make-instant-pot-spaghetti/#wprm-recipe-44488-step-0-0", + "image": "https://www.thekitchenmagpie.com/wp-content/uploads/images/2018/02/fryinggroundbeefandonionsinaninsantpot.jpg" + }, + { + "@type": "HowToStep", + "text": "Add in the ground beef and fry , stirring constantly, until it's no longer pink. Press the Cancel button to turn off the Instant Pot heating element. Drain the fat (keeping some for flavour if wanted).", + "name": "Add in the ground beef and fry , stirring constantly, until it's no longer pink. Press the Cancel button to turn off the Instant Pot heating element. Drain the fat (keeping some for flavour if wanted).", + "url": "https://www.thekitchenmagpie.com/how-to-make-instant-pot-spaghetti/#wprm-recipe-44488-step-0-1" + }, + { + "@type": "HowToStep", + "text": "Add in 1 cup of the beef broth, mixing it in with the ground beef on the bottom.", + "name": "Add in 1 cup of the beef broth, mixing it in with the ground beef on the bottom.", + "url": "https://www.thekitchenmagpie.com/how-to-make-instant-pot-spaghetti/#wprm-recipe-44488-step-0-2" + }, + { + "@type": "HowToStep", + "text": "Break the spaghetti noodles in half. Place in random, different criss-cross patterns on top of the beef/ beef broth mixture. You are trying to created space between the noodles to try and prevent sticking.", + "name": "Break the spaghetti noodles in half. Place in random, different criss-cross patterns on top of the beef/ beef broth mixture. You are trying to created space between the noodles to try and prevent sticking.", + "url": "https://www.thekitchenmagpie.com/how-to-make-instant-pot-spaghetti/#wprm-recipe-44488-step-0-3", + "image": "https://www.thekitchenmagpie.com/wp-content/uploads/images/2018/02/howtobreakspaghettinoodlesfortheinstantpot.jpg" + }, + { + "@type": "HowToStep", + "text": "In a large bowl or large glass spouted measuring glass, combine the remaining beef broth, tomatoes, 4 cheese sauce, Italian seasoning, salt, pepper and dash of white sugar. ", + "name": "In a large bowl or large glass spouted measuring glass, combine the remaining beef broth, tomatoes, 4 cheese sauce, Italian seasoning, salt, pepper and dash of white sugar. ", + "url": "https://www.thekitchenmagpie.com/how-to-make-instant-pot-spaghetti/#wprm-recipe-44488-step-0-4" + }, + { + "@type": "HowToStep", + "text": "Pout the liquid mixture on top of the pasta, around the sides, making sure you coat everything. Take a wooden spoon and gently push down on the spaghetti noodles, making sure that they are all underneath the liquid.", + "name": "Pout the liquid mixture on top of the pasta, around the sides, making sure you coat everything. Take a wooden spoon and gently push down on the spaghetti noodles, making sure that they are all underneath the liquid.", + "url": "https://www.thekitchenmagpie.com/how-to-make-instant-pot-spaghetti/#wprm-recipe-44488-step-0-5" + }, + { + "@type": "HowToStep", + "text": "Press the Manual Button ( you are going to use high pressure) and set for 7 minutes. Listen to make sure that it seals.", + "name": "Press the Manual Button ( you are going to use high pressure) and set for 7 minutes. Listen to make sure that it seals.", + "url": "https://www.thekitchenmagpie.com/how-to-make-instant-pot-spaghetti/#wprm-recipe-44488-step-0-6" + }, + { + "@type": "HowToStep", + "text": "When it's done, release the valve manually ( see the link in my suggestions in the post above). Stir the spaghetti, breaking up any noodles that stuck together. Let it sit for a few minutes, soaking up the extra liquid.", + "name": "When it's done, release the valve manually ( see the link in my suggestions in the post above). Stir the spaghetti, breaking up any noodles that stuck together. Let it sit for a few minutes, soaking up the extra liquid.", + "url": "https://www.thekitchenmagpie.com/how-to-make-instant-pot-spaghetti/#wprm-recipe-44488-step-0-7", + "image": "https://www.thekitchenmagpie.com/wp-content/uploads/images/2018/02/instantpotspaghetti3.jpg" + } + ], + "aggregateRating": { + "@type": "AggregateRating", + "ratingValue": "5", + "ratingCount": "15" + }, + "recipeCategory": [ + "supper" + ], + "recipeCuisine": [ + "American" + ], + "keywords": "Instant Pot Spaghetti", + "nutrition": { + "@type": "NutritionInformation", + "calories": "222 kcal", + "carbohydrateContent": "5 g", + "proteinContent": "27 g", + "fatContent": "9 g", + "saturatedFatContent": "3 g", + "cholesterolContent": "70 mg", + "sodiumContent": "699 mg", + "fiberContent": "1 g", + "sugarContent": "2 g", + "servingSize": "1 serving" + }, + "@id": "https://www.thekitchenmagpie.com/how-to-make-instant-pot-spaghetti/#recipe", + "isPartOf": { + "@id": "https://www.thekitchenmagpie.com/how-to-make-instant-pot-spaghetti/#article" + }, + "mainEntityOfPage": "https://www.thekitchenmagpie.com/how-to-make-instant-pot-spaghetti/#webpage", + "url": "https://www.thekitchenmagpie.com/how-to-make-instant-pot-spaghetti/" +} \ No newline at end of file diff --git a/mealie/test/data/recipes-raw/instant-pot-chicken-and-potatoes.json b/mealie/test/data/recipes-raw/instant-pot-chicken-and-potatoes.json new file mode 100644 index 000000000..5793e48e7 --- /dev/null +++ b/mealie/test/data/recipes-raw/instant-pot-chicken-and-potatoes.json @@ -0,0 +1,82 @@ +{ + "@context": "http://schema.org/", + "@type": "Recipe", + "name": "Instant Pot Chicken and Potatoes", + "author": { + "@type": "Person", + "name": "Tiffany" + }, + "description": "This is hands down the BEST Instant Pot Chicken and Potatoes recipe you'll ever try. Juicy ranch-seasoned chicken breast and parmesan potatoes cooked in 30 minutes in your pressure cooker - it doesn't get easier than this! ", + "datePublished": "2018-10-26T07:00:51+00:00", + "image": [ + "https://www.lecremedelacrumb.com/wp-content/uploads/2018/10/instant-pot-chicken-potatoes-2.jpg", + "https://www.lecremedelacrumb.com/wp-content/uploads/2018/10/instant-pot-chicken-potatoes-2-500x500.jpg", + "https://www.lecremedelacrumb.com/wp-content/uploads/2018/10/instant-pot-chicken-potatoes-2-500x375.jpg", + "https://www.lecremedelacrumb.com/wp-content/uploads/2018/10/instant-pot-chicken-potatoes-2-480x270.jpg" + ], + "recipeYield": [ + "4", + "4 people" + ], + "prepTime": "PT10M", + "cookTime": "PT15M", + "totalTime": "PT40M", + "recipeIngredient": [ + "4 boneless skinless chicken breasts", + "2 pounds baby red or gold potatoes", + "3 tablespoons olive oil", + "1 1/2 teaspoons salt (or to taste)", + "1/2 teaspoon pepper (or to taste)", + "1 teaspoon garlic powder", + "1 teaspoon dried thyme", + "1/2 teaspoon dried basil", + "1/2 teaspoon dried oregano", + "2 tablespoons + 2 teaspoons dry Ranch seasoning (divided)", + "1 cup chicken broth", + "3 tablespoons grated parmesan cheese" + ], + "recipeInstructions": [ + { + "@type": "HowToStep", + "text": "In a large bowl toss chicken and potatoes in the olive oil, then season with salt and pepper. Stir together garlic powder, thyme, basil, oregano, and 2 tablespoons of the Ranch seasoning. Sprinkle over the chicken and potatoes, tossing to distribute the ingredients as evenly as possible. ", + "name": "In a large bowl toss chicken and potatoes in the olive oil, then season with salt and pepper. Stir together garlic powder, thyme, basil, oregano, and 2 tablespoons of the Ranch seasoning. Sprinkle over the chicken and potatoes, tossing to distribute the ingredients as evenly as possible. ", + "url": "https://www.lecremedelacrumb.com/instant-pot-chicken-and-potatoes/#wprm-recipe-22284-step-0-0" + }, + { + "@type": "HowToStep", + "text": "Add chicken broth to the instant pot/pressure cooker, then place chicken in the broth, and top with the potatoes. Place the lid on in the locked position and turn the vent to the sealed position. Set pressure cooker to \"pressure cook\" for 15 minutes.", + "name": "Add chicken broth to the instant pot/pressure cooker, then place chicken in the broth, and top with the potatoes. Place the lid on in the locked position and turn the vent to the sealed position. Set pressure cooker to \"pressure cook\" for 15 minutes.", + "url": "https://www.lecremedelacrumb.com/instant-pot-chicken-and-potatoes/#wprm-recipe-22284-step-0-1" + }, + { + "@type": "HowToStep", + "text": "Once the cook time is finished, do a \"quick release\" by turning the vent to the venting position. Once float valve has dropped, remove the lid. Drain the pressure cooker or use a slotted spoon to transfer chicken and potatoes to a large platter. ", + "name": "Once the cook time is finished, do a \"quick release\" by turning the vent to the venting position. Once float valve has dropped, remove the lid. Drain the pressure cooker or use a slotted spoon to transfer chicken and potatoes to a large platter. ", + "url": "https://www.lecremedelacrumb.com/instant-pot-chicken-and-potatoes/#wprm-recipe-22284-step-0-2" + }, + { + "@type": "HowToStep", + "text": "Sprinkle with Ranch seasoning and parmesan cheese and garnish with chopped thyme or parsley if desired before serving. ", + "name": "Sprinkle with Ranch seasoning and parmesan cheese and garnish with chopped thyme or parsley if desired before serving. ", + "url": "https://www.lecremedelacrumb.com/instant-pot-chicken-and-potatoes/#wprm-recipe-22284-step-0-3" + } + ], + "aggregateRating": { + "@type": "AggregateRating", + "ratingValue": "4.76", + "ratingCount": "225" + }, + "recipeCategory": [ + "Main Course" + ], + "recipeCuisine": [ + "American" + ], + "keywords": "Chicken, healthy, instant pot, mashed potatoes, pressure cooker, ranch", + "@id": "https://www.lecremedelacrumb.com/instant-pot-chicken-and-potatoes/#recipe", + "isPartOf": { + "@id": "https://www.lecremedelacrumb.com/instant-pot-chicken-and-potatoes/#article" + }, + "mainEntityOfPage": "https://www.lecremedelacrumb.com/instant-pot-chicken-and-potatoes/#webpage", + "url": "https://www.lecremedelacrumb.com/instant-pot-chicken-and-potatoes/" +} \ No newline at end of file diff --git a/mealie/test/data/recipes-raw/instant-pot-kerala-vegetable-stew.json b/mealie/test/data/recipes-raw/instant-pot-kerala-vegetable-stew.json new file mode 100644 index 000000000..7cc743510 --- /dev/null +++ b/mealie/test/data/recipes-raw/instant-pot-kerala-vegetable-stew.json @@ -0,0 +1,166 @@ +{ + "@context": "http://schema.org/", + "@type": "Recipe", + "name": "How to make Instant Pot Kerala Vegetable Stew", + "author": { + "@type": "Person", + "name": "Dhwani" + }, + "description": "Instant Pot Kerala Vegetable Stew - A complete Comfort and Satisfying food that can be made in a fraction of the time. Veg Stew is Vegetarian / Vegan Instant Pot Recipe with lots of vegetables in coconut milk based hearty sauce that will change your life.", + "datePublished": "2019-01-16T19:25:09+00:00", + "image": [ + "https://cdn.cookingcarnival.com/wp-content/uploads/2019/01/Instant-Pot-Kerala-Vegetable-Stew-2.jpg", + "https://cdn.cookingcarnival.com/wp-content/uploads/2019/01/Instant-Pot-Kerala-Vegetable-Stew-2-500x500.jpg", + "https://cdn.cookingcarnival.com/wp-content/uploads/2019/01/Instant-Pot-Kerala-Vegetable-Stew-2-500x375.jpg", + "https://cdn.cookingcarnival.com/wp-content/uploads/2019/01/Instant-Pot-Kerala-Vegetable-Stew-2-480x270.jpg" + ], + "video": { + "name": "Instant Pot Kerala Vegetable Stew | Vegan stew recipe | vegetable Stew recipe in instant Pot", + "description": "Instant Pot Kerala Vegetable Stew - A complete Comfort and Satisfying food that can be made in a fraction of the time. Veg Stew is Vegetarian / Vegan Instant Pot Recipe with lots of vegetables in coconut milk based hearty sauce that will change your life.\n\nDetailed Recipe of Instant pot Kerela vegetable stew https://www.cookingcarnival.com/instant-pot-kerala-vegetable-stew/\nOfficial Facebook Page: https://www.facebook.com/cookingcarnival\n\nPinterest: https://www.pinterest.com/cookingcarnival/\n\nTwitter: https://twitter.com/carnivalcooking\n\nGoogle Plus: https://plus.google.com/+Cookingcarnival\n\nInstagram: https://www.instagram.com/cookingcarnival", + "uploadDate": "2019-01-17T19:46:14+00:00", + "duration": "PT2M17S", + "thumbnailUrl": "https://i.ytimg.com/vi/pej98AtiBWE/hqdefault.jpg", + "contentUrl": "https://youtu.be/pej98AtiBWE", + "embedUrl": "https://www.youtube.com/embed/pej98AtiBWE?feature=oembed", + "@type": "VideoObject" + }, + "recipeYield": [ + "4", + "4 people" + ], + "prepTime": "PT10M", + "cookTime": "PT10M", + "totalTime": "PT20M", + "recipeIngredient": [ + "2 cups - Cauliflower florets", + "1 cup - Chopped carrots", + "1 1/2 cup - Bell Peppers (chopped)", + "2 cups - Potatoes (Chopped)", + "3/4 cup - Chopped Onions", + "1 cup - Green beans (Chopped)", + "1 tsp - Ginger paste", + "1/2 tsp - Chili ((adust according to your liking) )", + "1 tsp - Garlic paste", + "2 - Cardamom Pods (See Notes)", + "1 inch - Cinnamon stick", + "3 - Cloves", + "20 Pieces - Whole Cashew Nuts", + "1 cup - Coconut milk (See Notes)", + "Salt to taste", + "1/2 tsp - White Pepper Powder (see notes)", + "2 tbsp - Oil (See Notes)", + "2 cups - Water" + ], + "recipeInstructions": [ + { + "@type": "HowToStep", + "text": "Turn on saute button of your IP.", + "name": "Turn on saute button of your IP.", + "url": "https://www.cookingcarnival.com/instant-pot-kerala-vegetable-stew/#wprm-recipe-8126-step-0-0" + }, + { + "@type": "HowToStep", + "text": "Heat oil in a pot, add cardamom pods, cinnamon stick, and cloves.", + "name": "Heat oil in a pot, add cardamom pods, cinnamon stick, and cloves.", + "url": "https://www.cookingcarnival.com/instant-pot-kerala-vegetable-stew/#wprm-recipe-8126-step-0-1" + }, + { + "@type": "HowToStep", + "text": "Now add ginger, garlic, chili and onions. Saute for few seconds.", + "name": "Now add ginger, garlic, chili and onions. Saute for few seconds.", + "url": "https://www.cookingcarnival.com/instant-pot-kerala-vegetable-stew/#wprm-recipe-8126-step-0-2" + }, + { + "@type": "HowToStep", + "text": "Add all the vegetables, salt, white pepper powder and water. Mix well.", + "name": "Add all the vegetables, salt, white pepper powder and water. Mix well.", + "url": "https://www.cookingcarnival.com/instant-pot-kerala-vegetable-stew/#wprm-recipe-8126-step-0-3" + }, + { + "@type": "HowToStep", + "text": "Cover your Instant pot with locking lid.", + "name": "Cover your Instant pot with locking lid.", + "url": "https://www.cookingcarnival.com/instant-pot-kerala-vegetable-stew/#wprm-recipe-8126-step-0-4" + }, + { + "@type": "HowToStep", + "text": "Turn off IP.", + "name": "Turn off IP.", + "url": "https://www.cookingcarnival.com/instant-pot-kerala-vegetable-stew/#wprm-recipe-8126-step-0-5" + }, + { + "@type": "HowToStep", + "text": "Press the manual or pressure cook button. Cook on high pressure for 3 minutes with pressure valve in the sealing position.", + "name": "Press the manual or pressure cook button. Cook on high pressure for 3 minutes with pressure valve in the sealing position.", + "url": "https://www.cookingcarnival.com/instant-pot-kerala-vegetable-stew/#wprm-recipe-8126-step-0-6" + }, + { + "@type": "HowToStep", + "text": "Meanwhile, take whole cashew and coconut milk in a blender jar and blend them well in to a smooth paste. Keep it aside.", + "name": "Meanwhile, take whole cashew and coconut milk in a blender jar and blend them well in to a smooth paste. Keep it aside.", + "url": "https://www.cookingcarnival.com/instant-pot-kerala-vegetable-stew/#wprm-recipe-8126-step-0-7" + }, + { + "@type": "HowToStep", + "text": "Once IP beeps and when you see LO:00, turn off your IP and quick release the pressure.", + "name": "Once IP beeps and when you see LO:00, turn off your IP and quick release the pressure.", + "url": "https://www.cookingcarnival.com/instant-pot-kerala-vegetable-stew/#wprm-recipe-8126-step-0-8" + }, + { + "@type": "HowToStep", + "text": "10. Open the Instant Pot, add prepared cashew-coconut paste. Stir well.", + "name": "10. Open the Instant Pot, add prepared cashew-coconut paste. Stir well.", + "url": "https://www.cookingcarnival.com/instant-pot-kerala-vegetable-stew/#wprm-recipe-8126-step-0-9" + }, + { + "@type": "HowToStep", + "text": "11. Turn on saute button and cook it for 1 to 2 more minutes, until everything well combined.", + "name": "11. Turn on saute button and cook it for 1 to 2 more minutes, until everything well combined.", + "url": "https://www.cookingcarnival.com/instant-pot-kerala-vegetable-stew/#wprm-recipe-8126-step-0-10" + }, + { + "@type": "HowToStep", + "text": "12. Switch off the IP.", + "name": "12. Switch off the IP.", + "url": "https://www.cookingcarnival.com/instant-pot-kerala-vegetable-stew/#wprm-recipe-8126-step-0-11" + }, + { + "@type": "HowToStep", + "text": "13. Instant Pot Kerala Vegetable Stew is ready. Enjoy!!", + "name": "13. Instant Pot Kerala Vegetable Stew is ready. Enjoy!!", + "url": "https://www.cookingcarnival.com/instant-pot-kerala-vegetable-stew/#wprm-recipe-8126-step-0-12" + } + ], + "aggregateRating": { + "@type": "AggregateRating", + "ratingValue": "4.88", + "ratingCount": "8" + }, + "recipeCategory": [ + "Main Course", + "Soups and Stew" + ], + "recipeCuisine": [ + "American", + "Indian" + ], + "keywords": "Easy vegan instant pot recipe, Instant pot, Vegetable Stew", + "nutrition": { + "@type": "NutritionInformation", + "servingSize": "1 person", + "calories": "201 kcal", + "carbohydrateContent": "24 g", + "proteinContent": "4 g", + "fatContent": "10 g", + "saturatedFatContent": "1 g", + "sodiumContent": "56 mg", + "fiberContent": "5 g", + "sugarContent": "9 g" + }, + "@id": "https://www.cookingcarnival.com/instant-pot-kerala-vegetable-stew/#recipe", + "isPartOf": { + "@id": "https://www.cookingcarnival.com/instant-pot-kerala-vegetable-stew/#article" + }, + "mainEntityOfPage": "https://www.cookingcarnival.com/instant-pot-kerala-vegetable-stew/#webpage", + "url": "https://www.cookingcarnival.com/instant-pot-kerala-vegetable-stew/" +} \ No newline at end of file diff --git a/mealie/test/data/recipes-raw/jalapeno-popper-dip.json b/mealie/test/data/recipes-raw/jalapeno-popper-dip.json new file mode 100644 index 000000000..867ab26ac --- /dev/null +++ b/mealie/test/data/recipes-raw/jalapeno-popper-dip.json @@ -0,0 +1,89 @@ +{ + "@context": "http://schema.org/", + "@type": "Recipe", + "name": "Jalape\u00f1o Popper Dip", + "author": { + "@type": "Person", + "name": "Michelle" + }, + "description": "Jalapeno Popper Dip is creamy, cheesy and has just the perfect amount of kick. Great appetizer for your next party or watching the big game!", + "datePublished": "2016-02-22T00:01:37+00:00", + "image": [ + "https://www.browneyedbaker.com/wp-content/uploads/2011/08/jalapeno-popper-dip-6-600.jpg" + ], + "recipeYield": [ + "10", + "10 to 12 servings" + ], + "prepTime": "PT15M", + "cookTime": "PT30M", + "totalTime": "PT45M", + "recipeIngredient": [ + "16 ounces cream cheese (at room temperature)", + "1 cup mayonnaise", + "8 pieces of bacon (cooked and chopped)", + "6 jalape\u00f1os (seeded and minced (if you can't get fresh, substitute a 4-ounce can diced jalape\u00f1o peppers, drained))", + "2 cloves garlic (minced)", + "\u00bd teaspoon cumin", + "6 ounces cheddar cheese (shredded (about 1\u00bd cups))", + "1 cup panko breadcrumbs", + "1 cup grated Parmesan cheese", + "4 tablespoons unsalted butter, melted" + ], + "recipeInstructions": [ + { + "@type": "HowToStep", + "text": "Preheat oven to 375 degrees F.", + "name": "Preheat oven to 375 degrees F.", + "url": "https://www.browneyedbaker.com/jalapeno-popper-dip/#wprm-recipe-44993-step-0-0" + }, + { + "@type": "HowToStep", + "text": "Combine the cream cheese, mayonnaise, bacon, jalapenos, garlic, cumin and cheddar cheese in a mixing bowl. Transfer the mixture into 2-quart baking dish.", + "name": "Combine the cream cheese, mayonnaise, bacon, jalapenos, garlic, cumin and cheddar cheese in a mixing bowl. Transfer the mixture into 2-quart baking dish.", + "url": "https://www.browneyedbaker.com/jalapeno-popper-dip/#wprm-recipe-44993-step-0-1" + }, + { + "@type": "HowToStep", + "text": "Combine the panko breadcrumbs, Parmesan cheese and melted butter in a small bowl, tossing with a fork until the mixture is evenly moistened. Sprinkle evenly over the cream cheese mixture.", + "name": "Combine the panko breadcrumbs, Parmesan cheese and melted butter in a small bowl, tossing with a fork until the mixture is evenly moistened. Sprinkle evenly over the cream cheese mixture.", + "url": "https://www.browneyedbaker.com/jalapeno-popper-dip/#wprm-recipe-44993-step-0-2" + }, + { + "@type": "HowToStep", + "text": "Bake in the preheated oven for 25 to 30 minutes, until the top is golden brown and the dip is bubbling. Let rest for 5 minutes before serving. Serve with your favorite tortilla chips, crackers, vegetables, etc.", + "name": "Bake in the preheated oven for 25 to 30 minutes, until the top is golden brown and the dip is bubbling. Let rest for 5 minutes before serving. Serve with your favorite tortilla chips, crackers, vegetables, etc.", + "url": "https://www.browneyedbaker.com/jalapeno-popper-dip/#wprm-recipe-44993-step-0-3" + } + ], + "aggregateRating": { + "@type": "AggregateRating", + "ratingValue": "4.34", + "ratingCount": "15" + }, + "recipeCategory": [ + "Appetizer" + ], + "recipeCuisine": [ + "American" + ], + "keywords": "cheese dip, game day food, party food", + "nutrition": { + "@type": "NutritionInformation", + "calories": "560 kcal", + "carbohydrateContent": "7 g", + "proteinContent": "14 g", + "fatContent": "52 g", + "saturatedFatContent": "21 g", + "cholesterolContent": "109 mg", + "sodiumContent": "707 mg", + "sugarContent": "2 g", + "servingSize": "1 serving" + }, + "@id": "https://www.browneyedbaker.com/jalapeno-popper-dip/#recipe", + "isPartOf": { + "@id": "https://www.browneyedbaker.com/jalapeno-popper-dip/#article" + }, + "mainEntityOfPage": "https://www.browneyedbaker.com/jalapeno-popper-dip/#webpage", + "url": "https://www.browneyedbaker.com/jalapeno-popper-dip/" +} \ No newline at end of file diff --git a/mealie/test/data/recipes-raw/microwave_sweet_potatoes_04783.json b/mealie/test/data/recipes-raw/microwave_sweet_potatoes_04783.json new file mode 100644 index 000000000..74a07ae91 --- /dev/null +++ b/mealie/test/data/recipes-raw/microwave_sweet_potatoes_04783.json @@ -0,0 +1,53 @@ +{ + "@context": "https://schema.org", + "@type": "Recipe", + "aggregateRating": { + "ratingCount": 3, + "ratingValue": 4 + }, + "author": { + "@type": "Person", + "name": "Justine Pattison" + }, + "cookTime": "PT10M", + "description": "Microwave jacket sweet potatoes make a wonderfully quick and easy meal. Take your pick of these three delicious fillings, or make all of them! The veggie chilli makes enough for four portions, great for lunch tomorrow. The smoked mackerel and pea fillings each make enough for two portions. \r\n\r\nThis recipe was tested using a 900W microwave oven. If your oven has more or fewer watts, you will need to adjust the cooking time.\r\n", + "image": [ + "https://food-images.files.bbci.co.uk/food/recipes/microwave_sweet_potatoes_04783_16x9.jpg" + ], + "keywords": "quick, jacket potato dinners, microwave recipes , quick and cheap dinners, quick delicious lunches, easy family dinners, lunch, student food, Jacket potato, sweet potato, peas, egg free, gluten free, nut free, pregnancy friendly", + "name": "Microwave jacket sweet potato ", + "prepTime": "PT30M", + "recipeCategory": "Main course", + "recipeIngredient": [ + "2 sweet potatoes, washed and dried", + "75g/2\u00bdoz smoked mackerel, skinned and roughly mashed with a fork", + "3 tbsp half-fat cr\u00e8me fra\u00eeche or soured cream", + "2 spring onions, trimmed and thinly sliced", + "\u00bd unwaxed lemon, finely grated zest only", + "freshly ground black pepper ", + "100g/3\u00bdoz frozen peas", + "100g/3\u00bdoz feta ", + "2 tbsp plain yoghurt", + "1 tbsp finely chopped fresh mint", + "freshly ground black pepper ", + "\u00bd red pepper, deseeded and diced", + "400g tin kidney beans in chilli sauce", + "198g tin sweetcorn in water", + "1 tbsp fresh lime juice", + "50g/1\u00beoz mature Cheddar, coarsely grated", + "4 tbsp soured cream", + "fresh coriander, to garnish", + "1 lime, cut into wedges, to serve" + ], + "recipeInstructions": [ + "Prick the sweet potatoes two or three times with a fork and put on a microwaveable plate. Microwave on high for 5\u20136 minutes for one potato or 7\u20138 minutes for two. Test the potatoes are soft by inserting a skewer through the middle, it should slide in easily. If the potatoes remain a little hard, cook for longer, testing again every 30 seconds. Divide the potatoes between plates, make a cross in the centre and open to fill.", + "To make the smoked mackerel filling, mix all the ingredients together and season with lots of black pepper.", + "To make the pea and feta filling, microwave the peas on high for 2\u20133 minutes, until thawed and just warm. Mash them with a fork, until well broken up, then mix in the feta, yoghurt and mint. Season with lots of black pepper.", + "To make the veggie chilli filling, put the red pepper in a large microwavable bowl and cook on high for 1\u00bd\u20132 minutes, until soft. Add the beans and sweetcorn in its water, stir well and microwave on high for 4\u20135 minutes, until hot. Stir in the lime juice and mix well. Spoon into the cooked sweet potatoes and top with the cheese. Microwave for 1\u20132 minutes, until the cheese melts. Top with the soured cream, coriander and lime wedges. " + ], + "recipeYield": "Serves 2", + "suitableForDiet": [ + "http://schema.org/GlutenFreeDiet" + ], + "url": "https://www.bbc.co.uk/food/recipes/microwave_sweet_potatoes_04783" +} \ No newline at end of file diff --git a/mealie/test/data/recipes-raw/moroccan-skirt-steak-with-roasted-pepper-couscous.json b/mealie/test/data/recipes-raw/moroccan-skirt-steak-with-roasted-pepper-couscous.json new file mode 100644 index 000000000..d25ee93d2 --- /dev/null +++ b/mealie/test/data/recipes-raw/moroccan-skirt-steak-with-roasted-pepper-couscous.json @@ -0,0 +1,243 @@ +{ + "@context": "http://schema.org", + "@type": "Recipe", + "mainEntityOfPage": "http://www.eatingwell.com/recipe/249961/moroccan-skirt-steak-with-roasted-pepper-couscous/", + "name": "Moroccan Skirt Steak with Roasted Pepper Couscous", + "image": { + "@type": "ImageObject", + "url": "https://imagesvc.meredithcorp.io/v3/mm/image?url=https%3A%2F%2Fstatic.onecms.io%2Fwp-content%2Fuploads%2Fsites%2F44%2F2019%2F08%2F26231251%2F3757257.jpg", + "width": 960, + "height": 960 + }, + "datePublished": "2016-06-03T04:27:31.000Z", + "description": "Thin cuts of beef, such as skirt steak or sirloin steak, cook very quickly when seared in a hot skillet--just right for a busy weeknight. We love how the spicy Moroccan flavors on the steak complement the sweet, roasted pepper-studded couscous. Serve with: Arugula salad and a glass of Pinot Noir.", + "prepTime": null, + "cookTime": null, + "totalTime": "P0DT0H35M", + "recipeIngredient": [ + "2 medium bell peppers", + "1 teaspoon ground cumin", + "1 teaspoon ground coriander", + "\u00be teaspoon salt", + "\u00bd teaspoon ground turmeric", + "\u00bd teaspoon ground cinnamon", + "\u00bd teaspoon freshly ground pepper", + "1 whole lemon, plus more lemon wedges for garnish", + "1 tablespoon 1 teaspoon plus 1 tablespoon extra-virgin olive oil, divided", + "\u2154 cup whole-wheat couscous", + "1 pound 1 pound skirt steak (see Note) or sirloin steak, 3/4 to 1 inch thick, trimmed", + "2 tablespoons chopped green olives" + ], + "recipeInstructions": [ + { + "@type": "HowToStep", + "text": "Position rack in upper third of oven; preheat broiler.\n" + }, + { + "@type": "HowToStep", + "text": "Place bell peppers on a baking sheet and roast under the broiler, turning every 5 minutes, until charred and softened, 10 to 15 minutes. Transfer to a clean cutting board; when cool enough to handle, chop the peppers into bite-size pieces.\n" + }, + { + "@type": "HowToStep", + "text": "Meanwhile, combine cumin, coriander, salt, turmeric, cinnamon and pepper in a small bowl. Grate 1/2 teaspoon zest from the lemon. Juice the lemon into a 1-cup measure and add enough water to make 1 cup. Pour into a small saucepan and add the lemon zest, 1 teaspoon of the spice mixture and 1 teaspoon olive oil. Bring to a boil. Stir in couscous, cover, remove from heat and let stand.\n" + }, + { + "@type": "HowToStep", + "text": "Heat the remaining 1 tablespoon oil in a large skillet (preferably cast-iron) over medium heat until shimmering (but not smoking). Rub the remaining spice mixture on both sides of steak. Cook the steak 2 to 3 minutes per side for medium-rare. Let rest on the cutting board for 5 minutes. Stir olives and the peppers into the couscous. Thinly slice the steak and serve with the couscous and lemon wedges, if desired.\n" + } + ], + "recipeCategory": [ + "Healthy Recipes", + "Healthy Ingredient Recipes", + "Healthy Meat & Poultry Recipes", + "Healthy Beef Recipes", + "Healthy Steak Recipes", + "Healthy New York Strip Steak Recipes" + ], + "recipeCuisine": [], + "author": [ + { + "@type": "Person", + "name": "EatingWell Test Kitchen" + } + ], + "aggregateRating": { + "@type": "AggregateRating", + "ratingValue": 4.538461538461538, + "ratingCount": 13, + "itemReviewed": "Moroccan Skirt Steak with Roasted Pepper Couscous", + "bestRating": "5", + "worstRating": "1" + }, + "nutrition": { + "@type": "NutritionInformation", + "calories": "453.7 calories", + "carbohydrateContent": "36 g", + "cholesterolContent": "96.4 mg", + "fatContent": "18.4 g", + "fiberContent": "6.5 g", + "proteinContent": "36.4 g", + "saturatedFatContent": "5.1 g", + "servingSize": null, + "sodiumContent": "663.3 mg", + "sugarContent": null, + "transFatContent": null, + "unsaturatedFatContent": null + }, + "review": [ + { + "@type": "Review", + "datePublished": "2011-10-30T21:53:57Z", + "reviewBody": "Wow! This steak was fabulous. Full of flavor even my kids liked it. The spices for the steak rub get added to the cous cous. Along with the sweet roasted peppers it was so delicious.", + "reviewRating": { + "@type": "Rating", + "worstRating": "1", + "bestRating": "5", + "ratingValue": 5 + }, + "author": { + "@type": "Person", + "name": "shari_martinez@sbcglobal.net", + "image": null, + "sameAs": "https://www.allrecipes.com/cook/18308949/" + } + }, + { + "@type": "Review", + "datePublished": "2011-10-30T02:40:11Z", + "reviewBody": "Not as well received as I had hoped The leftovers were good as a steak salad with honey-mustard seasoning although Kaja did not like the seasoned meat that way. I ate some couscous on my salad too. Leftover steak is unheard of at our house yet there it was. Offerred the kids a choice of unspiced steak (three takers) and Rice-a-Roni (four takers) Austin & I liked the steak Pros: Easy I liked it Cons: Nobody liked the couscous except me", + "reviewRating": { + "@type": "Rating", + "worstRating": "1", + "bestRating": "5", + "ratingValue": 3 + }, + "author": { + "@type": "Person", + "name": "Ellen", + "image": null, + "sameAs": "https://www.allrecipes.com/cook/19797391/" + } + }, + { + "@type": "Review", + "datePublished": "2011-10-30T03:02:54Z", + "reviewBody": "tasty and easy weeknight meal Initially I was a little leery of using cinnamon to season the meat but it actually turned out very good. The seasoning had a tasty and light flavor. I used sirloin because the skirt steak at the store looked very fatty. The couscous (although I didn't add the olives because I'm not a huge fan) was also very good and I enjoyed the sweetness of the bell pepper. I used the lemon garnish to squeeze over the couscous and meat and think it made it. I would definitely make this again! Pros: Tasty and easy weeknight meal", + "reviewRating": { + "@type": "Rating", + "worstRating": "1", + "bestRating": "5", + "ratingValue": 4 + }, + "author": { + "@type": "Person", + "name": "Kat Y", + "image": null, + "sameAs": "https://www.allrecipes.com/cook/2343563/" + } + }, + { + "@type": "Review", + "datePublished": "2011-10-30T17:25:23Z", + "reviewBody": "This was really good. It was definitely a change from what we're used to but in a good way. I had to use skillet steak because I couldn't find any skirt steak but it worked just fine. I also used the grill for the peppers and steak because my broiler is a bit questionable. Other than that I made it as stated and my husband and I really enjoyed it. A great out of the ordinary quick meal.", + "reviewRating": { + "@type": "Rating", + "worstRating": "1", + "bestRating": "5", + "ratingValue": 5 + }, + "author": { + "@type": "Person", + "name": "EatingWell User", + "image": null, + "sameAs": "https://www.allrecipes.com/cook/eatingwelluser/" + } + }, + { + "@type": "Review", + "datePublished": "2011-10-30T03:41:53Z", + "reviewBody": "I love this recipe so much that I schedule it my meal planning as often as possible. The flavors with this cut of meat are just wonderful. I can't eat grains so I pair it with fennel. Perfect!", + "reviewRating": { + "@type": "Rating", + "worstRating": "1", + "bestRating": "5", + "ratingValue": 5 + }, + "author": { + "@type": "Person", + "name": "EatingWell User", + "image": null, + "sameAs": "https://www.allrecipes.com/cook/eatingwelluser/" + } + }, + { + "@type": "Review", + "datePublished": "2013-02-20T17:33:48Z", + "reviewBody": "Great starting point! I like this one a lot but I would recommend the version of it on funnytummycafe.com. I've not been really adventurous with new flavors but this I will repeat! Pros: Wonderful flavors in the rub Cons: The couscous was TOO lemony", + "reviewRating": { + "@type": "Rating", + "worstRating": "1", + "bestRating": "5", + "ratingValue": 3 + }, + "author": { + "@type": "Person", + "name": "EatingWell User", + "image": null, + "sameAs": "https://www.allrecipes.com/cook/eatingwelluser/" + } + }, + { + "@type": "Review", + "datePublished": "2011-10-30T15:06:11Z", + "reviewBody": "I made this with chicken instead of steak and it was great! My husband has not liked couscous in the past but he really liked this version. Will definitely make again!", + "reviewRating": { + "@type": "Rating", + "worstRating": "1", + "bestRating": "5", + "ratingValue": 5 + }, + "author": { + "@type": "Person", + "name": "EatingWell User", + "image": null, + "sameAs": "https://www.allrecipes.com/cook/eatingwelluser/" + } + }, + { + "@type": "Review", + "datePublished": "2011-10-30T12:15:53Z", + "reviewBody": "WOW! Blew me away this was a great meal! Tons of flavor really quick and very filling! I served brown rice instead of couscous because that's what I had on hand. The husband never knew the difference! I can always tell when something is a real winner with him because he goes back for seconds (we're both on diets). I used one red and one green bell pepper and served a spring mix salad with tomato and feta on the side. Big bravo to the EatingWell kitchen! This will definitely be made again in my house!", + "reviewRating": { + "@type": "Rating", + "worstRating": "1", + "bestRating": "5", + "ratingValue": 5 + }, + "author": { + "@type": "Person", + "name": "EatingWell User", + "image": null, + "sameAs": "https://www.allrecipes.com/cook/eatingwelluser/" + } + }, + { + "@type": "Review", + "datePublished": "2011-10-30T15:12:50Z", + "reviewBody": "Quick & easy to prepare with mild spiced flavor. The rub produces a great crust on the steak. I felt the lemon flavor dominated the cous cous though to be fair I did seem to have a particularly juicy lemon so that may be why. My husband and I both thought that the leftover steak would be superb sliced thinly in a sandwich with a mint/yogurt dressing.", + "reviewRating": { + "@type": "Rating", + "worstRating": "1", + "bestRating": "5", + "ratingValue": 5 + }, + "author": { + "@type": "Person", + "name": "EatingWell User", + "image": null, + "sameAs": "https://www.allrecipes.com/cook/eatingwelluser/" + } + } + ], + "url": "http://www.eatingwell.com/recipe/249961/moroccan-skirt-steak-with-roasted-pepper-couscous/" +} \ No newline at end of file diff --git a/mealie/test/test_scraper.py b/mealie/test/test_scraper.py new file mode 100644 index 000000000..af5cbfae9 --- /dev/null +++ b/mealie/test/test_scraper.py @@ -0,0 +1,39 @@ +import json +from pathlib import Path + +import pytest +from services.scrape_services import normalize_data, normalize_instructions + +CWD = Path(__file__).parent +RAW_RECIPE_DIR = CWD.joinpath("data", "recipes-raw") + + +@pytest.mark.parametrize("json_file,num_steps", [ + ("best-homemade-salsa-recipe.json", 2), + ("blue-cheese-stuffed-turkey-meatballs-with-raspberry-balsamic-glaze-2.json", 3), + ("bon_appetit.json", 8), + ("chunky-apple-cake.json", 4), + ("dairy-free-impossible-pumpkin-pie.json", 7), + ("how-to-make-instant-pot-spaghetti.json", 8), + ("instant-pot-chicken-and-potatoes.json", 4), + ("instant-pot-kerala-vegetable-stew.json", 13), + ("jalapeno-popper-dip.json", 4), + ("microwave_sweet_potatoes_04783.json", 4), + ("moroccan-skirt-steak-with-roasted-pepper-couscous.json", 4), + ("Pizza-Knoblauch-Champignon-Paprika-vegan.html.json", 3), +]) +def test_normalize_data(json_file, num_steps): + recipe_data = normalize_data(json.load(open(RAW_RECIPE_DIR.joinpath(json_file)))) + assert len(recipe_data["recipeInstructions"]) == num_steps + + +@pytest.mark.parametrize("instructions", [ + "A\n\nB\n\nC\n\n", + "A\nB\nC\n", + "A\r\n\r\nB\r\n\r\nC\r\n\r\n", + "A\r\nB\r\nC\r\n", + ["A","B","C"], + [{"@type": "HowToStep", "text": x} for x in ["A","B","C"]] +]) +def test_normalize_instructions(instructions): + assert normalize_instructions(instructions) == [{"text": "A"}, {"text": "B"}, {"text": "C"}] diff --git a/mealie/utils/document_utils.py b/mealie/utils/document_utils.py deleted file mode 100644 index 576825f92..000000000 --- a/mealie/utils/document_utils.py +++ /dev/null @@ -1 +0,0 @@ -import datetime diff --git a/mealie/utils/snackbar.py b/mealie/utils/snackbar.py index 6ceb8230a..766bb5fd7 100644 --- a/mealie/utils/snackbar.py +++ b/mealie/utils/snackbar.py @@ -6,7 +6,6 @@ class SnackResponse: if additional_data: snackbar.update(additional_data) - print(snackbar) return snackbar diff --git a/requirements.dev.txt b/requirements.dev.txt index 7e5ba11cc..76a5d3767 100644 --- a/requirements.dev.txt +++ b/requirements.dev.txt @@ -5,6 +5,7 @@ APScheduler==3.6.3 astroid==2.4.2 async-exit-stack==1.0.1 async-generator==1.10 +attrs==20.3.0 beautifulsoup4==4.9.1 black==20.8b1 certifi==2020.6.20 @@ -24,6 +25,7 @@ html-text==0.5.2 html5lib==1.1 httptools==0.1.1 idna==2.10 +iniconfig==1.1.1 isodate==0.6.0 isort==5.4.2 itsdangerous==1.1.0 @@ -36,12 +38,16 @@ mccabe==0.6.1 mf2py==1.1.2 mongoengine==0.21.0 mypy-extensions==0.4.3 +packaging==20.8 pathspec==0.8.0 +pluggy==0.13.1 promise==2.3 +py==1.10.0 pydantic==1.6.1 pylint==2.6.0 pymongo==3.11.1 pyparsing==2.4.7 +pytest==6.2.1 python-dateutil==2.8.1 python-dotenv==0.15.0 python-multipart==0.0.5 @@ -50,7 +56,7 @@ pytz==2020.4 PyYAML==5.3.1 rdflib==4.2.2 rdflib-jsonld==0.5.0 -regex==2020.7.14 +regex==2020.11.13 requests==2.24.0 Rx==1.6.1 scrape-schema-recipe==0.1.1 diff --git a/requirements.txt b/requirements.txt index 3621635d2..b14cf8d73 100644 --- a/requirements.txt +++ b/requirements.txt @@ -37,7 +37,7 @@ jstyleson==0.0.2 lazy-object-proxy==1.4.3 livereload==2.6.3 lunr==0.5.8 -lxml==4.5.2 +lxml>=4.6.2 Markdown==3.3.3 MarkupSafe==1.1.1 mccabe==0.6.1 diff --git a/scratch.json b/scratch.json deleted file mode 100644 index a80b55f1a..000000000 --- a/scratch.json +++ /dev/null @@ -1 +0,0 @@ -// Test Notify \ No newline at end of file