Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[WIP] Shared recipe pagination #205

Open
wants to merge 8 commits into
base: dev
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 9 additions & 1 deletion app/controllers/admin.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# from flask import redirect, url_for
from flask import redirect

# from flask_login import current_user

Expand Down Expand Up @@ -31,3 +31,11 @@ def index(self):
self.test = User.load_all()

return self.template()

def update_recipe_ratios(self):
recipes = Recipe.load_all()

for recipe in recipes:
recipe.update_ratio()

return redirect("/admin/")
71 changes: 44 additions & 27 deletions app/controllers/cookbook.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,16 @@

from flask_classful import route

# from flask import request

from flask_login import current_user

from app.data.texts import texts
from .extended_flask_view import ExtendedFlaskView
from .forms.cookbook_filter import CookbookFilterForm

from app.helpers.formaters import coma_to_float


class CookbookView(ExtendedFlaskView):
template_folder = "cookbook"
Expand All @@ -19,49 +23,62 @@ def before_index(self):
message = texts.cookbook.not_logged_in
return redirect(url_for("CookbookView:not_logged_in", message=message))

self.recipes = Recipe.public_recipes()
# Get filters from form/args
ratio_from = coma_to_float(request.args.get("ratio_from", None))
ratio_to = coma_to_float(request.args.get("ratio_to", None))

if request.args.get("with_reaction", "n") == "y":
with_reaction = True
else:
with_reaction = False

if request.args.get("ingredient_name", "--všechny--") != "--všechny--":
ingredient_name = request.args.get("ingredient_name", None)
else:
ingredient_name = None

# page = request.args.get("page", 1, type=int)
# self.recipe_pagination = Recipe.public_recipes_paginated(page, 30)
# self.recipes = self.recipe_pagination.items
self.all_recipes = Recipe.public_recipes()
self.recipes = Recipe.get_filtered_paginated_public_recipes(
1,
filters={
"ratio_from": ratio_from,
"ratio_to": ratio_to,
"with_reaction": with_reaction,
"ingredient_name": ingredient_name,
},
)

# Get values for filters
ingredients = [x.ingredients for x in self.recipes]
ingredients = [x.ingredients for x in self.all_recipes]
flatten_ingredients = [y for x in ingredients for y in x]
ingredient_names = [x.name for x in flatten_ingredients]
self.ingredient_names = ["--všechny--"]
self.ingredient_names.extend(list(set(ingredient_names)))
self.ingredient_names.sort()

if request.method == "GET":
self.form = CookbookFilterForm(ingredient_names=self.ingredient_names)
else:
self.form = CookbookFilterForm(
request.form, ingredient_names=self.ingredient_names
)

# Get filters from request
ingredient_name = None
ratio_from = None
ratio_to = None
with_reaction = None

if request.method == "POST":
if not self.form.ingredient_name.data == "--všechny--":
ingredient_name = self.form.ingredient_name.data
ratio_from = self.form.ratio_from.data
ratio_to = self.form.ratio_to.data
with_reaction = self.form.with_reaction.data
self.form = CookbookFilterForm(ingredient_names=self.ingredient_names)
self.form.ratio_from.data = ratio_from
self.form.ratio_to.data = ratio_to
self.form.with_reaction.data = with_reaction
self.form.ingredient_name.data = ingredient_name

# Filter recipes
if ingredient_name:
self.recipes = [
x for x in self.recipes if ingredient_name in x.concat_ingredients
]

if ratio_from:
self.recipes = [x for x in self.recipes if x.ratio >= ratio_from]
# if ratio_from:
# self.recipes = [x for x in self.recipes if x.ratio >= ratio_from]

if ratio_to:
self.recipes = [x for x in self.recipes if x.ratio <= ratio_to]
# if ratio_to:
# self.recipes = [x for x in self.recipes if x.ratio <= ratio_to]

if with_reaction:
self.recipes = [x for x in self.recipes if x.has_reaction]
# if with_reaction:
# self.recipes = [x for x in self.recipes if x.has_reaction]

@route("/", methods=["GET", "POST"])
def index(self):
Expand Down
2 changes: 1 addition & 1 deletion app/controllers/files.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ def show(self, hash_value):
def delete(self, id):
file = File.load(id)
if file.can_current_user_delete:
file.delete()
file.remove()
else:
flash("Nemáte právo toto foto smazat.", "error")
return redirect(request.referrer)
Expand Down
29 changes: 29 additions & 0 deletions app/helpers/calculations.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
from sympy import poly
from sympy.solvers import solvers

import math


# CALCULATE RECIPE
def calculate_recipe(ingredients, diet):
Expand Down Expand Up @@ -198,3 +200,30 @@ def calculate_recipe(ingredients, diet):
diet.expire()

return {"ingredients": ingredients, "totals": totals}


def calculate_ratio_for_recipe(recipe):
totals = types.SimpleNamespace()
metrics = ["calorie", "sugar", "fat", "protein"]

totals.amount = 0

for ingredient in recipe.ingredients:
ingredient.amount = round(ingredient.load_amount_by_recipe(recipe.id), 2)
for metric in metrics:
value = getattr(totals, metric, 0)
ing_value = getattr(ingredient, metric)
setattr(totals, metric, value + (ingredient.amount * ing_value))

totals.amount += ingredient.amount

for metric in metrics:
value = getattr(totals, metric)
setattr(totals, metric, math.floor(value) / 100)

totals.amount = math.floor(totals.amount)

totals.ratio = (
math.floor((totals.fat / (totals.protein + totals.sugar)) * 100) / 100
)
return totals.ratio
10 changes: 10 additions & 0 deletions app/helpers/formaters.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,13 @@
# private
def parse_date(date):
return datetime.datetime.strptime(date, "%Y-%m-%d").date()


def coma_to_float(string):
if string is None:
return None
string = string.replace(",", ".")
try:
return float(string)
except Exception:
return None
31 changes: 20 additions & 11 deletions app/models/base_mixin.py
Original file line number Diff line number Diff line change
Expand Up @@ -66,14 +66,9 @@ def created_recently(cls, days=30):

# DATABASE OPERATIONS

def edit(self, **kw):
try:
db.session.commit()
return True
except Exception as e:
db.session.rollback()
application.logger.error("Edit error: {}".format(e))
return False
# This is more Crud
# def create(self, **kw):
# return self.save(**kw)

def save(self, **kw):
"""Saves (new) object
Expand All @@ -87,6 +82,23 @@ def save(self, **kw):
application.logger.error("Save error: {}".format(e))
return False

# This is more crUd
# def update(self, **kw):
# return self.edit(**kw)

def edit(self, **kw):
try:
db.session.commit()
return True
except Exception as e:
db.session.rollback()
application.logger.error("Edit error: {}".format(e))
return False

# This is more cruD
# def delete(self, **kw):
# return self.remove(**kw)

def remove(self, **kw):
"""Deletes object
"""
Expand All @@ -99,9 +111,6 @@ def remove(self, **kw):
application.logger.error("Remove error: {}".format(e))
return False

def delete(self, **kw):
return self.remove(**kw)

def expire(self, **kw):
"""Dumps database changes
"""
Expand Down
59 changes: 54 additions & 5 deletions app/models/recipes.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,15 @@
import math
import types

from sqlalchemy import or_
from flask_login import current_user

from app import db

# from app import cache

from flask_login import current_user
from app.helpers.calculations import calculate_ratio_for_recipe


from app.models.item_mixin import ItemMixin

Expand Down Expand Up @@ -38,6 +42,8 @@ class Recipe(db.Model, ItemMixin):
order_by="Ingredient.name",
)

ratio = db.Column(db.Float)

has_daily_plans = db.relationship("DailyPlanHasRecipes", back_populates="recipe")

@staticmethod
Expand Down Expand Up @@ -81,6 +87,47 @@ def public_recipes():
recipes = db.session.query(Recipe).filter(Recipe.public).all()
return recipes

@staticmethod
def public_recipes_paginated(page, items_per_page=20):
recipes = (
db.session.query(Recipe)
.filter(Recipe.is_shared == True)
.paginate(page, items_per_page, False)
) # noqa: E712
return recipes

def get_filtered_paginated_public_recipes(page=1, filters={}, items_per_page=30):
recipes_query = (
db.session.query(Recipe)
.join(Recipe.reactions, isouter=True)
.filter(
or_(
UserRecipeReactions.user_id == current_user.id,
UserRecipeReactions.id == None,
) # noqa: E712
)
.filter(Recipe.is_shared == True)
)
if "ratio_from" in filters and filters["ratio_from"]:
recipes_query = recipes_query.filter(Recipe.ratio >= filters["ratio_from"])
if "ratio_to" in filters and filters["ratio_to"]:
recipes_query = recipes_query.filter(Recipe.ratio <= filters["ratio_to"])
if "with_reaction" in filters and filters["with_reaction"] is True:
recipes_query = recipes_query.filter(
UserRecipeReactions.user_id == current_user.id
and UserRecipeReactions.recipe_id == Recipe.id
)
if "ingredient_name" in filters and filters["ingredient_name"]:
recipes_query = (
recipes_query.join(RecipeHasIngredients)
.join(Ingredient)
.filter(Ingredient.name == filters["ingredient_name"])
)

print(recipes_query)

return recipes_query.all()

def create_and_save(self, ingredients):
db.session.add(self)
db.session.flush()
Expand All @@ -104,6 +151,12 @@ def remove(self):
db.session.commit()
return True

def update_ratio(self):
if not self.ratio:
ratio = calculate_ratio_for_recipe(self)
self.ratio = ratio
self.edit()

def toggle_shared(self):
self.is_shared = not self.is_shared
self.edit()
Expand Down Expand Up @@ -156,10 +209,6 @@ def totals(self):
)
return totals

@property
def ratio(self):
return self.totals.ratio

@property
def values(self):
values = types.SimpleNamespace()
Expand Down
5 changes: 4 additions & 1 deletion app/templates/cookbook/index.html.j2
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@

{% block content %}
<div class="container" data-controller="recipe-reactions">
<form action="{{ url_for('CookbookView:index') }}" method="post" class="form-inline form-control" accept-charset="UTF-8">
<form action="{{ url_for('CookbookView:index') }}" method="get" class="form-inline form-control" accept-charset="UTF-8">
{{ form.csrf_token }}
{% from "macros/_form_element.html.j2" import render_field %}
<div>
Expand Down Expand Up @@ -59,6 +59,9 @@
{% endfor %}
</tbody>
</table>

{# {% from "macros/_pagination.html.j2" import pagination %} #}
{# {{ pagination(recipe_pagination, view_page="CookbookView:index") }} #}
</div>
{% endblock %}

34 changes: 34 additions & 0 deletions app/templates/macros/_pagination.html.j2
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
{% macro pagination(pagination, view_page) %}

<div class="text-center">
<!-- first page -->
{% if pagination.pages > 0 %}
<a href="{{ url_for(view_page, page=1) }}" class="mr-1"> << </a>
{% endif %}
<!-- previous page -->
{% if pagination.has_prev %}
<a href="{{ url_for(view_page, page=pagination.prev_num) }}" class="mr-1">Předchozí</a>
{% endif %}
<!-- all page numbers -->
{% for page_num in pagination.iter_pages() %}
{% if page_num %}
{% if page_num != pagination.page %}
<a href="{{ url_for(view_page, page=page_num) }}" class="mr-1">{{ page_num }}</a>
{% else %}
<a href="#" class="font-weight-bold mr-1">{{ page_num }}</a>
{% endif %}
{% else %}
<span class="ellipsis" style="white-space; nowrap; overflow: hidden; text-overflow: ellipsis">…</span>
{% endif %}
{% endfor %}
<!-- next page -->
{% if pagination.has_next %}
<a href="{{ url_for(view_page, page=pagination.next_num) }}" class="mr-1">Další</a>
{% endif %}
<!-- last page -->
{% if pagination.pages > 0 %}
<a href="{{ url_for(view_page, page=pagination.pages) }}" class="mr-1"> >> </a>
{% endif %}
</div>

{% endmacro %}
Loading