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:
Hayden 2022-06-01 11:59:50 -08:00 committed by GitHub
parent 592b1de39d
commit 52fbf6b833
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 77 additions and 9 deletions

View File

@ -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 ###

View File

@ -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);
}

View File

@ -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",

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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 {

View File

@ -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;

View File

@ -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")

View File

@ -34,6 +34,7 @@ class IngredientFood(CreateIngredientFood):
class CreateIngredientUnit(UnitFoodBase):
fraction: bool = True
abbreviation: str = ""
use_abbreviation: bool = False
class SaveIngredientUnit(CreateIngredientUnit):

View File

@ -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

View File

@ -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"},
]