Skip to content

Commit

Permalink
Merge pull request #8 from thorgate/fix/current-issues
Browse files Browse the repository at this point in the history
Fix current issues
  • Loading branch information
iharthi authored Jul 9, 2021
2 parents 3f191d4 + 599e232 commit c8c8da4
Show file tree
Hide file tree
Showing 10 changed files with 224 additions and 67 deletions.
40 changes: 40 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
# Changelog

All notable changes to this project will be documented in this file.

The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [1.1.0] - 2021-07-07

### Added

- Requests timing out in 2 minutes (can be overridden by subclassing the `ElvisClient`),
previously, there was no timeout and requests could hang for long time causing resource
leaks
- Add helper to provide choices tuples from Enums (can be used with Django)
- This changelog

### Changed

- Date fields are now properly parsed to datetime
- Elvis proxy exception formatting is improved so that exceptions can actually
be read

### Fixed

- ElvisModel was mutating its constructor argument, sometimes causing undesired data changes
- Getting transport orders now works in python 3

## [1.0.1] - 2017-01-05

### Added

- Released to PyPi

### Fixed

- Misc fixes related to automated deploys and quality checks

[1.1.0]: https://github.com/thorgate/python-lvis/compare/v1.0.1...v1.1.0
[1.0.1]: https://github.com/thorgate/python-lvis/compare/1.0.0-rc3...v1.0.1
1 change: 1 addition & 0 deletions MANIFEST.in
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
include AUTHORS.rst
include LICENSE
include README.md
include CHANGELOG.md
recursive-include elvis *py
7 changes: 4 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
python-lvis
===========
# python-lvis

[![pypi Status](https://badge.fury.io/py/python-lvis.png)](https://badge.fury.io/py/python-lvis)
[![Build Status](https://travis-ci.org/thorgate/python-lvis.svg?branch=master)](https://travis-ci.org/thorgate/python-lvis)

https://www.veoseleht.ee
Python bindings for https://www.veoseleht.ee API (aka ELVIS), requires ELVIS proxy that translates requests from
rest to SOAP (you can use https://gitlab.com/thorgate-public/lvisproxy/ proxy for this)

2 changes: 1 addition & 1 deletion elvis/__init__.py
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
__version__ = '1.0.1'
__version__ = '1.1.0'
__all__ = ['api', 'enums', 'models']
88 changes: 67 additions & 21 deletions elvis/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,15 +14,49 @@
FilterItem, SortItem, Address, ElvisModel, TransportOrderListPage, TransportOrder, TransportOrderStatusInfo,
TimberWarehouse, Waybill, WaybillStatusInfo, WaybillListPage, TimberAssortment, FineMeasurementFile,
)
from .utils import DATE_PREFIX, DATE_SUFFIX, ELVIS_TIMEZONE


class ElvisException(Exception):
def __init__(self, message, raw):
self.message = message
self.raw = raw

@classmethod
def reformat_elvis_exception_text(cls, value, indent=1):
new_value = value

if isinstance(value, dict):
new_value = "\n"
for key in value:
new_value = "%s%s`%s`:%s\n" % (
new_value,
" " * indent,
key,
cls.reformat_elvis_exception_text(
value[key], indent=indent+1
),
)

elif isinstance(value, list):
new_value = "\n"
for index, item in enumerate(value):
new_value = "%s%s[%s]:%s\n" % (
new_value,
" " * indent,
index,
cls.reformat_elvis_exception_text(
item, indent=indent+1
),
)

elif hasattr(value, "replace"):
new_value = value.replace(u"\\u000d\\u000a", "\n")

return new_value

def __str__(self):
return "ElvisException %s: %s" % (self.message, self.raw)
return "ElvisException %s: %s" % (self.message, self.reformat_elvis_exception_text(self.raw))


class ElvisEncoder(json.JSONEncoder):
Expand All @@ -39,7 +73,18 @@ def default(self, obj):
return float(obj)

if isinstance(obj, datetime):
return '/Date(%d)/' % ((obj - datetime(1970, 1, 1)).total_seconds() * 1000)
# Elvis timestamp offsets are from Tallinn time
timezone_offset = obj.utcoffset() - obj.astimezone(ELVIS_TIMEZONE).utcoffset()

timezone_offset_hours = timezone_offset.total_seconds() // 3600
timezone_offset_minutes = (timezone_offset.total_seconds() - timezone_offset_hours * 3600) // 60
return '%s%d+%02d%02d%s' % (
DATE_PREFIX,
(obj - datetime(1970, 1, 1)).total_seconds() * 1000,
timezone_offset_hours,
timezone_offset_minutes,
DATE_SUFFIX,
)

if not isinstance(obj, ElvisEncoder.ELVIS_OBJECTS):
return super(ElvisEncoder, self).default(obj)
Expand Down Expand Up @@ -119,27 +164,28 @@ def __request(self, endpoint, method, attrs=None):
else:
result = func(self.api_url % endpoint, params=attrs, headers=headers, timeout=self.request_timeout)

if result.status_code == 200:
text = result.text

try:
return {
"Success": True,
"raw": json.loads(text),
}
except ValueError:
return {
"Success": False,
"message": "not json",
"raw": text,
}
error_message = ""
try:
json_data = json.loads(result.text)
except ValueError:
error_message = "not json"
json_data = result.text

if result.status_code == 200:
success = True
else:
return {
"Success": False,
"message": "Bad status code: %d" % result.status_code,
"raw": result.text,
}
error_message = "Bad status code: %d" % result.status_code,
success = False

result = {
"Success": success,
"raw": json_data,
}

if error_message:
result["message"] = error_message

return result

# ENDPOINTS

Expand Down
46 changes: 31 additions & 15 deletions elvis/enums.py
Original file line number Diff line number Diff line change
@@ -1,17 +1,33 @@
class ElvisEnum(object):
@classmethod
def display_name(cls, status):
for item in cls.__dict__:
if not item.startswith("_") and isinstance(cls.__dict__[item], int) and cls.__dict__[item] == int(status):
return item
return "Unknown"

@classmethod
def get_status_choices(cls):
choices = []
for item in cls.__dict__:
if not item.startswith("_") and isinstance(cls.__dict__[item], int):
choices.append((cls.__dict__[item], item))

class AssortmentType(object):
return choices


class AssortmentType(ElvisEnum):
ELVIS = 0
COMPANY = 1


class Priority(object):
class Priority(ElvisEnum):
Low = 11001
Normal = 11002
High = 11003


class WarehouseListItemSearchField(object):
class WarehouseListItemSearchField(ElvisEnum):
CompanyRegistrationNumber = 0
UserId = 1
Code = 2
Expand All @@ -37,7 +53,7 @@ class WarehouseListItemSearchField(object):
AdditionalProperty = 22


class TransportOrderListItemSearchField(object):
class TransportOrderListItemSearchField(ElvisEnum):
GroupId = 0
OwnerCode = 1
RecieverCode = 2
Expand Down Expand Up @@ -72,7 +88,7 @@ class TransportOrderListItemSearchField(object):
ContactName = 31


class TransportOrderListItemSortField(object):
class TransportOrderListItemSortField(ElvisEnum):
Deadline = 0
CreatedOn = 1
OwnerName = 2
Expand All @@ -81,7 +97,7 @@ class TransportOrderListItemSortField(object):
Number = 5


class WarehouseListItemSortField(object):
class WarehouseListItemSortField(ElvisEnum):
Code = 0
Name = 1
TypeId = 2
Expand All @@ -90,36 +106,36 @@ class WarehouseListItemSortField(object):
CreatedOn = 5


class SortDirection(object):
class SortDirection(ElvisEnum):
Asc = 0
Desc = 1


class TransportOrderRoleContext(object):
class TransportOrderRoleContext(ElvisEnum):
Owner = 0
Reciever = 1
Transporter = 2


class WaybillRoleContext(object):
class WaybillRoleContext(ElvisEnum):
Owner = 1
Reciever = 2
Transporter = 4
All = 8


class WarehouseType(object):
class WarehouseType(ElvisEnum):
ForestEdge = 5001
Middle = 5002
End = 5003


class VehicleType(object):
class VehicleType(ElvisEnum):
Van = 9001
Trailer = 9002


class WaybillStatus(object):
class WaybillStatus(ElvisEnum):
Composing = 7001
Confirmed = 7002
Unloaded = 7003
Expand All @@ -130,7 +146,7 @@ class WaybillStatus(object):
Cancelled = 7008


class WaybillListItemSearchField(object):
class WaybillListItemSearchField(ElvisEnum):
OwnerCode = 0
RecieverCode = 1
RecieverName = 2
Expand Down Expand Up @@ -171,15 +187,15 @@ class WaybillListItemSearchField(object):
Inspected = 37


class WaybillListItemSortField(object):
class WaybillListItemSortField(ElvisEnum):
CreatedOn = 0
OwnerName = 1
DestinationWarehouseName = 2
TransporterName = 3
Number = 4


class TransportOrderStatus(object):
class TransportOrderStatus(ElvisEnum):
Composing = 4001
Submitted = 4002
Accepted = 4003
Expand Down
26 changes: 23 additions & 3 deletions elvis/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
from django.utils.encoding import force_bytes

from .enums import Priority, TransportOrderStatus
from .utils import decode_elvis_timestamp


class FilterItem(object):
Expand All @@ -19,14 +20,25 @@ def __init__(self, column, direction):


class ElvisModel(object):
_DATETIME_ATTRIBUTES = []

def __init__(self, **kwargs):
dict_data = kwargs.get('dict_data', None)
if dict_data:
self.__dict__ = dict_data
self.__dict__ = dict_data.copy()
self._already_loaded = True
else:
self._already_loaded = False

def __getattribute__(self, name):
if name.startswith("_"):
return super().__getattribute__(name)

if name not in self._DATETIME_ATTRIBUTES:
return super().__getattribute__(name)

return decode_elvis_timestamp(super().__getattribute__(name))


# noinspection PyPep8Naming
class AdditionalProperty(ElvisModel):
Expand Down Expand Up @@ -114,6 +126,8 @@ def __init__(self, **kwargs):

# noinspection PyPep8Naming
class TimberBatch(ElvisModel):
_DATETIME_ATTRIBUTES = ["DocDate"]

def __init__(self, **kwargs):
super(TimberBatch, self).__init__(**kwargs)

Expand Down Expand Up @@ -233,13 +247,15 @@ def __init__(self, **kwargs):

# noinspection PyPep8Naming
class WaybillStatusChangeLog(ElvisModel):
_DATETIME_ATTRIBUTES = ["ChangedOn"]

def __init__(self, **kwargs):

super(WaybillStatusChangeLog, self).__init__(**kwargs)

if not self._already_loaded:
self.ChangedBy = kwargs.get('changed_by')
self.ChangedOn = kwargs.get('changed_on')
self._ChangedOn = kwargs.get('changed_on')
self.Status = kwargs.get('status')

self.ExtensionData = None
Expand Down Expand Up @@ -757,7 +773,9 @@ def __init__(self, **kwargs):
def get_waybill_transporter(self, transport_hash):
transport = None
for t in self.Transports:
if transport_hash == hashlib.md5("%s%s%s" % (t.Driver.PersonCode, t.Trailer.RegistrationNumber, t.Van.RegistrationNumber)).hexdigest():
if transport_hash == hashlib.md5(
("%s%s%s" % (t.Driver.PersonCode, t.Trailer.RegistrationNumber, t.Van.RegistrationNumber)).encode("utf-8")
).hexdigest():
transport = t
break
assert transport
Expand All @@ -773,6 +791,8 @@ def get_waybill_transporter(self, transport_hash):

# noinspection PyPep8Naming
class TransportOrder(ElvisModel):
_DATETIME_ATTRIBUTES = ["Deadline"]

def __init__(self, **kwargs):
super(TransportOrder, self).__init__(**kwargs)

Expand Down
Loading

0 comments on commit c8c8da4

Please sign in to comment.