diff --git a/pyproject.toml b/pyproject.toml index f10bcd2..8c8bdd1 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "sigl" -version = "0.1.1" +version = "0.1.2" description = "Simple Grocery List" authors = ["Jonathan Krauss "] license = "BSD-3-Clause" diff --git a/sigl/domain/service.py b/sigl/domain/service.py index e90eddb..b7ef12f 100644 --- a/sigl/domain/service.py +++ b/sigl/domain/service.py @@ -6,6 +6,7 @@ Copyright (c) 2022 Asymworks, LLC. All Rights Reserved. from typing import List, Optional, Union +from sqlalchemy import func from sqlalchemy.orm import Session from sigl.exc import DomainError, NotFoundError @@ -44,11 +45,13 @@ def list_addItem( if not productName: raise DomainError('Product Name cannot be empty') - product = Product(name=productName, category=productCategory) - if remember is not None: - product.remember = remember + product = product_by_name(session, productName) + if not product: + product = Product(name=productName, category=productCategory) + if remember is not None: + product.remember = remember - session.add(product) + session.add(product) else: product = product_by_id(session, productId) @@ -104,10 +107,10 @@ def list_deleteCrossedOff(session: Session, id: int) -> ShoppingList: raise NotFoundError(f'List {id} does not exist') for entry in sList.entries: - if not entry.product.remember: - session.delete(entry.product) if entry.crossedOff: session.delete(entry) + if not entry.product.remember: + session.delete(entry.product) session.commit() @@ -230,10 +233,15 @@ def products_all(session: Session) -> List[Product]: def product_by_id(session: Session, id: int) -> Optional[Product]: - """Load a specific Product.""" + """Load a specific Product by Id.""" return session.query(Product).filter(Product.id == id).one_or_none() +def product_by_name(session: Session, name: str) -> Optional[Product]: + """Load a specific Product by Name.""" + return session.query(Product).filter(func.lower(Product.name) == func.lower(name)).one_or_none() + + def product_create( session: Session, name: str, diff --git a/sigl/templates/lists/addItem.html.j2 b/sigl/templates/lists/addItem.html.j2 index 5e38d91..92fae52 100644 --- a/sigl/templates/lists/addItem.html.j2 +++ b/sigl/templates/lists/addItem.html.j2 @@ -5,27 +5,31 @@
Add Item to {{ list.name }}
{% endblock %} +{% block head_scripts %} + + +{% endblock %} {% block main %}
-
+
Select Product to Add
- + + + + New Product
-
-
- - -
-
+
+ + +
+
+
@@ -49,4 +53,19 @@
+ +{% for p in products|sort(attribute='category')|sort(attribute='name') %} + {{ p.name }} +{% endfor %} + +{% endblock %} +{% block body_scripts %} + {% endblock %} \ No newline at end of file diff --git a/sigl/views/lists.py b/sigl/views/lists.py index 0b8c10f..9d6ccdc 100644 --- a/sigl/views/lists.py +++ b/sigl/views/lists.py @@ -6,6 +6,7 @@ Copyright (c) 2022 Asymworks, LLC. All Rights Reserved. from flask import ( Blueprint, + current_app, flash, jsonify, make_response, @@ -225,7 +226,7 @@ def addItem(id): sList = list_by_id(db.session, id) products = products_all(db.session) if request.method == 'POST': - if 'product' not in request.form: + if 'productName' not in request.form: flash( 'An internal error occurred. Please reload the page and try again', 'error' @@ -236,21 +237,20 @@ def addItem(id): products=products, ) - productId = request.form['product'] productName = request.form.get('productName', '').strip() productCategory = request.form.get('productCategory', '').strip() + remember = request.form.get('remember', 'off') == 'on' quantity = request.form.get('quantity', '').strip() notes = request.form.get('notes', '').strip() - if productId == 'new' or productId == '': - productId = None + current_app.logger.info(f'Remember Value: {remember}') list_addItem( db.session, id, - productId=productId, productName=productName, productCategory=productCategory, + remember=remember, quantity=quantity, notes=notes, ) diff --git a/tests/test_21_product_service.py b/tests/test_21_product_service.py index eec76c1..41980d0 100644 --- a/tests/test_21_product_service.py +++ b/tests/test_21_product_service.py @@ -6,7 +6,12 @@ Copyright (c) 2022 Asymworks, LLC. All Rights Reserved. import pytest -from sigl.domain.service import product_by_id, product_create, products_all +from sigl.domain.service import ( + product_by_id, + product_by_name, + product_create, + products_all, +) # Always use 'app' fixture so ORM gets initialized pytestmark = pytest.mark.usefixtures('app') @@ -60,3 +65,11 @@ def test_product_all_items_skips_non_remembered(session): assert p1 in products assert p3 in products assert p2 not in products + + +@pytest.mark.unit +def test_product_lookup_by_name(session): + """Test that a Product can be looked up by Name (case-insensitive).""" + p1 = product_create(session, 'Apples') + product = product_by_name(session, 'apples') + assert product == p1 diff --git a/tests/test_22_list_service.py b/tests/test_22_list_service.py index 554ab1e..bab874e 100644 --- a/tests/test_22_list_service.py +++ b/tests/test_22_list_service.py @@ -13,6 +13,7 @@ from sigl.domain.service import ( list_deleteCrossedOff, list_entry_set_crossedOff, product_by_id, + product_create, ) # Always use 'app' fixture so ORM gets initialized @@ -45,6 +46,42 @@ def test_list_add_product_defaults(session): assert list.entries[0] == entry +@pytest.mark.unit +def test_list_add_product_by_id(session): + """Test adding an existing Product to a List by Id.""" + p1 = product_create(session, 'Eggs', category='Dairy') + + list = list_create(session, 'Test') + entry = list_addItem(session, list.id, productId=p1.id) + + assert entry.id is not None + assert entry.product is not None + assert entry.product.name == 'Eggs' + assert entry.product.category == 'Dairy' + assert entry.product.remember is True + + assert len(list.entries) == 1 + assert list.entries[0] == entry + + +@pytest.mark.unit +def test_list_add_product_by_name(session): + """Test adding an existing Product to a List by Name.""" + product_create(session, 'Eggs', category='Dairy') + + list = list_create(session, 'Test') + entry = list_addItem(session, list.id, productName='eggs') + + assert entry.id is not None + assert entry.product is not None + assert entry.product.name == 'Eggs' + assert entry.product.category == 'Dairy' + assert entry.product.remember is True + + assert len(list.entries) == 1 + assert list.entries[0] == entry + + @pytest.mark.unit def test_list_add_product_no_remember(session): """Test adding a Product to a List without remembering it."""