mirror of https://github.com/kcal-app/kcal.git
Attempt to improve coherency of journal entry summaries
This commit is contained in:
parent
8d3f2cc35c
commit
69ce3cb277
|
|
@ -20,6 +20,8 @@ use Illuminate\Http\RedirectResponse;
|
||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
use Illuminate\Support\Carbon;
|
use Illuminate\Support\Carbon;
|
||||||
use Illuminate\Support\Facades\Auth;
|
use Illuminate\Support\Facades\Auth;
|
||||||
|
use Illuminate\Support\Pluralizer;
|
||||||
|
use Illuminate\Support\Str;
|
||||||
|
|
||||||
class JournalEntryController extends Controller
|
class JournalEntryController extends Controller
|
||||||
{
|
{
|
||||||
|
|
@ -156,75 +158,56 @@ class JournalEntryController extends Controller
|
||||||
/** @var \App\Models\JournalEntry[] $entries */
|
/** @var \App\Models\JournalEntry[] $entries */
|
||||||
$entries = [];
|
$entries = [];
|
||||||
$entry_key = 0;
|
$entry_key = 0;
|
||||||
|
$group_entries = isset($input['group_entries']) && (bool) $input['group_entries'];
|
||||||
// TODO: Improve efficiency. Potential for lots of queries here...
|
// TODO: Improve efficiency. Potential for lots of queries here...
|
||||||
foreach ($ingredients as $ingredient) {
|
foreach ($ingredients as $ingredient) {
|
||||||
// Set entry key (combined date and meal or individual entries).
|
// Set entry key (combined date and meal or individual entries).
|
||||||
if (isset($input['group_entries']) && (bool) $input['group_entries']) {
|
if ($group_entries) {
|
||||||
$entry_key = "{$ingredient['date']}{$ingredient['meal']}";
|
$entry_key = "{$ingredient['date']}{$ingredient['meal']}";
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
$entry_key++;
|
$entry_key++;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Prepare entry values.
|
// Get an existing entry (when grouping) or create a new one.
|
||||||
$entries[$entry_key] = $entries[$entry_key] ?? JournalEntry::make([
|
$entries[$entry_key] = $entries[$entry_key] ?? JournalEntry::make([
|
||||||
'date' => $ingredient['date'],
|
'date' => $ingredient['date'],
|
||||||
'meal' => $ingredient['meal'],
|
'meal' => $ingredient['meal'],
|
||||||
])->user()->associate(Auth::user());
|
])->user()->associate(Auth::user());
|
||||||
|
$entry = &$entries[$entry_key];
|
||||||
|
|
||||||
// Calculate amounts based on ingredient type.
|
// Calculate amounts based on ingredient type.
|
||||||
|
$item = NULL;
|
||||||
|
$amount = Number::floatFromString($ingredient['amount']);
|
||||||
if ($ingredient['type'] == Food::class) {
|
if ($ingredient['type'] == Food::class) {
|
||||||
$item = Food::whereId($ingredient['id'])->first();
|
$item = Food::whereId($ingredient['id'])->first();
|
||||||
$nutrient_multiplier = Nutrients::calculateFoodNutrientMultiplier(
|
$nutrient_multiplier = Nutrients::calculateFoodNutrientMultiplier($item, $amount, $ingredient['unit']);
|
||||||
$item,
|
|
||||||
Number::floatFromString($ingredient['amount']),
|
|
||||||
$ingredient['unit']
|
|
||||||
);
|
|
||||||
foreach (Nutrients::all()->pluck('value') as $nutrient) {
|
foreach (Nutrients::all()->pluck('value') as $nutrient) {
|
||||||
$entries[$entry_key]->{$nutrient} += $item->{$nutrient} * $nutrient_multiplier;
|
$entry->{$nutrient} += $item->{$nutrient} * $nutrient_multiplier;
|
||||||
}
|
}
|
||||||
$entries[$entry_key]->foods->add($item);
|
$entry->foods->add($item);
|
||||||
}
|
}
|
||||||
elseif ($ingredient['type'] == Recipe::class) {
|
elseif ($ingredient['type'] == Recipe::class) {
|
||||||
$item = Recipe::whereId($ingredient['id'])->first();
|
$item = Recipe::whereId($ingredient['id'])->first();
|
||||||
foreach (Nutrients::all()->pluck('value') as $nutrient) {
|
foreach (Nutrients::all()->pluck('value') as $nutrient) {
|
||||||
$entries[$entry_key]->{$nutrient} += Nutrients::calculateRecipeNutrientAmount(
|
$entry->{$nutrient} += Nutrients::calculateRecipeNutrientAmount($item, $nutrient, $amount, $ingredient['unit']);
|
||||||
$item,
|
|
||||||
$nutrient,
|
|
||||||
Number::floatFromString($ingredient['amount']),
|
|
||||||
$ingredient['unit']
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
$entries[$entry_key]->recipes->add($item);
|
$entry->recipes->add($item);
|
||||||
}
|
|
||||||
else {
|
|
||||||
return back()->withInput()->withErrors("Invalid ingredient type {$ingredient['type']}.");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set entry summary.
|
// Add to summary.
|
||||||
$unit = $ingredient['unit'];
|
if (!empty($entry->summary)) {
|
||||||
if ($item instanceof Food) {
|
$entry->summary .= '; ';
|
||||||
if ($unit === 'serving') {
|
|
||||||
if (empty($item->serving_unit) && empty($item->serving_unit_name)) {
|
|
||||||
$unit = null;
|
|
||||||
}
|
|
||||||
elseif (!empty($item->serving_unit_name)) {
|
|
||||||
$unit = $item->serving_unit_formatted;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
$entries[$entry_key]->summary .= (!empty($entries[$entry_key]->summary) ? ', ' : null);
|
|
||||||
$entries[$entry_key]->summary .= "{$ingredient['amount']} {$unit} {$item->name}";
|
|
||||||
if (isset($item->detail) && !empty($item->detail)) {
|
|
||||||
$entries[$entry_key]->summary .= ", {$item->detail}";
|
|
||||||
}
|
}
|
||||||
|
$entry->summary .= $this->createIngredientSummary($ingredient, $item, $amount);
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach ($entries as $entry) {
|
// Save all new entries.
|
||||||
$entry->save();
|
foreach ($entries as $new_entry) {
|
||||||
$entry->user->save();
|
$new_entry->save();
|
||||||
$entry->foods()->saveMany($entry->foods);
|
$new_entry->user->save();
|
||||||
$entry->recipes()->saveMany($entry->recipes);
|
$new_entry->foods()->saveMany($new_entry->foods);
|
||||||
|
$new_entry->recipes()->saveMany($new_entry->recipes);
|
||||||
}
|
}
|
||||||
|
|
||||||
$count = count($entries);
|
$count = count($entries);
|
||||||
|
|
@ -239,6 +222,65 @@ class JournalEntryController extends Controller
|
||||||
return redirect()->route('journal-entries.index', $parameters);
|
return redirect()->route('journal-entries.index', $parameters);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Attempt to create a coherent summary for an entry ingredient.
|
||||||
|
*/
|
||||||
|
private function createIngredientSummary(array $ingredient, Food|Recipe $item, float $amount): string {
|
||||||
|
$name = $item->name;
|
||||||
|
$unit = $ingredient['unit'];
|
||||||
|
|
||||||
|
// Determine unit with special handling for custom Food units.
|
||||||
|
if ($item instanceof Food) {
|
||||||
|
if ($unit === 'serving') {
|
||||||
|
$no_serving_unit = empty($item->serving_unit) && empty($item->serving_unit_name);
|
||||||
|
|
||||||
|
// If there is no serving unit or the serving unit name is
|
||||||
|
// exactly the same as the item name don't use a serving
|
||||||
|
// unit and pluralize the _item_ name.
|
||||||
|
if ($no_serving_unit || $item->serving_unit_name === $name) {
|
||||||
|
$unit = null;
|
||||||
|
$name = Pluralizer::plural($name, $amount);
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the serving unit name is already _part_ of the item
|
||||||
|
// name, just keep the defined unit (e.g. name: "tortilla
|
||||||
|
// chips" and serving name "chips").
|
||||||
|
elseif (Str::contains($name, $item->serving_unit_name)) {
|
||||||
|
$unit = 'serving';
|
||||||
|
}
|
||||||
|
|
||||||
|
// If a serving unit name is set, use the formatted serving
|
||||||
|
// unit name as a base.
|
||||||
|
elseif (!empty($item->serving_unit_name)) {
|
||||||
|
$unit = $item->serving_unit_formatted;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Pluralize unit with supplied plurals or Pluralizer.
|
||||||
|
if (Nutrients::units()->has($unit)) {
|
||||||
|
$value = 'label';
|
||||||
|
if ($amount > 1) {
|
||||||
|
$value = 'plural';
|
||||||
|
}
|
||||||
|
$unit = Nutrients::units()->get($unit)[$value];
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$unit = Pluralizer::plural($unit, $amount);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add amount, unit, and name to summary.
|
||||||
|
$amount = Number::fractionStringFromFloat($amount);
|
||||||
|
$summary = "{$amount} {$unit} {$name}";
|
||||||
|
|
||||||
|
// Add detail if available.
|
||||||
|
if (isset($item->detail) && !empty($item->detail)) {
|
||||||
|
$summary .= ", {$item->detail}";
|
||||||
|
}
|
||||||
|
|
||||||
|
return $summary;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Store an entry from nutrients.
|
* Store an entry from nutrients.
|
||||||
*/
|
*/
|
||||||
|
|
|
||||||
|
|
@ -16,6 +16,7 @@ class Nutrients
|
||||||
* Each entry has two keys:
|
* Each entry has two keys:
|
||||||
* - value: Machine name for the unit.
|
* - value: Machine name for the unit.
|
||||||
* - label: Human-readable name for the unit.
|
* - label: Human-readable name for the unit.
|
||||||
|
* - plural: Human-readable plural form of the unit name.
|
||||||
* - type: Unit type -- matching types can be converted.
|
* - type: Unit type -- matching types can be converted.
|
||||||
*
|
*
|
||||||
* @return \Illuminate\Support\Collection
|
* @return \Illuminate\Support\Collection
|
||||||
|
|
@ -25,31 +26,37 @@ class Nutrients
|
||||||
'cup' => [
|
'cup' => [
|
||||||
'value' => 'cup',
|
'value' => 'cup',
|
||||||
'label' => 'cup',
|
'label' => 'cup',
|
||||||
|
'plural' => 'cups',
|
||||||
'type' => 'volume',
|
'type' => 'volume',
|
||||||
],
|
],
|
||||||
'gram' => [
|
'gram' => [
|
||||||
'value' => 'gram',
|
'value' => 'gram',
|
||||||
'label' => 'gram',
|
'label' => 'gram',
|
||||||
|
'plural' => 'grams',
|
||||||
'type' => 'weight',
|
'type' => 'weight',
|
||||||
],
|
],
|
||||||
'oz' => [
|
'oz' => [
|
||||||
'value' => 'oz',
|
'value' => 'oz',
|
||||||
'label' => 'oz',
|
'label' => 'oz',
|
||||||
|
'plural' => 'oz',
|
||||||
'type' => 'weight',
|
'type' => 'weight',
|
||||||
],
|
],
|
||||||
'serving' => [
|
'serving' => [
|
||||||
'value' => 'serving',
|
'value' => 'serving',
|
||||||
'label' => 'serving',
|
'label' => 'serving',
|
||||||
|
'plural' => 'servings',
|
||||||
'type' => 'division',
|
'type' => 'division',
|
||||||
],
|
],
|
||||||
'tbsp' => [
|
'tbsp' => [
|
||||||
'value' => 'tbsp',
|
'value' => 'tbsp',
|
||||||
'label' => 'tbsp.',
|
'label' => 'tbsp.',
|
||||||
|
'plural' => 'tbsp.',
|
||||||
'type' => 'volume',
|
'type' => 'volume',
|
||||||
],
|
],
|
||||||
'tsp' => [
|
'tsp' => [
|
||||||
'value' => 'tsp',
|
'value' => 'tsp',
|
||||||
'label' => 'tsp.',
|
'label' => 'tsp.',
|
||||||
|
'plural' => 'tsp.',
|
||||||
'type' => 'volume',
|
'type' => 'volume',
|
||||||
],
|
],
|
||||||
]);
|
]);
|
||||||
|
|
|
||||||
File diff suppressed because one or more lines are too long
|
|
@ -17,7 +17,7 @@
|
||||||
<div class="journal-entry-template hidden">
|
<div class="journal-entry-template hidden">
|
||||||
@include('journal-entries.partials.entry-item-input', ['default_date' => $default_date])
|
@include('journal-entries.partials.entry-item-input', ['default_date' => $default_date])
|
||||||
</div>
|
</div>
|
||||||
<x-inputs.icon-green class="add-entry-item" x-on:click="addEntryNode($el);">
|
<x-inputs.icon-green type="button" class="add-entry-item" x-on:click="addEntryNode($el);">
|
||||||
<svg class="h-10 w-10 pointer-events-none" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor">
|
<svg class="h-10 w-10 pointer-events-none" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor">
|
||||||
<path fill-rule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zm1-11a1 1 0 10-2 0v2H7a1 1 0 100 2h2v2a1 1 0 102 0v-2h2a1 1 0 100-2h-2V7z" clip-rule="evenodd" />
|
<path fill-rule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zm1-11a1 1 0 10-2 0v2H7a1 1 0 100 2h2v2a1 1 0 102 0v-2h2a1 1 0 100-2h-2V7z" clip-rule="evenodd" />
|
||||||
</svg>
|
</svg>
|
||||||
|
|
|
||||||
|
|
@ -38,7 +38,8 @@
|
||||||
size="5"
|
size="5"
|
||||||
class="block w-full"
|
class="block w-full"
|
||||||
placeholder="Amount"
|
placeholder="Amount"
|
||||||
:value="$amount ?? null" />
|
:value="$amount ?? null"
|
||||||
|
required />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Unit -->
|
<!-- Unit -->
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue