Skip to content

Commit

Permalink
Merge pull request #370 from karlomikus/develop
Browse files Browse the repository at this point in the history
Prices and docs update
  • Loading branch information
karlomikus authored Nov 28, 2024
2 parents 10c09cb + 7dfe8de commit bb564da
Show file tree
Hide file tree
Showing 108 changed files with 4,326 additions and 1,235 deletions.
4 changes: 4 additions & 0 deletions .github/workflows/php.yml
Original file line number Diff line number Diff line change
Expand Up @@ -48,3 +48,7 @@ jobs:

- name: Execute PHPStan
run: vendor/bin/phpstan analyse

- uses: stoplightio/spectral-action@latest
with:
file_glob: 'docs/*.yaml'
1 change: 1 addition & 0 deletions .spectral.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
extends: ["spectral:oas"]
23 changes: 23 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,26 @@
# v4.2.0
## New
- Added cocktail prices
- Added `/cocktails/{id}/prices` endpoint
- Show all calculated prices per price category
- Automatically converts units to calculate the price if possible
- Added `/menu/export` endpoint
- Exports menu as CSV
- Added `/bars/{id}/cocktails` endpoint, showing bar shelf cocktails
- Added `/bars/{id}/ingredients/recommend` endpoint, showing next recommended ingredients for bar
- `BasicCocktail` schema now includes `short_ingredients` property
- Bar shelf is included in datapack export
- Bar shelf can be imported from datapack
- Added `total_bar_shelf_ingredients` to stats endpoint
- You can run the app as a worker if you set `APP_ROLE=worker` env variable

## Fixes
- Fixed missing substitute ingredient unit conversion in exports
- Fixed missing max amount when importing from export

## Changes
- Stat `total_bar_shelf_cocktails` now includes extra ingredients if `use_parent_as_substitute` flag is enabled

# v4.1.0
## New
- Added bar shelf
Expand Down
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,9 @@ This repository only contains the API server, if you are looking for easy to use
- Create public bar menus
- Manage custom API personal access tokens with custom permissions set by users
- Detailed statistics about recipes and user tastes
- Data export support in various formats
- Support for multiple ingredient prices
- Automatic cocktail price calculation based on ingredients

## Managed instance

Expand Down
2 changes: 1 addition & 1 deletion app/Console/Commands/BarImportRecipes.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,9 @@
use Kami\Cocktail\Models\User;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\Cache;
use Kami\Cocktail\Models\UserRoleEnum;
use Illuminate\Support\Facades\Storage;
use Kami\Cocktail\External\BarOptionsEnum;
use Kami\Cocktail\Models\Enums\UserRoleEnum;
use Kami\Cocktail\External\Import\FromDataPack;

class BarImportRecipes extends Command
Expand Down
48 changes: 48 additions & 0 deletions app/Console/Commands/BarSeed.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
<?php

declare(strict_types=1);

namespace Kami\Cocktail\Console\Commands;

use Illuminate\Console\Command;
use Kami\Cocktail\Models\Ingredient;
use Kami\Cocktail\Models\PriceCategory;
use Kami\Cocktail\Models\IngredientPrice;

class BarSeed extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'bar:seed';

/**
* The console command description.
*
* @var string
*/
protected $description = '[DEV] Seed with random data';

/**
* Execute the console command.
*
* @return int
*/
public function handle()
{
$ingredients = Ingredient::all();
$priceCategories = PriceCategory::all();

foreach ($priceCategories as $priceCategory) {
foreach ($ingredients as $ingredient) {
IngredientPrice::factory()->for($ingredient)->for($priceCategory)->create(['units' => 'ml']);
}
}

$this->output->success('Done!');

return Command::SUCCESS;
}
}
9 changes: 9 additions & 0 deletions app/Console/Commands/SetupMeilisearch.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

namespace Kami\Cocktail\Console\Commands;

use Kami\Cocktail\Models\Bar;
use Illuminate\Console\Command;
use Laravel\Scout\EngineManager;
use Illuminate\Support\Facades\DB;
Expand Down Expand Up @@ -113,6 +114,14 @@ public function handle(): int
file_put_contents($envFile, $envContents . PHP_EOL . 'MEILISEARCH_API_KEY_UID=' . $searchApiKey->getUid());
}

$this->line('Updating search tokens for bars...');
DB::transaction(function () {
$bars = Bar::all();
foreach ($bars as $bar) {
$bar->updateSearchToken();
}
});

$this->info('Meilisearch setup done!');

return Command::SUCCESS;
Expand Down
3 changes: 3 additions & 0 deletions app/External/Export/ToDataPack.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\File;
use Kami\Cocktail\Models\Ingredient;
use Kami\Cocktail\Models\BarIngredient;
use Kami\RecipeUtils\UnitConverter\Units;
use Kami\Cocktail\External\ForceUnitConvertEnum;
use Kami\Cocktail\Exceptions\ExportFileNotCreatedException;
Expand Down Expand Up @@ -125,6 +126,8 @@ private function dumpBaseData(int $barId, ZipArchive &$zip): void
'base_price_categories' => DB::table('price_categories')->select('name', 'currency', 'description')->where('bar_id', $barId)->get()->toArray(),
];

$baseDataFiles['bar_shelf'] = BarIngredient::where('bar_id', $barId)->with('ingredient')->get()->map(fn ($bi) => $bi->ingredient->getExternalId())->toArray();

foreach ($baseDataFiles as $file => $data) {
$exportData = $this->prepareDataOutput($data);

Expand Down
33 changes: 27 additions & 6 deletions app/External/Import/FromDataPack.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,8 @@
use Illuminate\Support\Facades\File;
use Kami\Cocktail\Models\Ingredient;
use Illuminate\Support\Facades\Storage;
use Kami\Cocktail\Models\BarStatusEnum;
use Kami\Cocktail\External\BarOptionsEnum;
use Kami\Cocktail\Models\Enums\BarStatusEnum;
use Illuminate\Contracts\Filesystem\Filesystem;
use Kami\Cocktail\External\Model\Cocktail as CocktailExternal;
use Kami\Cocktail\External\Model\Ingredient as IngredientExternal;
Expand All @@ -26,6 +26,9 @@ class FromDataPack
{
private Filesystem $uploadsDisk;

/** @var array<string> */
private array $barShelf = [];

public function __construct()
{
$this->uploadsDisk = Storage::disk('uploads');
Expand Down Expand Up @@ -55,6 +58,10 @@ public function process(Filesystem $dataDisk, Bar $bar, User $user, array $flags
}
}

if ($dataDisk->exists('bar_shelf.json')) {
$this->loadBarShelfFromImportData($dataDisk->path('bar_shelf.json'), $bar->id);
}

if (in_array(BarOptionsEnum::Ingredients, $flags)) {
$this->importIngredients($dataDisk, $bar, $user);
}
Expand Down Expand Up @@ -185,6 +192,10 @@ private function importIngredients(Filesystem $dataDisk, Bar $bar, User $user):
DB::table('ingredients')->where('slug', $ingredient->slug)->where('bar_id', $bar->id)->update(['parent_ingredient_id' => $parentIngredientId->id]);
}
}

if (in_array($ingredient->slug, $this->barShelf)) {
DB::table('bar_ingredients')->insert(['ingredient_id' => $ingredient->id, 'bar_id' => $bar->id]);
}
}

if (count($imagesToInsert) > 0) {
Expand Down Expand Up @@ -270,8 +281,9 @@ private function importBaseCocktails(Filesystem $dataDisk, Bar $bar, User $user)
$ciId = DB::table('cocktail_ingredients')->insertGetId([
'cocktail_id' => $cocktailId,
'ingredient_id' => $matchedIngredientId,
'amount' => $cocktailIngredient->amount,
'units' => $cocktailIngredient->units,
'amount' => $cocktailIngredient->amount->amountMin,
'amount_max' => $cocktailIngredient->amount->amountMax,
'units' => $cocktailIngredient->amount->units->value,
'optional' => $cocktailIngredient->optional,
'note' => $cocktailIngredient->note,
'sort' => $sort,
Expand All @@ -289,9 +301,9 @@ private function importBaseCocktails(Filesystem $dataDisk, Bar $bar, User $user)
DB::table('cocktail_ingredient_substitutes')->insert([
'cocktail_ingredient_id' => $ciId,
'ingredient_id' => $matchedSubIngredientId,
'amount' => $substitute->amount,
'amount_max' => $substitute->amountMax,
'units' => $substitute->units,
'amount' => $substitute->amount->amountMin,
'amount_max' => $substitute->amount->amountMax,
'units' => $substitute->amount->units->value,
'created_at' => now(),
'updated_at' => null,
]);
Expand Down Expand Up @@ -349,4 +361,13 @@ private function getDataFromDir(string $dir, Filesystem $dataDisk): Generator
}
}
}

private function loadBarShelfFromImportData(string $filename, int $barId): void
{
if ($data = file_get_contents($filename)) {
$ingredients = json_decode($data, true);

$this->barShelf = array_map(fn (string $slug) => $slug . '-' . $barId, $ingredients);
}
}
}
12 changes: 6 additions & 6 deletions app/External/Import/FromJsonSchema.php
Original file line number Diff line number Diff line change
Expand Up @@ -109,21 +109,21 @@ public function process(
$foundExternalSubIngredient->origin
)
),
$substitute->amount,
$substitute->amountMax,
$substitute->units,
$substitute->amount->amountMin,
$substitute->amount->amountMax,
$substitute->amount->units->value,
);
}

$ingredient = new CocktailIngredientDTO(
$ingredientId,
$externalIngredients->firstWhere('id', $scrapedIngredient->ingredient->id)->name,
$scrapedIngredient->amount,
$scrapedIngredient->units,
$scrapedIngredient->amount->amountMin,
$scrapedIngredient->amount->units->value,
$sort,
$scrapedIngredient->optional,
$substitutes,
$scrapedIngredient->amountMax,
$scrapedIngredient->amount->amountMax,
$scrapedIngredient->note,
);

Expand Down
3 changes: 2 additions & 1 deletion app/External/Model/Cocktail.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
use Kami\Cocktail\External\SupportsDataPack;
use Kami\Cocktail\Models\Image as ImageModel;
use Kami\Cocktail\Models\Cocktail as CocktailModel;
use Kami\Cocktail\Models\ValueObjects\CocktailIngredientFormatter;
use Kami\Cocktail\Models\CocktailIngredient as CocktailIngredientModel;

readonly class Cocktail implements SupportsDataPack, SupportsDraft2, SupportsJSONLD
Expand Down Expand Up @@ -203,7 +204,7 @@ public function toJSONLD(): string
"recipeCuisine" => "Cocktail",
"keywords" => implode(', ', $this->tags),
"recipeIngredient" => array_map(function (CocktailIngredient $ci) {
return $ci->formatter->printIngredient();
return (new CocktailIngredientFormatter($ci->amount, $ci->ingredient->name, $ci->optional))->format();
}, $this->ingredients),
], ['image' => $image]);

Expand Down
50 changes: 27 additions & 23 deletions app/External/Model/CocktailIngredient.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@
use Kami\RecipeUtils\UnitConverter\Units;
use Kami\Cocktail\External\SupportsDraft2;
use Kami\Cocktail\External\SupportsDataPack;
use Kami\Cocktail\Models\CocktailIngredientFormatter;
use Kami\Cocktail\Models\ValueObjects\UnitValueObject;
use Kami\Cocktail\Models\ValueObjects\AmountValueObject;
use Kami\Cocktail\Models\CocktailIngredient as CocktailIngredientModel;
use Kami\Cocktail\Models\CocktailIngredientSubstitute as CocktailIngredientSubstituteModel;

Expand All @@ -19,33 +20,32 @@
*/
private function __construct(
public IngredientBasic $ingredient,
public float $amount,
public ?string $units,
public AmountValueObject $amount,
public bool $optional = false,
public ?float $amountMax = null,
public ?string $note = null,
public array $substitutes = [],
public int $sort = 0,
public ?CocktailIngredientFormatter $formatter = null,
) {
}

public static function fromModel(CocktailIngredientModel $model, ?Units $toUnits = null): self
{
$substitutes = $model->substitutes->map(function (CocktailIngredientSubstituteModel $substitute) {
return CocktailIngredientSubstitute::fromModel($substitute);
$substitutes = $model->substitutes->map(function (CocktailIngredientSubstituteModel $substitute) use ($toUnits) {
return CocktailIngredientSubstitute::fromModel($substitute, $toUnits);
})->toArray();

$amount = $model->getAmount();
if ($toUnits && !$model->getAmount()->units->isDash()) {
$amount = $amount->convertTo(new UnitValueObject($toUnits->value));
}

return new self(
IngredientBasic::fromModel($model->ingredient),
$model->amount,
$model->units,
$amount,
(bool) $model->optional,
$model->amount_max,
$model->note,
$substitutes,
$model->sort,
$model->getConvertedTo($toUnits),
);
}

Expand All @@ -71,10 +71,12 @@ public static function fromDataPackArray(array $sourceArray): self
'origin' => $sourceArray['origin'] ?? null,
'category' => $sourceArray['category'] ?? null,
]),
$sourceArray['amount'] ?? 0.0,
$sourceArray['units'],
new AmountValueObject(
$sourceArray['amount'] ?? 0.0,
new UnitValueObject($sourceArray['units']),
$sourceArray['amount_max'] ?? null,
),
$sourceArray['optional'] ?? false,
$sourceArray['amount_max'] ?? null,
$sourceArray['note'] ?? null,
$substitutes,
$sourceArray['sort'] ?? 0,
Expand All @@ -85,10 +87,10 @@ public function toDataPackArray(): array
{
return [
...$this->ingredient->toDataPackArray(),
'amount' => $this->formatter?->getAmount() ?? $this->amount,
'units' => $this->formatter?->getUnits() ?? $this->units,
'amount' => $this->amount->amountMin,
'units' => $this->amount->units->value,
'optional' => $this->optional,
'amount_max' => $this->formatter?->getMaxAmount() ?? $this->amountMax,
'amount_max' => $this->amount->amountMax,
'note' => $this->note,
'substitutes' => array_map(fn ($model) => $model->toDataPackArray(), $this->substitutes),
'sort' => $this->sort,
Expand All @@ -113,10 +115,12 @@ public static function fromDraft2Array(array $sourceArray): self
'_id' => $sourceArray['_id'],
'name' => $sourceArray['name'] ?? '',
]),
$sourceArray['amount'] ?? 0.0,
$sourceArray['units'],
new AmountValueObject(
$sourceArray['amount'] ?? 0.0,
new UnitValueObject($sourceArray['units']),
$sourceArray['amount_max'] ?? null,
),
$sourceArray['optional'] ?? false,
$sourceArray['amount_max'] ?? null,
$sourceArray['note'] ?? null,
$substitutes,
$sourceArray['sort'] ?? 0,
Expand All @@ -127,10 +131,10 @@ public function toDraft2Array(): array
{
return [
'_id' => $this->ingredient->id,
'amount' => $this->formatter?->getAmount() ?? $this->amount,
'units' => $this->formatter?->getUnits() ?? $this->units,
'amount' => $this->amount->amountMin,
'units' => $this->amount->units->value,
'optional' => $this->optional,
'amount_max' => $this->formatter?->getMaxAmount() ?? $this->amountMax,
'amount_max' => $this->amount->amountMax,
'note' => $this->note,
'substitutes' => array_map(fn ($model) => $model->toDraft2Array(), $this->substitutes),
'sort' => $this->sort,
Expand Down
Loading

0 comments on commit bb564da

Please sign in to comment.