diff --git a/.flake8 b/.flake8 new file mode 100644 index 000000000..f3353121e --- /dev/null +++ b/.flake8 @@ -0,0 +1,22 @@ +[flake8] +extend-ignore = + # Whitespace before ':' - Required for black compatibility + E203, + # Line break occurred before a binary operator - Required for black compatibility + W503, + # Comparison to False should be 'if cond is False:' or 'if not cond:' + E712 +exclude = + .git, + **/__pycache__, + **/.git, + **/.svn, + **/.hg, + **/CVS, + **/.DS_Store, + .vscode, + **/*.pyc +per-file-ignores= + cookbook/apps.py:F401 +max-line-length = 179 + diff --git a/.gitignore b/.gitignore index 143271c6c..c4d38e418 100644 --- a/.gitignore +++ b/.gitignore @@ -47,6 +47,11 @@ docs/reports/** # Django stuff: *.log +mediafiles/ +*.sqlite3* +staticfiles/ +postgresql/ +data/ # Sphinx documentation docs/_build/ @@ -59,32 +64,23 @@ target/ \.idea/dataSources\.local\.xml -venv/ - -mediafiles/ - -*.sqlite3* - \.idea/workspace\.xml \.idea/misc\.xml # Deployment - \.env -staticfiles/ -postgresql/ -data/ - +cookbook/static/vue +vue/webpack-stats.json /docker-compose.override.yml vue/node_modules plugins -vetur.config.js -cookbook/static/vue -vue/webpack-stats.json +vue3/node_modules cookbook/templates/sw.js -.prettierignore vue/.yarn vue3/.vite -vue3/node_modules + +# Configs +vetur.config.js +venv/ diff --git a/.idea/watcherTasks.xml b/.idea/watcherTasks.xml new file mode 100644 index 000000000..0976eb2bc --- /dev/null +++ b/.idea/watcherTasks.xml @@ -0,0 +1,31 @@ + + + + + + + + \ No newline at end of file diff --git a/.prettierignore b/.prettierignore new file mode 100644 index 000000000..aea0c3a51 --- /dev/null +++ b/.prettierignore @@ -0,0 +1,13 @@ +# generated files +api.ts +vue/src/apps/*.js +vue/node_modules +staticfiles/ +docs/reports/ +/vue3/src/openapi/ + +# ignored files - prettier interferes with django templates and github actions +*.html +*.yml +*.yaml + diff --git a/.prettierrc b/.prettierrc new file mode 100644 index 000000000..956e17f3c --- /dev/null +++ b/.prettierrc @@ -0,0 +1,7 @@ +{ + "printWidth": 179, + "trailingComma": "es5", + "tabWidth": 2, + "semi": false, + "experimentalTernaries": true +} diff --git a/.vscode/settings.json b/.vscode/settings.json index 1f900399c..c3e6602d3 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -3,5 +3,12 @@ "cookbook/tests" ], "python.testing.unittestEnabled": false, - "python.testing.pytestEnabled": true -} \ No newline at end of file + "python.testing.pytestEnabled": true, + "editor.formatOnSave": true, + "editor.defaultFormatter": "esbenp.prettier-vscode", + "[python]": { + "editor.defaultFormatter": "eeyore.yapf", + }, + "yapf.args": [], + "isort.args": [] +} diff --git a/Dockerfile b/Dockerfile index 3f17d0f11..c5856bcfa 100644 --- a/Dockerfile +++ b/Dockerfile @@ -6,6 +6,8 @@ RUN apk add --no-cache postgresql-libs postgresql-client gettext zlib libjpeg li #Print all logs without buffering it. ENV PYTHONUNBUFFERED 1 +ENV DOCKER true + #This port will be used by gunicorn. EXPOSE 8080 @@ -33,6 +35,12 @@ RUN apk add --no-cache --virtual .build-deps gcc musl-dev postgresql-dev zlib-de #Copy project and execute it. COPY . ./ +# collect the static files +RUN /opt/recipes/venv/bin/python manage.py collectstatic_js_reverse +RUN /opt/recipes/venv/bin/python manage.py collectstatic --noinput +# copy the collected static files to a different location, so they can be moved into a potentially mounted volume +RUN mv /opt/recipes/staticfiles /opt/recipes/staticfiles-collect + # collect information from git repositories RUN /opt/recipes/venv/bin/python version.py # delete git repositories to reduce image size diff --git a/boot.sh b/boot.sh index ae3dbb51d..c14d66aab 100644 --- a/boot.sh +++ b/boot.sh @@ -67,12 +67,25 @@ echo "Migrating database" python manage.py migrate -echo "Generating static files" +if [[ "${DOCKER}" == "true" ]]; then + if [[ -d "/opt/recipes/staticfiles-collect" ]]; then + echo "Copying cached static files from docker build" -python manage.py collectstatic_js_reverse -python manage.py collectstatic --noinput + mkdir -p /opt/recipes/staticfiles + rm -rf /opt/recipes/staticfiles/* + mv /opt/recipes/staticfiles-collect/* /opt/recipes/staticfiles + rm -rf /opt/recipes/staticfiles-collect + else + echo "Static files are already up to date" + fi +else + echo "Collecting static files, this may take a while..." -echo "Done" + python manage.py collectstatic_js_reverse + python manage.py collectstatic --noinput + + echo "Done" +fi chmod -R 755 /opt/recipes/mediafiles diff --git a/cookbook/admin.py b/cookbook/admin.py index 402d6f3f4..88a1cd79a 100644 --- a/cookbook/admin.py +++ b/cookbook/admin.py @@ -185,7 +185,7 @@ class StepAdmin(admin.ModelAdmin): @admin.display(description="Name") def recipe_and_name(obj): if not obj.recipe_set.exists(): - return f"Orphaned Step{'':s if not obj.name else f': {obj.name}'}" + return f"Orphaned Step{'' if not obj.name else f': {obj.name}'}" return f"{obj.recipe_set.first().name}: {obj.name}" if obj.name else obj.recipe_set.first().name @@ -376,10 +376,17 @@ class ShareLinkAdmin(admin.ModelAdmin): admin.site.register(ShareLink, ShareLinkAdmin) +@admin.action(description='Delete all properties with type') +def delete_properties_with_type(modeladmin, request, queryset): + for pt in queryset: + Property.objects.filter(property_type=pt).delete() + + class PropertyTypeAdmin(admin.ModelAdmin): - search_fields = ('space',) + search_fields = ('name',) list_display = ('id', 'space', 'name', 'fdc_id') + actions = [delete_properties_with_type] admin.site.register(PropertyType, PropertyTypeAdmin) diff --git a/cookbook/helper/recipe_url_import.py b/cookbook/helper/recipe_url_import.py index aa0bb2d87..eca6813aa 100644 --- a/cookbook/helper/recipe_url_import.py +++ b/cookbook/helper/recipe_url_import.py @@ -15,12 +15,9 @@ from cookbook.models import Automation, Keyword, PropertyType def get_from_scraper(scrape, request): - # converting the scrape_me object to the existing json format based on ld+json + # converting the scrape_html object to the existing json format based on ld+json - recipe_json = { - 'steps': [], - 'internal': True - } + recipe_json = {'steps': [], 'internal': True} keywords = [] # assign source URL @@ -157,11 +154,18 @@ def get_from_scraper(scrape, request): # assign steps try: for i in parse_instructions(scrape.instructions()): - recipe_json['steps'].append({'instruction': i, 'ingredients': [], 'show_ingredients_table': request.user.userpreference.show_step_ingredients, }) + recipe_json['steps'].append({ + 'instruction': i, + 'ingredients': [], + 'show_ingredients_table': request.user.userpreference.show_step_ingredients, + }) except Exception: pass if len(recipe_json['steps']) == 0: - recipe_json['steps'].append({'instruction': '', 'ingredients': [], }) + recipe_json['steps'].append({ + 'instruction': '', + 'ingredients': [], + }) recipe_json['description'] = recipe_json['description'][:512] if len(recipe_json['description']) > 256: # split at 256 as long descriptions don't look good on recipe cards @@ -182,20 +186,20 @@ def get_from_scraper(scrape, request): 'original_text': x } if unit: - ingredient['unit'] = {'name': unit, } + ingredient['unit'] = { + 'name': unit, + } recipe_json['steps'][0]['ingredients'].append(ingredient) except Exception: - recipe_json['steps'][0]['ingredients'].append( - { - 'amount': 0, - 'unit': None, - 'food': { - 'name': x, - }, - 'note': '', - 'original_text': x - } - ) + recipe_json['steps'][0]['ingredients'].append({ + 'amount': 0, + 'unit': None, + 'food': { + 'name': x, + }, + 'note': '', + 'original_text': x + }) except Exception: pass @@ -248,14 +252,16 @@ def get_from_youtube_scraper(url, request): 'working_time': 0, 'waiting_time': 0, 'image': "", - 'keywords': [{'name': kw.name, 'label': kw.name, 'id': kw.pk}], + 'keywords': [{ + 'name': kw.name, + 'label': kw.name, + 'id': kw.pk + }], 'source_url': url, - 'steps': [ - { - 'ingredients': [], - 'instruction': '' - } - ] + 'steps': [{ + 'ingredients': [], + 'instruction': '' + }] } try: @@ -452,10 +458,7 @@ def normalize_string(string): def iso_duration_to_minutes(string): - match = re.match( - r'P((?P\d+)Y)?((?P\d+)M)?((?P\d+)W)?((?P\d+)D)?T((?P\d+)H)?((?P\d+)M)?((?P\d+)S)?', - string - ).groupdict() + match = re.match(r'P((?P\d+)Y)?((?P\d+)M)?((?P\d+)W)?((?P\d+)D)?T((?P\d+)H)?((?P\d+)M)?((?P\d+)S)?', string).groupdict() return int(match['days'] or 0) * 24 * 60 + int(match['hours'] or 0) * 60 + int(match['minutes'] or 0) diff --git a/cookbook/helper/scrapers/scrapers.py b/cookbook/helper/scrapers/scrapers.py index 7d6c08b15..0cf01333b 100644 --- a/cookbook/helper/scrapers/scrapers.py +++ b/cookbook/helper/scrapers/scrapers.py @@ -30,7 +30,7 @@ def text_scraper(text, url=None): html=None, url=None, ): - self.wild_mode = False + self.supported_only = False self.meta_http_equiv = False self.soup = BeautifulSoup(html, "html.parser") self.url = url diff --git a/cookbook/tests/other/test_automations.py b/cookbook/tests/other/test_automations.py index 8b7b5568f..810a73f74 100644 --- a/cookbook/tests/other/test_automations.py +++ b/cookbook/tests/other/test_automations.py @@ -4,10 +4,10 @@ import pytest from django.contrib import auth from django.test import RequestFactory from django_scopes import scope +from recipe_scrapers import scrape_html from cookbook.helper.automation_helper import AutomationEngine from cookbook.helper.recipe_url_import import get_from_scraper -from cookbook.helper.scrapers.scrapers import text_scraper from cookbook.models import Automation DATA_DIR = "cookbook/tests/other/test_data/" @@ -73,12 +73,14 @@ def test_unit_automation(u1_s1, arg): assert (automation.apply_unit_automation(arg[0]) == target_name) is True -@pytest.mark.parametrize("arg", [ - [[1, 'egg', 'white'], '', [1, '', 'egg', 'white']], - [[1, 'Egg', 'white'], '', [1, '', 'Egg', 'white']], - [[1, 'êgg', 'white'], '', [1, 'êgg', 'white']], - [[1, 'egg', 'white'], 'whole', [1, 'whole', 'egg', 'white']], -]) +@pytest.mark.parametrize( + "arg", [ + [[1, 'egg', 'white'], '', [1, '', 'egg', 'white']], + [[1, 'Egg', 'white'], '', [1, '', 'Egg', 'white']], + [[1, 'êgg', 'white'], '', [1, 'êgg', 'white']], + [[1, 'egg', 'white'], 'whole', [1, 'whole', 'egg', 'white']], + ] +) def test_never_unit_automation(u1_s1, arg): user = auth.get_user(u1_s1) space = user.userspace_set.first().space @@ -97,13 +99,15 @@ def test_never_unit_automation(u1_s1, arg): ['.*allrecipes.*', True], ['.*google.*', False], ]) -@pytest.mark.parametrize("arg", [ - [Automation.DESCRIPTION_REPLACE], - [Automation.INSTRUCTION_REPLACE], - [Automation.NAME_REPLACE], - [Automation.FOOD_REPLACE], - [Automation.UNIT_REPLACE], -]) +@pytest.mark.parametrize( + "arg", [ + [Automation.DESCRIPTION_REPLACE], + [Automation.INSTRUCTION_REPLACE], + [Automation.NAME_REPLACE], + [Automation.FOOD_REPLACE], + [Automation.UNIT_REPLACE], + ] +) def test_regex_automation(u1_s1, arg, source): user = auth.get_user(u1_s1) space = user.userspace_set.first().space @@ -124,11 +128,13 @@ def test_regex_automation(u1_s1, arg, source): assert (automation.apply_regex_replace_automation(fail, arg[0]) == target) == False -@pytest.mark.parametrize("arg", [ - ['second first', 'first second'], - ['longer string second first longer string', 'longer string first second longer string'], - ['second fails first', 'second fails first'], -]) +@pytest.mark.parametrize( + "arg", [ + ['second first', 'first second'], + ['longer string second first longer string', 'longer string first second longer string'], + ['second fails first', 'second fails first'], + ] +) def test_transpose_automation(u1_s1, arg): user = auth.get_user(u1_s1) space = user.userspace_set.first().space @@ -160,7 +166,7 @@ def test_url_import_regex_replace(u1_s1): else: test_file = os.path.join(os.getcwd(), 'cookbook', 'tests', 'other', 'test_data', recipe) with open(test_file, 'r', encoding='UTF-8') as d: - scrape = text_scraper(text=d.read(), url="https://www.allrecipes.com") + scrape = scrape_html(html=d.read(), org_url="https://testrecipe.test", supported_only=False) with scope(space=space): for t in types: Automation.objects.get_or_create(name=t, type=t, param_1='.*', param_2=find_text, param_3='', created_by=user, space=space) diff --git a/cookbook/views/api.py b/cookbook/views/api.py index a0ceada12..f890caeaa 100644 --- a/cookbook/views/api.py +++ b/cookbook/views/api.py @@ -35,7 +35,7 @@ from django.utils.translation import gettext as _ from django_scopes import scopes_disabled from icalendar import Calendar, Event from oauth2_provider.models import AccessToken -from recipe_scrapers import scrape_me +from recipe_scrapers import scrape_html from recipe_scrapers._exceptions import NoSchemaFoundInWildMode from requests.exceptions import MissingSchema from rest_framework import decorators, status, viewsets @@ -1437,8 +1437,8 @@ class RecipeUrlImportView(APIView): else: try: if validators.url(url, public=True): - scrape = scrape_me(url_path=url, wild_mode=True) - + html = requests.get(url).content + scrape = scrape_html(org_url=url, html=html, supported_only=False) else: return Response({'error': True, 'msg': _('Invalid Url')}, status=status.HTTP_400_BAD_REQUEST) except NoSchemaFoundInWildMode: diff --git a/docs/contribute.md b/docs/contribute.md deleted file mode 100644 index 545ded755..000000000 --- a/docs/contribute.md +++ /dev/null @@ -1,117 +0,0 @@ -If you like this application and want it to improve, feel free to contribute to its development. - -!!! success "Contribution List" - If you help bring this project forward you deserve to be credited for it. - Feel free to add yourself to `CONTRIBUTERS.md` or message me to add you if you have contributed anything. - -## Issues -The most basic but also very important way of contributing is reporting issues and commenting on ideas and feature requests -over at [GitHub issues](https://github.com/vabene1111/recipes/issues). - -Without feedback improvement can't happen, so don't hesitate to say what you want to say. - -## Contributing Code -If you want to contribute bug fixes or small tweaks then your pull requests are always welcome! - -!!! danger "Discuss First!" - If you want to contribute larger features that introduce more complexity to the project please - make sure to **first submit a technical description** outlining what and how you want to do it. - This allows me and the community to give feedback and manage the complexity of the overall - application. If you don't do this please don't be mad if I reject your PR - -!!! info - The dev setup is a little messy as this application combines the best (at least in my opinion) of both Django and Vue.js. - -### Devcontainer Setup -There is a [devcontainer](https://containers.dev) set up to ease development. It is optimized for VSCode, but should be able to -be used by other editors as well. Once the container is running, you can do things like start a Django dev server, start a Vue.js -dev server, run python tests, etc. by either using the VSCode tasks below, or manually running commands described in the individual -technology sections below. - -In VSCode, simply check out the git repository, and then via the command palette, choose `Dev Containers: Reopen in container`. - -If you need to change python dependencies (requierments.txt) or OS packages, you will need to rebuild the container. If you are -changing OS package requirements, you will need to update both the main `Dockerfile` and the `.devcontainer/Dockerfile`. - -### VSCode Tasks -If you use VSCode, there are a number of tasks that are available. Here are a few of the key ones: - -* `Setup Dev Server` - Runs all the prerequisite steps so that the dev server can be run inside VSCode. -* `Setup Tests` - Runs all prerequisites so tests can be run inside VSCode. - -Once these are run, you should be able to run/debug a django server in VSCode as well as run/debug tests directly through VSCode. -There are also a few other tasks specified in case you have specific development needs: - -* `Run Dev Server` - Runs a django development server not connected to VSCode. -* `Run all pytests` - Runs all the pytests outside of VSCode. -* `Yarn Serve` - Runs development Vue.js server not connected to VSCode. Useful if you want to make Vue changes and see them in realtime. -* `Serve Documentation` - Runs a documentation server. Useful if you want to see how changes to documentation show up. - -### Django -This application is developed using the Django framework for Python. They have excellent -[documentation](https://www.djangoproject.com/start/) on how to get started, so I will only give you the basics here. - -1. Clone this repository wherever you like and install the Python language for your OS (I recommend using version 3.10 or above). -2. Open it in your favorite editor/IDE (e.g. PyCharm). - a. If you want, create a virtual environment for all your packages. -3. Install all required packages: `pip install -r requirements.txt`. -4. Run the migrations: `python manage.py migrate`. -5. Start the development server: `python manage.py runserver`. - -There is **no** need to set any environment variables. By default, a simple SQLite database is used and all settings are -populated from default values. - -### Vue.js -Most new frontend pages are build using [Vue.js](https://vuejs.org/). - -In order to work on these pages, you will have to install a Javascript package manager of your choice. The following examples use yarn. - -In the `vue` folder run `yarn install` to install the dependencies. After that you can use `yarn serve` to start the development server, -and proceed to test your changes. If you do not wish to work on those pages, but instead want the application to work properly during -development, run `yarn build` to build the frontend pages once. - -#### API Client -The API Client is generated automatically from the OpenAPI interface provided by the Django REST framework. -For this [openapi-generator](https://github.com/OpenAPITools/openapi-generator) is used. - -Install it using your desired setup method. (For example, using `npm install @openapitools/openapi-generator-cli -g`.) - -Navigate to `vue/src/utils/openapi`. - -Generate the schema using `openapi-generator-cli generate -g typescript-axios -i http://127.0.0.1:8000/openapi/`. (Replace your dev server url if required.) - -## Contribute Documentation -The documentation is built from the markdown files in the [docs](https://github.com/vabene1111/recipes/tree/develop/docs) -folder of the GitHub repository. - -In order to contribute to the documentation, you can fork the repository and edit the markdown files in the browser. - -Now install mkdocs and dependencies: `pip install mkdocs-material mkdocs-include-markdown-plugin`. - -If you want to test the documentation, locally run `mkdocs serve` from the project root. - -## Contribute Translations - -If you know any foreign languages that the project has not been completely translated to yet, feel free to contribute translations. - -Translations are managed on [translate.tandoor.dev](https://translate.tandoor.dev/), a self hosted instance of [Weblate](https://weblate.org/de/). - -You can simply register an account and then follow these steps to add translations: - -1. After registering, you are asked to select your languages. This is optional but allows weblate to only show you relevant translations. -2. In the navigation click on `Projects` and then `Browse all projects`. -3. Select Tandoor and on the top-right hand corner, select `Watch project Tandoor` (click on `Not watching`). -4. Go back to the dashboard. It now shows you the relevant translations for your languages. Click on the pencil icon to get started. - -!!! info "Creating a new language" - To create a new language you must first select Tandoor (the project) and then a component. - Here you will have the option to add the language. Afterwards you can also simply add it to the other components as well. - Once a new language is (partially) finished let me know on GitHub so I can add it to the language-switcher in Tandoor itself. - -There is also [a lot of documentation](https://docs.weblate.org/en/latest/user/translating.html) available from Weblate directly. - -![2021-04-11_16-03](https://user-images.githubusercontent.com/6819595/114307359-926e0380-9adf-11eb-9a2b-febba56e4d8c.gif) - -It is also possible to provide the translations directly by creating a new language -using `manage.py makemessages -l -i venv`. Once finished, simply open a PR with the changed files. This sometimes causes issues merging -with weblate, so I would prefer the use of weblate. diff --git a/docs/contribute/assets/flake8_watcher.png b/docs/contribute/assets/flake8_watcher.png new file mode 100644 index 000000000..f5bf77be0 Binary files /dev/null and b/docs/contribute/assets/flake8_watcher.png differ diff --git a/docs/contribute/assets/isort_watcher.png b/docs/contribute/assets/isort_watcher.png new file mode 100644 index 000000000..eb9c1d787 Binary files /dev/null and b/docs/contribute/assets/isort_watcher.png differ diff --git a/docs/contribute/assets/linting_error.png b/docs/contribute/assets/linting_error.png new file mode 100644 index 000000000..0acf97f3f Binary files /dev/null and b/docs/contribute/assets/linting_error.png differ diff --git a/docs/contribute/assets/prettier_watcher.png b/docs/contribute/assets/prettier_watcher.png new file mode 100644 index 000000000..893732d9f Binary files /dev/null and b/docs/contribute/assets/prettier_watcher.png differ diff --git a/docs/contribute/assets/yapf_watcher.png b/docs/contribute/assets/yapf_watcher.png new file mode 100644 index 000000000..9ddd968cd Binary files /dev/null and b/docs/contribute/assets/yapf_watcher.png differ diff --git a/docs/contribute/contribute.md b/docs/contribute/contribute.md new file mode 100644 index 000000000..26e6db4bc --- /dev/null +++ b/docs/contribute/contribute.md @@ -0,0 +1,55 @@ +If you like this application and want it to give back, there are many ways to contribute. + +!!! success "Contribution List" +If you help bring this project forward you deserve to be credited for it. +Feel free to add yourself to `CONTRIBUTERS.md` or message me to add you if you have contributed anything. + +## Translations + +If you know any foreign languages you can: +Improve the translations for any of the existing languages. + +Add a new language to the long list of existing translations. + +- Armenian +- Bulgarian +- Catalan +- Czech +- Danish +- Dutch +- English +- French +- German +- Hungarian +- Italian +- Latvian +- Norwegian +- Polish +- Russian +- Spanish +- Swedish + +See [here](/contribute/translations) for further information on how to contribute translation to Tandoor. + +## Issues and Feature Requests + +The most basic but also very important way of contributing is reporting issues and commenting on ideas and feature requests +over at [GitHub issues](https://github.com/vabene1111/recipes/issues). + +Without feedback improvement can't happen, so don't hesitate to say what you want to say. + +## Documentation + +Helping improve the documentation for Tandoor is one of the easiest ways to give back and doesn't even require deep technical knowledge. +You can write guides on how to install and configure Tandoor expanding our repository of non-standard configuations. +Or you can write how-to guides using some of Tandoor's advanced features such as authentication or automation. + +See [here](/contribute/documentation) for more information on how to add documentation to Tandoor. + +## Contributing Code + +For the truly ambitious, you can help write code to fix issues, add additional features, or write your own scripts using +Tandoor's extensive API and share your work with the community. + +Before writing any code, please make sure that you review [contribution guidelines](/contribute/guidelines) and +[VSCode](/contribute/vscode) or [PyCharm](/contribute/pycharm) specific configurations. diff --git a/docs/contribute/documentation.md b/docs/contribute/documentation.md new file mode 100644 index 000000000..3587f9bc0 --- /dev/null +++ b/docs/contribute/documentation.md @@ -0,0 +1,26 @@ +The documentation is built from the markdown files in the [docs](https://github.com/vabene1111/recipes/tree/develop/docs) +folder of the GitHub repository. + +In order to contribute to the documentation, there are a couple of methods that you can use. + +## Directly on GitHub + +You can fork the develop repository and edit the markdown files directly on the GitHub website. + +## With an IDE + +You can fork the develop repository and edit the markdown files from your favorite IDE such as VSCode or PyCharm. +One advantage of using as IDE is that you can preview your changes by: + +### Installing mkdocs + +Now install mkdocs and dependencies: `pip install mkdocs-material mkdocs-include-markdown-plugin`. + +### Serving Documetation + +If you want to test the documentation, locally run `mkdocs serve` from the project root. + +## Super Low Tech + +Create your documentation in any work processor (or even create a video!) and [open a feature request](https://github.com/vabene1111/recipes/issues) +attaching your document and requesting that someone add the documentation to Tandoor. diff --git a/docs/contribute/guidelines.md b/docs/contribute/guidelines.md new file mode 100644 index 000000000..f1b3eea9e --- /dev/null +++ b/docs/contribute/guidelines.md @@ -0,0 +1,63 @@ +If you want to contribute bug fixes or small tweaks then your pull requests are always welcome! + +!!! danger "Discuss First!" +If you want to contribute larger features that introduce more complexity to the project please +make sure to **first submit a technical description** outlining what and how you want to do it. +This allows me and the community to give feedback and manage the complexity of the overall +application. If you don't do this please don't be mad if I reject your PR. + +## License + +Contributing to Tandoor requires signing a Contributor License Agreement. You can review the CLA [here](https://cla-assistant.io/TandoorRecipes/recipes). + +## Linting & Formatting + +Tandoor uses a number of libraries to maintain style and formatting consistency. +To contribute to the project you are required to use the following packages with the project defined configurations: + +- flake8 +- yapf +- isort +- prettier + +!!! tip "Manual Formatting" + It is possible to run formatting manually, but it is recommended to setup your IDE to format on save. + ``` bash + flake8 file.py --ignore=E501 | isort -q file.py | yapf -i file.py + prettier --write file.vue + ``` + +## Testing +Django uses pytest-django to implement a full suite of testing. If you make any functional changes, please implment the appropriate +tests. + +Tandoor is also actively soliciting contribors willing to setup vue3 testing. If you have knowledge in this area it would be greatly appreciated. + +## API Client + +Tandoor uses [django-rest-framework](https://www.django-rest-framework.org/) for API implementation. Making contributions that impact the API requires an understanding of +Viewsets and Serializers. + +Also double check that your changes are actively reflected in the schema so that client apis are generated accurately. + +The API Client is generated automatically from the OpenAPI interface provided by the Django REST framework. +For this [openapi-generator](https://github.com/OpenAPITools/openapi-generator) is used. + +Install it using your desired setup method. (For example, using `npm install @openapitools/openapi-generator-cli -g`.) + +### Vue + +Navigate to `vue/src/utils/openapi`. + +Generate the schema using `openapi-generator-cli generate -g typescript-axios -i http://127.0.0.1:8000/openapi/`. (Replace your dev server url if required.) + +### Vue3 + +Navigate to `vue3/src/openapi`. + +Generate the schema using `openapi-generator-cli generate -g typescript-fetch -i http://127.0.0.1:8000/openapi/`. (Replace your dev server url if required.) + +## Install and Configuration + +Instructions for [VSCode](/contribute/vscode) +Instructions for [PyCharm](/contribute/pycharm) diff --git a/docs/contribute/installation.md b/docs/contribute/installation.md new file mode 100644 index 000000000..39eaba5a8 --- /dev/null +++ b/docs/contribute/installation.md @@ -0,0 +1,39 @@ +!!! info +The dev setup is a little messy as this application combines the best (at least in my opinion) of both Django and Vue.js. + +### Devcontainer Setup + +There is a [devcontainer](https://containers.dev) set up to ease development. It is optimized for VSCode, but should be able to +be used by other editors as well. Once the container is running, you can do things like start a Django dev server, start a Vue.js +dev server, run python tests, etc. by either using the VSCode tasks below, or manually running commands described in the individual +technology sections below. + +In VSCode, simply check out the git repository, and then via the command palette, choose `Dev Containers: Reopen in container`. + +If you need to change python dependencies (requierments.txt) or OS packages, you will need to rebuild the container. If you are +changing OS package requirements, you will need to update both the main `Dockerfile` and the `.devcontainer/Dockerfile`. + +### Django + +This application is developed using the Django framework for Python. They have excellent +[documentation](https://www.djangoproject.com/start/) on how to get started, so I will only give you the basics here. + +1. Clone this repository wherever you like and install the Python language for your OS (I recommend using version 3.10 or above). +2. Open it in your favorite editor/IDE (e.g. PyCharm). + a. If you want, create a virtual environment for all your packages. +3. Install all required packages: `pip install -r requirements.txt`. +4. Run the migrations: `python manage.py migrate`. +5. Start the development server: `python manage.py runserver`. + +There is **no** need to set any environment variables. By default, a simple SQLite database is used and all settings are +populated from default values. + +### Vue.js + +Most new frontend pages are build using [Vue.js](https://vuejs.org/). + +In order to work on these pages, you will have to install a Javascript package manager of your choice. The following examples use yarn. + +In the `vue` folder run `yarn install` to install the dependencies. After that you can use `yarn serve` to start the development server, +and proceed to test your changes. If you do not wish to work on those pages, but instead want the application to work properly during +development, run `yarn build` to build the frontend pages once. diff --git a/docs/contribute/pycharm.md b/docs/contribute/pycharm.md new file mode 100644 index 000000000..927e2ecbc --- /dev/null +++ b/docs/contribute/pycharm.md @@ -0,0 +1,62 @@ +PyCharm can be configured to format and lint on save. Doing so requires some manual configuration as outlined below. + +## Setup File Watchers + +1. Navigate to File -> Settings -> Plugins +2. Download and install [File Watchers](https://plugins.jetbrains.com/plugin/7177-file-watchers) +3. Navigate to File -> Settings -> Tools -> Black +4. Confirm 'Use Black Formatter' is unchecked for both 'On code reformat' and 'On save' + +## Setup flake8 Watcher + +1. Navigate to File -> Settings -> Tools -> File Watchers +2. Click the '+' to add a new watcher. +3. Configure the watcher as below. + + ![flake8_watcher](assets/flake8_watcher.png) + +4. Navigate to File -> Settings -> Editor -> Inspections -> File watcher problems +5. Under Severity select 'Edit Severities' +6. Click the '+' to add a severity calling it 'Linting Error' +7. Configure a background and effect as below. + + ![linting error](assets/linting_error.png) + +## Setup isort + +1. Navigate to File -> Settings -> Tools -> File Watchers +2. Click the '+' to add a new watcher. +3. Configure the watcher as below. + + ![yapf_watcher](assets/isort_watcher.png) + +## Setup yapf + +1. Navigate to File -> Settings -> Tools -> File Watchers +2. Click the '+' to add a new watcher. +3. Configure the watcher as below. + + ![yapf_watcher](assets/yapf_watcher.png) + + +!!! hint + Adding a comma at the end of a list will trigger yapf to put each element of the list on a new line + +## Setup prettier + +1. Navigate to File -> Settings -> Tools -> File Watchers +2. Click the '+' to add a new watcher. +3. Change 'File Type' to 'Any'. +4. Click the three dots next to 'Scope' to create a custom scope. +5. Click '+' to add a new scope + +- Name: prettier +- Pattern: `file:vue/src//*||file:vue3/src//*||file:docs//*` + +6. Configure the watcher as below. + + ![perttier_watcher](assets/prettier_watcher.png) + +- Arguments: `--cwd $ProjectFileDir$\vue prettier -w --config $ProjectFileDir$\.prettierrc $FilePath$` + +## Setup Volar?? diff --git a/docs/contribute/related.md b/docs/contribute/related.md new file mode 100644 index 000000000..ad2cce45a --- /dev/null +++ b/docs/contribute/related.md @@ -0,0 +1,25 @@ +## Recipe Scraper + +While not directly related to Tandoor, we make extensive use of the brilliant [recipe-scrapers](https://github.com/hhursev/recipe-scrapers) +package by hhursev. + +If you have the skills to add new sites or help resolve issues you are indirectly helping Tandoor. + +## Unofficial mobile app + +Maintained by [phantomate](https://github.com/phantomate/Untare) + +[iPhone](https://apps.apple.com/nl/app/untare/id6448643329?l=en&platform=iphone) +[Android](https://play.google.com/store/apps/details?id=unofficial.tandoor.recipes) + +## GPT Recipe + +Maintained by [John Pedrie](https://github.com/jdpedrie/gpt-recipe) + +Convert pictures of recipes to a structure that can be imported to Tandoor with ChatGPT. + +## Tandoor Menu Generator + +Maintained by [smilerz](https://github.com/smilerz/tandoor-menu-generator) + +Generate a mealplan tbased on complex criteria and optionally insert it into an SVG menu template. diff --git a/docs/contribute/translations.md b/docs/contribute/translations.md new file mode 100644 index 000000000..cd8d451c2 --- /dev/null +++ b/docs/contribute/translations.md @@ -0,0 +1,21 @@ +Translations are managed on [translate.tandoor.dev](https://translate.tandoor.dev/), a self hosted instance of [Weblate](https://weblate.org/de/). + +You can simply register an account and then follow these steps to add translations: + +1. After registering, you are asked to select your languages. This is optional but allows weblate to only show you relevant translations. +2. In the navigation click on `Projects` and then `Browse all projects`. +3. Select Tandoor and on the top-right hand corner, select `Watch project Tandoor` (click on `Not watching`). +4. Go back to the dashboard. It now shows you the relevant translations for your languages. Click on the pencil icon to get started. + +!!! info "Creating a new language" +To create a new language you must first select Tandoor (the project) and then a component. +Here you will have the option to add the language. Afterwards you can also simply add it to the other components as well. +Once a new language is (partially) finished let me know on GitHub so I can add it to the language-switcher in Tandoor itself. + +There is also [a lot of documentation](https://docs.weblate.org/en/latest/user/translating.html) available from Weblate directly. + +![2021-04-11_16-03](https://user-images.githubusercontent.com/6819595/114307359-926e0380-9adf-11eb-9a2b-febba56e4d8c.gif) + +It is also possible to provide the translations directly by creating a new language +using `manage.py makemessages -l -i venv`. Once finished, simply open a PR with the changed files. This sometimes causes issues merging +with weblate, so I would prefer the use of weblate. diff --git a/docs/contribute/vscode.md b/docs/contribute/vscode.md new file mode 100644 index 000000000..ab938d0ed --- /dev/null +++ b/docs/contribute/vscode.md @@ -0,0 +1,45 @@ +Configurations for debugging django, volar, testing, linting and formatting are all include in the project files. + +## Extensions + +VSCode can be configured to format and lint on save instead of manually formatting files before submitting a pull request. +To enable auto-formatting and linting install the following extensions in VSCode: + +Name: Flake8 +Publisher: Microsoft +VS Marketplace Link: https://marketplace.visualstudio.com/items?itemName=ms-python.flake8 + +Name: yapf +Publisher: EeyoreLee +VS Marketplace Link: https://marketplace.visualstudio.com/items?itemName=eeyore.yapf + +Name: isort +Publisher: Microsoft +VS Marketplace Link: https://marketplace.visualstudio.com/items?itemName=ms-python.isort + +Name: Vue - Official +Publisher: Vue +VS Marketplace Link: https://marketplace.visualstudio.com/items?itemName=Vue.volar + +Name: Prettier - Code formatter +Publisher: Prettier +VS Marketplace Link: https://marketplace.visualstudio.com/items?itemName=esbenp.prettier-vscode + + +!!! hint + Adding a comma at the end of a list will trigger yapf to put each element of the list on a new line + +## VSCode Tasks + +There are a number of built in tasks that are available. Here are a few of the key ones: + +- `Setup Dev Server` - Runs all the prerequisite steps so that the dev server can be run inside VSCode. +- `Setup Tests` - Runs all prerequisites so tests can be run inside VSCode. + +Once these are run, you should be able to run/debug a django server in VSCode as well as run/debug tests directly through VSCode. +There are also a few other tasks specified in case you have specific development needs: + +- `Run Dev Server` - Runs a django development server not connected to VSCode. +- `Run all pytests` - Runs all the pytests outside of VSCode. +- `Yarn Serve` - Runs development Vue.js server not connected to VSCode. Useful if you want to make Vue changes and see them in realtime. +- `Serve Documentation` - Runs a documentation server. Useful if you want to see how changes to documentation show up. diff --git a/docs/system/settings.md b/docs/system/settings.md deleted file mode 100644 index 990090fd8..000000000 --- a/docs/system/settings.md +++ /dev/null @@ -1,27 +0,0 @@ -Following is a description of the different settings for a space - -!!! WARNING WIP - Some settings and especially this page is work in Progress and the settings may - behave differently the described here. - -## Use Plural form - -Default Value: `off` - -This setting enables tandoor to display a plural form of a food or unit, if the -plural version is entered for the food or unit. The plural version is displayed if the -amount needed for a recipe is greater than 1 and will be adjusted to the current amount. - -In addition, this setting enables two new settings for an ingredient: - -- Always show the plural version of the food: This will always display the plural version for -a food, even if the amount is below or equal to 1. Requirement for this setting to activate -is a plural version available in the database. -- Always show the plural version of the unit: This will always display the plural version for -a unit, even if the amount is below or equal to 1. Requirement for this setting to activate -is a plural version available in the database. - -!!! WARNING Note - This setting is only meant to be a very simple version to enable some kind of pluralization - for food and units. This feature may not meet your needs, but pluralization is a difficult - topic and was discussed [here](https://github.com/TandoorRecipes/recipes/pull/1860). diff --git a/mkdocs.yml b/mkdocs.yml index 68d2b70a9..f51e36471 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -36,6 +36,7 @@ nav: - TrueNAS Portainer: install/truenas_portainer.md - WSL: install/wsl.md - ArchLinux: install/archlinux.md + - HomeAssistant: install/homeassistant.md - Manual: install/manual.md - Other setups: install/other.md - Features: @@ -46,10 +47,21 @@ nav: - Connectors: features/connectors.md - Storages and Sync: features/external_recipes.md - Import/Export: features/import_export.md + - Telegram bot: features/telegram_bot.md - System: - Configuration: system/configuration.md - Updating: system/updating.md + - Migrate sqlite to postgres: system/migration_sqlite-postgres.md - Permission System: system/permissions.md - Backup: system/backup.md - - Contributing: contribute.md + - Contributing: + - Overview: contribute/contribute.md + - Translations: contribute/translations.md + - Documentation: contribute/documentation.md + - Code: contribute/guidelines.md + - Installation: contribute/installation.md + - IDE Setup: + - VSCode: contribute/vscode.md + - PyCharm: contribute/pycharm.md + - Related Projects: contribute/related.md - FAQ: faq.md diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 000000000..33bc62172 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,12 @@ +[tool.yapf] +column_limit = 179 +based_on_style = "pep8" +DISABLE_ENDING_COMMA_HEURISTIC = false +COALESCE_BRACKETS = true +DEDENT_CLOSING_BRACKETS = true +FORCE_MULTILINE_DICT = false + +[tool.isort] +multi_line_output = 5 +skip = [".gitignore", ".dockerignore"] +line_length = 179 diff --git a/recipes/settings.py b/recipes/settings.py index 268c8f68b..d9b2b6b24 100644 --- a/recipes/settings.py +++ b/recipes/settings.py @@ -104,10 +104,30 @@ MESSAGE_TAGS = {messages.ERROR: 'danger'} # Application definition INSTALLED_APPS = [ - 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.messages', - 'django.contrib.sites', 'django.contrib.staticfiles', 'django.contrib.postgres', 'oauth2_provider', 'django_tables2', 'corsheaders', 'crispy_forms', - 'crispy_bootstrap4', 'rest_framework', 'rest_framework.authtoken', 'django_cleanup.apps.CleanupConfig', 'webpack_loader', 'django_js_reverse', 'hcaptcha', 'allauth', - 'allauth.account', 'allauth.socialaccount', 'cookbook.apps.CookbookConfig', 'treebeard', + 'django.contrib.admin', + 'django.contrib.auth', + 'django.contrib.contenttypes', + 'django.contrib.sessions', + 'django.contrib.messages', + 'django.contrib.sites', + 'django.contrib.staticfiles', + 'django.contrib.postgres', + 'oauth2_provider', + 'django_tables2', + 'corsheaders', + 'crispy_forms', + 'crispy_bootstrap4', + 'rest_framework', + 'rest_framework.authtoken', + 'django_cleanup.apps.CleanupConfig', + 'webpack_loader', + 'django_js_reverse', + 'hcaptcha', + 'allauth', + 'allauth.account', + 'allauth.socialaccount', + 'cookbook.apps.CookbookConfig', + 'treebeard', ] PLUGINS_DIRECTORY = os.path.join(BASE_DIR, 'recipes', 'plugins') @@ -177,10 +197,18 @@ ENABLE_PDF_EXPORT = bool(int(os.getenv('ENABLE_PDF_EXPORT', False))) EXPORT_FILE_CACHE_DURATION = int(os.getenv('EXPORT_FILE_CACHE_DURATION', 600)) MIDDLEWARE = [ - 'corsheaders.middleware.CorsMiddleware', 'django.middleware.security.SecurityMiddleware', 'whitenoise.middleware.WhiteNoiseMiddleware', - 'django.contrib.sessions.middleware.SessionMiddleware', 'django.middleware.common.CommonMiddleware', 'django.middleware.csrf.CsrfViewMiddleware', - 'django.contrib.auth.middleware.AuthenticationMiddleware', 'django.contrib.messages.middleware.MessageMiddleware', 'django.middleware.locale.LocaleMiddleware', - 'django.middleware.clickjacking.XFrameOptionsMiddleware', 'cookbook.helper.scope_middleware.ScopeMiddleware', 'allauth.account.middleware.AccountMiddleware', + 'corsheaders.middleware.CorsMiddleware', + 'django.middleware.security.SecurityMiddleware', + 'whitenoise.middleware.WhiteNoiseMiddleware', + 'django.contrib.sessions.middleware.SessionMiddleware', + 'django.middleware.common.CommonMiddleware', + 'django.middleware.csrf.CsrfViewMiddleware', + 'django.contrib.auth.middleware.AuthenticationMiddleware', + 'django.contrib.messages.middleware.MessageMiddleware', + 'django.middleware.locale.LocaleMiddleware', + 'django.middleware.clickjacking.XFrameOptionsMiddleware', + 'cookbook.helper.scope_middleware.ScopeMiddleware', + 'allauth.account.middleware.AccountMiddleware', ] if DEBUG_TOOLBAR: @@ -211,7 +239,11 @@ if LDAP_AUTH: AUTH_LDAP_START_TLS = bool(int(os.getenv('AUTH_LDAP_START_TLS', False))) AUTH_LDAP_BIND_DN = os.getenv('AUTH_LDAP_BIND_DN') AUTH_LDAP_BIND_PASSWORD = os.getenv('AUTH_LDAP_BIND_PASSWORD') - AUTH_LDAP_USER_SEARCH = LDAPSearch(os.getenv('AUTH_LDAP_USER_SEARCH_BASE_DN'), ldap.SCOPE_SUBTREE, os.getenv('AUTH_LDAP_USER_SEARCH_FILTER_STR', '(uid=%(user)s)'), ) + AUTH_LDAP_USER_SEARCH = LDAPSearch( + os.getenv('AUTH_LDAP_USER_SEARCH_BASE_DN'), + ldap.SCOPE_SUBTREE, + os.getenv('AUTH_LDAP_USER_SEARCH_FILTER_STR', '(uid=%(user)s)'), + ) AUTH_LDAP_USER_ATTR_MAP = ast.literal_eval(os.getenv('AUTH_LDAP_USER_ATTR_MAP')) if os.getenv('AUTH_LDAP_USER_ATTR_MAP') else { 'first_name': 'givenName', 'last_name': 'sn', @@ -238,7 +270,10 @@ if LDAP_AUTH: }, } -AUTHENTICATION_BACKENDS += ['django.contrib.auth.backends.ModelBackend', 'allauth.account.auth_backends.AuthenticationBackend', ] +AUTHENTICATION_BACKENDS += [ + 'django.contrib.auth.backends.ModelBackend', + 'allauth.account.auth_backends.AuthenticationBackend', +] # django allauth site id SITE_ID = int(os.getenv('ALLAUTH_SITE_ID', 1)) @@ -252,15 +287,20 @@ if REMOTE_USER_AUTH: # Password validation # https://docs.djangoproject.com/en/2.0/ref/settings/#auth-password-validators -AUTH_PASSWORD_VALIDATORS = [{ - 'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator', -}, { - 'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator', -}, { - 'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator', -}, { - 'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator', -}, ] +AUTH_PASSWORD_VALIDATORS = [ + { + 'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator', + }, + { + 'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator', + }, + { + 'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator', + }, + { + 'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator', + }, +] SECURE_PROXY_SSL_HEADER = ('HTTP_X_FORWARDED_PROTO', 'https') @@ -269,25 +309,35 @@ READ_SCOPE = 'read' WRITE_SCOPE = 'write' REST_FRAMEWORK = { - 'DEFAULT_AUTHENTICATION_CLASSES': - ('rest_framework.authentication.SessionAuthentication', 'oauth2_provider.contrib.rest_framework.OAuth2Authentication', 'rest_framework.authentication.BasicAuthentication', - ), - 'DEFAULT_PERMISSION_CLASSES': ['rest_framework.permissions.IsAuthenticated', ], + 'DEFAULT_AUTHENTICATION_CLASSES': ( + 'rest_framework.authentication.SessionAuthentication', + 'oauth2_provider.contrib.rest_framework.OAuth2Authentication', + 'rest_framework.authentication.BasicAuthentication', + ), + 'DEFAULT_PERMISSION_CLASSES': [ + 'rest_framework.permissions.IsAuthenticated', + ], } ROOT_URLCONF = 'recipes.urls' -TEMPLATES = [{ - 'BACKEND': 'django.template.backends.django.DjangoTemplates', - 'DIRS': [os.path.join(BASE_DIR, 'templates'), os.path.join(BASE_DIR, 'cookbook', 'templates')], - 'APP_DIRS': True, - 'OPTIONS': { - 'context_processors': [ - 'django.template.context_processors.debug', 'django.template.context_processors.request', 'django.contrib.auth.context_processors.auth', - 'django.contrib.messages.context_processors.messages', 'django.template.context_processors.media', 'cookbook.helper.context_processors.context_settings', - ], +TEMPLATES = [ + { + 'BACKEND': 'django.template.backends.django.DjangoTemplates', + 'DIRS': [os.path.join(BASE_DIR, 'templates'), os.path.join(BASE_DIR, 'cookbook', 'templates')], + 'APP_DIRS': True, + 'OPTIONS': { + 'context_processors': [ + 'django.template.context_processors.debug', + 'django.template.context_processors.request', + 'django.contrib.auth.context_processors.auth', + 'django.contrib.messages.context_processors.messages', + 'django.template.context_processors.media', + 'cookbook.helper.context_processors.context_settings', + ], + }, }, -}, ] +] WSGI_APPLICATION = 'recipes.wsgi.application' @@ -358,7 +408,12 @@ def setup_database(db_url=None, db_options=None, db_engine=None, pg_host=None, p DATABASES = setup_database() -CACHES = {'default': {'BACKEND': 'django.core.cache.backends.locmem.LocMemCache', 'LOCATION': 'default', }} +CACHES = { + 'default': { + 'BACKEND': 'django.core.cache.backends.locmem.LocMemCache', + 'LOCATION': 'default', + } +} # Vue webpack settings VUE_DIR = os.path.join(BASE_DIR, 'vue') @@ -402,9 +457,25 @@ USE_L10N = True USE_TZ = True -LANGUAGES = [('hy', _('Armenian ')), ('bg', _('Bulgarian')), ('ca', _('Catalan')), ('cs', _('Czech')), ('da', _('Danish')), ('nl', _('Dutch')), ('en', _('English')), - ('fr', _('French')), ('de', _('German')), ('hu', _('Hungarian')), ('it', _('Italian')), ('lv', _('Latvian')), ('nb', _('Norwegian ')), ('pl', _('Polish')), - ('ru', _('Russian')), ('es', _('Spanish')), ('sv', _('Swedish')), ] +LANGUAGES = [ + ('hy', _('Armenian ')), + ('bg', _('Bulgarian')), + ('ca', _('Catalan')), + ('cs', _('Czech')), + ('da', _('Danish')), + ('nl', _('Dutch')), + ('en', _('English')), + ('fr', _('French')), + ('de', _('German')), + ('hu', _('Hungarian')), + ('it', _('Italian')), + ('lv', _('Latvian')), + ('nb', _('Norwegian ')), + ('pl', _('Polish')), + ('ru', _('Russian')), + ('es', _('Spanish')), + ('sv', _('Swedish')), +] # Static files (CSS, JavaScript, Images) # https://docs.djangoproject.com/en/2.0/howto/static-files/ @@ -470,7 +541,13 @@ ACCOUNT_EMAIL_SUBJECT_PREFIX = os.getenv('ACCOUNT_EMAIL_SUBJECT_PREFIX', '[Tando ACCOUNT_FORMS = {'signup': 'cookbook.forms.AllAuthSignupForm', 'reset_password': 'cookbook.forms.CustomPasswordResetForm'} ACCOUNT_EMAIL_UNKNOWN_ACCOUNTS = False -ACCOUNT_RATE_LIMITS = {"change_password": "1/m/user", "reset_password": "1/m/ip,1/m/key", "reset_password_from_key": "1/m/ip", "signup": "5/m/ip", "login": "5/m/ip", } +ACCOUNT_RATE_LIMITS = { + "change_password": "1/m/user", + "reset_password": "1/m/ip,1/m/key", + "reset_password_from_key": "1/m/ip", + "signup": "5/m/ip", + "login": "5/m/ip", +} DISABLE_EXTERNAL_CONNECTORS = bool(int(os.getenv('DISABLE_EXTERNAL_CONNECTORS', False))) EXTERNAL_CONNECTORS_QUEUE_SIZE = int(os.getenv('EXTERNAL_CONNECTORS_QUEUE_SIZE', 100)) diff --git a/requirements.txt b/requirements.txt index b457d2c46..51189602f 100644 --- a/requirements.txt +++ b/requirements.txt @@ -10,7 +10,7 @@ drf-writable-nested==0.7.0 django-oauth-toolkit==2.3.0 django-debug-toolbar==4.3.0 bleach==6.0.0 -gunicorn==21.2.0 +gunicorn==22.0.0 lxml==5.1.0 Markdown==3.5.1 Pillow==10.3.0 @@ -30,7 +30,7 @@ Jinja2==3.1.3 django-webpack-loader==3.0.1 git+https://github.com/BITSOLVER/django-js-reverse@071e304fd600107bc64bbde6f2491f1fe049ec82 django-allauth==0.61.1 -recipe-scrapers==14.53.0 +recipe-scrapers==15.0.0-rc2 django-scopes==2.0.0 django-treebeard==4.7 django-cors-headers==4.3.1 @@ -43,7 +43,7 @@ django-auth-ldap==4.6.0 pyppeteer==2.0.0 validators==0.20.0 pytube==15.0.0 -aiohttp==3.9.3 +aiohttp==3.9.4 # Development pytest==8.0.0 @@ -53,3 +53,6 @@ pytest-factoryboy==2.6.0 pytest-html==4.1.1 pytest-asyncio==0.23.5 pytest-xdist==3.5.0 +autopep8==2.0.4 +flake8==6.1.0 +yapf==0.40.2 diff --git a/vue/package.json b/vue/package.json index 02c8fe1fc..98d8f337b 100644 --- a/vue/package.json +++ b/vue/package.json @@ -61,6 +61,7 @@ "babel-eslint": "^10.1.0", "eslint": "^8.46.0", "eslint-plugin-vue": "^8.7.1", + "prettier": "^3.2.5", "typescript": "~5.3.3", "vue-cli-plugin-i18n": "^2.3.2", "webpack-bundle-tracker": "3.0.1", diff --git a/vue/src/components/PropertyViewComponent.vue b/vue/src/components/PropertyViewComponent.vue index 6cceeccd7..b7c1a871a 100644 --- a/vue/src/components/PropertyViewComponent.vue +++ b/vue/src/components/PropertyViewComponent.vue @@ -67,7 +67,7 @@ diff --git a/vue/yarn.lock b/vue/yarn.lock index 663c77a06..eed173f13 100644 --- a/vue/yarn.lock +++ b/vue/yarn.lock @@ -9136,6 +9136,11 @@ prepend-http@^2.0.0: resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.8.8.tgz#e8c5d7e98a4305ffe3de2e1fc4aca1a71c28b1da" integrity sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q== +prettier@^3.2.5: + version "3.2.5" + resolved "https://registry.yarnpkg.com/prettier/-/prettier-3.2.5.tgz#e52bc3090586e824964a8813b09aba6233b28368" + integrity sha512-3/GWa9aOC0YeD7LUfvOG2NiDyhOWRvt1k+rcKhOuYnMY24iiCphgneUfJDyFXd6rZCAnuLBv6UeAULtrhT/F4A== + pretty-bytes@^5.3.0, pretty-bytes@^5.4.1: version "5.6.0" resolved "https://registry.yarnpkg.com/pretty-bytes/-/pretty-bytes-5.6.0.tgz#356256f643804773c82f64723fe78c92c62beaeb"