karrio-server-events 2025.5rc34__py3-none-any.whl → 2025.5rc36__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.
@@ -86,7 +86,7 @@ def shipment_cancelled(sender, instance, *args, **kwargs):
86
86
  - shipment cancelled/deleted (label voided)
87
87
  """
88
88
  event = EventTypes.shipment_cancelled.value
89
- data = serializers.Shipment(instance)
89
+ data = serializers.Shipment(instance).data
90
90
  event_at = instance.updated_at
91
91
  context = dict(
92
92
  user_id=utils.failsafe(lambda: instance.created_by.id),
@@ -22,46 +22,61 @@ def run_data_archiving(*args, **kwargs):
22
22
  days=conf.settings.TRACKER_DATA_RETENTION
23
23
  )
24
24
 
25
- # Use efficient existence checks and batch processing
26
- BATCH_SIZE = 1000 # Process in batches to avoid memory issues
27
-
28
- # Optimize querysets by only selecting required fields for existence checks
29
- tracing_exists = tracing.TracingRecord.objects.filter(created_at__lt=log_retention).exists()
30
- event_exists = events.Event.objects.filter(created_at__lt=log_retention).exists()
31
- api_log_exists = core.APILog.objects.filter(requested_at__lt=log_retention).exists()
32
- tracking_exists = manager.Tracking.objects.filter(created_at__lt=tracker_retention).exists()
33
- shipping_exists = manager.Shipment.objects.filter(created_at__lt=shipment_retention).exists()
34
- order_exists = orders.Order.objects.filter(created_at__lt=order_retention).exists()
35
-
36
- if tracing_exists:
37
- logger.info("Archiving SDK tracing backlog", retention_days=conf.settings.API_LOGS_DATA_RETENTION)
38
- tracing_data = tracing.TracingRecord.objects.filter(created_at__lt=log_retention)
39
- utils.failsafe(lambda: _bulk_delete_tracing_data(tracing_data))
40
-
41
- if event_exists:
42
- logger.info("Archiving events backlog", retention_days=conf.settings.API_LOGS_DATA_RETENTION)
43
- event_data = events.Event.objects.filter(created_at__lt=log_retention)
44
- utils.failsafe(lambda: event_data.delete())
45
-
46
- if api_log_exists:
47
- logger.info("Archiving API request logs backlog", retention_days=conf.settings.API_LOGS_DATA_RETENTION)
48
- api_log_data = core.APILog.objects.filter(requested_at__lt=log_retention)
49
- utils.failsafe(lambda: api_log_data.delete())
50
-
51
- if tracking_exists:
52
- logger.info("Archiving tracking data backlog", retention_days=conf.settings.TRACKER_DATA_RETENTION)
53
- tracking_data = manager.Tracking.objects.filter(created_at__lt=tracker_retention)
54
- utils.failsafe(lambda: _bulk_delete_tracking_data(tracking_data))
55
-
56
- if shipping_exists:
57
- logger.info("Archiving shipping data backlog", retention_days=conf.settings.SHIPMENT_DATA_RETENTION)
58
- shipping_data = manager.Shipment.objects.filter(created_at__lt=shipment_retention)
59
- utils.failsafe(lambda: _bulk_delete_shipment_data(shipping_data))
60
-
61
- if order_exists:
62
- logger.info("Archiving order data backlog", retention_days=conf.settings.ORDER_DATA_RETENTION)
63
- order_data = orders.Order.objects.filter(created_at__lt=order_retention)
64
- utils.failsafe(lambda: _bulk_delete_order_data(order_data))
25
+ # Prepare querysets once and rely on delete helpers to determine if work was done.
26
+ tracing_data = tracing.TracingRecord.objects.filter(created_at__lt=log_retention)
27
+ event_data = events.Event.objects.filter(created_at__lt=log_retention)
28
+ api_log_data = core.APILog.objects.filter(requested_at__lt=log_retention)
29
+ tracking_data = manager.Tracking.objects.filter(created_at__lt=tracker_retention)
30
+ shipping_data = manager.Shipment.objects.filter(created_at__lt=shipment_retention)
31
+ order_data = orders.Order.objects.filter(created_at__lt=order_retention)
32
+
33
+ tracing_deleted = utils.failsafe(lambda: _bulk_delete_tracing_data(tracing_data)) or 0
34
+ if tracing_deleted:
35
+ logger.info(
36
+ "Archiving SDK tracing backlog",
37
+ retention_days=conf.settings.API_LOGS_DATA_RETENTION,
38
+ deleted_records=tracing_deleted,
39
+ )
40
+
41
+ events_deleted = utils.failsafe(lambda: event_data.delete()[0]) or 0
42
+ if events_deleted:
43
+ logger.info(
44
+ "Archiving events backlog",
45
+ retention_days=conf.settings.API_LOGS_DATA_RETENTION,
46
+ deleted_records=events_deleted,
47
+ )
48
+
49
+ api_logs_deleted = utils.failsafe(lambda: api_log_data.delete()[0]) or 0
50
+ if api_logs_deleted:
51
+ logger.info(
52
+ "Archiving API request logs backlog",
53
+ retention_days=conf.settings.API_LOGS_DATA_RETENTION,
54
+ deleted_records=api_logs_deleted,
55
+ )
56
+
57
+ tracking_deleted = utils.failsafe(lambda: _bulk_delete_tracking_data(tracking_data)) or 0
58
+ if tracking_deleted:
59
+ logger.info(
60
+ "Archiving tracking data backlog",
61
+ retention_days=conf.settings.TRACKER_DATA_RETENTION,
62
+ deleted_records=tracking_deleted,
63
+ )
64
+
65
+ shipping_deleted = utils.failsafe(lambda: _bulk_delete_shipment_data(shipping_data)) or 0
66
+ if shipping_deleted:
67
+ logger.info(
68
+ "Archiving shipping data backlog",
69
+ retention_days=conf.settings.SHIPMENT_DATA_RETENTION,
70
+ deleted_records=shipping_deleted,
71
+ )
72
+
73
+ order_deleted = utils.failsafe(lambda: _bulk_delete_order_data(order_data)) or 0
74
+ if order_deleted:
75
+ logger.info(
76
+ "Archiving order data backlog",
77
+ retention_days=conf.settings.ORDER_DATA_RETENTION,
78
+ deleted_records=order_deleted,
79
+ )
65
80
 
66
81
  logger.info("Finished scheduled backlog archiving")
67
82
 
@@ -69,100 +84,117 @@ def run_data_archiving(*args, **kwargs):
69
84
  def _bulk_delete_tracing_data(tracing_queryset):
70
85
  """Bulk delete tracing data to avoid N+1 queries with organization links."""
71
86
  BATCH_SIZE = 1000
72
-
87
+ queryset = tracing_queryset.order_by("pk")
88
+
73
89
  try:
74
90
  from karrio.server.orgs.models import TracingRecordLink
75
-
91
+
76
92
  # Process in batches to avoid memory issues
77
93
  total_deleted = 0
78
94
  while True:
79
95
  # Get a batch of IDs to delete
80
- batch_ids = list(tracing_queryset.values_list('id', flat=True)[:BATCH_SIZE])
81
-
96
+ batch_ids = list(queryset.values_list("id", flat=True)[:BATCH_SIZE])
97
+
82
98
  if not batch_ids:
83
99
  break
84
-
100
+
85
101
  # Bulk delete TracingRecordLink entries first to avoid CASCADE N+1 queries
86
102
  TracingRecordLink.objects.filter(item_id__in=batch_ids).delete()
87
-
103
+
88
104
  # Delete the tracing records in this batch
89
- deleted_count = tracing_queryset.filter(id__in=batch_ids).delete()[0]
105
+ deleted_count = queryset.filter(id__in=batch_ids).delete()[0]
90
106
  total_deleted += deleted_count
91
-
107
+
92
108
  logger.info("Deleted tracing records batch", batch_count=deleted_count, total_deleted=total_deleted)
93
-
109
+
110
+ return total_deleted
111
+
94
112
  except ImportError:
95
113
  # Organizations module not installed, just delete in batches
96
114
  total_deleted = 0
97
115
  while True:
98
116
  # Get a batch to delete
99
- batch_ids = list(tracing_queryset.values_list('id', flat=True)[:BATCH_SIZE])
100
-
117
+ batch_ids = list(queryset.values_list("id", flat=True)[:BATCH_SIZE])
118
+
101
119
  if not batch_ids:
102
120
  break
103
-
104
- deleted_count = tracing_queryset.filter(id__in=batch_ids).delete()[0]
121
+
122
+ deleted_count = queryset.filter(id__in=batch_ids).delete()[0]
105
123
  total_deleted += deleted_count
106
-
124
+
107
125
  logger.info("Deleted tracing records batch", batch_count=deleted_count, total_deleted=total_deleted)
108
126
 
127
+ return total_deleted
128
+
109
129
 
110
130
  def _bulk_delete_tracking_data(tracking_queryset):
111
131
  """Bulk delete tracking data to avoid N+1 queries with organization links."""
132
+ queryset = tracking_queryset.order_by("pk")
133
+
112
134
  try:
113
135
  from karrio.server.orgs.models import TrackingLink
114
-
136
+
115
137
  # Get the tracking record IDs that will be deleted
116
- tracking_ids = list(tracking_queryset.values_list('id', flat=True))
117
-
138
+ tracking_ids = list(queryset.values_list("id", flat=True))
139
+
118
140
  if tracking_ids:
119
141
  # Bulk delete TrackingLink entries first to avoid CASCADE N+1 queries
120
142
  TrackingLink.objects.filter(item_id__in=tracking_ids).delete()
121
-
143
+
122
144
  # Now delete the tracking records themselves
123
- tracking_queryset.delete()
124
-
145
+ deleted = queryset.delete()[0]
146
+
125
147
  except ImportError:
126
148
  # Organizations module not installed, just delete normally
127
- tracking_queryset.delete()
149
+ deleted = queryset.delete()[0]
150
+
151
+ return deleted
128
152
 
129
153
 
130
154
  def _bulk_delete_shipment_data(shipment_queryset):
131
155
  """Bulk delete shipment data to avoid N+1 queries with organization links."""
156
+ queryset = shipment_queryset.order_by("pk")
157
+
132
158
  try:
133
159
  from karrio.server.orgs.models import ShipmentLink
134
-
160
+
135
161
  # Get the shipment record IDs that will be deleted
136
- shipment_ids = list(shipment_queryset.values_list('id', flat=True))
137
-
162
+ shipment_ids = list(queryset.values_list("id", flat=True))
163
+
138
164
  if shipment_ids:
139
165
  # Bulk delete ShipmentLink entries first to avoid CASCADE N+1 queries
140
166
  ShipmentLink.objects.filter(item_id__in=shipment_ids).delete()
141
-
167
+
142
168
  # Now delete the shipment records themselves
143
- shipment_queryset.delete()
144
-
169
+ deleted = queryset.delete()[0]
170
+
145
171
  except ImportError:
146
172
  # Organizations module not installed, just delete normally
147
- shipment_queryset.delete()
173
+ deleted = queryset.delete()[0]
174
+
175
+ return deleted
148
176
 
149
177
 
150
178
  def _bulk_delete_order_data(order_queryset):
151
179
  """Bulk delete order data to avoid N+1 queries with organization links."""
180
+ queryset = order_queryset.order_by("pk")
181
+
152
182
  try:
153
183
  from karrio.server.orgs.models import OrderLink
154
-
184
+
155
185
  # Get the order record IDs that will be deleted
156
- order_ids = list(order_queryset.values_list('id', flat=True))
157
-
186
+ order_ids = list(queryset.values_list("id", flat=True))
187
+
158
188
  if order_ids:
159
189
  # Bulk delete OrderLink entries first to avoid CASCADE N+1 queries
160
190
  OrderLink.objects.filter(item_id__in=order_ids).delete()
161
-
191
+
162
192
  # Now delete the order records themselves
163
- order_queryset.delete()
164
-
193
+ deleted = queryset.delete()[0]
194
+
165
195
  except ImportError:
166
196
  # Organizations module not installed, just delete normally
167
- order_queryset.delete()
197
+ deleted = queryset.delete()[0]
198
+
199
+ return deleted
168
200
 
@@ -38,6 +38,7 @@ def update_trackers(
38
38
  ):
39
39
  logger.info("Starting scheduled trackers update", delta_seconds=delta.seconds, tracker_count=len(tracker_ids) if tracker_ids else 0)
40
40
 
41
+ # TrackingManager now handles all necessary prefetching including carrier config
41
42
  active_trackers = lib.identity(
42
43
  models.Tracking.objects.filter(id__in=tracker_ids)
43
44
  if any(tracker_ids)
@@ -152,69 +153,28 @@ def save_updated_trackers(
152
153
  t for t in trackers if t.tracking_number == details.tracking_number
153
154
  ]
154
155
  for tracker in related_trackers:
155
- # update values only if changed; This is important for webhooks notification
156
- changes = []
157
- meta = details.meta or {}
156
+ # Compute status from tracking details
158
157
  status = utils.compute_tracking_status(details).value
159
- events = utils.process_events(
160
- response_events=details.events,
161
- current_events=tracker.events,
162
- )
158
+ # Merge options with existing tracker options
163
159
  options = {
164
160
  **(tracker.options or {}),
165
161
  tracker.tracking_number: details.meta,
166
162
  }
167
- info = lib.to_dict(details.info or {})
168
-
169
- if events != tracker.events:
170
- tracker.events = events
171
- changes.append("events")
172
-
173
- if options != tracker.options:
174
- tracker.options = options
175
- changes.append("options")
176
-
177
- if details.meta != tracker.meta:
178
- tracker.meta = meta
179
- changes.append("meta")
180
-
181
- if details.delivered != tracker.delivered:
182
- tracker.delivered = details.delivered
183
- changes.append("delivered")
184
-
185
- if status != tracker.status:
186
- tracker.status = status
187
- changes.append("status")
188
-
189
- if details.estimated_delivery != tracker.estimated_delivery:
190
- tracker.estimated_delivery = details.estimated_delivery
191
- changes.append("estimated_delivery")
192
-
193
- if details.images is not None and (
194
- details.images.delivery_image != tracker.delivery_image
195
- or details.images.signature_image != tracker.signature_image
196
- ):
197
- changes.append("delivery_image")
198
- changes.append("signature_image")
199
- tracker.delivery_image = (
200
- details.images.delivery_image or tracker.delivery_image
201
- )
202
- tracker.signature_image = (
203
- details.images.signature_image or tracker.signature_image
204
- )
205
-
206
- if any(info.keys()) and info != tracker.info:
207
- tracker.info = serializers.process_dictionaries_mutations(
208
- ["info"], dict(info=info), tracker
209
- )["info"]
210
- changes.append("info")
211
-
212
- if any(changes):
213
- tracker.save(update_fields=changes)
214
- serializers.update_shipment_tracker(tracker)
215
- logger.debug("Tracking info updated successfully", tracking_number=details.tracking_number, changes=changes)
216
- else:
217
- logger.debug("No changes detected", tracking_number=details.tracking_number)
163
+
164
+ # Use the centralized update_tracker function
165
+ serializers.update_tracker(
166
+ tracker,
167
+ dict(
168
+ events=details.events,
169
+ delivered=details.delivered,
170
+ status=status,
171
+ estimated_delivery=details.estimated_delivery,
172
+ options=options,
173
+ meta=details.meta,
174
+ info=lib.to_dict(details.info or {}),
175
+ images=details.images,
176
+ ),
177
+ )
218
178
 
219
179
  except Exception as update_error:
220
180
  logger.warning("Failed to update tracker", tracking_number=details.tracking_number, error=str(update_error))
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: karrio_server_events
3
- Version: 2025.5rc34
3
+ Version: 2025.5rc36
4
4
  Summary: Multi-carrier shipping API Events module
5
5
  Author-email: karrio <hello@karrio.io>
6
6
  License-Expression: Apache-2.0
@@ -4,7 +4,7 @@ karrio/server/events/apps.py,sha256=DIV8JdPB_3hD4DwhcQTevhph3PaceMkFSEGM1lOcdVY,
4
4
  karrio/server/events/filters.py,sha256=vMeY42IsoFl482-yX1BI17sw-9x9euOnzTl-860W3sg,2230
5
5
  karrio/server/events/models.py,sha256=WQQKpDnbUW97beFmCBJDTckSoE-h-HtxeRfjoNWepQY,2582
6
6
  karrio/server/events/router.py,sha256=IBUR7rfBkdEHQzWxYOPcVSM8NBp3fte9G6Q5BVTUNNw,95
7
- karrio/server/events/signals.py,sha256=lIqouHZ_RGSzZXQgBeTT5Y0YoQL_TpzoifqxQKYJTSo,4539
7
+ karrio/server/events/signals.py,sha256=3VlwVPWEyALcjn1MEbuh3u9Xojq8hSgmwYFz7lulW3M,4544
8
8
  karrio/server/events/tasks.py,sha256=kqs-W2VnDHOQNvieN8Ltehk578W5FTXTobj7t_mi9Xk,787
9
9
  karrio/server/events/tests.py,sha256=mrbGGRNg5jwbTJtWWa7zSKdDyeB4vmgZCRc2nk6VY-g,60
10
10
  karrio/server/events/urls.py,sha256=oLTTzDWPhEFfK93EvzHTDRxqkw8Fr3iXneDXwFUkvcU,217
@@ -25,8 +25,8 @@ karrio/server/events/serializers/event.py,sha256=dKYqldfSEjKMY7uB1rBFfEfSZ_dfEp8
25
25
  karrio/server/events/serializers/webhook.py,sha256=Ri5JS1SXpSsPUvQH989T_7Amu1p46y5TP54MEEhK8j8,889
26
26
  karrio/server/events/task_definitions/__init__.py,sha256=bpT73UG7mZL_JjEqMwbYx6q69jA8J5Jcoul1LcDokhA,81
27
27
  karrio/server/events/task_definitions/base/__init__.py,sha256=3vP52gFdCo9hctLRKnYvEi9iz1UK8GL_a47Nw44h3Nc,1834
28
- karrio/server/events/task_definitions/base/archiving.py,sha256=n2Jxn6Jj-3fMtNCBnCUXrq4_iePmvU31j4ntCilIXJk,7303
29
- karrio/server/events/task_definitions/base/tracking.py,sha256=av_uiT_0zNuZEC9X31EUXnuIF6gavtM8_1g300QUtzk,9026
28
+ karrio/server/events/task_definitions/base/archiving.py,sha256=HVUfgzTSsfa-o2u6N1VuyPopnVLdFRwvv-3ix5LjKHc,7270
29
+ karrio/server/events/task_definitions/base/tracking.py,sha256=3i4ArtrPLioVNcPICQiHPkXOpEayVCJqlMYHexWMchY,7081
30
30
  karrio/server/events/task_definitions/base/webhook.py,sha256=QNRq3xhLWQEbrs3zSy6Sa0AXUPhWE7YenKpPzZoFwL0,3616
31
31
  karrio/server/events/tests/__init__.py,sha256=5szv9hDvu0R0jtm3XWktY19rCaE8bZ2LdjRm4oJ0pYA,220
32
32
  karrio/server/events/tests/test_events.py,sha256=Fxi1hv_lyuRJw5Y_ydywhq5P3akpxet4mFMFSiHU96c,4066
@@ -39,7 +39,7 @@ karrio/server/graph/schemas/events/__init__.py,sha256=Yg1rWew5DgSb_OnLy8n7qqwPLf
39
39
  karrio/server/graph/schemas/events/inputs.py,sha256=Mgy9qCUrbL-sMI5dNXsnbW8K5_PnGKM13brzZrc5OWk,1526
40
40
  karrio/server/graph/schemas/events/mutations.py,sha256=930g2D5TnAEgEjfEfLml1Q9OGCyxe2D592BhzSGDQAQ,1862
41
41
  karrio/server/graph/schemas/events/types.py,sha256=TPlrDwQV4LZXu-1t4gFl_eYayU39THmgYT3uv7VXnnw,2759
42
- karrio_server_events-2025.5rc34.dist-info/METADATA,sha256=P-Rizyp2GNrfNJxOf3naA-6ObqnEKc0-cuj5BOUChxY,705
43
- karrio_server_events-2025.5rc34.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
44
- karrio_server_events-2025.5rc34.dist-info/top_level.txt,sha256=D1D7x8R3cTfjF_15mfiO7wCQ5QMtuM4x8GaPr7z5i78,12
45
- karrio_server_events-2025.5rc34.dist-info/RECORD,,
42
+ karrio_server_events-2025.5rc36.dist-info/METADATA,sha256=3cWUxQ6QHNZtyeRohQmJ6U9gY509aq_-kRF2hM3omPI,705
43
+ karrio_server_events-2025.5rc36.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
44
+ karrio_server_events-2025.5rc36.dist-info/top_level.txt,sha256=D1D7x8R3cTfjF_15mfiO7wCQ5QMtuM4x8GaPr7z5i78,12
45
+ karrio_server_events-2025.5rc36.dist-info/RECORD,,