mirror of https://github.com/kcal-app/kcal.git
Implement FDA guideline-based rounding
Rounding is left out of Food nutrients for now to prevent rounding up before summing in certain places.
This commit is contained in:
parent
0f2a506add
commit
17e640303d
|
|
@ -103,7 +103,7 @@ final class IngredientAmount extends Model
|
||||||
public function getNutrientsSummaryAttribute(): string {
|
public function getNutrientsSummaryAttribute(): string {
|
||||||
$summary = [];
|
$summary = [];
|
||||||
foreach (Nutrients::all() as $nutrient) {
|
foreach (Nutrients::all() as $nutrient) {
|
||||||
$amount = round($this->{$nutrient['value']}(), 2);
|
$amount = Nutrients::round($this->{$nutrient['value']}(), $nutrient['value']);
|
||||||
$summary[] = "{$nutrient['label']}: {$amount}{$nutrient['unit']}";
|
$summary[] = "{$nutrient['label']}: {$amount}{$nutrient['unit']}";
|
||||||
}
|
}
|
||||||
return implode(', ', $summary);
|
return implode(', ', $summary);
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,7 @@ use App\Models\Traits\Ingredient;
|
||||||
use App\Models\Traits\Journalable;
|
use App\Models\Traits\Journalable;
|
||||||
use App\Models\Traits\Sluggable;
|
use App\Models\Traits\Sluggable;
|
||||||
use App\Models\Traits\Taggable;
|
use App\Models\Traits\Taggable;
|
||||||
|
use App\Support\Nutrients;
|
||||||
use ElasticScoutDriverPlus\QueryDsl;
|
use ElasticScoutDriverPlus\QueryDsl;
|
||||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||||
use Illuminate\Database\Eloquent\Model;
|
use Illuminate\Database\Eloquent\Model;
|
||||||
|
|
@ -165,7 +166,7 @@ final class Recipe extends Model implements HasMedia
|
||||||
if (empty($this->weight)) {
|
if (empty($this->weight)) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
return round($this->weight / $this->servings, 2);
|
return round($this->weight / $this->servings);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -209,13 +210,6 @@ final class Recipe extends Model implements HasMedia
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Add nutrient calculations handling to overloading.
|
* Add nutrient calculations handling to overloading.
|
||||||
*
|
|
||||||
* @param string $method
|
|
||||||
* @param array $parameters
|
|
||||||
*
|
|
||||||
* @return mixed
|
|
||||||
*
|
|
||||||
* @noinspection PhpMissingParamTypeInspection
|
|
||||||
*/
|
*/
|
||||||
public function __call($method, $parameters): mixed {
|
public function __call($method, $parameters): mixed {
|
||||||
if (in_array($method, $this->nutrientTotalMethods)) {
|
if (in_array($method, $this->nutrientTotalMethods)) {
|
||||||
|
|
@ -223,15 +217,7 @@ final class Recipe extends Model implements HasMedia
|
||||||
}
|
}
|
||||||
elseif (in_array($method, $this->nutrientPerServingMethods)) {
|
elseif (in_array($method, $this->nutrientPerServingMethods)) {
|
||||||
$sum = $this->sumNutrient(substr($method, 0, -10)) / $this->servings;
|
$sum = $this->sumNutrient(substr($method, 0, -10)) / $this->servings;
|
||||||
|
return Nutrients::round($sum, substr($method, 0, -10));
|
||||||
// Per-serving calculations are rounded, though actual food label
|
|
||||||
// rounding standards are more complex.
|
|
||||||
if ($sum > 1) {
|
|
||||||
return round($sum);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
return round($sum, 2);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
return parent::__call($method, $parameters);
|
return parent::__call($method, $parameters);
|
||||||
|
|
|
||||||
|
|
@ -188,4 +188,24 @@ class Nutrients
|
||||||
throw new \DomainException("Unsupported recipe unit: {$fromUnit}");
|
throw new \DomainException("Unsupported recipe unit: {$fromUnit}");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Round a nutrient amount according to FDA guidelines.
|
||||||
|
*
|
||||||
|
* Note: this stays mostly true to the guidelines except that carbohydrates
|
||||||
|
* and protein are meant to state "less than 1 gram" when the amount is less
|
||||||
|
* than 1 gram. Instead, this method treats anything less than 1 gram as
|
||||||
|
* zero.
|
||||||
|
*
|
||||||
|
* @url https://labelcalc.com/food-labeling/a-guide-to-using-fda-rounding-rules-for-your-food-label/
|
||||||
|
*/
|
||||||
|
public static function round(float $amount, string $nutrient): float {
|
||||||
|
return match ($nutrient) {
|
||||||
|
'calories' => ($amount < 5 ? 0 : ($amount <= 50 ? round( $amount / 5 ) * 5 : round( $amount / 10 ) * 10)),
|
||||||
|
'carbohydrates', 'protein' => ($amount < 1 ? 0 : round( $amount)),
|
||||||
|
'cholesterol', 'fat' => ($amount < 0.5 ? 0 : ($amount <= 5 ? round( $amount / 5, 1 ) * 5 : round($amount))),
|
||||||
|
'sodium' => ($amount < 5 ? 0 : ($amount <= 140 ? round( $amount / 5 ) * 5 : round( $amount / 10 ) * 10)),
|
||||||
|
default => throw new \UnexpectedValueException()
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -104,7 +104,7 @@
|
||||||
</div>
|
</div>
|
||||||
<span class="text-sm text-gray-500">
|
<span class="text-sm text-gray-500">
|
||||||
@foreach(\App\Support\Nutrients::all()->sortBy('weight') as $nutrient)
|
@foreach(\App\Support\Nutrients::all()->sortBy('weight') as $nutrient)
|
||||||
{{ round($entries->where('meal', $meal)->sum($nutrient['value']), 2) }}{{ $nutrient['unit'] }}
|
{{ \App\Support\Nutrients::round($entries->where('meal', $meal)->sum($nutrient['value']), $nutrient['value']) }}{{ $nutrient['unit'] }}
|
||||||
{{ $nutrient['value'] }}@if(!$loop->last), @endif
|
{{ $nutrient['value'] }}@if(!$loop->last), @endif
|
||||||
@endforeach
|
@endforeach
|
||||||
</span>
|
</span>
|
||||||
|
|
@ -124,7 +124,7 @@
|
||||||
<div>
|
<div>
|
||||||
<span class="font-bold">nutrients:</span>
|
<span class="font-bold">nutrients:</span>
|
||||||
@foreach(\App\Support\Nutrients::all()->sortBy('weight') as $nutrient)
|
@foreach(\App\Support\Nutrients::all()->sortBy('weight') as $nutrient)
|
||||||
{{ round($entry->{$nutrient['value']}, 2) }}{{ $nutrient['unit'] }}
|
{{ \App\Support\Nutrients::round($entry->{$nutrient['value']}, $nutrient['value']) }}{{ $nutrient['unit'] }}
|
||||||
{{ $nutrient['value'] }}@if(!$loop->last), @endif
|
{{ $nutrient['value'] }}@if(!$loop->last), @endif
|
||||||
@endforeach
|
@endforeach
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue