mirror of
https://github.com/mealie-recipes/mealie.git
synced 2025-05-24 01:12:54 -04:00
feat: add unit abbreviation support (#1332)
* add 'use-abbreviation' db column * type generation * add view and edit elements * check for use_abbreviation to display * fix: alembic version check * test: add use_abbreviation prop tests
This commit is contained in:
parent
592b1de39d
commit
52fbf6b833
@ -0,0 +1,30 @@
|
||||
"""Add use_abbreviation column to ingredients
|
||||
|
||||
Revision ID: ab0bae02578f
|
||||
Revises: 09dfc897ad62
|
||||
Create Date: 2022-06-01 11:12:06.748383
|
||||
|
||||
"""
|
||||
import sqlalchemy as sa
|
||||
|
||||
from alembic import op
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = "ab0bae02578f"
|
||||
down_revision = "09dfc897ad62"
|
||||
branch_labels = None
|
||||
depends_on = None
|
||||
|
||||
|
||||
def upgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.add_column("ingredient_units", sa.Column("use_abbreviation", sa.Boolean(), nullable=True))
|
||||
|
||||
op.execute("UPDATE ingredient_units SET use_abbreviation = FALSE WHERE use_abbreviation IS NULL")
|
||||
# ### end Alembic commands ###
|
||||
|
||||
|
||||
def downgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.drop_column("ingredient_units", "use_abbreviation")
|
||||
# ### end Alembic commands ###
|
@ -19,6 +19,8 @@ export function parseIngredientText(ingredient: RecipeIngredient, disableAmount:
|
||||
|
||||
let returnQty = "";
|
||||
|
||||
let unitDisplay = unit?.name;
|
||||
|
||||
// casting to number is required as sometimes quantity is a string
|
||||
if (quantity && Number(quantity) !== 0) {
|
||||
console.log("Using Quantity", quantity, typeof quantity);
|
||||
@ -34,8 +36,12 @@ export function parseIngredientText(ingredient: RecipeIngredient, disableAmount:
|
||||
} else {
|
||||
returnQty = (quantity * scale).toString();
|
||||
}
|
||||
|
||||
if (unit?.useAbbreviation && unit.abbreviation) {
|
||||
unitDisplay = unit.abbreviation;
|
||||
}
|
||||
}
|
||||
|
||||
const text = `${returnQty} ${unit?.name || " "} ${food?.name || " "} ${note || " "}`.replace(/ {2,}/g, " ");
|
||||
const text = `${returnQty} ${unitDisplay || " "} ${food?.name || " "} ${note || " "}`.replace(/ {2,}/g, " ");
|
||||
return sanitizeIngredientHTML(text);
|
||||
}
|
||||
|
@ -36,6 +36,7 @@
|
||||
<v-text-field v-model="editTarget.abbreviation" label="Abbreviation"></v-text-field>
|
||||
<v-text-field v-model="editTarget.description" label="Description"></v-text-field>
|
||||
<v-checkbox v-model="editTarget.fraction" hide-details label="Display as Fraction"></v-checkbox>
|
||||
<v-checkbox v-model="editTarget.useAbbreviation" hide-details label="Use Abbreviation"></v-checkbox>
|
||||
</v-form>
|
||||
</v-card-text>
|
||||
</BaseDialog>
|
||||
@ -106,6 +107,11 @@
|
||||
Combine
|
||||
</BaseButton>
|
||||
</template>
|
||||
<template #item.useAbbreviation="{ item }">
|
||||
<v-icon :color="item.useAbbreviation ? 'success' : undefined">
|
||||
{{ item.useAbbreviation ? $globals.icons.check : $globals.icons.close }}
|
||||
</v-icon>
|
||||
</template>
|
||||
<template #item.fraction="{ item }">
|
||||
<v-icon :color="item.fraction ? 'success' : undefined">
|
||||
{{ item.fraction ? $globals.icons.check : $globals.icons.close }}
|
||||
@ -153,10 +159,15 @@ export default defineComponent({
|
||||
value: "abbreviation",
|
||||
show: true,
|
||||
},
|
||||
{
|
||||
text: "Use Abbv.",
|
||||
value: "useAbbreviation",
|
||||
show: true,
|
||||
},
|
||||
{
|
||||
text: "Description",
|
||||
value: "description",
|
||||
show: true,
|
||||
show: false,
|
||||
},
|
||||
{
|
||||
text: "Fraction",
|
||||
|
@ -133,6 +133,7 @@ export interface IngredientUnit {
|
||||
description?: string;
|
||||
fraction?: boolean;
|
||||
abbreviation?: string;
|
||||
useAbbreviation?: boolean;
|
||||
id: string;
|
||||
}
|
||||
export interface CreateIngredientUnit {
|
||||
@ -140,6 +141,7 @@ export interface CreateIngredientUnit {
|
||||
description?: string;
|
||||
fraction?: boolean;
|
||||
abbreviation?: string;
|
||||
useAbbreviation?: boolean;
|
||||
}
|
||||
export interface IngredientFood {
|
||||
name: string;
|
||||
|
@ -112,6 +112,7 @@ export interface IngredientUnit {
|
||||
description?: string;
|
||||
fraction?: boolean;
|
||||
abbreviation?: string;
|
||||
useAbbreviation?: boolean;
|
||||
id: string;
|
||||
}
|
||||
export interface CreateIngredientUnit {
|
||||
@ -119,6 +120,7 @@ export interface CreateIngredientUnit {
|
||||
description?: string;
|
||||
fraction?: boolean;
|
||||
abbreviation?: string;
|
||||
useAbbreviation?: boolean;
|
||||
}
|
||||
export interface IngredientFood {
|
||||
name: string;
|
||||
|
@ -207,6 +207,7 @@ export interface IngredientUnit {
|
||||
description?: string;
|
||||
fraction?: boolean;
|
||||
abbreviation?: string;
|
||||
useAbbreviation?: boolean;
|
||||
id: string;
|
||||
}
|
||||
export interface ReadGroupPreferences {
|
||||
@ -287,6 +288,7 @@ export interface CreateIngredientUnit {
|
||||
description?: string;
|
||||
fraction?: boolean;
|
||||
abbreviation?: string;
|
||||
useAbbreviation?: boolean;
|
||||
}
|
||||
export interface CreateIngredientFood {
|
||||
name: string;
|
||||
|
@ -148,6 +148,7 @@ export interface IngredientUnit {
|
||||
description?: string;
|
||||
fraction?: boolean;
|
||||
abbreviation?: string;
|
||||
useAbbreviation?: boolean;
|
||||
id: string;
|
||||
}
|
||||
export interface CreateIngredientUnit {
|
||||
@ -155,6 +156,7 @@ export interface CreateIngredientUnit {
|
||||
description?: string;
|
||||
fraction?: boolean;
|
||||
abbreviation?: string;
|
||||
useAbbreviation?: boolean;
|
||||
}
|
||||
export interface IngredientFood {
|
||||
name: string;
|
||||
|
@ -49,6 +49,7 @@ export interface CreateIngredientUnit {
|
||||
description?: string;
|
||||
fraction?: boolean;
|
||||
abbreviation?: string;
|
||||
useAbbreviation?: boolean;
|
||||
}
|
||||
export interface CreateRecipe {
|
||||
name: string;
|
||||
@ -117,6 +118,7 @@ export interface IngredientUnit {
|
||||
description?: string;
|
||||
fraction?: boolean;
|
||||
abbreviation?: string;
|
||||
useAbbreviation?: boolean;
|
||||
id: string;
|
||||
}
|
||||
export interface IngredientsRequest {
|
||||
@ -340,6 +342,7 @@ export interface SaveIngredientUnit {
|
||||
description?: string;
|
||||
fraction?: boolean;
|
||||
abbreviation?: string;
|
||||
useAbbreviation?: boolean;
|
||||
groupId: string;
|
||||
}
|
||||
export interface ScrapeRecipe {
|
||||
|
@ -164,6 +164,7 @@ export interface IngredientUnit {
|
||||
description?: string;
|
||||
fraction?: boolean;
|
||||
abbreviation?: string;
|
||||
useAbbreviation?: boolean;
|
||||
id: string;
|
||||
}
|
||||
export interface CreateIngredientUnit {
|
||||
@ -171,6 +172,7 @@ export interface CreateIngredientUnit {
|
||||
description?: string;
|
||||
fraction?: boolean;
|
||||
abbreviation?: string;
|
||||
useAbbreviation?: boolean;
|
||||
}
|
||||
export interface IngredientFood {
|
||||
name: string;
|
||||
|
@ -18,6 +18,7 @@ class IngredientUnitModel(SqlAlchemyBase, BaseMixins):
|
||||
name = Column(String)
|
||||
description = Column(String)
|
||||
abbreviation = Column(String)
|
||||
use_abbreviation = Column(Boolean, default=False)
|
||||
fraction = Column(Boolean, default=True)
|
||||
ingredients = orm.relationship("RecipeIngredient", back_populates="unit")
|
||||
|
||||
|
@ -34,6 +34,7 @@ class IngredientFood(CreateIngredientFood):
|
||||
class CreateIngredientUnit(UnitFoodBase):
|
||||
fraction: bool = True
|
||||
abbreviation: str = ""
|
||||
use_abbreviation: bool = False
|
||||
|
||||
|
||||
class SaveIngredientUnit(CreateIngredientUnit):
|
||||
|
@ -9,17 +9,19 @@ from tests.utils.fixture_schemas import TestUser
|
||||
class Routes:
|
||||
base = "/api/units"
|
||||
|
||||
@staticmethod
|
||||
def item(item_id: int) -> str:
|
||||
return f"{Routes.base}/{item_id}"
|
||||
|
||||
|
||||
@pytest.fixture(scope="function")
|
||||
def unit(api_client: TestClient, unique_user: TestUser) -> dict:
|
||||
def unit(api_client: TestClient, unique_user: TestUser):
|
||||
data = CreateIngredientUnit(
|
||||
name=random_string(10),
|
||||
description=random_string(10),
|
||||
fraction=random_bool(),
|
||||
abbreviation=random_string(3) + ".",
|
||||
abbreviation=f"{random_string(3)}.",
|
||||
use_abbreviation=random_bool(),
|
||||
).dict(by_alias=True)
|
||||
|
||||
response = api_client.post(Routes.base, json=data, headers=unique_user.token)
|
||||
@ -52,6 +54,7 @@ def test_read_unit(api_client: TestClient, unit: dict, unique_user: TestUser):
|
||||
assert as_json["description"] == unit["description"]
|
||||
assert as_json["fraction"] == unit["fraction"]
|
||||
assert as_json["abbreviation"] == unit["abbreviation"]
|
||||
assert as_json["useAbbreviation"] == unit["useAbbreviation"]
|
||||
|
||||
|
||||
def test_update_unit(api_client: TestClient, unit: dict, unique_user: TestUser):
|
||||
@ -60,8 +63,10 @@ def test_update_unit(api_client: TestClient, unit: dict, unique_user: TestUser):
|
||||
"name": random_string(10),
|
||||
"description": random_string(10),
|
||||
"fraction": not unit["fraction"],
|
||||
"abbreviation": random_string(3) + ".",
|
||||
"abbreviation": f"{random_string(3)}.",
|
||||
"useAbbreviation": not unit["useAbbreviation"],
|
||||
}
|
||||
|
||||
response = api_client.put(Routes.item(unit["id"]), json=update_data, headers=unique_user.token)
|
||||
assert response.status_code == 200
|
||||
as_json = response.json()
|
||||
@ -71,14 +76,15 @@ def test_update_unit(api_client: TestClient, unit: dict, unique_user: TestUser):
|
||||
assert as_json["description"] == update_data["description"]
|
||||
assert as_json["fraction"] == update_data["fraction"]
|
||||
assert as_json["abbreviation"] == update_data["abbreviation"]
|
||||
assert as_json["useAbbreviation"] == update_data["useAbbreviation"]
|
||||
|
||||
|
||||
def test_delete_unit(api_client: TestClient, unit: dict, unique_user: TestUser):
|
||||
id = unit["id"]
|
||||
item_id = unit["id"]
|
||||
|
||||
response = api_client.delete(Routes.item(id), headers=unique_user.token)
|
||||
response = api_client.delete(Routes.item(item_id), headers=unique_user.token)
|
||||
|
||||
assert response.status_code == 200
|
||||
|
||||
response = api_client.get(Routes.item(id), headers=unique_user.token)
|
||||
response = api_client.get(Routes.item(item_id), headers=unique_user.token)
|
||||
assert response.status_code == 404
|
||||
|
@ -4,7 +4,7 @@ from mealie.core.config import get_app_settings
|
||||
from mealie.services.backups_v2.alchemy_exporter import AlchemyExporter
|
||||
|
||||
ALEMBIC_VERSIONS = [
|
||||
{"version_num": "09dfc897ad62"},
|
||||
{"version_num": "ab0bae02578f"},
|
||||
]
|
||||
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user