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\Support\Carbon;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Illuminate\Support\Pluralizer;
|
||||
use Illuminate\Support\Str;
|
||||
|
||||
class JournalEntryController extends Controller
|
||||
{
|
||||
|
|
@ -156,75 +158,56 @@ class JournalEntryController extends Controller
|
|||
/** @var \App\Models\JournalEntry[] $entries */
|
||||
$entries = [];
|
||||
$entry_key = 0;
|
||||
$group_entries = isset($input['group_entries']) && (bool) $input['group_entries'];
|
||||
// TODO: Improve efficiency. Potential for lots of queries here...
|
||||
foreach ($ingredients as $ingredient) {
|
||||
// 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']}";
|
||||
}
|
||||
else {
|
||||
$entry_key++;
|
||||
}
|
||||
|
||||
// Prepare entry values.
|
||||
// Get an existing entry (when grouping) or create a new one.
|
||||
$entries[$entry_key] = $entries[$entry_key] ?? JournalEntry::make([
|
||||
'date' => $ingredient['date'],
|
||||
'meal' => $ingredient['meal'],
|
||||
])->user()->associate(Auth::user());
|
||||
$entry = &$entries[$entry_key];
|
||||
|
||||
// Calculate amounts based on ingredient type.
|
||||
$item = NULL;
|
||||
$amount = Number::floatFromString($ingredient['amount']);
|
||||
if ($ingredient['type'] == Food::class) {
|
||||
$item = Food::whereId($ingredient['id'])->first();
|
||||
$nutrient_multiplier = Nutrients::calculateFoodNutrientMultiplier(
|
||||
$item,
|
||||
Number::floatFromString($ingredient['amount']),
|
||||
$ingredient['unit']
|
||||
);
|
||||
$nutrient_multiplier = Nutrients::calculateFoodNutrientMultiplier($item, $amount, $ingredient['unit']);
|
||||
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) {
|
||||
$item = Recipe::whereId($ingredient['id'])->first();
|
||||
foreach (Nutrients::all()->pluck('value') as $nutrient) {
|
||||
$entries[$entry_key]->{$nutrient} += Nutrients::calculateRecipeNutrientAmount(
|
||||
$item,
|
||||
$nutrient,
|
||||
Number::floatFromString($ingredient['amount']),
|
||||
$ingredient['unit']
|
||||
);
|
||||
$entry->{$nutrient} += Nutrients::calculateRecipeNutrientAmount($item, $nutrient, $amount, $ingredient['unit']);
|
||||
}
|
||||
$entries[$entry_key]->recipes->add($item);
|
||||
}
|
||||
else {
|
||||
return back()->withInput()->withErrors("Invalid ingredient type {$ingredient['type']}.");
|
||||
$entry->recipes->add($item);
|
||||
}
|
||||
|
||||
// Set entry summary.
|
||||
$unit = $ingredient['unit'];
|
||||
if ($item instanceof Food) {
|
||||
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}";
|
||||
// Add to summary.
|
||||
if (!empty($entry->summary)) {
|
||||
$entry->summary .= '; ';
|
||||
}
|
||||
$entry->summary .= $this->createIngredientSummary($ingredient, $item, $amount);
|
||||
}
|
||||
|
||||
foreach ($entries as $entry) {
|
||||
$entry->save();
|
||||
$entry->user->save();
|
||||
$entry->foods()->saveMany($entry->foods);
|
||||
$entry->recipes()->saveMany($entry->recipes);
|
||||
// Save all new entries.
|
||||
foreach ($entries as $new_entry) {
|
||||
$new_entry->save();
|
||||
$new_entry->user->save();
|
||||
$new_entry->foods()->saveMany($new_entry->foods);
|
||||
$new_entry->recipes()->saveMany($new_entry->recipes);
|
||||
}
|
||||
|
||||
$count = count($entries);
|
||||
|
|
@ -239,6 +222,65 @@ class JournalEntryController extends Controller
|
|||
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.
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@ class Nutrients
|
|||
* Each entry has two keys:
|
||||
* - value: Machine 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.
|
||||
*
|
||||
* @return \Illuminate\Support\Collection
|
||||
|
|
@ -25,31 +26,37 @@ class Nutrients
|
|||
'cup' => [
|
||||
'value' => 'cup',
|
||||
'label' => 'cup',
|
||||
'plural' => 'cups',
|
||||
'type' => 'volume',
|
||||
],
|
||||
'gram' => [
|
||||
'value' => 'gram',
|
||||
'label' => 'gram',
|
||||
'plural' => 'grams',
|
||||
'type' => 'weight',
|
||||
],
|
||||
'oz' => [
|
||||
'value' => 'oz',
|
||||
'label' => 'oz',
|
||||
'plural' => 'oz',
|
||||
'type' => 'weight',
|
||||
],
|
||||
'serving' => [
|
||||
'value' => 'serving',
|
||||
'label' => 'serving',
|
||||
'plural' => 'servings',
|
||||
'type' => 'division',
|
||||
],
|
||||
'tbsp' => [
|
||||
'value' => 'tbsp',
|
||||
'label' => 'tbsp.',
|
||||
'plural' => 'tbsp.',
|
||||
'type' => 'volume',
|
||||
],
|
||||
'tsp' => [
|
||||
'value' => 'tsp',
|
||||
'label' => 'tsp.',
|
||||
'plural' => 'tsp.',
|
||||
'type' => 'volume',
|
||||
],
|
||||
]);
|
||||
|
|
|
|||
File diff suppressed because one or more lines are too long
|
|
@ -17,7 +17,7 @@
|
|||
<div class="journal-entry-template hidden">
|
||||
@include('journal-entries.partials.entry-item-input', ['default_date' => $default_date])
|
||||
</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">
|
||||
<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>
|
||||
|
|
|
|||
|
|
@ -38,7 +38,8 @@
|
|||
size="5"
|
||||
class="block w-full"
|
||||
placeholder="Amount"
|
||||
:value="$amount ?? null" />
|
||||
:value="$amount ?? null"
|
||||
required />
|
||||
</div>
|
||||
|
||||
<!-- Unit -->
|
||||
|
|
|
|||
Loading…
Reference in New Issue