Store information about objects transferred in last successful sync

This commit is contained in:
Mira Weller
2025-03-04 16:18:10 +01:00
parent 87570df3f8
commit e051470e91
6 changed files with 130 additions and 63 deletions

View File

@@ -15,8 +15,8 @@ from django_scopes import scope, scopes_disabled
from pretix.base.datasync.sourcefields import (
EVENT, EVENT_OR_SUBEVENT, ORDER, ORDER_POSITION, get_data_fields,
)
from pretix.base.models import Event, Order
from pretix.base.services.tasks import TransactionAwareTask
from pretix.base.logentrytype_registry import make_link
from pretix.base.models import Order, OrderPosition
from pretix.base.signals import EventPluginRegistry, periodic_task
from pretix.celery_app import app
@@ -58,6 +58,34 @@ class OrderSyncQueue(models.Model):
return self.provider_class.max_attempts
class OrderSyncLink(models.Model):
class Meta:
indexes = [
models.Index(fields=("order", "sync_provider")),
]
order = models.ForeignKey(
Order, on_delete=models.CASCADE, related_name="synced_objects"
)
sync_provider = models.CharField(blank=False, null=False, max_length=128)
order_position = models.ForeignKey(
OrderPosition, on_delete=models.CASCADE, related_name="synced_objects", blank=True, null=True,
)
external_object_type = models.CharField(blank=False, null=False, max_length=128)
external_pk_name = models.CharField(blank=False, null=False, max_length=128)
external_pk_value = models.CharField(blank=False, null=False, max_length=128)
external_link_href = models.CharField(blank=True, null=True, max_length=255)
external_link_display_name = models.CharField(blank=True, null=True, max_length=255)
timestamp = models.DateTimeField(blank=False, null=False, auto_now_add=True)
def external_link_html(self):
if not self.external_link_display_name:
return None
prov, meta = sync_targets.get(identifier=self.sync_provider)
if prov:
return prov.get_external_link_html(self.order.event, self.external_link_href, self.external_link_display_name)
@receiver(periodic_task, dispatch_uid="data_sync_periodic")
def on_periodic_task(sender, **kwargs):
sync_all.apply_async()
@@ -73,7 +101,6 @@ def sync_event_to_target(event, target_cls, queued_orders):
p.sync_queued_orders(queued_orders)
@app.task()
def sync_all():
with scopes_disabled():
@@ -100,7 +127,6 @@ StaticMapping = namedtuple('StaticMapping', ('pk', 'pretix_model', 'external_obj
class OutboundSyncProvider:
#identifier = None
max_attempts = 5
syncer_class = None
@@ -126,8 +152,20 @@ class OutboundSyncProvider:
triggered_by=triggered_by,
not_before=not_before)
@classmethod
def get_external_link_info(cls, event, external_link_href, external_link_display_name):
return {
"href": external_link_href,
"val": external_link_display_name,
}
@classmethod
def get_external_link_html(cls, event, external_link_href, external_link_display_name):
info = cls.get_external_link_info(event, external_link_href, external_link_display_name)
return make_link(info, '{val}')
def next_retry_date(self, sq):
return datetime.now() + timedelta(days=1)
return datetime.now() + timedelta(hours=1)
def sync_queued_orders(self, queued_orders):
for sq in queued_orders:
@@ -135,16 +173,14 @@ class OutboundSyncProvider:
mapped_objects = self.sync_order(sq.order)
except SyncConfigError as e:
logger.warning(
f"Could not sync order {sq.order.code} to {self.__name__} (config error)",
f"Could not sync order {sq.order.code} to {type(self).__name__} (config error)",
exc_info=True,
)
sq.order.log_action(
"pretix.event.order.data_sync.failed",
{
"error": e.messages,
"full_message": e.full_message,
},
)
sq.order.log_action("pretix.event.order.data_sync.failed", {
"provider": self.identifier,
"error": e.messages,
"full_message": e.full_message,
})
sq.delete()
except Exception as e:
# TODO: different handling per Exception, or even per HTTP response code?
@@ -156,20 +192,19 @@ class OutboundSyncProvider:
)
if sq.failed_attempts >= self.max_attempts:
sentry_sdk.capture_exception(e)
sq.order.log_action(
"pretix.event.order.data_sync.failed",
{
"error": [_("Maximum number of retries exceeded.")],
"full_message": str(e),
},
)
sq.order.log_action("pretix.event.order.data_sync.failed", {
"provider": self.identifier,
"error": [_("Maximum number of retries exceeded.")],
"full_message": str(e),
})
sq.delete()
else:
sq.save()
else:
sq.order.log_action(
"pretix.event.order.data_sync.success", {"objects": mapped_objects}
)
sq.order.log_action("pretix.event.order.data_sync.success", {
"provider": self.identifier,
"objects": mapped_objects
})
sq.delete()
def order_valid_for_sync(self, order):
@@ -177,7 +212,7 @@ class OutboundSyncProvider:
@property
def mappings(self):
raise NotImplemented
raise NotImplementedError
@cached_property
def data_fields(self):
@@ -223,7 +258,16 @@ class OutboundSyncProvider:
if not pk_value:
return None
return self.sync_object_with_properties(inputs, mapping, mapped_objects, pk_value, properties)
info = self.sync_object_with_properties(inputs, mapping, mapped_objects, pk_value, properties)
OrderSyncLink.objects.create(
order=inputs.get(ORDER), order_position=inputs.get(ORDER_POSITION), sync_provider=self.identifier,
external_object_type=info.get('object_type'),
external_pk_name=info.get('pk_field'),
external_pk_value=info.get('pk_value'),
external_link_href=info.get('external_link_href'),
external_link_display_name=info.get('external_link_display_name'),
)
return info
def sync_order(self, order):
if not self.order_valid_for_sync(order):
@@ -238,6 +282,7 @@ class OutboundSyncProvider:
"voucher",
)
)
order.synced_objects.filter(sync_provider=self.identifier).delete()
order_inputs = {ORDER: order, EVENT: self.event}
mapped_objects = {}
for mapping in self.mappings:
@@ -259,7 +304,7 @@ class OutboundSyncProvider:
"""
Called after sync_object has been called successfully for all objects of a specific order. Can be used for saving
bulk information per order.
bulk information per order.
"""
def finalize_sync_order(self, order):
pass