mirror of
https://github.com/pretix/pretix.git
synced 2026-05-15 16:54:00 +00:00
Store information about objects transferred in last successful sync
This commit is contained in:
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user