Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,8 @@ cython_debug/
.ruff_cache
.idea
.openapi/
openapi/openapi-dev.json
.github/copilot-instructions.md
# Makefile
make/local.mk

Expand Down
50 changes: 48 additions & 2 deletions mpt_api_client/models/model.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import re
from collections import UserList
from collections.abc import Iterable
from types import MappingProxyType
from typing import Any, Self, get_args, get_origin, override

from mpt_api_client.http.types import Response
Expand All @@ -12,9 +13,47 @@
_SNAKE_CASE_BOUNDARY = re.compile(r"([a-z0-9])([A-Z])")
_SNAKE_CASE_ACRONYM = re.compile(r"(?<=[A-Z])(?=[A-Z][a-z0-9])")

# Explicit bidirectional mappings for API field names that contain two or more consecutive
# uppercase letters (e.g. PPx1, unitLP). The generic regex cannot round-trip these correctly,
# so we maintain an explicit lookup table that is checked before the regex is applied.
_FIELD_NAME_MAPPINGS: MappingProxyType[str, str] = MappingProxyType({
# PP* price columns
"PPx1": "ppx1",
"PPxM": "ppxm",
"PPxY": "ppxy",
# SP* price columns
"SPx1": "spx1",
"SPxM": "spxm",
"SPxY": "spxy",
# LP* price columns
"LPx1": "lpx1",
"LPxM": "lpxm",
"LPxY": "lpxy",
# unit + 2-letter acronym suffix
"unitLP": "unit_lp",
"unitPP": "unit_pp",
"unitSP": "unit_sp",
# total + 2-letter acronym suffix
"totalGT": "total_gt",
"totalPP": "total_pp",
"totalSP": "total_sp",
"totalST": "total_st",
})

_FIELD_NAME_MAPPINGS_REVERSE: MappingProxyType[str, str] = MappingProxyType({
snake: camel for camel, snake in _FIELD_NAME_MAPPINGS.items()
})


def to_snake_case(key: str) -> str:
"""Converts a camelCase string to snake_case."""
"""Converts a camelCase string to snake_case.

Explicit mappings in ``_FIELD_NAME_MAPPINGS`` take priority over the generic
regex for fields that contain two or more consecutive uppercase letters.
"""
mapped = _FIELD_NAME_MAPPINGS.get(key)
if mapped is not None:
return mapped
if "_" in key and key.islower():
return key
# Common pattern for PascalCase/camelCase conversion
Expand All @@ -24,7 +63,14 @@ def to_snake_case(key: str) -> str:


def to_camel_case(key: str) -> str:
"""Converts a snake_case string to camelCase."""
"""Converts a snake_case string to camelCase.

Explicit mappings in ``_FIELD_NAME_MAPPINGS_REVERSE`` take priority over the
generic logic for fields that contain two or more consecutive uppercase letters.
"""
mapped = _FIELD_NAME_MAPPINGS_REVERSE.get(key)
if mapped is not None:
return mapped
parts = key.split("_")
return parts[0] + "".join(x.title() for x in parts[1:]) # noqa: WPS111 WPS221

Expand Down
29 changes: 28 additions & 1 deletion mpt_api_client/resources/catalog/authorizations.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,37 @@
ManagedResourceMixin,
)
from mpt_api_client.models import Model
from mpt_api_client.models.model import BaseModel


class Authorization(Model):
"""Authorization resource."""
"""Authorization resource.

Attributes:
name: Authorization name.
external_ids: External identifiers for the authorization.
currency: Currency code associated with the authorization.
notes: Additional notes.
product: Reference to the product.
vendor: Reference to the vendor.
owner: Reference to the owner account.
statistics: Authorization statistics.
journal: Journal reference.
eligibility: Eligibility information.
audit: Audit information (created, updated events).
"""

name: str | None
external_ids: BaseModel | None
currency: str | None
notes: str | None
product: BaseModel | None
vendor: BaseModel | None
owner: BaseModel | None
statistics: BaseModel | None
journal: BaseModel | None
eligibility: BaseModel | None
audit: BaseModel | None


class AuthorizationsServiceConfig:
Expand Down
29 changes: 28 additions & 1 deletion mpt_api_client/resources/catalog/items.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,41 @@
ManagedResourceMixin,
)
from mpt_api_client.models import Model
from mpt_api_client.models.model import BaseModel
from mpt_api_client.resources.catalog.mixins import (
AsyncPublishableMixin,
PublishableMixin,
)


class Item(Model): # noqa: WPS110
"""Item resource."""
"""Item resource.

Attributes:
name: Item name.
description: Item description.
external_ids: External identifiers for the item.
group: Reference to the item group.
unit: Reference to the unit of measure.
terms: Reference to the terms and conditions.
quantity_not_applicable: Whether quantity is not applicable to this item.
status: Item status.
product: Reference to the product.
parameters: List of parameters associated with this item.
audit: Audit information (created, updated events).
"""

name: str | None
description: str | None
external_ids: BaseModel | None
group: BaseModel | None
unit: BaseModel | None
terms: BaseModel | None
quantity_not_applicable: bool | None
status: str | None
product: BaseModel | None
parameters: list[BaseModel] | None
audit: BaseModel | None


class ItemsServiceConfig:
Expand Down
27 changes: 26 additions & 1 deletion mpt_api_client/resources/catalog/listings.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,35 @@
ManagedResourceMixin,
)
from mpt_api_client.models import Model
from mpt_api_client.models.model import BaseModel


class Listing(Model):
"""Listing resource."""
"""Listing resource.

Attributes:
authorization: Reference to the authorization.
product: Reference to the product.
vendor: Reference to the vendor.
seller: Reference to the seller.
price_list: Reference to the associated price list.
primary: Whether this is the primary listing.
notes: Additional notes.
statistics: Listing statistics.
eligibility: Eligibility information.
audit: Audit information (created, updated events).
"""

authorization: BaseModel | None
product: BaseModel | None
vendor: BaseModel | None
seller: BaseModel | None
price_list: BaseModel | None
primary: bool | None
notes: str | None
statistics: BaseModel | None
eligibility: BaseModel | None
audit: BaseModel | None


class ListingsServiceConfig:
Expand Down
47 changes: 46 additions & 1 deletion mpt_api_client/resources/catalog/price_list_items.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,55 @@
UpdateMixin,
)
from mpt_api_client.models import Model
from mpt_api_client.models.model import BaseModel


class PriceListItem(Model):
"""Price List Item resource."""
"""Price List Item resource.

Attributes:
status: Price list item status.
description: Price list item description.
reason_for_change: Reason for the price change.
unit_lp: Unit list price.
unit_pp: Unit purchase price.
markup: Markup percentage.
margin: Margin percentage.
unit_sp: Unit sell price.
ppx1: Purchase price for 1-year term.
ppxm: Purchase price for monthly term.
ppxy: Purchase price for yearly term.
spx1: Sell price for 1-year term.
spxm: Sell price for monthly term.
spxy: Sell price for yearly term.
lpx1: List price for 1-year term.
lpxm: List price for monthly term.
lpxy: List price for yearly term.
price_list: Reference to the parent price list.
item: Reference to the associated item.
audit: Audit information (created, updated events).
"""

status: str | None
description: str | None
reason_for_change: str | None
unit_lp: float | None
unit_pp: float | None
markup: float | None
margin: float | None
unit_sp: float | None
ppx1: float | None
ppxm: float | None
ppxy: float | None
spx1: float | None
spxm: float | None
spxy: float | None
lpx1: float | None
lpxm: float | None
lpxy: float | None
price_list: BaseModel | None
item: BaseModel | None
audit: BaseModel | None
Comment on lines +40 to +59
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is not ideal way to transform the Pascal Case into snake case for those variables.

Probably we want to specify a map of variables so we can have better namings.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We could specify a map for the given resource PriceListItem to map the json to attributes as we would like.

The solution will work only if the resource is loaded using PriceListItem, but if it is loaded as side/sub resource by RQL we will have non consisting mapping.

A better solution would be to keep a list of keywords and their mappings. Not great but better unified attribute naming.

@robcsegal any thoughts in here?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, a list of keywords and their mappings would better. Not sure of a better way right now.



class PriceListItemsServiceConfig:
Expand Down
27 changes: 26 additions & 1 deletion mpt_api_client/resources/catalog/price_lists.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,39 @@
ManagedResourceMixin,
)
from mpt_api_client.models import Model
from mpt_api_client.models.model import BaseModel
from mpt_api_client.resources.catalog.price_list_items import (
AsyncPriceListItemsService,
PriceListItemsService,
)


class PriceList(Model):
"""Price List resource."""
"""Price List resource.

Attributes:
currency: Currency code for this price list.
precision: Decimal precision for prices.
default_markup: Default markup percentage.
default_margin: Default margin percentage.
notes: Additional notes.
external_ids: External identifiers for the price list.
statistics: Price list statistics.
product: Reference to the associated product.
vendor: Reference to the vendor.
audit: Audit information (created, updated events).
"""

currency: str | None
precision: int | None
default_markup: float | None
default_margin: float | None
notes: str | None
external_ids: BaseModel | None
statistics: BaseModel | None
product: BaseModel | None
vendor: BaseModel | None
audit: BaseModel | None


class PriceListsServiceConfig:
Expand Down
29 changes: 28 additions & 1 deletion mpt_api_client/resources/catalog/pricing_policies.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,41 @@
ManagedResourceMixin,
)
from mpt_api_client.models import Model, ResourceData
from mpt_api_client.models.model import BaseModel
from mpt_api_client.resources.catalog.pricing_policy_attachments import (
AsyncPricingPolicyAttachmentsService,
PricingPolicyAttachmentsService,
)


class PricingPolicy(Model):
"""Pricing policy resource."""
"""Pricing policy resource.

Attributes:
name: Pricing policy name.
external_ids: External identifiers for the pricing policy.
client: Reference to the client account.
eligibility: Eligibility information.
markup: Markup percentage.
margin: Margin percentage.
notes: Additional notes.
products: List of associated products.
status: Pricing policy status.
statistics: Pricing policy statistics.
audit: Audit information (created, updated events).
"""

name: str | None
external_ids: BaseModel | None
client: BaseModel | None
eligibility: BaseModel | None
markup: float | None
margin: float | None
notes: str | None
products: list[BaseModel] | None
status: str | None
statistics: BaseModel | None
audit: BaseModel | None


class PricingPoliciesServiceConfig:
Expand Down
23 changes: 22 additions & 1 deletion mpt_api_client/resources/catalog/pricing_policy_attachments.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,31 @@
ModifiableResourceMixin,
)
from mpt_api_client.models import Model
from mpt_api_client.models.model import BaseModel


class PricingPolicyAttachment(Model):
"""Pricing Policy Attachment resource."""
"""Pricing Policy Attachment resource.

Attributes:
name: Attachment name.
type: Attachment type.
size: File size in bytes.
description: Attachment description.
file_name: Original file name.
content_type: MIME content type of the attachment.
status: Attachment status.
audit: Audit information (created, updated events).
"""

name: str | None
type: str | None
size: int | None
description: str | None
file_name: str | None
content_type: str | None
status: str | None
audit: BaseModel | None


class PricingPolicyAttachmentsServiceConfig:
Expand Down
Loading
Loading