Added support for multiple int where's

This commit is contained in:
Donkie
2023-09-09 10:18:26 +02:00
parent 6d68a40313
commit 6ee3b8df70
8 changed files with 168 additions and 42 deletions

View File

@@ -119,42 +119,55 @@ async def find(
alias="vendor_name", alias="vendor_name",
default=None, default=None,
title="Vendor Name", title="Vendor Name",
description="Partial case-insensitive search term for the filament vendor name.", description=(
"Partial case-insensitive search term for the filament vendor name. "
"Separate multiple terms with a comma."
),
deprecated=True, deprecated=True,
), ),
vendor_id_old: Optional[int] = Query( vendor_id_old: Optional[str] = Query(
alias="vendor_id", alias="vendor_id",
default=None, default=None,
title="Vendor ID", title="Vendor ID",
description="Match an exact vendor ID.", description="Match an exact vendor ID. Separate multiple IDs with a comma.",
deprecated=True, deprecated=True,
examples=["1", "1,2"],
), ),
vendor_name: Optional[str] = Query( vendor_name: Optional[str] = Query(
alias="vendor.name", alias="vendor.name",
default=None, default=None,
title="Vendor Name", title="Vendor Name",
description="Partial case-insensitive search term for the filament vendor name.", description=(
"Partial case-insensitive search term for the filament vendor name. "
"Separate multiple terms with a comma."
),
), ),
vendor_id: Optional[int] = Query( vendor_id: Optional[str] = Query(
alias="vendor.id", alias="vendor.id",
default=None, default=None,
title="Vendor ID", title="Vendor ID",
description="Match an exact vendor ID.", description="Match an exact vendor ID. Separate multiple IDs with a comma.",
examples=["1", "1,2"],
), ),
name: Optional[str] = Query( name: Optional[str] = Query(
default=None, default=None,
title="Filament Name", title="Filament Name",
description="Partial case-insensitive search term for the filament name.", description="Partial case-insensitive search term for the filament name. Separate multiple terms with a comma.",
), ),
material: Optional[str] = Query( material: Optional[str] = Query(
default=None, default=None,
title="Filament Material", title="Filament Material",
description="Partial case-insensitive search term for the filament material.", description=(
"Partial case-insensitive search term for the filament material. Separate multiple terms with a comma."
),
), ),
article_number: Optional[str] = Query( article_number: Optional[str] = Query(
default=None, default=None,
title="Filament Article Number", title="Filament Article Number",
description="Partial case-insensitive search term for the filament article number.", description=(
"Partial case-insensitive search term for the filament article number. "
"Separate multiple terms with a comma."
),
), ),
sort: Optional[str] = Query( sort: Optional[str] = Query(
default=None, default=None,
@@ -181,10 +194,19 @@ async def find(
field, direction = sort_item.split(":") field, direction = sort_item.split(":")
sort_by[field] = SortOrder[direction.upper()] sort_by[field] = SortOrder[direction.upper()]
vendor_id = vendor_id if vendor_id is not None else vendor_id_old
if vendor_id is not None:
try:
vendor_ids = [int(vendor_id_item) for vendor_id_item in vendor_id.split(",")]
except ValueError as e:
raise RequestValidationError([ErrorWrapper(ValueError("Invalid vendor_id"), ("query", "vendor_id"))]) from e
else:
vendor_ids = None
db_items, total_count = await filament.find( db_items, total_count = await filament.find(
db=db, db=db,
vendor_name=vendor_name if vendor_name is not None else vendor_name_old, vendor_name=vendor_name if vendor_name is not None else vendor_name_old,
vendor_id=vendor_id if vendor_id is not None else vendor_id_old, vendor_id=vendor_ids,
name=name, name=name,
material=material, material=material,
article_number=article_number, article_number=article_number,

View File

@@ -77,76 +77,92 @@ async def find(
alias="filament_name", alias="filament_name",
default=None, default=None,
title="Filament Name", title="Filament Name",
description="Partial case-insensitive search term for the filament name.", description="Partial case-insensitive search term for the filament name. Separate multiple terms with a comma.",
deprecated=True, deprecated=True,
), ),
filament_id_old: Optional[int] = Query( filament_id_old: Optional[str] = Query(
alias="filament_id", alias="filament_id",
default=None, default=None,
title="Filament ID", title="Filament ID",
description="Match an exact filament ID.", description="Match an exact filament ID. Separate multiple IDs with a comma.",
deprecated=True, deprecated=True,
examples=["1", "1,2"],
), ),
filament_material_old: Optional[str] = Query( filament_material_old: Optional[str] = Query(
alias="filament_material", alias="filament_material",
default=None, default=None,
title="Filament Material", title="Filament Material",
description="Partial case-insensitive search term for the filament material.", description=(
"Partial case-insensitive search term for the filament material. Separate multiple terms with a comma."
),
deprecated=True, deprecated=True,
), ),
vendor_name_old: Optional[str] = Query( vendor_name_old: Optional[str] = Query(
alias="vendor_name", alias="vendor_name",
default=None, default=None,
title="Vendor Name", title="Vendor Name",
description="Partial case-insensitive search term for the filament vendor name.", description=(
"Partial case-insensitive search term for the filament vendor name. Separate multiple terms with a comma."
),
deprecated=True, deprecated=True,
), ),
vendor_id_old: Optional[int] = Query( vendor_id_old: Optional[str] = Query(
alias="vendor_id", alias="vendor_id",
default=None, default=None,
title="Vendor ID", title="Vendor ID",
description="Match an exact vendor ID.", description="Match an exact vendor ID. Separate multiple IDs with a comma.",
deprecated=True, deprecated=True,
examples=["1", "1,2"],
), ),
filament_name: Optional[str] = Query( filament_name: Optional[str] = Query(
alias="filament.name", alias="filament.name",
default=None, default=None,
title="Filament Name", title="Filament Name",
description="Partial case-insensitive search term for the filament name.", description="Partial case-insensitive search term for the filament name. Separate multiple terms with a comma.",
), ),
filament_id: Optional[int] = Query( filament_id: Optional[str] = Query(
alias="filament.id", alias="filament.id",
default=None, default=None,
title="Filament ID", title="Filament ID",
description="Match an exact filament ID.", description="Match an exact filament ID. Separate multiple IDs with a comma.",
examples=["1", "1,2"],
), ),
filament_material: Optional[str] = Query( filament_material: Optional[str] = Query(
alias="filament.material", alias="filament.material",
default=None, default=None,
title="Filament Material", title="Filament Material",
description="Partial case-insensitive search term for the filament material.", description=(
"Partial case-insensitive search term for the filament material. Separate multiple terms with a comma."
),
), ),
vendor_name: Optional[str] = Query( vendor_name: Optional[str] = Query(
alias="vendor.name", alias="vendor.name",
default=None, default=None,
title="Vendor Name", title="Vendor Name",
description="Partial case-insensitive search term for the filament vendor name.", description=(
"Partial case-insensitive search term for the filament vendor name. Separate multiple terms with a comma."
),
), ),
vendor_id: Optional[int] = Query( vendor_id: Optional[str] = Query(
alias="vendor.id", alias="vendor.id",
default=None, default=None,
title="Vendor ID", title="Vendor ID",
description="Match an exact vendor ID.", description="Match an exact vendor ID. Separate multiple IDs with a comma.",
examples=["1", "1,2"],
), ),
location: Optional[str] = Query( location: Optional[str] = Query(
default=None, default=None,
title="Location", title="Location",
description="Partial case-insensitive search term for the spool location.", description=(
"Partial case-insensitive search term for the spool location. Separate multiple terms with a comma."
),
), ),
lot_nr: Optional[str] = Query( lot_nr: Optional[str] = Query(
default=None, default=None,
title="Lot/Batch Number", title="Lot/Batch Number",
description="Partial case-insensitive search term for the spool lot number.", description=(
"Partial case-insensitive search term for the spool lot number. Separate multiple terms with a comma."
),
), ),
allow_archived: bool = Query( allow_archived: bool = Query(
default=False, default=False,
@@ -178,13 +194,33 @@ async def find(
field, direction = sort_item.split(":") field, direction = sort_item.split(":")
sort_by[field] = SortOrder[direction.upper()] sort_by[field] = SortOrder[direction.upper()]
filament_id = filament_id if filament_id is not None else filament_id_old
if filament_id is not None:
try:
filament_ids = [int(filament_id_item) for filament_id_item in filament_id.split(",")]
except ValueError as e:
raise RequestValidationError(
[ErrorWrapper(ValueError("Invalid filament_id"), ("query", "filament_id"))],
) from e
else:
filament_ids = None
vendor_id = vendor_id if vendor_id is not None else vendor_id_old
if vendor_id is not None:
try:
vendor_ids = [int(vendor_id_item) for vendor_id_item in vendor_id.split(",")]
except ValueError as e:
raise RequestValidationError([ErrorWrapper(ValueError("Invalid vendor_id"), ("query", "vendor_id"))]) from e
else:
vendor_ids = None
db_items, total_count = await spool.find( db_items, total_count = await spool.find(
db=db, db=db,
filament_name=filament_name if filament_name is not None else filament_name_old, filament_name=filament_name if filament_name is not None else filament_name_old,
filament_id=filament_id if filament_id is not None else filament_id_old, filament_id=filament_ids,
filament_material=filament_material if filament_material is not None else filament_material_old, filament_material=filament_material if filament_material is not None else filament_material_old,
vendor_name=vendor_name if vendor_name is not None else vendor_name_old, vendor_name=vendor_name if vendor_name is not None else vendor_name_old,
vendor_id=vendor_id if vendor_id is not None else vendor_id_old, vendor_id=vendor_ids,
location=location, location=location,
lot_nr=lot_nr, lot_nr=lot_nr,
allow_archived=allow_archived, allow_archived=allow_archived,

View File

@@ -53,7 +53,7 @@ async def find(
name: Optional[str] = Query( name: Optional[str] = Query(
default=None, default=None,
title="Vendor Name", title="Vendor Name",
description="Partial case-insensitive search term for the vendor name.", description="Partial case-insensitive search term for the vendor name. Separate multiple terms with a comma.",
), ),
sort: Optional[str] = Query( sort: Optional[str] = Query(
default=None, default=None,

View File

@@ -1,7 +1,8 @@
"""Helper functions for interacting with filament database objects.""" """Helper functions for interacting with filament database objects."""
import logging import logging
from typing import Optional from collections.abc import Sequence
from typing import Optional, Union
from sqlalchemy import func, select from sqlalchemy import func, select
from sqlalchemy.exc import IntegrityError from sqlalchemy.exc import IntegrityError
@@ -77,7 +78,7 @@ async def find(
*, *,
db: AsyncSession, db: AsyncSession,
vendor_name: Optional[str] = None, vendor_name: Optional[str] = None,
vendor_id: Optional[int] = None, vendor_id: Optional[Union[int, Sequence[int]]] = None,
name: Optional[str] = None, name: Optional[str] = None,
material: Optional[str] = None, material: Optional[str] = None,
article_number: Optional[str] = None, article_number: Optional[str] = None,

View File

@@ -1,7 +1,8 @@
"""Helper functions for interacting with spool database objects.""" """Helper functions for interacting with spool database objects."""
from collections.abc import Sequence
from datetime import datetime, timezone from datetime import datetime, timezone
from typing import Optional from typing import Optional, Union
import sqlalchemy import sqlalchemy
from sqlalchemy import case, func from sqlalchemy import case, func
@@ -87,10 +88,10 @@ async def find(
*, *,
db: AsyncSession, db: AsyncSession,
filament_name: Optional[str] = None, filament_name: Optional[str] = None,
filament_id: Optional[int] = None, filament_id: Optional[Union[int, Sequence[int]]] = None,
filament_material: Optional[str] = None, filament_material: Optional[str] = None,
vendor_name: Optional[str] = None, vendor_name: Optional[str] = None,
vendor_id: Optional[int] = None, vendor_id: Optional[Union[int, Sequence[int]]] = None,
location: Optional[str] = None, location: Optional[str] = None,
lot_nr: Optional[str] = None, lot_nr: Optional[str] = None,
allow_archived: bool = False, allow_archived: bool = False,

View File

@@ -1,7 +1,8 @@
"""Utility functions for the database module.""" """Utility functions for the database module."""
from collections.abc import Sequence
from enum import Enum from enum import Enum
from typing import Any, Optional from typing import Any, Optional, Union
import sqlalchemy import sqlalchemy
from sqlalchemy import Select from sqlalchemy import Select
@@ -68,23 +69,30 @@ def add_where_clause_str(
def add_where_clause_int( def add_where_clause_int(
stmt: Select, stmt: Select,
field: attributes.InstrumentedAttribute[int], field: attributes.InstrumentedAttribute[int],
value: Optional[int], value: Optional[Union[int, Sequence[int]]],
) -> Select: ) -> Select:
"""Add a where clause to a select statement for a field.""" """Add a where clause to a select statement for a field."""
if value is not None: if value is not None:
stmt = stmt.where(field == value) if isinstance(value, int):
value = [value]
stmt = stmt.where(field.in_(value))
return stmt return stmt
def add_where_clause_int_opt( def add_where_clause_int_opt(
stmt: Select, stmt: Select,
field: attributes.InstrumentedAttribute[Optional[int]], field: attributes.InstrumentedAttribute[Optional[int]],
value: Optional[int], value: Optional[Union[int, Sequence[int]]],
) -> Select: ) -> Select:
"""Add a where clause to a select statement for a field.""" """Add a where clause to a select statement for a field."""
if value is not None: if value is not None:
if value == -1: if isinstance(value, int):
stmt = stmt.where(field.is_(None)) value = [value]
else: statements = []
stmt = stmt.where(field == value) for value_part in value:
if value_part == -1:
statements.append(field.is_(None))
else:
statements.append(field == value_part)
stmt = stmt.where(sqlalchemy.or_(*statements))
return stmt return stmt

View File

@@ -332,6 +332,28 @@ def test_find_filaments_by_vendor_id(filaments: Fixture, field_name: str):
assert filament_lists_equal(filaments_result, filaments.filaments[:2]) assert filament_lists_equal(filaments_result, filaments.filaments[:2])
def test_find_filaments_by_multiple_vendor_ids(filaments: Fixture):
# Execute
vendor_1 = filaments.filaments[0]["vendor"]["id"]
vendor_2 = filaments.filaments[4]["vendor"]["id"]
result = httpx.get(
f"{URL}/api/v1/filament",
params={"vendor.id": f"{vendor_1},{vendor_2}"},
)
result.raise_for_status()
# Verify
filaments_result = result.json()
assert filament_lists_equal(
filaments_result,
[
filaments.filaments[0],
filaments.filaments[1],
filaments.filaments[4],
],
)
def test_find_filaments_by_empty_vendor_id(filaments: Fixture): def test_find_filaments_by_empty_vendor_id(filaments: Fixture):
# Execute # Execute
result = httpx.get( result = httpx.get(

View File

@@ -302,6 +302,22 @@ def test_find_spools_by_filament_id(spools: Fixture, field_name: str):
assert spool_lists_equal(spools_result, (spools.spools[0], spools.spools[1])) assert spool_lists_equal(spools_result, (spools.spools[0], spools.spools[1]))
def test_find_spools_by_multiple_filament_ids(spools: Fixture):
# Execute
filament_1 = spools.spools[0]["filament"]["id"]
filament_2 = spools.spools[3]["filament"]["id"]
result = httpx.get(
f"{URL}/api/v1/spool",
params={"filament.id": f"{filament_1},{filament_2}"},
)
result.raise_for_status()
# Verify
spools_result = result.json()
assert spool_lists_equal(spools_result, (spools.spools[0], spools.spools[1], spools.spools[3]))
@pytest.mark.parametrize("field_name", ["filament_material", "filament.material"]) @pytest.mark.parametrize("field_name", ["filament_material", "filament.material"])
def test_find_spools_by_filament_material(spools: Fixture, field_name: str): def test_find_spools_by_filament_material(spools: Fixture, field_name: str):
# Execute # Execute
@@ -370,6 +386,26 @@ def test_find_spools_by_filament_vendor_id(spools: Fixture, field_name: str):
assert spool_lists_equal(spools_result, (spools.spools[0], spools.spools[1])) assert spool_lists_equal(spools_result, (spools.spools[0], spools.spools[1]))
def test_find_spools_by_multiple_vendor_ids(spools: Fixture):
# Execute
vendor_1 = spools.spools[0]["filament"]["vendor"]["id"]
vendor_2 = spools.spools[4]["filament"]["vendor"]["id"]
result = httpx.get(
f"{URL}/api/v1/spool",
params={
"vendor.id": f"{vendor_1},{vendor_2}",
"allow_archived": True,
},
)
result.raise_for_status()
# Verify
spools_result = result.json()
assert len(spools_result) == 4
assert spool_lists_equal(spools_result, (spools.spools[0], spools.spools[1], spools.spools[2], spools.spools[4]))
def test_find_spools_by_empty_filament_vendor_id(spools: Fixture): def test_find_spools_by_empty_filament_vendor_id(spools: Fixture):
# Execute # Execute
result = httpx.get( result = httpx.get(