karrio-server-data 2025.5rc1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- karrio/server/data/__init__.py +0 -0
- karrio/server/data/admin.py +1 -0
- karrio/server/data/apps.py +13 -0
- karrio/server/data/filters.py +43 -0
- karrio/server/data/migrations/0001_initial.py +62 -0
- karrio/server/data/migrations/0002_alter_batchoperation_resource_type_and_more.py +28 -0
- karrio/server/data/migrations/0003_datatemplate_metadata_alter_batchoperation_resources.py +36 -0
- karrio/server/data/migrations/__init__.py +0 -0
- karrio/server/data/models.py +97 -0
- karrio/server/data/resources/__init__.py +53 -0
- karrio/server/data/resources/orders.py +523 -0
- karrio/server/data/resources/shipments.py +473 -0
- karrio/server/data/resources/trackers.py +212 -0
- karrio/server/data/serializers/__init__.py +26 -0
- karrio/server/data/serializers/base.py +107 -0
- karrio/server/data/serializers/batch.py +9 -0
- karrio/server/data/serializers/batch_orders.py +99 -0
- karrio/server/data/serializers/batch_shipments.py +102 -0
- karrio/server/data/serializers/batch_trackers.py +131 -0
- karrio/server/data/serializers/data.py +109 -0
- karrio/server/data/signals.py +52 -0
- karrio/server/data/tests.py +3 -0
- karrio/server/data/urls.py +13 -0
- karrio/server/data/views/__init__.py +0 -0
- karrio/server/data/views/batch.py +72 -0
- karrio/server/data/views/batch_order.py +40 -0
- karrio/server/data/views/batch_shipment.py +40 -0
- karrio/server/data/views/batch_tracking.py +40 -0
- karrio/server/data/views/data.py +171 -0
- karrio/server/events/task_definitions/__init__.py +1 -0
- karrio/server/events/task_definitions/data/__init__.py +136 -0
- karrio/server/events/task_definitions/data/batch.py +130 -0
- karrio/server/events/task_definitions/data/shipments.py +51 -0
- karrio/server/graph/schemas/__init__.py +1 -0
- karrio/server/graph/schemas/data/__init__.py +51 -0
- karrio/server/graph/schemas/data/inputs.py +39 -0
- karrio/server/graph/schemas/data/mutations.py +53 -0
- karrio/server/graph/schemas/data/types.py +78 -0
- karrio/server/settings/data.py +15 -0
- karrio_server_data-2025.5rc1.dist-info/METADATA +18 -0
- karrio_server_data-2025.5rc1.dist-info/RECORD +43 -0
- karrio_server_data-2025.5rc1.dist-info/WHEEL +5 -0
- karrio_server_data-2025.5rc1.dist-info/top_level.txt +2 -0
@@ -0,0 +1,26 @@
|
|
1
|
+
from karrio.server.serializers import *
|
2
|
+
from karrio.server.core.serializers import *
|
3
|
+
from karrio.server.events.serializers.base import *
|
4
|
+
from karrio.server.orders.serializers import OrderData
|
5
|
+
from karrio.server.orders.serializers.order import OrderSerializer
|
6
|
+
from karrio.server.events.serializers.event import EventSerializer
|
7
|
+
from karrio.server.manager.serializers import (
|
8
|
+
ShipmentSerializer,
|
9
|
+
ShipmentData,
|
10
|
+
TrackingSerializer,
|
11
|
+
TrackingData,
|
12
|
+
)
|
13
|
+
from karrio.server.data.serializers.base import (
|
14
|
+
ImportData,
|
15
|
+
BatchObject,
|
16
|
+
ResourceType,
|
17
|
+
ResourceStatus,
|
18
|
+
BatchOperation,
|
19
|
+
BatchOperationData,
|
20
|
+
BatchOperationStatus,
|
21
|
+
OPERATION_STATUS,
|
22
|
+
RESOURCE_TYPE,
|
23
|
+
)
|
24
|
+
from karrio.server.data.serializers.batch_orders import BatchOrderData
|
25
|
+
from karrio.server.data.serializers.batch_trackers import BatchTrackerData
|
26
|
+
from karrio.server.data.serializers.batch_shipments import BatchShipmentData
|
@@ -0,0 +1,107 @@
|
|
1
|
+
import karrio.lib as lib
|
2
|
+
import rest_framework.fields as fields
|
3
|
+
import karrio.server.serializers as serializers
|
4
|
+
|
5
|
+
|
6
|
+
class ResourceType(lib.StrEnum):
|
7
|
+
order = "orders"
|
8
|
+
shipment = "shipments"
|
9
|
+
trackers = "trackers"
|
10
|
+
billing = "billing"
|
11
|
+
|
12
|
+
@classmethod
|
13
|
+
def get_default_mapping(cls, resource_type: str) -> dict:
|
14
|
+
from karrio.server.data import resources
|
15
|
+
|
16
|
+
if resource_type == "orders":
|
17
|
+
return resources.orders.DEFAULT_HEADERS
|
18
|
+
if resource_type == "shipments":
|
19
|
+
return resources.shipments.DEFAULT_HEADERS
|
20
|
+
if resource_type == "trackers":
|
21
|
+
return resources.trackers.DEFAULT_HEADERS
|
22
|
+
|
23
|
+
return {}
|
24
|
+
|
25
|
+
@classmethod
|
26
|
+
def get_model(cls, resource_type: str) -> dict:
|
27
|
+
if resource_type == "orders":
|
28
|
+
from karrio.server.orders.models import Order
|
29
|
+
|
30
|
+
return Order
|
31
|
+
if resource_type == "shipments":
|
32
|
+
from karrio.server.manager.models import Shipment
|
33
|
+
|
34
|
+
return Shipment
|
35
|
+
if resource_type == "trackers":
|
36
|
+
from karrio.server.manager.models import Tracking
|
37
|
+
|
38
|
+
return Tracking
|
39
|
+
|
40
|
+
return None
|
41
|
+
|
42
|
+
@classmethod
|
43
|
+
def get_serialiazer(cls, resource_type: str) -> dict:
|
44
|
+
if resource_type == "orders":
|
45
|
+
from karrio.server.data.serializers import BatchOrderData
|
46
|
+
|
47
|
+
return BatchOrderData
|
48
|
+
if resource_type == "shipments":
|
49
|
+
from karrio.server.data.serializers import BatchShipmentData
|
50
|
+
|
51
|
+
return BatchShipmentData
|
52
|
+
if resource_type == "trackers":
|
53
|
+
from karrio.server.data.serializers import BatchTrackerData
|
54
|
+
|
55
|
+
return BatchTrackerData
|
56
|
+
|
57
|
+
return None
|
58
|
+
|
59
|
+
|
60
|
+
class ResourceStatus(lib.StrEnum):
|
61
|
+
queued = "queued"
|
62
|
+
created = "created"
|
63
|
+
has_errors = "has_errors"
|
64
|
+
incomplete = "incomplete"
|
65
|
+
processed = "processed"
|
66
|
+
|
67
|
+
|
68
|
+
class BatchOperationStatus(lib.StrEnum):
|
69
|
+
queued = "queued"
|
70
|
+
running = "running"
|
71
|
+
failed = "failed"
|
72
|
+
completed = "completed"
|
73
|
+
completed_with_errors = "completed_with_errors"
|
74
|
+
|
75
|
+
|
76
|
+
RESOURCE_TYPE = [(c.value, c.value) for c in list(ResourceType)]
|
77
|
+
OPERATION_STATUS = [(c.value, c.value) for c in list(BatchOperationStatus)]
|
78
|
+
|
79
|
+
|
80
|
+
class ImportData(serializers.Serializer):
|
81
|
+
resource_type = fields.ChoiceField(required=True, choices=RESOURCE_TYPE)
|
82
|
+
data_template = fields.CharField(required=False)
|
83
|
+
data_file = fields.FileField(required=True)
|
84
|
+
|
85
|
+
|
86
|
+
class BatchObject(serializers.EntitySerializer):
|
87
|
+
status = fields.ChoiceField(
|
88
|
+
choices=OPERATION_STATUS,
|
89
|
+
help_text="The batch operation resource status",
|
90
|
+
)
|
91
|
+
errors = serializers.PlainDictField(
|
92
|
+
required=False,
|
93
|
+
allow_null=True,
|
94
|
+
help_text="Resource processing errors",
|
95
|
+
)
|
96
|
+
|
97
|
+
|
98
|
+
class BatchOperationData(serializers.Serializer):
|
99
|
+
status = fields.ChoiceField(choices=OPERATION_STATUS)
|
100
|
+
resource_type = fields.ChoiceField(required=True, choices=RESOURCE_TYPE)
|
101
|
+
resources = BatchObject(many=True)
|
102
|
+
|
103
|
+
|
104
|
+
class BatchOperation(serializers.EntitySerializer, BatchOperationData):
|
105
|
+
created_at = fields.DateTimeField()
|
106
|
+
updated_at = fields.DateTimeField()
|
107
|
+
test_mode = fields.BooleanField(required=True)
|
@@ -0,0 +1,9 @@
|
|
1
|
+
import karrio.server.serializers as serializers
|
2
|
+
import karrio.server.data.models as models
|
3
|
+
|
4
|
+
|
5
|
+
@serializers.owned_model_serializer
|
6
|
+
class BatchOperationModelSerializer(serializers.ModelSerializer):
|
7
|
+
class Meta:
|
8
|
+
model = models.BatchOperation
|
9
|
+
exclude = ["created_at", "updated_at", "created_by"]
|
@@ -0,0 +1,99 @@
|
|
1
|
+
from django.db import transaction
|
2
|
+
|
3
|
+
import karrio.lib as lib
|
4
|
+
import karrio.server.conf as conf
|
5
|
+
import karrio.server.orders.models as orders
|
6
|
+
import karrio.server.serializers as serializers
|
7
|
+
import karrio.server.data.serializers.base as base
|
8
|
+
import karrio.server.core.exceptions as exceptions
|
9
|
+
import karrio.server.orders.serializers as order_serializers
|
10
|
+
|
11
|
+
|
12
|
+
@serializers.owned_model_serializer
|
13
|
+
class BatchOrderData(serializers.Serializer):
|
14
|
+
orders = order_serializers.OrderData(
|
15
|
+
many=True,
|
16
|
+
allow_empty=False,
|
17
|
+
help_text="The list of orders to process.",
|
18
|
+
)
|
19
|
+
|
20
|
+
@transaction.atomic
|
21
|
+
def create(self, validated_data: dict, context: serializers.Context, **kwargs):
|
22
|
+
import karrio.server.events.tasks as tasks
|
23
|
+
import karrio.server.data.serializers.batch as batch
|
24
|
+
|
25
|
+
operation_data = dict(resource_type="orders", test_mode=context.test_mode)
|
26
|
+
operation = (
|
27
|
+
batch.BatchOperationModelSerializer.map(
|
28
|
+
data=operation_data, context=context
|
29
|
+
)
|
30
|
+
.save()
|
31
|
+
.instance
|
32
|
+
)
|
33
|
+
|
34
|
+
sid = transaction.savepoint()
|
35
|
+
resources = BatchOrderData.save_resources(
|
36
|
+
context=context,
|
37
|
+
data=validated_data,
|
38
|
+
batch_id=operation.id,
|
39
|
+
format_errors=False,
|
40
|
+
)
|
41
|
+
errors = [r["errors"] for r in resources if r.get("errors") is not None]
|
42
|
+
transaction.savepoint_rollback(sid)
|
43
|
+
|
44
|
+
if any(errors):
|
45
|
+
raise exceptions.APIExceptions(errors, code="invalid_data")
|
46
|
+
|
47
|
+
tasks.save_batch_resources(
|
48
|
+
operation.id,
|
49
|
+
data=validated_data,
|
50
|
+
schema=conf.settings.schema,
|
51
|
+
ctx=dict(
|
52
|
+
test_mode=context.test_mode,
|
53
|
+
org_id=getattr(context.org, "id", None),
|
54
|
+
user_id=getattr(context.user, "id", None),
|
55
|
+
),
|
56
|
+
)
|
57
|
+
|
58
|
+
return operation
|
59
|
+
|
60
|
+
@staticmethod
|
61
|
+
def save_resources(
|
62
|
+
data: dict,
|
63
|
+
batch_id: str,
|
64
|
+
context: serializers.Context,
|
65
|
+
format_errors: bool = True,
|
66
|
+
):
|
67
|
+
orders_data = data["orders"]
|
68
|
+
resources = []
|
69
|
+
|
70
|
+
for index, order_data in enumerate(orders_data):
|
71
|
+
try:
|
72
|
+
queryset = orders.Order.access_by(context).filter(
|
73
|
+
order_id=order_data["order_id"], source=order_data["source"]
|
74
|
+
)
|
75
|
+
order = (
|
76
|
+
queryset.first()
|
77
|
+
if queryset.exists()
|
78
|
+
else (
|
79
|
+
order_serializers.OrderSerializer.map(
|
80
|
+
data=order_data, context=context
|
81
|
+
)
|
82
|
+
.save()
|
83
|
+
.instance
|
84
|
+
)
|
85
|
+
)
|
86
|
+
resources.append(
|
87
|
+
dict(id=order.id, status=base.ResourceStatus.queued.value)
|
88
|
+
)
|
89
|
+
except Exception as e:
|
90
|
+
setattr(e, "index", index)
|
91
|
+
resources.append(
|
92
|
+
dict(
|
93
|
+
id=index,
|
94
|
+
status=base.ResourceStatus.has_errors.value,
|
95
|
+
errors=(lib.to_dict(e) if format_errors else e),
|
96
|
+
)
|
97
|
+
)
|
98
|
+
|
99
|
+
return resources
|
@@ -0,0 +1,102 @@
|
|
1
|
+
from django.db import transaction
|
2
|
+
|
3
|
+
import karrio.lib as lib
|
4
|
+
import karrio.server.conf as conf
|
5
|
+
import karrio.server.manager.models as manager
|
6
|
+
import karrio.server.serializers as serializers
|
7
|
+
import karrio.server.core.exceptions as exceptions
|
8
|
+
import karrio.server.data.serializers.base as base
|
9
|
+
import karrio.server.manager.serializers as manager_serializers
|
10
|
+
|
11
|
+
|
12
|
+
class ShipmentDataReference(manager_serializers.ShipmentData):
|
13
|
+
id = serializers.CharField(
|
14
|
+
help_text="The shipment id.",
|
15
|
+
required=False,
|
16
|
+
)
|
17
|
+
|
18
|
+
|
19
|
+
@serializers.owned_model_serializer
|
20
|
+
class BatchShipmentData(serializers.Serializer):
|
21
|
+
shipments = ShipmentDataReference(
|
22
|
+
many=True,
|
23
|
+
allow_empty=False,
|
24
|
+
help_text="The list of shipments to process.",
|
25
|
+
)
|
26
|
+
|
27
|
+
@transaction.atomic
|
28
|
+
def create(self, validated_data: dict, context: serializers.Context, **kwargs):
|
29
|
+
import karrio.server.events.tasks as tasks
|
30
|
+
import karrio.server.data.serializers.batch as batch
|
31
|
+
|
32
|
+
operation_data = dict(resource_type="shipments", test_mode=context.test_mode)
|
33
|
+
operation = (
|
34
|
+
batch.BatchOperationModelSerializer.map(
|
35
|
+
data=operation_data, context=context
|
36
|
+
)
|
37
|
+
.save()
|
38
|
+
.instance
|
39
|
+
)
|
40
|
+
|
41
|
+
sid = transaction.savepoint()
|
42
|
+
resources = BatchShipmentData.save_resources(
|
43
|
+
context=context,
|
44
|
+
data=validated_data,
|
45
|
+
batch_id=operation.id,
|
46
|
+
format_errors=False,
|
47
|
+
)
|
48
|
+
errors = [r["errors"] for r in resources if r.get("errors") is not None]
|
49
|
+
transaction.savepoint_rollback(sid)
|
50
|
+
|
51
|
+
if any(errors):
|
52
|
+
raise exceptions.APIExceptions(errors, code="invalid_data")
|
53
|
+
|
54
|
+
tasks.save_batch_resources(
|
55
|
+
operation.id,
|
56
|
+
data=validated_data,
|
57
|
+
schema=conf.settings.schema,
|
58
|
+
ctx=dict(
|
59
|
+
test_mode=context.test_mode,
|
60
|
+
org_id=getattr(context.org, "id", None),
|
61
|
+
user_id=getattr(context.user, "id", None),
|
62
|
+
),
|
63
|
+
)
|
64
|
+
|
65
|
+
return operation
|
66
|
+
|
67
|
+
@staticmethod
|
68
|
+
def save_resources(
|
69
|
+
data: dict,
|
70
|
+
batch_id: str,
|
71
|
+
context: serializers.Context,
|
72
|
+
format_errors: bool = True,
|
73
|
+
):
|
74
|
+
shipments_data = data["shipments"]
|
75
|
+
resources = []
|
76
|
+
|
77
|
+
for index, shipment_data in enumerate(shipments_data):
|
78
|
+
try:
|
79
|
+
shipment = (
|
80
|
+
manager.Shipment.access_by(context).get(id=shipment_data["id"])
|
81
|
+
if shipment_data.get("id") is not None
|
82
|
+
else (
|
83
|
+
manager_serializers.ShipmentSerializer.map(
|
84
|
+
data=shipment_data, context=context
|
85
|
+
)
|
86
|
+
.save(fetch_rates=False)
|
87
|
+
.instance
|
88
|
+
)
|
89
|
+
)
|
90
|
+
resources.append(
|
91
|
+
dict(id=shipment.id, status=base.ResourceStatus.queued.value)
|
92
|
+
)
|
93
|
+
except Exception as e:
|
94
|
+
resources.append(
|
95
|
+
dict(
|
96
|
+
id=index,
|
97
|
+
status=base.ResourceStatus.has_errors.value,
|
98
|
+
errors=(lib.to_dict(e) if format_errors else e),
|
99
|
+
)
|
100
|
+
)
|
101
|
+
|
102
|
+
return resources
|
@@ -0,0 +1,131 @@
|
|
1
|
+
from django.db import transaction
|
2
|
+
|
3
|
+
import karrio.lib as lib
|
4
|
+
import karrio.server.conf as conf
|
5
|
+
import karrio.server.core.utils as utils
|
6
|
+
import karrio.server.core.gateway as gateway
|
7
|
+
import karrio.server.core.exceptions as exceptions
|
8
|
+
import karrio.server.serializers as serializers
|
9
|
+
import karrio.server.manager.models as models
|
10
|
+
import karrio.server.manager.serializers as manager
|
11
|
+
import karrio.server.data.serializers.base as base
|
12
|
+
|
13
|
+
|
14
|
+
@serializers.owned_model_serializer
|
15
|
+
class BatchTrackerData(serializers.Serializer):
|
16
|
+
trackers = manager.TrackingData(
|
17
|
+
many=True,
|
18
|
+
allow_empty=False,
|
19
|
+
help_text="The list of tracking info to process.",
|
20
|
+
)
|
21
|
+
|
22
|
+
@transaction.atomic
|
23
|
+
def create(self, validated_data: dict, context: serializers.Context, **kwargs):
|
24
|
+
import karrio.server.events.tasks as tasks
|
25
|
+
import karrio.server.data.serializers.batch as batch
|
26
|
+
|
27
|
+
operation_data = dict(resource_type="trackers", test_mode=context.test_mode)
|
28
|
+
operation = (
|
29
|
+
batch.BatchOperationModelSerializer
|
30
|
+
.map(data=operation_data, context=context)
|
31
|
+
.save()
|
32
|
+
.instance
|
33
|
+
)
|
34
|
+
|
35
|
+
sid = transaction.savepoint()
|
36
|
+
resources = BatchTrackerData.save_resources(
|
37
|
+
context=context,
|
38
|
+
data=validated_data,
|
39
|
+
batch_id=operation.id,
|
40
|
+
format_errors=False,
|
41
|
+
)
|
42
|
+
errors = [r['errors'] for r in resources if r.get('errors') is not None]
|
43
|
+
transaction.savepoint_rollback(sid)
|
44
|
+
|
45
|
+
if any(errors):
|
46
|
+
raise exceptions.APIExceptions(errors, code="invalid_data")
|
47
|
+
|
48
|
+
tasks.save_batch_resources(
|
49
|
+
operation.id,
|
50
|
+
data=validated_data,
|
51
|
+
schema=conf.settings.schema,
|
52
|
+
ctx=dict(
|
53
|
+
test_mode=context.test_mode,
|
54
|
+
org_id=getattr(context.org, "id", None),
|
55
|
+
user_id=getattr(context.user, "id", None),
|
56
|
+
),
|
57
|
+
)
|
58
|
+
|
59
|
+
return operation
|
60
|
+
|
61
|
+
@staticmethod
|
62
|
+
def save_resources(data: dict, batch_id: str, context: serializers.Context, format_errors: bool = True):
|
63
|
+
meta = dict(batch_id=batch_id)
|
64
|
+
trackers_data = data['trackers']
|
65
|
+
carrier_names = set([t['carrier_name'] for t in trackers_data])
|
66
|
+
carriers = {
|
67
|
+
carrier_name: utils.failsafe(lambda: gateway.Carriers.first(
|
68
|
+
context=context,
|
69
|
+
capability='tracking',
|
70
|
+
carrier_name=carrier_name,
|
71
|
+
raise_not_found=False,
|
72
|
+
))
|
73
|
+
for carrier_name in carrier_names
|
74
|
+
}
|
75
|
+
resources = []
|
76
|
+
trackers = []
|
77
|
+
|
78
|
+
for index, tracker_data in enumerate(trackers_data):
|
79
|
+
try:
|
80
|
+
carrier_name = tracker_data['carrier_name']
|
81
|
+
carrier = carriers[carrier_name]
|
82
|
+
|
83
|
+
if carrier is None:
|
84
|
+
raise exceptions.APIException(
|
85
|
+
f"No carrier connection found for '{tracker_data['carrier_name']}'",
|
86
|
+
code="invalid_carrier",
|
87
|
+
)
|
88
|
+
|
89
|
+
_tracker = (
|
90
|
+
models.Tracking.access_by(context)
|
91
|
+
.filter(tracking_number=tracker_data["tracking_number"])
|
92
|
+
.first()
|
93
|
+
)
|
94
|
+
_exists = getattr(_tracker, "carrier_name", None) == carrier_name
|
95
|
+
tracker = (
|
96
|
+
_tracker if _exists
|
97
|
+
else models.Tracking(
|
98
|
+
meta=meta,
|
99
|
+
status="unknown",
|
100
|
+
test_mode=context.test_mode,
|
101
|
+
created_by_id=context.user.id,
|
102
|
+
tracking_carrier_id=carrier.id,
|
103
|
+
tracking_number=tracker_data["tracking_number"],
|
104
|
+
events = utils.default_tracking_event(
|
105
|
+
description="Awaiting update from carrier...",
|
106
|
+
code="UNKNOWN",
|
107
|
+
),
|
108
|
+
)
|
109
|
+
)
|
110
|
+
|
111
|
+
if _exists is False:
|
112
|
+
trackers.append(tracker)
|
113
|
+
|
114
|
+
resources.append(dict(
|
115
|
+
id=tracker.id,
|
116
|
+
status=(base.ResourceStatus.processed.value
|
117
|
+
if _exists else base.ResourceStatus.queued.value)
|
118
|
+
))
|
119
|
+
except Exception as e:
|
120
|
+
setattr(e, "index", index)
|
121
|
+
resources.append(dict(
|
122
|
+
id=index,
|
123
|
+
status=base.ResourceStatus.has_errors.value,
|
124
|
+
errors=(lib.to_dict(e) if format_errors else e),
|
125
|
+
))
|
126
|
+
|
127
|
+
if any(trackers):
|
128
|
+
models.Tracking.objects.bulk_create(trackers)
|
129
|
+
serializers.bulk_link_org(trackers, context)
|
130
|
+
|
131
|
+
return resources
|
@@ -0,0 +1,109 @@
|
|
1
|
+
import tablib
|
2
|
+
import logging
|
3
|
+
from django.db import transaction
|
4
|
+
|
5
|
+
from karrio.server.conf import settings
|
6
|
+
import karrio.server.core.exceptions as exceptions
|
7
|
+
import karrio.server.data.models as models
|
8
|
+
import karrio.server.data.resources as resources
|
9
|
+
import karrio.server.data.serializers as serializers
|
10
|
+
import karrio.server.data.serializers.batch as batch
|
11
|
+
|
12
|
+
logger = logging.getLogger(__name__)
|
13
|
+
|
14
|
+
|
15
|
+
@serializers.owned_model_serializer
|
16
|
+
class DataTemplateModelSerializer(serializers.ModelSerializer):
|
17
|
+
class Meta:
|
18
|
+
model = models.DataTemplate
|
19
|
+
exclude = ["created_at", "updated_at", "created_by", "org"]
|
20
|
+
|
21
|
+
|
22
|
+
@serializers.owned_model_serializer
|
23
|
+
class ImportDataSerializer(serializers.ImportData):
|
24
|
+
@transaction.atomic
|
25
|
+
def create(
|
26
|
+
self, validated_data: dict, context: serializers.Context, **kwargs
|
27
|
+
) -> models.BatchOperation:
|
28
|
+
import karrio.server.events.tasks as tasks
|
29
|
+
|
30
|
+
resource_type = validated_data["resource_type"]
|
31
|
+
data_field = validated_data["data_file"]
|
32
|
+
template = (
|
33
|
+
models.DataTemplate.access_by(context).first(
|
34
|
+
slug=validated_data.get("data_template")
|
35
|
+
)
|
36
|
+
if "data_template" in validated_data
|
37
|
+
else None
|
38
|
+
)
|
39
|
+
data_fields: dict = getattr(
|
40
|
+
template,
|
41
|
+
"data_fields",
|
42
|
+
serializers.ResourceType.get_default_mapping(resource_type),
|
43
|
+
)
|
44
|
+
resource = resources.get_import_resource(
|
45
|
+
resource_type=resource_type,
|
46
|
+
data_fields=data_fields,
|
47
|
+
params=validated_data,
|
48
|
+
context=context,
|
49
|
+
)
|
50
|
+
dataset = tablib.Dataset().load(
|
51
|
+
data_field.read().decode(),
|
52
|
+
headers=data_fields.values(),
|
53
|
+
)
|
54
|
+
|
55
|
+
# dry run data import to validate file content.
|
56
|
+
validation = resource.import_data(dataset, dry_run=True)
|
57
|
+
check_dataset_validation_errors(validation)
|
58
|
+
|
59
|
+
operation = (
|
60
|
+
batch.BatchOperationModelSerializer.map(
|
61
|
+
data=dict(resource_type=resource_type, test_mode=context.test_mode),
|
62
|
+
context=context,
|
63
|
+
)
|
64
|
+
.save()
|
65
|
+
.instance
|
66
|
+
)
|
67
|
+
|
68
|
+
tasks.queue_batch_import(
|
69
|
+
operation.id,
|
70
|
+
data=dict(
|
71
|
+
dataset=dataset,
|
72
|
+
import_data=validated_data,
|
73
|
+
),
|
74
|
+
ctx=dict(
|
75
|
+
org_id=getattr(context.org, "id", None),
|
76
|
+
user_id=getattr(context.user, "id", None),
|
77
|
+
test_mode=context.test_mode,
|
78
|
+
),
|
79
|
+
schema=settings.schema,
|
80
|
+
)
|
81
|
+
|
82
|
+
return operation
|
83
|
+
|
84
|
+
|
85
|
+
def check_dataset_validation_errors(validation):
|
86
|
+
if any(validation.base_errors):
|
87
|
+
raise exceptions.APIExceptions(
|
88
|
+
validation.base_errors,
|
89
|
+
code="invalid_data",
|
90
|
+
)
|
91
|
+
|
92
|
+
errors = []
|
93
|
+
flattened_row_errors = sum(
|
94
|
+
[
|
95
|
+
[(i, e.error) for e in row.errors]
|
96
|
+
for i, row in enumerate(validation.rows)
|
97
|
+
],
|
98
|
+
[]
|
99
|
+
)
|
100
|
+
|
101
|
+
for index, error in flattened_row_errors:
|
102
|
+
setattr(error, "index", index)
|
103
|
+
errors.append(error)
|
104
|
+
|
105
|
+
if any(errors):
|
106
|
+
raise exceptions.APIExceptions(
|
107
|
+
errors,
|
108
|
+
code="invalid_data",
|
109
|
+
)
|
@@ -0,0 +1,52 @@
|
|
1
|
+
import logging
|
2
|
+
from django.db.models import signals
|
3
|
+
|
4
|
+
from karrio.server.conf import settings
|
5
|
+
import karrio.server.core.utils as utils
|
6
|
+
import karrio.server.data.models as models
|
7
|
+
import karrio.server.events.tasks as tasks
|
8
|
+
import karrio.server.data.serializers as serializers
|
9
|
+
|
10
|
+
logger = logging.getLogger(__name__)
|
11
|
+
|
12
|
+
|
13
|
+
def register_all():
|
14
|
+
signals.post_save.connect(batch_operation_updated, sender=models.BatchOperation)
|
15
|
+
|
16
|
+
logger.info("karrio.data signals registered...")
|
17
|
+
|
18
|
+
|
19
|
+
@utils.disable_for_loaddata
|
20
|
+
def batch_operation_updated(sender, instance, *args, **kwargs):
|
21
|
+
"""Process batch related events
|
22
|
+
- Create event on batch creation and completion
|
23
|
+
- Notifiy webhook subscribers of batch operation updates
|
24
|
+
"""
|
25
|
+
changes = kwargs.get("update_fields") or []
|
26
|
+
post_create = "created_at" in changes
|
27
|
+
|
28
|
+
if post_create:
|
29
|
+
event = serializers.EventTypes.batch_queued.value
|
30
|
+
elif instance.status == serializers.BatchOperationStatus.running.value:
|
31
|
+
event = serializers.EventTypes.batch_running.value
|
32
|
+
# Run post creation processing for batch_operation resources
|
33
|
+
tasks.process_batch_resources(instance.id, schema=settings.schema)
|
34
|
+
elif instance.status == serializers.BatchOperationStatus.completed.value:
|
35
|
+
event = serializers.EventTypes.batch_completed.value
|
36
|
+
else:
|
37
|
+
return
|
38
|
+
|
39
|
+
data = serializers.BatchOperation(instance).data
|
40
|
+
event_at = instance.updated_at
|
41
|
+
context = dict(
|
42
|
+
user_id=utils.failsafe(lambda: instance.created_by.id),
|
43
|
+
test_mode=instance.test_mode,
|
44
|
+
org_id=utils.failsafe(
|
45
|
+
lambda: instance.org.first().id if hasattr(instance, "org") else None
|
46
|
+
),
|
47
|
+
)
|
48
|
+
|
49
|
+
if settings.MULTI_ORGANIZATIONS and context["org_id"] is None:
|
50
|
+
return
|
51
|
+
|
52
|
+
tasks.notify_webhooks(event, data, event_at, context, schema=settings.schema)
|
@@ -0,0 +1,13 @@
|
|
1
|
+
"""
|
2
|
+
karrio server data module urls
|
3
|
+
"""
|
4
|
+
from django.urls import include, path
|
5
|
+
|
6
|
+
app_name = "karrio.server.data"
|
7
|
+
urlpatterns = [
|
8
|
+
path("v1/", include("karrio.server.data.views.data")),
|
9
|
+
path("v1/", include("karrio.server.data.views.batch")),
|
10
|
+
path("v1/", include("karrio.server.data.views.batch_order")),
|
11
|
+
path("v1/", include("karrio.server.data.views.batch_shipment")),
|
12
|
+
path("v1/", include("karrio.server.data.views.batch_tracking")),
|
13
|
+
]
|
File without changes
|