init
This commit is contained in:
		
							
								
								
									
										428
									
								
								.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										428
									
								
								.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,428 @@ | ||||
| # Created by https://www.toptal.com/developers/gitignore/api/python,flask,vim,visualstudiocode,pycharm | ||||
| # Edit at https://www.toptal.com/developers/gitignore?templates=python,flask,vim,visualstudiocode,pycharm | ||||
|  | ||||
| ### Flask ### | ||||
| instance/* | ||||
| !instance/.gitignore | ||||
| .webassets-cache | ||||
| .env | ||||
|  | ||||
| ### Flask.Python Stack ### | ||||
| # Byte-compiled / optimized / DLL files | ||||
| __pycache__/ | ||||
| *.py[cod] | ||||
| *$py.class | ||||
|  | ||||
| # C extensions | ||||
| *.so | ||||
|  | ||||
| # Distribution / packaging | ||||
| .Python | ||||
| build/ | ||||
| develop-eggs/ | ||||
| dist/ | ||||
| downloads/ | ||||
| eggs/ | ||||
| .eggs/ | ||||
| lib/ | ||||
| lib64/ | ||||
| parts/ | ||||
| sdist/ | ||||
| var/ | ||||
| wheels/ | ||||
| share/python-wheels/ | ||||
| *.egg-info/ | ||||
| .installed.cfg | ||||
| *.egg | ||||
| MANIFEST | ||||
|  | ||||
| # PyInstaller | ||||
| #  Usually these files are written by a python script from a template | ||||
| #  before PyInstaller builds the exe, so as to inject date/other infos into it. | ||||
| *.manifest | ||||
| *.spec | ||||
|  | ||||
| # Installer logs | ||||
| pip-log.txt | ||||
| pip-delete-this-directory.txt | ||||
|  | ||||
| # Unit test / coverage reports | ||||
| htmlcov/ | ||||
| .tox/ | ||||
| .nox/ | ||||
| .coverage | ||||
| .coverage.* | ||||
| .cache | ||||
| nosetests.xml | ||||
| coverage.xml | ||||
| *.cover | ||||
| *.py,cover | ||||
| .hypothesis/ | ||||
| .pytest_cache/ | ||||
| cover/ | ||||
|  | ||||
| # Translations | ||||
| *.mo | ||||
| *.pot | ||||
|  | ||||
| # Django stuff: | ||||
| *.log | ||||
| local_settings.py | ||||
| db.sqlite3 | ||||
| db.sqlite3-journal | ||||
|  | ||||
| # Flask stuff: | ||||
| instance/ | ||||
|  | ||||
| # Scrapy stuff: | ||||
| .scrapy | ||||
|  | ||||
| # Sphinx documentation | ||||
| docs/_build/ | ||||
|  | ||||
| # PyBuilder | ||||
| .pybuilder/ | ||||
| target/ | ||||
|  | ||||
| # Jupyter Notebook | ||||
| .ipynb_checkpoints | ||||
|  | ||||
| # IPython | ||||
| profile_default/ | ||||
| ipython_config.py | ||||
|  | ||||
| # pyenv | ||||
| #   For a library or package, you might want to ignore these files since the code is | ||||
| #   intended to run in multiple environments; otherwise, check them in: | ||||
| # .python-version | ||||
|  | ||||
| # pipenv | ||||
| #   According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. | ||||
| #   However, in case of collaboration, if having platform-specific dependencies or dependencies | ||||
| #   having no cross-platform support, pipenv may install dependencies that don't work, or not | ||||
| #   install all needed dependencies. | ||||
| #Pipfile.lock | ||||
|  | ||||
| # poetry | ||||
| #   Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control. | ||||
| #   This is especially recommended for binary packages to ensure reproducibility, and is more | ||||
| #   commonly ignored for libraries. | ||||
| #   https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control | ||||
| #poetry.lock | ||||
|  | ||||
| # pdm | ||||
| #   Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control. | ||||
| #pdm.lock | ||||
| #   pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it | ||||
| #   in version control. | ||||
| #   https://pdm.fming.dev/#use-with-ide | ||||
| .pdm.toml | ||||
|  | ||||
| # PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm | ||||
| __pypackages__/ | ||||
|  | ||||
| # Celery stuff | ||||
| celerybeat-schedule | ||||
| celerybeat.pid | ||||
|  | ||||
| # SageMath parsed files | ||||
| *.sage.py | ||||
|  | ||||
| # Environments | ||||
| .venv | ||||
| env/ | ||||
| venv/ | ||||
| ENV/ | ||||
| env.bak/ | ||||
| venv.bak/ | ||||
|  | ||||
| # Spyder project settings | ||||
| .spyderproject | ||||
| .spyproject | ||||
|  | ||||
| # Rope project settings | ||||
| .ropeproject | ||||
|  | ||||
| # mkdocs documentation | ||||
| /site | ||||
|  | ||||
| # mypy | ||||
| .mypy_cache/ | ||||
| .dmypy.json | ||||
| dmypy.json | ||||
|  | ||||
| # Pyre type checker | ||||
| .pyre/ | ||||
|  | ||||
| # pytype static type analyzer | ||||
| .pytype/ | ||||
|  | ||||
| # Cython debug symbols | ||||
| cython_debug/ | ||||
|  | ||||
| # PyCharm | ||||
| #  JetBrains specific template is maintained in a separate JetBrains.gitignore that can | ||||
| #  be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore | ||||
| #  and can be added to the global gitignore or merged into this file.  For a more nuclear | ||||
| #  option (not recommended) you can uncomment the following to ignore the entire idea folder. | ||||
| #.idea/ | ||||
|  | ||||
| ### PyCharm ### | ||||
| # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider | ||||
| # Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 | ||||
|  | ||||
| # User-specific stuff | ||||
| .idea/**/workspace.xml | ||||
| .idea/**/tasks.xml | ||||
| .idea/**/usage.statistics.xml | ||||
| .idea/**/dictionaries | ||||
| .idea/**/shelf | ||||
|  | ||||
| # AWS User-specific | ||||
| .idea/**/aws.xml | ||||
|  | ||||
| # Generated files | ||||
| .idea/**/contentModel.xml | ||||
|  | ||||
| # Sensitive or high-churn files | ||||
| .idea/**/dataSources/ | ||||
| .idea/**/dataSources.ids | ||||
| .idea/**/dataSources.local.xml | ||||
| .idea/**/sqlDataSources.xml | ||||
| .idea/**/dynamic.xml | ||||
| .idea/**/uiDesigner.xml | ||||
| .idea/**/dbnavigator.xml | ||||
|  | ||||
| # Gradle | ||||
| .idea/**/gradle.xml | ||||
| .idea/**/libraries | ||||
|  | ||||
| # Gradle and Maven with auto-import | ||||
| # When using Gradle or Maven with auto-import, you should exclude module files, | ||||
| # since they will be recreated, and may cause churn.  Uncomment if using | ||||
| # auto-import. | ||||
| # .idea/artifacts | ||||
| # .idea/compiler.xml | ||||
| # .idea/jarRepositories.xml | ||||
| # .idea/modules.xml | ||||
| # .idea/*.iml | ||||
| # .idea/modules | ||||
| # *.iml | ||||
| # *.ipr | ||||
|  | ||||
| # CMake | ||||
| cmake-build-*/ | ||||
|  | ||||
| # Mongo Explorer plugin | ||||
| .idea/**/mongoSettings.xml | ||||
|  | ||||
| # File-based project format | ||||
| *.iws | ||||
|  | ||||
| # IntelliJ | ||||
| out/ | ||||
|  | ||||
| # mpeltonen/sbt-idea plugin | ||||
| .idea_modules/ | ||||
|  | ||||
| # JIRA plugin | ||||
| atlassian-ide-plugin.xml | ||||
|  | ||||
| # Cursive Clojure plugin | ||||
| .idea/replstate.xml | ||||
|  | ||||
| # SonarLint plugin | ||||
| .idea/sonarlint/ | ||||
|  | ||||
| # Crashlytics plugin (for Android Studio and IntelliJ) | ||||
| com_crashlytics_export_strings.xml | ||||
| crashlytics.properties | ||||
| crashlytics-build.properties | ||||
| fabric.properties | ||||
|  | ||||
| # Editor-based Rest Client | ||||
| .idea/httpRequests | ||||
|  | ||||
| # Android studio 3.1+ serialized cache file | ||||
| .idea/caches/build_file_checksums.ser | ||||
|  | ||||
| ### PyCharm Patch ### | ||||
| # Comment Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-215987721 | ||||
|  | ||||
| # *.iml | ||||
| # modules.xml | ||||
| # .idea/misc.xml | ||||
| # *.ipr | ||||
|  | ||||
| # Sonarlint plugin | ||||
| # https://plugins.jetbrains.com/plugin/7973-sonarlint | ||||
| .idea/**/sonarlint/ | ||||
|  | ||||
| # SonarQube Plugin | ||||
| # https://plugins.jetbrains.com/plugin/7238-sonarqube-community-plugin | ||||
| .idea/**/sonarIssues.xml | ||||
|  | ||||
| # Markdown Navigator plugin | ||||
| # https://plugins.jetbrains.com/plugin/7896-markdown-navigator-enhanced | ||||
| .idea/**/markdown-navigator.xml | ||||
| .idea/**/markdown-navigator-enh.xml | ||||
| .idea/**/markdown-navigator/ | ||||
|  | ||||
| # Cache file creation bug | ||||
| # See https://youtrack.jetbrains.com/issue/JBR-2257 | ||||
| .idea/$CACHE_FILE$ | ||||
|  | ||||
| # CodeStream plugin | ||||
| # https://plugins.jetbrains.com/plugin/12206-codestream | ||||
| .idea/codestream.xml | ||||
|  | ||||
| # Azure Toolkit for IntelliJ plugin | ||||
| # https://plugins.jetbrains.com/plugin/8053-azure-toolkit-for-intellij | ||||
| .idea/**/azureSettings.xml | ||||
|  | ||||
| ### Python ### | ||||
| # Byte-compiled / optimized / DLL files | ||||
|  | ||||
| # C extensions | ||||
|  | ||||
| # Distribution / packaging | ||||
|  | ||||
| # PyInstaller | ||||
| #  Usually these files are written by a python script from a template | ||||
| #  before PyInstaller builds the exe, so as to inject date/other infos into it. | ||||
|  | ||||
| # Installer logs | ||||
|  | ||||
| # Unit test / coverage reports | ||||
|  | ||||
| # Translations | ||||
|  | ||||
| # Django stuff: | ||||
|  | ||||
| # Flask stuff: | ||||
|  | ||||
| # Scrapy stuff: | ||||
|  | ||||
| # Sphinx documentation | ||||
|  | ||||
| # PyBuilder | ||||
|  | ||||
| # Jupyter Notebook | ||||
|  | ||||
| # IPython | ||||
|  | ||||
| # pyenv | ||||
| #   For a library or package, you might want to ignore these files since the code is | ||||
| #   intended to run in multiple environments; otherwise, check them in: | ||||
| # .python-version | ||||
|  | ||||
| # pipenv | ||||
| #   According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. | ||||
| #   However, in case of collaboration, if having platform-specific dependencies or dependencies | ||||
| #   having no cross-platform support, pipenv may install dependencies that don't work, or not | ||||
| #   install all needed dependencies. | ||||
|  | ||||
| # poetry | ||||
| #   Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control. | ||||
| #   This is especially recommended for binary packages to ensure reproducibility, and is more | ||||
| #   commonly ignored for libraries. | ||||
| #   https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control | ||||
|  | ||||
| # pdm | ||||
| #   Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control. | ||||
| #   pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it | ||||
| #   in version control. | ||||
| #   https://pdm.fming.dev/#use-with-ide | ||||
|  | ||||
| # PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm | ||||
|  | ||||
| # Celery stuff | ||||
|  | ||||
| # SageMath parsed files | ||||
|  | ||||
| # Environments | ||||
|  | ||||
| # Spyder project settings | ||||
|  | ||||
| # Rope project settings | ||||
|  | ||||
| # mkdocs documentation | ||||
|  | ||||
| # mypy | ||||
|  | ||||
| # Pyre type checker | ||||
|  | ||||
| # pytype static type analyzer | ||||
|  | ||||
| # Cython debug symbols | ||||
|  | ||||
| # PyCharm | ||||
| #  JetBrains specific template is maintained in a separate JetBrains.gitignore that can | ||||
| #  be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore | ||||
| #  and can be added to the global gitignore or merged into this file.  For a more nuclear | ||||
| #  option (not recommended) you can uncomment the following to ignore the entire idea folder. | ||||
|  | ||||
| ### Vim ### | ||||
| # Swap | ||||
| [._]*.s[a-v][a-z] | ||||
| !*.svg  # comment out if you don't need vector files | ||||
| [._]*.sw[a-p] | ||||
| [._]s[a-rt-v][a-z] | ||||
| [._]ss[a-gi-z] | ||||
| [._]sw[a-p] | ||||
|  | ||||
| # Session | ||||
| Session.vim | ||||
| Sessionx.vim | ||||
|  | ||||
| # Temporary | ||||
| .netrwhist | ||||
| *~ | ||||
| # Auto-generated tag files | ||||
| tags | ||||
| # Persistent undo | ||||
| [._]*.un~ | ||||
|  | ||||
| ### VisualStudioCode ### | ||||
| .vscode/* | ||||
| !.vscode/settings.json | ||||
| !.vscode/tasks.json | ||||
| !.vscode/launch.json | ||||
| !.vscode/extensions.json | ||||
| !.vscode/*.code-snippets | ||||
|  | ||||
| # Local History for Visual Studio Code | ||||
| .history/ | ||||
|  | ||||
| # Built Visual Studio Code Extensions | ||||
| *.vsix | ||||
|  | ||||
| ### VisualStudioCode Patch ### | ||||
| # Ignore all local history of files | ||||
| .history | ||||
| .ionide | ||||
|  | ||||
| # Support for Project snippet scope | ||||
| .vscode/*.code-snippets | ||||
|  | ||||
| # Ignore code-workspaces | ||||
| *.code-workspace | ||||
|  | ||||
| # End of https://www.toptal.com/developers/gitignore/api/python,flask,vim,visualstudiocode,pycharm | ||||
|  | ||||
| ### venv ### | ||||
| # Virtualenv | ||||
| # http://iamzed.com/2009/05/07/a-primer-on-virtualenv/ | ||||
| .Python | ||||
| [Bb]in | ||||
| [Ii]nclude | ||||
| [Ll]ib | ||||
| [Ll]ib64 | ||||
| [Ll]ocal | ||||
| [Ss]cripts | ||||
| pyvenv.cfg | ||||
| .venv | ||||
| pip-selfcheck.json | ||||
| *.db | ||||
|  | ||||
| .idea | ||||
							
								
								
									
										11
									
								
								kuhkal/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								kuhkal/__init__.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,11 @@ | ||||
| from flask import Flask | ||||
| from flask_sqlalchemy import SQLAlchemy | ||||
|  | ||||
| app = Flask(__name__) | ||||
| app.config["SQLALCHEMY_DATABASE_URI"] = "sqlite:///kuhinja.db" | ||||
| app.config["SQLALCHEMY_TRACK_MODIFICATIONS"] = False | ||||
|  | ||||
| db = SQLAlchemy(app) | ||||
|  | ||||
| from . import routes | ||||
|  | ||||
							
								
								
									
										26
									
								
								kuhkal/models.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										26
									
								
								kuhkal/models.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,26 @@ | ||||
| from . import db | ||||
|  | ||||
| class RecipeIngredientAssoc(db.Model): | ||||
|     __tablename__ = "RecipeIngredientAssoc" | ||||
|     recipe_id = db.Column(db.Integer, db.ForeignKey("recipe.id"), primary_key=True) | ||||
|     ingredient_id = db.Column( | ||||
|         db.Integer, db.ForeignKey("ingredient.id"), primary_key=True | ||||
|     ) | ||||
|     ingredient_ammount = db.Column(db.Numeric(10, 2)) | ||||
|     ingredients = db.relationship("Ingredient", back_populates="recipe") | ||||
|     recipe = db.relationship("Recipe", back_populates="ingredients") | ||||
|  | ||||
|  | ||||
| class Recipe(db.Model): | ||||
|     __tablename__ = "recipe" | ||||
|     id = db.Column(db.Integer, primary_key=True) | ||||
|     name = db.Column(db.String(120), unique=True, nullable=False) | ||||
|     ingredients = db.relationship("RecipeIngredientAssoc", back_populates="recipe") | ||||
|  | ||||
|  | ||||
| class Ingredient(db.Model): | ||||
|     __tablename__ = "ingredient" | ||||
|     id = db.Column(db.Integer, primary_key=True) | ||||
|     name = db.Column(db.String(120), unique=True, nullable=False) | ||||
|     price = db.Column(db.Numeric(10, 2)) | ||||
|     recipe = db.relationship("RecipeIngredientAssoc", back_populates="ingredients") | ||||
							
								
								
									
										206
									
								
								kuhkal/routes.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										206
									
								
								kuhkal/routes.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,206 @@ | ||||
| from flask import render_template, request, redirect, jsonify | ||||
|  | ||||
| from . import app | ||||
| from .models import Recipe, Ingredient, RecipeIngredientAssoc | ||||
| from . import db | ||||
| from .utils import get_all_recipes | ||||
|  | ||||
|  | ||||
| @app.get("/") | ||||
| def index(): | ||||
|     data = {"recipes": get_all_recipes()} | ||||
|     return render_template("pages/index.html", data=data) | ||||
|  | ||||
|  | ||||
| @app.route("/ingredients") | ||||
| @app.route("/sastojci") | ||||
| def show_ingredients(): | ||||
|     ingredients = db.session.query(Ingredient).all() | ||||
|     data = {"ingredients": ingredients} | ||||
|     return render_template("pages/ingredients.html", data=data) | ||||
|  | ||||
|  | ||||
| @app.get("/ingredients/new") | ||||
| def show_new_ingredient_form(): | ||||
|     return render_template("pages/add_ingredient.html") | ||||
|  | ||||
|  | ||||
| @app.post("/ingredients/new") | ||||
| def add_new_ingredient(): | ||||
|     name = request.form["name"] | ||||
|     price = request.form["price"] | ||||
|     ingredient = Ingredient(name=name, price=price) | ||||
|     try: | ||||
|         db.session.add(ingredient) | ||||
|         db.session.commit() | ||||
|     except Exception as e: | ||||
|         return "greska, vec postoji sastojak" | ||||
|  | ||||
|     return "napravljeno" | ||||
|  | ||||
|  | ||||
| @app.get("/ingredients/edit/<int:id>") | ||||
| def show_edit_ingredient_form(id): | ||||
|     ingredient = Ingredient.query.get(id) | ||||
|     return render_template("pages/edit_ingredient.html", ingredient=ingredient) | ||||
|  | ||||
|  | ||||
| @app.post("/ingredients/edit/<int:id>") | ||||
| def edit_ingredient(id): | ||||
|     old_ingredient = Ingredient.query.get(id) | ||||
|     name = request.form["name"] | ||||
|     price = request.form["price"] | ||||
|     if old_ingredient.name != name: | ||||
|         old_ingredient.name = name | ||||
|     if old_ingredient.price != price: | ||||
|         old_ingredient.price = price | ||||
|  | ||||
|     db.session.flush() | ||||
|     db.session.commit() | ||||
|     return "updated" | ||||
|  | ||||
|  | ||||
| @app.get("/ingredients/delete/<int:id>") | ||||
| def delete_ingredient(id): | ||||
|     Ingredient.query.filter_by(id=id).delete() | ||||
|     db.session.commit() | ||||
|     return "Deleted" | ||||
|  | ||||
|  | ||||
| # Recipes CRUD | ||||
| @app.get("/recipes/<int:id>") | ||||
| def show_recipe(id: int): | ||||
|     recipe = Recipe.query.get(id) | ||||
|     ingredients = [] | ||||
|     for ingredient in recipe.ingredients: | ||||
|         ingr = Ingredient.query.get(ingredient.ingredient_id) | ||||
|         ingr_dict = { | ||||
|             "name": ingr.name, | ||||
|             "price": ingr.price, | ||||
|             "ammount": ingredient.ingredient_ammount, | ||||
|         } | ||||
|         ingredients.append(ingr_dict) | ||||
|     data = {"recipe": recipe, "ingredients": ingredients} | ||||
|     return render_template("pages/show_recipe.html", data=data) | ||||
|  | ||||
|  | ||||
| @app.get("/recipes/new") | ||||
| def create_recipe(): | ||||
|     ingredients = Ingredient.query.all() | ||||
|     data = {"ingredients": ingredients} | ||||
|     return render_template("pages/create_recipe.html", data=data) | ||||
|  | ||||
|  | ||||
| @app.post("/recipes/new") | ||||
| def create_recipe_in_db(): | ||||
|     recipe = Recipe(name=request.form["recipe_name"]) | ||||
|     ingredient_ids = list(map(int, request.form.getlist("ingredients"))) | ||||
|     ingredients = map(Ingredient.query.get, ingredient_ids) | ||||
|     for ing in ingredients: | ||||
|         assoc = RecipeIngredientAssoc() | ||||
|         assoc.ingredient = ing | ||||
|         assoc.ingredient_id = ing.id | ||||
|         assoc.ingredient_name = ing.name | ||||
|         assoc.ingredient_price = ing.price | ||||
|         assoc.recipe = recipe | ||||
|         assoc.recipe_id = recipe.id | ||||
|         ammount = float(request.form[f"{ing.id}_ammount"]) | ||||
|         assoc.ingredient_ammount = ammount | ||||
|     db.session.add(recipe) | ||||
|     db.session.commit() | ||||
|     return "created" | ||||
|  | ||||
|  | ||||
| @app.get("/recipes/edit/<int:id>") | ||||
| def edit_recipe_form(id): | ||||
|     recipe = Recipe.query.get(id) | ||||
|     ingredients_inside = [] | ||||
|     for ingredient in recipe.ingredients: | ||||
|         ingr = Ingredient.query.get(ingredient.ingredient_id) | ||||
|         ingr_dict = { | ||||
|             "name": ingr.name, | ||||
|             "price": ingr.price, | ||||
|             "ammount": ingredient.ingredient_ammount, | ||||
|             "id": ingr.id, | ||||
|         } | ||||
|         ingredients_inside.append(ingr_dict) | ||||
|     all_ingredients = Ingredient.query.all() | ||||
|     ingredients_rest = [ | ||||
|         ingr for ingr in all_ingredients if ingr not in ingredients_inside | ||||
|     ] | ||||
|     data = { | ||||
|         "recipe": recipe, | ||||
|         "ingredients_inside": ingredients_inside, | ||||
|         "ingredients_rest": ingredients_rest, | ||||
|     } | ||||
|     return render_template("pages/edit_recipe.html", data=data) | ||||
|  | ||||
|  | ||||
| @app.post("/recipes/edit/<int:id>") | ||||
| def edit_recipe_in_db(id): | ||||
|     old_recipe = Recipe.query.get(id) | ||||
|     ingredient_ids = list(map(int, request.form.getlist("ingredients"))) | ||||
|     ingredients = map(Ingredient.query.get, ingredient_ids) | ||||
|     ingredient_inside_ids = [] | ||||
|  | ||||
|     for assoc in old_recipe.ingredients: | ||||
|         ingredient_inside_ids.append(assoc.ingredient_id) | ||||
|         if assoc.ingredient_id in ingredient_ids: | ||||
|             ammount = int(request.form[f"{assoc.ingredient_id}_ammount"]) | ||||
|             assoc.ingredient_ammount = ammount | ||||
|             db.session.add(assoc) | ||||
|  | ||||
|     for ing_id in ingredient_ids: | ||||
|         if ing_id not in ingredient_inside_ids: | ||||
|             assoc = RecipeIngredientAssoc() | ||||
|             ing = Ingredient.query.get(ing_id) | ||||
|             assoc.ingredient = ing | ||||
|             assoc.ingredient_id = ing.id | ||||
|             assoc.ingredient_name = ing.name | ||||
|             assoc.ingredient_price = ing.price | ||||
|             assoc.recipe = old_recipe | ||||
|             assoc.recipe_id = old_recipe.id | ||||
|             ammount = int(request.form[f"{ing.id}_ammount"]) | ||||
|             assoc.ingredient_ammount = ammount | ||||
|  | ||||
|     db.session.add(old_recipe) | ||||
|     db.session.commit() | ||||
|     return "ok" | ||||
|  | ||||
|  | ||||
| @app.get("/calculate") | ||||
| def calculator(): | ||||
|     data = {"recipes": Recipe.query.all()} | ||||
|     return render_template("pages/calculate.html", data=data) | ||||
|  | ||||
|  | ||||
| @app.post("/calculate") | ||||
| def calculate(): | ||||
|     req_data = request.get_json() | ||||
|     recipe_id = req_data["recipe_id"] | ||||
|     recipe = Recipe.query.get(recipe_id) | ||||
|     ingredients_inside = [] | ||||
|     ammount = req_data["ammount"] | ||||
|     for ingredient in recipe.ingredients: | ||||
|         ingr = Ingredient.query.get(ingredient.ingredient_id) | ||||
|         ingr_dict = { | ||||
|             "name": ingr.name, | ||||
|             "price": ingr.price, | ||||
|             "ammount": ingredient.ingredient_ammount, | ||||
|             "calculated_ammount": float(ingredient.ingredient_ammount) | ||||
|             * (int(ammount) / 10), | ||||
|             "calculated_price": float(ingr.price) * (int(ammount) / 10), | ||||
|             "id": ingr.id, | ||||
|         } | ||||
|         ingredients_inside.append(ingr_dict) | ||||
|     price = calculate_price(ingredients_inside) | ||||
|     data = {"ingredients": ingredients_inside, "price": price} | ||||
|  | ||||
|     return jsonify(data) | ||||
|  | ||||
|  | ||||
| def calculate_price(ingredients: list) -> float: | ||||
|     price = 0 | ||||
|     for ingredient in ingredients: | ||||
|         price += ingredient["calculated_price"] | ||||
|     return price | ||||
							
								
								
									
										9
									
								
								kuhkal/templates/includes/calculate_form.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								kuhkal/templates/includes/calculate_form.html
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,9 @@ | ||||
| <form @submit.prevent method="POST"> | ||||
| 	<select x-model.number="recipe_id" name="recipe_id"> | ||||
| 		{% for recipe in data["recipes"] %} | ||||
| 			<option value="{{recipe.id}}">{{recipe.name}}</option> | ||||
| 		{% endfor %} | ||||
| 	</select> | ||||
| 	<input @input.debounce.300="getResults()" x-model.number="ammount" type="number" step="1" name="ammount" placeholder="Koliko obroka"> | ||||
| 	<!--<button @click="getResults()" type="submit">Izracunaj</button> --> | ||||
| </form> | ||||
							
								
								
									
										18
									
								
								kuhkal/templates/includes/calculate_results.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										18
									
								
								kuhkal/templates/includes/calculate_results.html
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,18 @@ | ||||
| <div> | ||||
| 	 | ||||
| 	<table class="table"> | ||||
| 		<thead> | ||||
| 			<th>Sastojak</th> | ||||
| 			<th>Kolicina</th> | ||||
| 			<th>Cena</th> | ||||
| 		</thead> | ||||
| 		<template x-for="ingredient in results['ingredients']"> | ||||
| 			<tr> | ||||
| 				<td x-text="ingredient['name']"></td> | ||||
| 				<td x-text="ingredient['calculated_ammount']"></td> | ||||
| 				<td x-text="ingredient['calculated_price'] + ' RSD'"></td> | ||||
| 			</tr> | ||||
| 		</template> | ||||
| 	</table> | ||||
| 	<p>Cena: <b x-text="results['price']"></b> RSD</p> | ||||
| </div> | ||||
							
								
								
									
										11
									
								
								kuhkal/templates/includes/edit_ingredient_form.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								kuhkal/templates/includes/edit_ingredient_form.html
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,11 @@ | ||||
| <form method="POST"> | ||||
| 	<label for="ingredient_name">Naziv</label> | ||||
| 	<input id="ingredient_name" type="text" required name="name" value="{{ingredient.name}}"> | ||||
| 	<br> | ||||
| 	<label for="ingredient_price">Cena</label> | ||||
| 	<input id="ingredient_price" type="number" required name="price" value="{{ingredient.price}}"> | ||||
| 	<br> | ||||
| 	<button> | ||||
| 		Dodaj Sastojak | ||||
| 	</button> | ||||
| </form> | ||||
							
								
								
									
										15
									
								
								kuhkal/templates/includes/edit_recipe_form.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										15
									
								
								kuhkal/templates/includes/edit_recipe_form.html
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,15 @@ | ||||
| <form method="POST"> | ||||
| 	<label for="recipe_name">Naziv: </label> | ||||
| 	<input type="text" name="recipe_name" value='{{data["recipe"].name}}'> | ||||
| 	<br> | ||||
| 	{% for ingredient in data["ingredients_inside"] %} | ||||
| 	<input checked type="checkbox" id="{{ingredient.name}}" name="ingredients" value="{{ingredient.id}}"> <label for="{{ingredient.name}}">{{ingredient.name}}</label> <input type="number" name="{{ingredient.id}}_ammount" placeholder="kg/L" value="{{ingredient.ammount}}"> | ||||
| 	<br> | ||||
| 	{% endfor %} | ||||
| 	<hr> | ||||
| 	{% for ingredient in data["ingredients_rest"] %} | ||||
| 	<input type="checkbox" id="{{ingredient.name}}" name="ingredients" value="{{ingredient.id}}"> <label for="{{ingredient.name}}">{{ingredient.name}}</label> <input type="number" name="{{ingredient.id}}_ammount" placeholder="kg/L"> | ||||
| 	<br> | ||||
| 	{% endfor %} | ||||
| 	<button>Dodaj recept</button> | ||||
| </form> | ||||
							
								
								
									
										3
									
								
								kuhkal/templates/includes/footer.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								kuhkal/templates/includes/footer.html
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,3 @@ | ||||
| <footer> | ||||
| 	<hr> | ||||
| </footer> | ||||
							
								
								
									
										13
									
								
								kuhkal/templates/includes/header.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								kuhkal/templates/includes/header.html
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,13 @@ | ||||
| <nav class="navbar navbar-expand-lg navbar-light bg-light"> | ||||
| 	<a class="navbar-brand" href="#">SK Digitron</a> | ||||
| 	  <button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation"> | ||||
| 		      <span class="navbar-toggler-icon"></span> | ||||
| 		        </button> | ||||
| 	 <div class="collapse navbar-collapse" id="navbarSupportedContent"> | ||||
| 		<ul class="navbar-nav mr-auto"> | ||||
| 			<li class="nav-item"><a class="nav-link"  href="/calculate">Digitron</a></li> | ||||
| 			<li class="nav-item"><a class="nav-link"  href="/">Recepti</a></li> | ||||
| 			<li class="nav-item"><a class="nav-link" href="/ingredients">Sastojci</a></li> | ||||
| 		</ul> | ||||
| 	 </div> | ||||
| </nav> | ||||
							
								
								
									
										11
									
								
								kuhkal/templates/includes/new_ingredient_form.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								kuhkal/templates/includes/new_ingredient_form.html
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,11 @@ | ||||
| <form method="POST"> | ||||
| 	<label for="ingredient_name">Naziv</label> | ||||
| 	<input id="ingredient_name" type="text" required name="name" placeholder="Naziv..."> | ||||
| 	<br> | ||||
| 	<label for="ingredient_price">Cena</label> | ||||
| 	<input id="ingredient_price" type="number" required name="price" placeholder="Cena..."> | ||||
| 	<br> | ||||
| 	<button> | ||||
| 		Dodaj Sastojak | ||||
| 	</button> | ||||
| </form> | ||||
							
								
								
									
										10
									
								
								kuhkal/templates/includes/new_recipe_form.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								kuhkal/templates/includes/new_recipe_form.html
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,10 @@ | ||||
| <form method="POST"> | ||||
| 	<label for="recipe_name">Naziv: </label> | ||||
| 	<input type="text" name="recipe_name"> | ||||
| 	<br> | ||||
| 	{% for ingredient in data["ingredients"] %} | ||||
| 	<input type="checkbox" id="{{ingredient.name}}" name="ingredients" value="{{ingredient.id}}"> <label for="{{ingredient.name}}">{{ingredient.name}}</label> <input step="0.01"type="number" name="{{ingredient.id}}_ammount" placeholder="kg/L"> | ||||
| 	<br> | ||||
| 	{% endfor %} | ||||
| 	<button>Dodaj recept</button> | ||||
| </form> | ||||
							
								
								
									
										17
									
								
								kuhkal/templates/layouts/base.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								kuhkal/templates/layouts/base.html
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,17 @@ | ||||
| <!doctype html> | ||||
| <html> | ||||
| 	<head> | ||||
| 		<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.2.0/dist/css/bootstrap.min.css" integrity="sha384-gH2yIJqKdNHPEq0n4Mqa/HGKIhSkIHeL5AyhkYV8i59U5AR6csBvApHHNl/vI1Bx" crossorigin="anonymous"> | ||||
| 		<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.2.0/dist/js/bootstrap.min.js" integrity="sha384-ODmDIVzN+pFdexxHEHFBQH3/9/vQ9uori45z4JjnFsRydbmQbmL5t1tQ0culUzyK" crossorigin="anonymous"></script> | ||||
| 		<script src="//unpkg.com/alpinejs" defer></script> | ||||
| 		<title>Kuhinja Kalkulator</title> | ||||
| 	</head> | ||||
| 	<body> | ||||
| 		<div class="container"> | ||||
| 			{% include "includes/header.html" %} | ||||
| 			{% block content %} | ||||
| 			{% endblock content %} | ||||
| 			{% include "includes/footer.html" %} | ||||
| 		</div> | ||||
| 	</body> | ||||
| </html> | ||||
							
								
								
									
										4
									
								
								kuhkal/templates/pages/add_ingredient.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										4
									
								
								kuhkal/templates/pages/add_ingredient.html
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,4 @@ | ||||
| {% extends "layouts/base.html" %} | ||||
| {% block content %} | ||||
| 	{% include "includes/new_ingredient_form.html" %} | ||||
| {% endblock %} | ||||
							
								
								
									
										24
									
								
								kuhkal/templates/pages/calculate.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										24
									
								
								kuhkal/templates/pages/calculate.html
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,24 @@ | ||||
| {% extends "layouts/base.html" %} | ||||
|  | ||||
| {% block content %} | ||||
| <div x-data="{ results: {}, | ||||
| 	       recipe_id: 1, | ||||
| 	       ammount:40, | ||||
| 	       async getResults() { | ||||
| 	       this.results = await (await fetch('http://localhost:5000/calculate', { | ||||
| 							method:'POST', | ||||
| 							body: JSON.stringify({ | ||||
| 								recipe_id: this.recipe_id, | ||||
| 								ammount: this.ammount | ||||
| 							}), | ||||
| 							headers: { | ||||
| 								'Content-type': 'application/json; charset=UTF-8', | ||||
| 						      }, | ||||
| 						})).json(); | ||||
| 						console.log(this.results); | ||||
| 					} | ||||
| 	 	   } "> | ||||
| 		{% include "includes/calculate_form.html" %} | ||||
| 		{% include "includes/calculate_results.html" %} | ||||
| 	</div> | ||||
| {% endblock content %} | ||||
							
								
								
									
										4
									
								
								kuhkal/templates/pages/create_recipe.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										4
									
								
								kuhkal/templates/pages/create_recipe.html
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,4 @@ | ||||
| {% extends "layouts/base.html" %} | ||||
| {% block content %} | ||||
| 	{% include "includes/new_recipe_form.html" %} | ||||
| {% endblock content %} | ||||
							
								
								
									
										4
									
								
								kuhkal/templates/pages/edit_ingredient.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										4
									
								
								kuhkal/templates/pages/edit_ingredient.html
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,4 @@ | ||||
| {% extends "layouts/base.html" %} | ||||
| {% block content %} | ||||
| 	{% include "includes/edit_ingredient_form.html" %} | ||||
| {% endblock %} | ||||
							
								
								
									
										4
									
								
								kuhkal/templates/pages/edit_recipe.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										4
									
								
								kuhkal/templates/pages/edit_recipe.html
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,4 @@ | ||||
| {% extends "layouts/base.html" %} | ||||
| {% block content %} | ||||
| 	{% include "includes/edit_recipe_form.html" %} | ||||
| {% endblock content %} | ||||
							
								
								
									
										27
									
								
								kuhkal/templates/pages/index.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										27
									
								
								kuhkal/templates/pages/index.html
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,27 @@ | ||||
| {% extends "layouts/base.html" %} | ||||
|  | ||||
| {% block content %} | ||||
| 	<div class="row"> | ||||
| 	{% for recipe in data["recipes"] %} | ||||
| 		<li> | ||||
| 			<h3>{{recipe.recipe.name}}</h3> | ||||
| 	<table class="table"> | ||||
| 			<thead> | ||||
| 				<tr> | ||||
| 					<th>Sastojak</th> | ||||
| 					<th>Kolicina</th> | ||||
| 					<th>Cena</th> | ||||
| 				</tr> | ||||
| 			</thead> | ||||
| 			{% for i in recipe.ingredients %} | ||||
| 			<tr> | ||||
| 				<td>{{ i.name }} </td> | ||||
| 				<td>{{ i.ammount }}</td> | ||||
| 				<td>{{ i.price }}</td> | ||||
| 			</tr> | ||||
| 			{% endfor %} | ||||
| 	</table> | ||||
| 		</li> | ||||
| 	{% endfor %} | ||||
| 	</div> | ||||
| {% endblock content %} | ||||
							
								
								
									
										19
									
								
								kuhkal/templates/pages/ingredients.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										19
									
								
								kuhkal/templates/pages/ingredients.html
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,19 @@ | ||||
| {% extends "layouts/base.html" %} | ||||
| {% block content %} | ||||
| 	<a href="/ingredients/new" class="btn btn-danger">Dodaj sastojak</a> | ||||
| 	<table class="table"> | ||||
| 		<thead> | ||||
| 			<th>Naziv</th> | ||||
| 			<th>Cena</th> | ||||
| 			<th>Akcije</th> | ||||
| 		</thead> | ||||
| 		{% for ingredient in data["ingredients"] %} | ||||
| 			<tr> | ||||
| 				<td>{{ingredient.name}}</td> | ||||
| 				<td>{{ingredient.price}} RSD</td> | ||||
| 				<td><a href="/ingredients/edit/{{ingredient.id}}" class="btn btn-danger">Izmeni</a></td> | ||||
| 				<td><a href="/ingredients/delete/{{ingredient.id}}" class="btn btn-dark">Obrisi</a></td> | ||||
| 			</tr> | ||||
| 		{% endfor %} | ||||
| 	</table> | ||||
| {% endblock content %} | ||||
							
								
								
									
										10
									
								
								kuhkal/templates/pages/show_recipe.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								kuhkal/templates/pages/show_recipe.html
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,10 @@ | ||||
| {% extends "layouts/base.html" %} | ||||
|  | ||||
| {% block content %} | ||||
| 	<h3>{{data["recipe"].name}}<h3> | ||||
| 	<ul> | ||||
| 		{% for ingredient in data["ingredients"] %} | ||||
| 		<li>{{ingredient.name}}</li> | ||||
| 		{% endfor %} | ||||
| 	</ul> | ||||
| {% endblock content %} | ||||
							
								
								
									
										19
									
								
								kuhkal/utils.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										19
									
								
								kuhkal/utils.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,19 @@ | ||||
| from .models import Recipe, Ingredient | ||||
| import typing as t | ||||
| from . import db | ||||
|  | ||||
| def get_all_recipes() -> t.List: | ||||
|     recipes = db.session.query(Recipe).all() | ||||
|     all_recipes = [] | ||||
|     for recipe in recipes: | ||||
|         ingredients = [] | ||||
|         for ingredient in recipe.ingredients: | ||||
|             ingr = Ingredient.query.get(ingredient.ingredient_id) | ||||
|             ingr_dict = { | ||||
|                 "name": ingr.name, | ||||
|                 "price": ingr.price, | ||||
|                 "ammount": ingredient.ingredient_ammount, | ||||
|             } | ||||
|             ingredients.append(ingr_dict) | ||||
|         all_recipes.append({"recipe": recipe, "ingredients": ingredients}) | ||||
|     return all_recipes | ||||
							
								
								
									
										4
									
								
								requirements.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										4
									
								
								requirements.txt
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,4 @@ | ||||
| flask | ||||
| Flask-SQLAlchemy | ||||
| black | ||||
| bandit | ||||
							
								
								
									
										4
									
								
								run.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										4
									
								
								run.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,4 @@ | ||||
| from kuhkal import app | ||||
|  | ||||
| if __name__ == "__main__": | ||||
|     app.run() | ||||
							
								
								
									
										31
									
								
								test_db.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										31
									
								
								test_db.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,31 @@ | ||||
| from kuhkal import models | ||||
| from kuhkal import db | ||||
|  | ||||
| def main(): | ||||
|     db.create_all() | ||||
|     recipe = models.Recipe(name="Pasulj") | ||||
|     ing_1 = models.Ingredient(name="Meso", price=30) | ||||
|     ing_2 = models.Ingredient(name="Luk", price=38) | ||||
|     db.session.add(ing_1) | ||||
|     db.session.add(ing_2) | ||||
|     db.session.commit() | ||||
|     ingredients = [ing_2, ing_1] | ||||
|     for ing in ingredients: | ||||
|         assoc = models.RecipeIngredientAssoc() | ||||
|         assoc.ingredient = ing | ||||
|         assoc.ingredient_id = ing.id | ||||
|         assoc.ingredient_name= ing.name | ||||
|         assoc.ingredient_price= ing.price | ||||
|         assoc.recipe = recipe | ||||
|         assoc.recipe_id = recipe.id | ||||
|         assoc.ingredient_ammount = 30 | ||||
|     for ing in recipe.ingredients: | ||||
|         print(ing.ingredient.name) | ||||
|     db.session.add(recipe) | ||||
|     db.session.commit() | ||||
|  | ||||
|  | ||||
| if __name__ == "__main__": | ||||
|     main() | ||||
|      | ||||
|      | ||||
		Reference in New Issue
	
	Block a user