Skip to content
368 changes: 360 additions & 8 deletions inpost/api.py

Large diffs are not rendered by default.

9 changes: 5 additions & 4 deletions inpost/static/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,11 @@
from .headers import appjson
from .statuses import ParcelCarrierSize, ParcelLockerSize, ParcelDeliveryType, ParcelShipmentType, \
ParcelAdditionalInsurance, ParcelType, ParcelOwnership, CompartmentExpectedStatus, CompartmentActualStatus, \
ParcelServiceName, ParcelStatus
ParcelServiceName, ParcelStatus, ReturnsStatus
from .exceptions import NoParcelError, UnidentifiedParcelError, ParcelTypeError, NotAuthenticatedError, ReAuthenticationError, \
PhoneNumberError, SmsCodeError, RefreshTokenError, UnidentifiedAPIError, UserLocationError, \
UnidentifiedError, NotFoundError, UnauthorizedError, SingleParamError
UnidentifiedError, NotFoundError, UnauthorizedError, SingleParamError, MissingParamsError
from .endpoints import login, send_sms_code, confirm_sms_code, refresh_token, parcels, parcel, collect, \
compartment_open, compartment_status, terminate_collect_session, friends, shared, sent, returns, parcel_prices, \
tickets, logout
compartment_open, compartment_status, terminate_collect_session, friendship, shared, sent, returns, parcel_prices, \
tickets, logout, multi, validate_friendship, accept_friendship
from .friends import Friend
5 changes: 4 additions & 1 deletion inpost/static/endpoints.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,14 @@
refresh_token: str = 'https://api-inmobile-pl.easypack24.net/v1/authenticate' # post
parcels: str = 'https://api-inmobile-pl.easypack24.net/v3/parcels/tracked' # get
parcel: str = 'https://api-inmobile-pl.easypack24.net/v3/parcels/tracked/' # get
multi: str = 'https://api-inmobile-pl.easypack24.net/v3/parcels/multi/' # get
collect: str = 'https://api-inmobile-pl.easypack24.net/v1/collect/validate' # post
compartment_open: str = 'https://api-inmobile-pl.easypack24.net/v1/collect/compartment/open' # post
compartment_status: str = 'https://api-inmobile-pl.easypack24.net/v1/collect/compartment/status' # post
terminate_collect_session: str = 'https://api-inmobile-pl.easypack24.net/v1/collect/terminate' # post
friends: str = 'https://api-inmobile-pl.easypack24.net/v1/friends/' # get
friendship: str = 'https://api-inmobile-pl.easypack24.net/v1/friends/' # get, post, patch, delete
validate_friendship: str = 'https://api-inmobile-pl.easypack24.net/v1/invitations/validate' # post
accept_friendship: str = 'https://api-inmobile-pl.easypack24.net/v1/invitations/accept' # post
shared: str = 'https://api-inmobile-pl.easypack24.net/v1/parcels/shared' # post
sent: str = 'https://api-inmobile-pl.easypack24.net/v3/parcels/sent/' # get
returns: str = 'https://api-inmobile-pl.easypack24.net/v1/returns/parcels/' # get
Expand Down
5 changes: 5 additions & 0 deletions inpost/static/exceptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,11 @@ class SingleParamError(BaseInpostError):
pass


class MissingParamsError(BaseInpostError):
"""Is raised when none of params are filled"""
pass


class UnidentifiedError(BaseInpostError):
"""Is raised when no other error match"""
pass
34 changes: 34 additions & 0 deletions inpost/static/friends.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import logging

from arrow import get, Arrow


class Friend:
def __init__(self, friend_data, logger: logging.Logger):
self.uuid: str = friend_data['uuid'] if 'uuid' in friend_data else None
self.phone_number: str = friend_data['phoneNumber']
self.name: str = friend_data['name']
self._log: logging.Logger = logger.getChild(f'{__class__.__name__}.{self.uuid}')
self.invitaion_code: str | None = friend_data['invitationCode'] if 'invitationCode' in friend_data else None
self.created_date: Arrow | None = get(friend_data['createdDate']) if 'createdDate' in friend_data else None
self.expiry_date: Arrow | None = get(friend_data['expiryDate']) if 'expiryDate' in friend_data else None

if self.invitaion_code:
self._log.debug(f'created friendship with {self.name} using from_invitation')
else:
self._log.debug(f'created friendship with {self.name}')

@classmethod
def from_invitation(cls, invitation_data, logger: logging.Logger):
return cls(friend_data={'uuid': invitation_data['friend']['uuid'],
'phoneNumber': invitation_data['friend']['phoneNumber'],
'name': invitation_data['friend']['name'],
'invitationCode': invitation_data['invitationCode'],
'createdDate': invitation_data['createdDate'],
'expiryDate': invitation_data['expiryDate']
},
logger=logger)

def __repr__(self):
fields = tuple(f"{k}={v}" for k, v in self.__dict__.items() if k != '_log')
return self.__class__.__name__ + str(tuple(sorted(fields))).replace("\'", "")
118 changes: 85 additions & 33 deletions inpost/static/parcels.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,20 @@
from inpost.static.statuses import *


class Parcel:
class BaseParcel:
def __init__(self, parcel_data: dict, logger: logging.Logger):
self.shipment_number: str = parcel_data['shipmentNumber']
self._log: logging.Logger = logger.getChild(f'{__class__.__name__}.{self.shipment_number}')
self.status: ParcelStatus = ParcelStatus[parcel_data['status']]
# self.parcel_size: ParcelLockerSize | ParcelCarrierSize = ParcelLockerSize[parcel_data['parcelSize']] \
# if self.shipment_type == ParcelShipmentType.parcel else ParcelCarrierSize[parcel_data['parcelSize']]
self.expiry_date: arrow | None = get(parcel_data['expiryDate']) if 'expiryDate' in parcel_data else None
self.operations: Operations = Operations(operations_data=parcel_data['operations'], logger=self._log)
self.event_log: List[EventLog] = [EventLog(eventlog_data=event, logger=self._log)
for event in parcel_data['eventLog']]


class Parcel(BaseParcel):
"""Object representation of :class:`inpost.api.Inpost` compartment properties

:param parcel_data: :class:`dict` containing all parcel data
Expand All @@ -19,15 +32,16 @@ class Parcel:

def __init__(self, parcel_data: dict, logger: logging.Logger):
"""Constructor method"""
self.shipment_number: str = parcel_data['shipmentNumber']
super().__init__(parcel_data, logger)
# self.shipment_number: str = parcel_data['shipmentNumber']
self._log: logging.Logger = logger.getChild(f'{__class__.__name__}.{self.shipment_number}')
self.shipment_type: ParcelShipmentType = ParcelShipmentType[parcel_data['shipmentType']]
self._open_code: str | None = parcel_data['openCode'] if 'openCode' in parcel_data else None
self._qr_code: QRCode | None = QRCode(qrcode_data=parcel_data['qrCode'], logger=self._log) \
if 'qrCode' in parcel_data else None
self.stored_date: arrow | None = get(parcel_data['storedDate']) if 'storedDate' in parcel_data else None
self.pickup_date: arrow | None = get(parcel_data['pickUpDate']) if 'pickUpDate' in parcel_data else None
self.expiry_date: arrow | None = get(parcel_data['expiryDate']) if 'expiryDate' in parcel_data else None
# self.expiry_date: arrow | None = get(parcel_data['expiryDate']) if 'expiryDate' in parcel_data else None
self.parcel_size: ParcelLockerSize | ParcelCarrierSize = ParcelLockerSize[parcel_data['parcelSize']] \
if self.shipment_type == ParcelShipmentType.parcel else ParcelCarrierSize[parcel_data['parcelSize']]
self.receiver: Receiver = Receiver(receiver_data=parcel_data['receiver'], logger=self._log)
Expand All @@ -37,10 +51,10 @@ def __init__(self, parcel_data: dict, logger: logging.Logger):
self.multi_compartment: MultiCompartment | None = MultiCompartment(
parcel_data['multiCompartment'], logger=self._log) if 'multiCompartment' in parcel_data else None
self.is_end_off_week_collection: bool = parcel_data['endOfWeekCollection']
self.operations: Operations = Operations(operations_data=parcel_data['operations'], logger=self._log)
# self.operations: Operations = Operations(operations_data=parcel_data['operations'], logger=self._log)
self.status: ParcelStatus = ParcelStatus[parcel_data['status']]
self.event_log: List[EventLog] = [EventLog(eventlog_data=event, logger=self._log)
for event in parcel_data['eventLog']]
# self.event_log: List[EventLog] = [EventLog(eventlog_data=event, logger=self._log)
# for event in parcel_data['eventLog']]
self.avizo_transaction_status: str = parcel_data['avizoTransactionStatus']
self.shared_to: List[SharedTo] = [SharedTo(sharedto_data=person, logger=self._log)
for person in parcel_data['sharedTo']]
Expand All @@ -63,7 +77,7 @@ def __init__(self, parcel_data: dict, logger: logging.Logger):
self._log.debug(f'unexpected ownership status: {parcel_data["ownershipStatus"]}')

def __repr__(self):
fields = tuple(f"{k}={v}" for k, v in self.__dict__.items())
fields = tuple(f"{k}={v}" for k, v in self.__dict__.items() if k != '_log')
return self.__class__.__name__ + str(tuple(sorted(fields))).replace("\'", "")

def __str__(self):
Expand All @@ -83,7 +97,7 @@ def open_code(self) -> str | None:
self._log.debug('got open code')
return self._open_code

self._log.debug('wrong ParcelShipmentType')
self._log.debug(f'wrong ParcelShipmentType: {repr(self.shipment_type)}')
return None

@property
Expand All @@ -97,7 +111,7 @@ def generate_qr_image(self) -> BytesIO | None:
self._log.debug('got qr image')
return self._qr_code.qr_image

self._log.debug('wrong ParcelShipmentType')
self._log.debug(f'wrong ParcelShipmentType: {repr(self.shipment_type)}')
return None

@property
Expand All @@ -111,7 +125,7 @@ def compartment_properties(self):
self._log.debug('got compartment properties')
return self._compartment_properties

self._log.debug('wrong ParcelShipmentType')
self._log.debug(f'wrong ParcelShipmentType: {repr(self.shipment_type)}')
return None

@compartment_properties.setter
Expand All @@ -126,7 +140,7 @@ def compartment_properties(self, compartmentproperties_data: dict):
self._compartment_properties = CompartmentProperties(compartmentproperties_data=compartmentproperties_data,
logger=self._log)

self._log.debug('wrong ParcelShipmentType')
self._log.debug(f'wrong ParcelShipmentType: {repr(self.shipment_type)}')

@property
def compartment_location(self):
Expand All @@ -139,20 +153,20 @@ def compartment_location(self):
self._log.debug('got compartment location')
return self._compartment_properties.location if self._compartment_properties else None

self._log.debug('wrong ParcelShipmentType')
self._log.debug(f'wrong ParcelShipmentType: {repr(self.shipment_type)}')
return None

@compartment_location.setter
def compartment_location(self, location_data):
def compartment_location(self, location_data: dict):
"""Set compartment location for :class:`Parcel`
:param location_data: :class:`dict` containing `compartment properties` data for :class:`Parcel`
:type location_data: CompartmentProperties"""
self._log.debug('setting compartment location')
self._log.debug(f'setting compartment location with {location_data}')
if self.shipment_type == ParcelShipmentType.parcel:
self._log.debug('compartment location set')
self._compartment_properties.location = location_data

self._log.debug('wrong ParcelShipmentType')
self._log.debug(f'wrong ParcelShipmentType: {repr(self.shipment_type)}')

@property
def compartment_status(self) -> CompartmentActualStatus | None:
Expand All @@ -166,17 +180,17 @@ def compartment_status(self) -> CompartmentActualStatus | None:
self._log.debug('got compartment status')
return self._compartment_properties.status if self._compartment_properties else None

self._log.debug('wrong ParcelShipmentType')
self._log.debug(f'wrong ParcelShipmentType: {repr(self.shipment_type)}')
return None

@compartment_status.setter
def compartment_status(self, status):
self._log.debug('setting compartment status')
self._log.debug(f'setting compartment status with {status}')
if self.shipment_type == ParcelShipmentType.parcel:
self._log.debug('compartment status set')
self._compartment_properties.status = status

self._log.debug('wrong ParcelShipmentType')
self._log.debug(f'wrong ParcelShipmentType: {repr(self.shipment_type)}')

@property
def compartment_open_data(self):
Expand All @@ -193,7 +207,7 @@ def compartment_open_data(self):
'receiverPhoneNumber': self.receiver.phone_number
}

self._log.debug('wrong ParcelShipmentType')
self._log.debug(f'wrong ParcelShipmentType: {repr(self.shipment_type)}')
return None

@property
Expand All @@ -211,9 +225,45 @@ def mocked_location(self):
'accuracy': round(random.uniform(1, 4), 1)
}

self._log.debug('wrong ParcelShipmentType')
self._log.debug(f'wrong ParcelShipmentType: {repr(self.shipment_type)}')
return None

@property
def is_multicompartment(self):
"""Specifies if parcel is in multi compartment
:return: True if parcel is in multicompartment
:rtype: bool"""
return self.multi_compartment is not None

@property
def is_main_multicompartment(self):
"""Specifies if parcel is main parcel in multi compartment
:return: True if parcel is in multicompartment
:rtype: bool"""
if self.is_multicompartment:
return self.multi_compartment.shipment_numbers is not None

return None

# @property
# def get_from_multicompartment(self):
# return


class ReturnParcel(BaseParcel):
def __init__(self, parcel_data: dict, logger: logging.Logger):
super().__init__(parcel_data, logger)
self.uuid: str = parcel_data['uuid']
self.rma: str = parcel_data['rma']
self.organization_name: str = parcel_data['organizationName']
self.created_date: arrow = parcel_data['createdDate']
self.accepted_date: arrow = parcel_data['acceptedDate']
self.expiry_date: arrow = parcel_data['expiryDate']
self.sent_date: arrow = parcel_data['sentDate']
self.delivered_date: arrow = parcel_data['deliveredDate']
self.order_number: str = parcel_data['orderNumber']
self.form_type: str = parcel_data['formType']


class Receiver:
"""Object representation of :class:`Parcel` receiver
Expand All @@ -233,7 +283,7 @@ def __init__(self, receiver_data: dict, logger: logging.Logger):
self._log.debug('created')

def __repr__(self):
fields = tuple(f"{k}={v}" for k, v in self.__dict__.items())
fields = tuple(f"{k}={v}" for k, v in self.__dict__.items() if k != '_log')
return self.__class__.__name__ + str(tuple(sorted(fields))).replace("\'", "")


Expand All @@ -253,7 +303,7 @@ def __init__(self, sender_data: dict, logger: logging.Logger):
self._log.debug('created')

def __repr__(self):
fields = tuple(f"{k}={v}" for k, v in self.__dict__.items())
fields = tuple(f"{k}={v}" for k, v in self.__dict__.items() if k != '_log')
return self.__class__.__name__ + str(tuple(sorted(fields))).replace("\'", "")

def __str__(self) -> str:
Expand Down Expand Up @@ -296,7 +346,7 @@ def __init__(self, pickuppoint_data: dict, logger: logging.Logger):
self._log.debug(f'unknown delivery type: {pickuppoint_data["type"]}')

def __repr__(self):
fields = tuple(f"{k}={v}" for k, v in self.__dict__.items())
fields = tuple(f"{k}={v}" for k, v in self.__dict__.items() if k != '_log')
return self.__class__.__name__ + str(tuple(sorted(fields))).replace("\'", "")

def __str__(self) -> str:
Expand Down Expand Up @@ -332,7 +382,7 @@ def __init__(self, multicompartment_data: dict, logger: logging.Logger):
self._log.debug('created')

def __repr__(self):
fields = tuple(f"{k}={v}" for k, v in self.__dict__.items())
fields = tuple(f"{k}={v}" for k, v in self.__dict__.items() if k != '_log')
return self.__class__.__name__ + str(tuple(sorted(fields))).replace("\'", "")


Expand All @@ -359,12 +409,13 @@ def __init__(self, operations_data: dict, logger: logging.Logger):
self.can_share_to_observe: bool = operations_data['canShareToObserve']
self.can_share_open_code: bool = operations_data['canShareOpenCode']
self.can_share_parcel: bool = operations_data['canShareParcel']
self.send: bool | None = operations_data['send'] if 'send' in operations_data else None

self._log: logging.Logger = logger.getChild(__class__.__name__)
self._log.debug('created')

def __repr__(self):
fields = tuple(f"{k}={v}" for k, v in self.__dict__.items())
fields = tuple(f"{k}={v}" for k, v in self.__dict__.items() if k != '_log')
return self.__class__.__name__ + str(tuple(sorted(fields))).replace("\'", "")


Expand All @@ -379,17 +430,18 @@ class EventLog:
def __init__(self, eventlog_data: dict, logger: logging.Logger):
"""Constructor method"""
self.type: str = eventlog_data['type']
self.name: ParcelStatus = ParcelStatus[eventlog_data['name']]
self.name: ParcelStatus | ReturnsStatus = ParcelStatus[
eventlog_data['name']] if self.type == 'PARCEL_STATUS' else ReturnsStatus[eventlog_data['name']]
self.date: arrow = get(eventlog_data['date'])

self._log: logging.Logger = logger.getChild(__class__.__name__)
self._log.debug('created')

if self.name == ParcelStatus.UNKNOWN:
self._log.debug(f'unknown parcel status: {eventlog_data["name"]}')
if self.name == ParcelStatus.UNKNOWN or self.name == ReturnsStatus.UNKNOWN:
self._log.debug(f'unknown {self.type}: {eventlog_data["name"]}')

def __repr__(self):
fields = tuple(f"{k}={v}" for k, v in self.__dict__.items())
fields = tuple(f"{k}={v}" for k, v in self.__dict__.items() if k != '_log')
return self.__class__.__name__ + str(tuple(sorted(fields))).replace("\'", "")


Expand All @@ -411,7 +463,7 @@ def __init__(self, sharedto_data: dict, logger: logging.Logger):
self._log.debug('created')

def __repr__(self):
fields = tuple(f"{k}={v}" for k, v in self.__dict__.items())
fields = tuple(f"{k}={v}" for k, v in self.__dict__.items() if k != '_log')
return self.__class__.__name__ + str(tuple(sorted(fields))).replace("\'", "")


Expand All @@ -431,7 +483,7 @@ def __init__(self, qrcode_data: str, logger: logging.Logger):
self._log.debug('created')

def __repr__(self):
fields = tuple(f"{k}={v}" for k, v in self.__dict__.items())
fields = tuple(f"{k}={v}" for k, v in self.__dict__.items() if k != '_log')
return self.__class__.__name__ + str(tuple(sorted(fields))).replace("\'", "")

@property
Expand Down Expand Up @@ -482,7 +534,7 @@ def __init__(self, compartmentlocation_data: dict, logger: logging.Logger):
self._log.debug('created')

def __repr__(self):
fields = tuple(f"{k}={v}" for k, v in self.__dict__.items())
fields = tuple(f"{k}={v}" for k, v in self.__dict__.items() if k != '_log')
return self.__class__.__name__ + str(tuple(sorted(fields))).replace("\'", "")


Expand All @@ -505,7 +557,7 @@ def __init__(self, compartmentproperties_data: dict, logger: logging.Logger):
self._log.debug('created')

def __repr__(self):
fields = tuple(f"{k}={v}" for k, v in self.__dict__.items())
fields = tuple(f"{k}={v}" for k, v in self.__dict__.items() if k != '_log')
return self.__class__.__name__ + str(tuple(sorted(fields))).replace("\'", "")

@property
Expand Down
Loading