added support for fractions
This commit is contained in:
parent
9863447bac
commit
7732aa7646
@ -19,6 +19,14 @@ POSTGRES_USER=djangodb
|
||||
POSTGRES_PASSWORD=
|
||||
POSTGRES_DB=djangodb
|
||||
|
||||
# the default value for the user preference 'fractions' (enable/disable fraction support)
|
||||
# when unset: 0 (disabled)
|
||||
FRACTION_PREF_DEFAULT=0
|
||||
|
||||
# the default value for the user preference 'comments' (enable/disable commenting system)
|
||||
# when unset: 1 (true)
|
||||
COMMENT_PREF_DEFAULT=1
|
||||
|
||||
# Users can set a amount of time after which the shopping list is refreshed when they are in viewing mode
|
||||
# This is the minimum interval users can set. Setting this to low will allow users to refresh very frequently which
|
||||
# might cause high load on the server. (Technically they can obviously refresh as often as they want with their own scripts)
|
||||
@ -36,14 +44,9 @@ SHOPPING_MIN_AUTOSYNC_INTERVAL=5
|
||||
# when unset: 1 (true) - this is temporary until an appropriate amount of time has passed for everyone to migrate
|
||||
GUNICORN_MEDIA=0
|
||||
|
||||
|
||||
# allow authentication via reverse proxy (e.g. authelia), leave of if you dont know what you are doing
|
||||
# docs: https://github.com/vabene1111/recipes/tree/develop/docs/docker/nginx-proxy%20with%20proxy%20authentication
|
||||
# when unset: 0 (false)
|
||||
REVERSE_PROXY_AUTH=0
|
||||
|
||||
|
||||
# the default value for the user preference 'comments' (enable/disable commenting system)
|
||||
# when unset: 1 (true)
|
||||
COMMENT_PREF_DEFAULT=1
|
||||
|
||||
|
@ -31,11 +31,12 @@ class UserPreferenceForm(forms.ModelForm):
|
||||
|
||||
class Meta:
|
||||
model = UserPreference
|
||||
fields = ('default_unit', 'theme', 'nav_color', 'default_page', 'show_recent', 'search_style', 'plan_share', 'ingredient_decimals', 'shopping_auto_sync', 'comments')
|
||||
fields = ('default_unit', 'use_fractions', 'theme', 'nav_color', 'default_page', 'show_recent', 'search_style', 'plan_share', 'ingredient_decimals', 'shopping_auto_sync', 'comments')
|
||||
|
||||
help_texts = {
|
||||
'nav_color': _('Color of the top navigation bar. Not all colors work with all themes, just try them out!'),
|
||||
'default_unit': _('Default Unit to be used when inserting a new ingredient into a recipe.'),
|
||||
'use_fractions': _('Enables support for fractions in ingredient amounts (e.g. convert decimals to fractions automatically)'),
|
||||
'plan_share': _('Default user to share newly created meal plan entries with.'),
|
||||
'show_recent': _('Show recently viewed recipes on search page.'),
|
||||
'ingredient_decimals': _('Number of decimals to round ingredients.'),
|
||||
|
24
cookbook/migrations/0090_auto_20201214_1359.py
Normal file
24
cookbook/migrations/0090_auto_20201214_1359.py
Normal file
@ -0,0 +1,24 @@
|
||||
# Generated by Django 3.1.3 on 2020-12-14 12:59
|
||||
|
||||
import datetime
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('cookbook', '0089_auto_20201117_2222'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='userpreference',
|
||||
name='use_fractions',
|
||||
field=models.BooleanField(default=False),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='invitelink',
|
||||
name='valid_until',
|
||||
field=models.DateField(default=datetime.date(2020, 12, 28)),
|
||||
),
|
||||
]
|
@ -9,7 +9,7 @@ from django.utils.translation import gettext as _
|
||||
from django.db import models
|
||||
from django_random_queryset import RandomManager
|
||||
|
||||
from recipes.settings import COMMENT_PREF_DEFAULT
|
||||
from recipes.settings import COMMENT_PREF_DEFAULT, FRACTION_PREF_DEFAULT
|
||||
|
||||
|
||||
def get_user_name(self):
|
||||
@ -69,6 +69,7 @@ class UserPreference(models.Model):
|
||||
theme = models.CharField(choices=THEMES, max_length=128, default=FLATLY)
|
||||
nav_color = models.CharField(choices=COLORS, max_length=128, default=PRIMARY)
|
||||
default_unit = models.CharField(max_length=32, default='g')
|
||||
use_fractions = models.BooleanField(default=FRACTION_PREF_DEFAULT)
|
||||
default_page = models.CharField(choices=PAGES, max_length=64, default=SEARCH)
|
||||
search_style = models.CharField(choices=SEARCH_STYLE, max_length=64, default=LARGE)
|
||||
show_recent = models.BooleanField(default=True)
|
||||
|
43
cookbook/static/js/frac.js
Normal file
43
cookbook/static/js/frac.js
Normal file
@ -0,0 +1,43 @@
|
||||
/* frac.js (C) 2012-present SheetJS -- http://sheetjs.com */
|
||||
/*https://developer.aliyun.com/mirror/npm/package/frac/v/0.3.0 Apache license*/
|
||||
var frac = function frac(x, D, mixed) {
|
||||
var n1 = Math.floor(x), d1 = 1;
|
||||
var n2 = n1+1, d2 = 1;
|
||||
if(x !== n1) while(d1 <= D && d2 <= D) {
|
||||
var m = (n1 + n2) / (d1 + d2);
|
||||
if(x === m) {
|
||||
if(d1 + d2 <= D) { d1+=d2; n1+=n2; d2=D+1; }
|
||||
else if(d1 > d2) d2=D+1;
|
||||
else d1=D+1;
|
||||
break;
|
||||
}
|
||||
else if(x < m) { n2 = n1+n2; d2 = d1+d2; }
|
||||
else { n1 = n1+n2; d1 = d1+d2; }
|
||||
}
|
||||
if(d1 > D) { d1 = d2; n1 = n2; }
|
||||
if(!mixed) return [0, n1, d1];
|
||||
var q = Math.floor(n1/d1);
|
||||
return [q, n1 - q*d1, d1];
|
||||
};
|
||||
frac.cont = function cont(x, D, mixed) {
|
||||
var sgn = x < 0 ? -1 : 1;
|
||||
var B = x * sgn;
|
||||
var P_2 = 0, P_1 = 1, P = 0;
|
||||
var Q_2 = 1, Q_1 = 0, Q = 0;
|
||||
var A = Math.floor(B);
|
||||
while(Q_1 < D) {
|
||||
A = Math.floor(B);
|
||||
P = A * P_1 + P_2;
|
||||
Q = A * Q_1 + Q_2;
|
||||
if((B - A) < 0.00000005) break;
|
||||
B = 1 / (B - A);
|
||||
P_2 = P_1; P_1 = P;
|
||||
Q_2 = Q_1; Q_1 = Q;
|
||||
}
|
||||
if(Q > D) { if(Q_1 > D) { Q = Q_2; P = P_2; } else { Q = Q_1; P = P_1; } }
|
||||
if(!mixed) return [0, sgn * P, Q];
|
||||
var q = Math.floor(sgn * P/Q);
|
||||
return [q, sgn*P - q*Q, Q];
|
||||
};
|
||||
// eslint-disable-next-line no-undef
|
||||
if(typeof module !== 'undefined' && typeof DO_NOT_EXPORT_FRAC === 'undefined') module.exports = frac;
|
@ -12,6 +12,8 @@
|
||||
{% include 'include/vue_base.html' %}
|
||||
<script src="{% static 'js/moment-with-locales.min.js' %}"></script>
|
||||
|
||||
<script src="{% static 'js/frac.js' %}"></script>
|
||||
|
||||
<link rel="stylesheet" href="{% static 'css/pretty-checkbox.min.css' %}">
|
||||
<link rel="stylesheet" href="{% static 'custom/css/markdown_blockquote.css' %}">
|
||||
|
||||
@ -150,7 +152,7 @@
|
||||
<span>⁣</span>
|
||||
</template>
|
||||
<template v-if="!i.no_amount">
|
||||
<span>[[roundDecimals(i.amount * ingredient_factor)]]</span>
|
||||
<span v-html="calculateAmount(i.amount)"></span>
|
||||
{# Allow for amounts without units, such as "2 eggs" #}
|
||||
<template v-if="i.unit">
|
||||
[[i.unit.name]]
|
||||
@ -221,7 +223,7 @@
|
||||
</td>
|
||||
<td style="text-align: right">{{ recipe.nutrition.calories|floatformat:2 }}</td>
|
||||
<td>kcal</td>
|
||||
</tr>
|
||||
</tr>
|
||||
<tr>
|
||||
<tr>
|
||||
<td style="padding-top: 8px!important; ">
|
||||
@ -229,7 +231,7 @@
|
||||
</td>
|
||||
<td style="text-align: right">{{ recipe.nutrition.carbohydrates|floatformat:2 }}</td>
|
||||
<td>g</td>
|
||||
</tr>
|
||||
</tr>
|
||||
<tr>
|
||||
<tr>
|
||||
<td style="padding-top: 8px!important; ">
|
||||
@ -237,23 +239,23 @@
|
||||
</td>
|
||||
<td style="text-align: right">{{ recipe.nutrition.fats|floatformat:2 }}</td>
|
||||
<td>g</td>
|
||||
</tr>
|
||||
</tr>
|
||||
<tr>
|
||||
<tr>
|
||||
<td style="padding-top: 8px!important; ">
|
||||
<b>{% trans 'Proteins' %}</b>
|
||||
</td>
|
||||
<td style="text-align: right">{{ recipe.nutrition.proteins|floatformat:2 }}</td>
|
||||
<td>g</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<tr>
|
||||
<td style="padding-top: 8px!important; ">
|
||||
<b>{% trans 'Proteins' %}</b>
|
||||
</td>
|
||||
<td style="text-align: right">{{ recipe.nutrition.proteins|floatformat:2 }}</td>
|
||||
<td>g</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td></td>
|
||||
<td></td>
|
||||
<td></td>
|
||||
</tr>
|
||||
</table>
|
||||
{% if recipe.nutrition.source %}
|
||||
Source: {{ recipe.nutrition.source }}
|
||||
Source: {{ recipe.nutrition.source }}
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
@ -325,7 +327,7 @@
|
||||
<span>⁣</span>
|
||||
</template>
|
||||
<template v-if="!i.no_amount">
|
||||
<span>[[roundDecimals(i.amount * ingredient_factor)]]</span>
|
||||
<span v-html="calculateAmount(i.amount)"></span>
|
||||
{# Allow for amounts without units, such as "2 eggs" #}
|
||||
<template v-if="i.unit">
|
||||
[[i.unit.name]]
|
||||
@ -577,8 +579,28 @@
|
||||
},
|
||||
getShoppingUrl: function () {
|
||||
return `{% url 'view_shopping' %}?r=[${this.recipe.id},${this.ingredient_factor}]`
|
||||
}
|
||||
},
|
||||
calculateAmount: function (amount) {
|
||||
{% if request.user.userpreference.use_fractions %}
|
||||
let return_string = ''
|
||||
let fraction = frac.cont(amount, 9, true)
|
||||
|
||||
if (fraction[0] > 0) {
|
||||
return_string += fraction[0]
|
||||
}
|
||||
|
||||
if (fraction[1] > 0) {
|
||||
return_string += ` <sup>${(fraction[1])}</sup>⁄<sub>${(fraction[2])}</sub>`
|
||||
}
|
||||
|
||||
return return_string
|
||||
{% else %}
|
||||
return this.roundDecimals(amount * this.ingredient_factor)
|
||||
{% endif %}
|
||||
},
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
</script>
|
||||
{% endblock %}
|
@ -202,6 +202,7 @@ def user_settings(request):
|
||||
up.plan_share.set(form.cleaned_data['plan_share'])
|
||||
up.ingredient_decimals = form.cleaned_data['ingredient_decimals']
|
||||
up.comments = form.cleaned_data['comments']
|
||||
up.use_fractions = form.cleaned_data['use_fractions']
|
||||
|
||||
up.shopping_auto_sync = form.cleaned_data['shopping_auto_sync']
|
||||
if up.shopping_auto_sync < settings.SHOPPING_MIN_AUTOSYNC_INTERVAL:
|
||||
|
@ -31,6 +31,7 @@ REVERSE_PROXY_AUTH = bool(int(os.getenv('REVERSE_PROXY_AUTH', False)))
|
||||
|
||||
# default value for user preference 'comment'
|
||||
COMMENT_PREF_DEFAULT = bool(int(os.getenv('COMMENT_PREF_DEFAULT', True)))
|
||||
FRACTION_PREF_DEFAULT = bool(int(os.getenv('FRACTION_PREF_DEFAULT', False)))
|
||||
|
||||
# minimum interval that users can set for automatic sync of shopping lists
|
||||
SHOPPING_MIN_AUTOSYNC_INTERVAL = int(os.getenv('SHOPPING_MIN_AUTOSYNC_INTERVAL', 5))
|
||||
|
Loading…
Reference in New Issue
Block a user