Skip to content

Commit

Permalink
feat: add basic support for URL filters
Browse files Browse the repository at this point in the history
  • Loading branch information
waza-ari committed Jan 11, 2024
1 parent 3f5f9a1 commit 9a65d50
Show file tree
Hide file tree
Showing 14 changed files with 178 additions and 22 deletions.
10 changes: 9 additions & 1 deletion easyverein/models/contact_details.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

from typing import Any, Literal

from pydantic import EmailStr, Field
from pydantic import BaseModel, EmailStr, Field

from ..core.types import Date, DateTime
from .base import EasyVereinBase
Expand Down Expand Up @@ -135,3 +135,11 @@ class ContactDetailsCreate(ContactDetailsUpdate, required_mixin(["isCompany"])):
"""
Pydantic model for creating new contact details
"""


class ContactDetailsFilter(BaseModel):
"""
Pydantic model used to filter contact details
"""

# TODO: implement
10 changes: 9 additions & 1 deletion easyverein/models/custom_field.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

from typing import Literal

from pydantic import Field
from pydantic import BaseModel, Field

from ..core.types import (
EasyVereinReference,
Expand Down Expand Up @@ -91,3 +91,11 @@ class CustomFieldUpdate(CustomField):
"""

pass


class CustomFieldFilter(BaseModel):
"""
Pydantic model used to filter custom fields
"""

# TODO: implement
58 changes: 57 additions & 1 deletion easyverein/models/invoice.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,15 @@

from typing import Literal

from ..core.types import Date, EasyVereinReference, PositiveIntWithZero
from pydantic import BaseModel

from ..core.types import (
Date,
EasyVereinReference,
FilterIntList,
FilterStrList,
PositiveIntWithZero,
)
from .base import EasyVereinBase
from .mixins.required_attributes import required_mixin

Expand Down Expand Up @@ -73,5 +81,53 @@ class InvoiceUpdate(Invoice):
"""


class InvoiceFilter(BaseModel):
"""
Pydantic model used to filter invoices
"""

id__in: FilterIntList | None = None
relatedAddress: int | None = None
relatedAddress__isnull: bool = None
relatedBookings: FilterIntList | None = None
relatedBookings__isnull: bool = None
relatedBookings__ne: FilterIntList | None = None
payedFromUser: int | None = None
payedFromUser__isnull: bool = None
approvedFromAdmin: int | None = None
approvedFromAdmin__isnull: bool = None
canceledInvoice__isnull: bool = None
date: Date | None = None
date__gt: Date | None = None
date__lt: Date | None = None
dateItHappened: Date | None = None
dateItHappened__gt: Date | None = None
dateItHappened__lt: Date | None = None
invNumber__in: FilterStrList | None = None
receiver: str | None = None
totalPrice: float | None = None
totalPrice__gte: float | None = None
totalPrice__lte: float | None = None
kind: str | None = None
kind__in: FilterStrList | None = None
refNumber: str | None = None
paymentDifference: float | None = None
paymentDifference__gte: float | None = None
paymentDifference__lte: float | None = None
paymentDifference__ne: float | None = None
isRequest: bool | None = None
accnumber: int | None = None
accnumber__ne: int | None = None
isDraft: bool | None = None
isTemplate: bool | None = None
actualCallStateName: str | None = None
actualCallStateName__ne: str | None = None
deleted: bool | None = None
customfilter: int | None = None
usesessionfilter: bool | None = None
ordering: str | None = None
search: str | None = None


from .invoice_item import InvoiceItem # noqa: E402
from .member import Member # noqa: E402
10 changes: 9 additions & 1 deletion easyverein/models/invoice_item.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

from typing import Annotated

from pydantic import PositiveInt, StringConstraints
from pydantic import BaseModel, PositiveInt, StringConstraints

from ..core.types import EasyVereinReference
from .base import EasyVereinBase
Expand Down Expand Up @@ -53,4 +53,12 @@ class InvoiceItemUpdate(InvoiceItem):
"""


class InvoiceItemFilter(BaseModel):
"""
Pydantic model used to filter invoice items
"""

# TODO: implement


from .invoice import Invoice # noqa: E402
10 changes: 9 additions & 1 deletion easyverein/models/member.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

from typing import Literal

from pydantic import Field, PositiveInt
from pydantic import BaseModel, Field, PositiveInt

from ..core.types import AnyHttpURL, DateTime, EasyVereinReference
from .base import EasyVereinBase
Expand Down Expand Up @@ -120,5 +120,13 @@ class MemberCreate(MemberUpdate, required_mixin(["contactDetails"])):
emailOrUserName: str


class MemberFilter(BaseModel):
"""
Pydantic model used to filter members
"""

# TODO: implement


from .contact_details import ContactDetails # noqa: E402
from .member_custom_field import MemberCustomField # noqa: E402
10 changes: 10 additions & 0 deletions easyverein/models/member_custom_field.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
from __future__ import annotations

from pydantic import BaseModel

from ..core.types import EasyVereinReference
from .base import EasyVereinBase
from .mixins.required_attributes import required_mixin
Expand Down Expand Up @@ -47,5 +49,13 @@ class MemberCustomFieldUpdate(MemberCustomField):
pass


class MemberCustomFieldFilter(BaseModel):
"""
Pydantic model used to filter members custom fields
"""

# TODO: implement


from .custom_field import CustomField # noqa: E402
from .member import Member # noqa: E402
5 changes: 4 additions & 1 deletion easyverein/modules/contact_details.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,15 @@

from ..core.client import EasyvereinClient
from ..models import ContactDetails, ContactDetailsCreate, ContactDetailsUpdate
from ..models.contact_details import ContactDetailsFilter
from .mixins.crud import CRUDMixin
from .mixins.recycle_bin import RecycleBinMixin


class ContactDetailsMixin(
CRUDMixin[ContactDetails, ContactDetailsCreate, ContactDetailsUpdate],
CRUDMixin[
ContactDetails, ContactDetailsCreate, ContactDetailsUpdate, ContactDetailsFilter
],
RecycleBinMixin[ContactDetails],
):
def __init__(self, client: EasyvereinClient, logger: logging.Logger):
Expand Down
9 changes: 7 additions & 2 deletions easyverein/modules/custom_field.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,18 @@
import logging

from ..core.client import EasyvereinClient
from ..models.custom_field import CustomField, CustomFieldCreate, CustomFieldUpdate
from ..models.custom_field import (
CustomField,
CustomFieldCreate,
CustomFieldFilter,
CustomFieldUpdate,
)
from .mixins.crud import CRUDMixin
from .mixins.recycle_bin import RecycleBinMixin


class CustomFieldMixin(
CRUDMixin[CustomField, CustomFieldCreate, CustomFieldUpdate],
CRUDMixin[CustomField, CustomFieldCreate, CustomFieldUpdate, CustomFieldFilter],
RecycleBinMixin[CustomField],
):
def __init__(self, client: EasyvereinClient, logger: logging.Logger):
Expand Down
5 changes: 3 additions & 2 deletions easyverein/modules/invoice.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,15 @@

from ..core.client import EasyvereinClient
from ..core.exceptions import EasyvereinAPIException
from ..models.invoice import Invoice, InvoiceCreate, InvoiceUpdate
from ..models.invoice import Invoice, InvoiceCreate, InvoiceFilter, InvoiceUpdate
from ..models.invoice_item import InvoiceItem
from .mixins.crud import CRUDMixin
from .mixins.recycle_bin import RecycleBinMixin


class InvoiceMixin(
CRUDMixin[Invoice, InvoiceCreate, InvoiceUpdate], RecycleBinMixin[Invoice]
CRUDMixin[Invoice, InvoiceCreate, InvoiceUpdate, InvoiceFilter],
RecycleBinMixin[Invoice],
):
def __init__(self, client: EasyvereinClient, logger: logging.Logger):
super().__init__()
Expand Down
11 changes: 9 additions & 2 deletions easyverein/modules/invoice_item.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,18 @@
import logging

from ..core.client import EasyvereinClient
from ..models.invoice_item import InvoiceItem, InvoiceItemCreate, InvoiceItemUpdate
from ..models.invoice_item import (
InvoiceItem,
InvoiceItemCreate,
InvoiceItemFilter,
InvoiceItemUpdate,
)
from .mixins.crud import CRUDMixin


class InvoiceItemMixin(CRUDMixin[InvoiceItem, InvoiceItemCreate, InvoiceItemUpdate]):
class InvoiceItemMixin(
CRUDMixin[InvoiceItem, InvoiceItemCreate, InvoiceItemUpdate, InvoiceItemFilter]
):
def __init__(self, client: EasyvereinClient, logger: logging.Logger):
self.endpoint_name = "invoice-item"
self.return_type = InvoiceItem
Expand Down
4 changes: 2 additions & 2 deletions easyverein/modules/member.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,13 @@
import logging

from ..core.client import EasyvereinClient
from ..models.member import Member, MemberCreate, MemberUpdate
from ..models.member import Member, MemberCreate, MemberFilter, MemberUpdate
from .mixins.crud import CRUDMixin
from .mixins.recycle_bin import RecycleBinMixin


class MemberMixin(
CRUDMixin[Member, MemberCreate, MemberUpdate], RecycleBinMixin[Member]
CRUDMixin[Member, MemberCreate, MemberUpdate, MemberFilter], RecycleBinMixin[Member]
):
def __init__(self, client: EasyvereinClient, logger: logging.Logger):
self.endpoint_name = "member"
Expand Down
8 changes: 7 additions & 1 deletion easyverein/modules/member_custom_field.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,18 @@
from ..core.client import EasyvereinClient
from ..core.protocol import IsEVClientProtocol
from ..models import MemberCustomField, MemberCustomFieldCreate, MemberCustomFieldUpdate
from ..models.member_custom_field import MemberCustomFieldFilter
from .mixins.crud import CRUDMixin
from .mixins.recycle_bin import RecycleBinMixin


class MemberCustomFieldMixin(
CRUDMixin[MemberCustomField, MemberCustomFieldCreate, MemberCustomFieldUpdate],
CRUDMixin[
MemberCustomField,
MemberCustomFieldCreate,
MemberCustomFieldUpdate,
MemberCustomFieldFilter,
],
RecycleBinMixin[MemberCustomField],
):
def __init__(self, client: EasyvereinClient, logger: logging.Logger):
Expand Down
29 changes: 22 additions & 7 deletions easyverein/modules/mixins/crud.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,28 +11,40 @@
ModelType = TypeVar("ModelType", bound=BaseModel)
CreateModelType = TypeVar("CreateModelType", bound=BaseModel)
UpdateModelType = TypeVar("UpdateModelType", bound=BaseModel)
FilterType = TypeVar("FilterType", bound=BaseModel)


class CRUDMixin(Generic[ModelType, CreateModelType, UpdateModelType]):
class CRUDMixin(Generic[ModelType, CreateModelType, UpdateModelType, FilterType]):
def get(
self: IsEVClientProtocol, query: str = None, limit: int = 10
self: IsEVClientProtocol,
query: str = None,
search: FilterType = None,
limit: int = 10,
) -> list[ModelType]:
"""
Fetches a single page of a given page size. The page size is defined by the `limit` parameter
with an API sided upper limit of 100.
Args:
query: Query to use with API. Refer to the EV API help for more information on how to use queries
search: Filter to use with API. Refer to the EV API help for more information on how to use filters
limit: Defines how many resources to return.
"""
self.logger.info(f"Fetching selected {self.endpoint_name} objects from API")

url = self.c.get_url(f"/{self.endpoint_name}", {"limit": limit, "query": query})
url_params = {"limit": limit, "query": query}
if search:
url_params |= search.model_dump(exclude_unset=True, exclude_defaults=True)

url = self.c.get_url(f"/{self.endpoint_name}", url_params)

return self.c.fetch(url, self.return_type)

def get_all(
self: IsEVClientProtocol, query: str = None, limit_per_page: int = 10
self: IsEVClientProtocol,
query: str = None,
search: FilterType = None,
limit_per_page: int = 10,
) -> list[ModelType]:
"""
Convenient method that fetches all objects from the EV API, abstracting away the need to handle pagination.
Expand All @@ -43,13 +55,16 @@ def get_all(
Args:
query: Query to use with API. Defaults to None. Refer to the EV API help for more
information on how to use queries
search: Filter to use with API. Refer to the EV API help for more information on how to use filters
limit_per_page: Defines how many resources to return. Defaults to 10.
"""
self.logger.info(f"Fetching selected {self.endpoint_name} objects from API")

url = self.c.get_url(
f"/{self.endpoint_name}", {"limit": limit_per_page, "query": query}
)
url_params = {"limit": limit_per_page, "query": query}
if search:
url_params |= search.model_dump(exclude_unset=True, exclude_defaults=True)

url = self.c.get_url(f"/{self.endpoint_name}", url_params)

return self.c.fetch_paginated(url, self.return_type, limit_per_page)

Expand Down
21 changes: 21 additions & 0 deletions tests/test_filters.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
from easyverein import EasyvereinAPI
from easyverein.models.invoice import Invoice, InvoiceFilter


class TestFilter:
def test_filter_invoices(self, ev_connection: EasyvereinAPI):
search = InvoiceFilter(
invNumber__in=["1", "3", "5"], canceledInvoice__isnull=True, isDraft=False
)

invoices = ev_connection.invoice.get(search=search)

# Check if the response is a list
assert isinstance(invoices, list)

# We should have 5 invoices based on the example data
assert len(invoices) == 3

# Check if all the invoices are of type Invoice
for invoice in invoices:
assert isinstance(invoice, Invoice)

0 comments on commit 9a65d50

Please sign in to comment.