# # This file is part of pretix (Community Edition). # # Copyright (C) 2014-2020 Raphael Michel and contributors # Copyright (C) 2020-2021 rami.io GmbH and contributors # # This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General # Public License as published by the Free Software Foundation in version 3 of the License. # # ADDITIONAL TERMS APPLY: Pursuant to Section 7 of the GNU Affero General Public License, additional terms are # applicable granting you additional permissions and placing additional restrictions on your usage of this software. # Please refer to the pretix LICENSE file to obtain the full terms applicable to this work. If you did not receive # this file, see . # # This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied # warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more # details. # # You should have received a copy of the GNU Affero General Public License along with this program. If not, see # . # from itertools import groupby from django.contrib import messages from django.db.models import Q from django.dispatch import receiver from django.http import HttpResponseNotAllowed from django.shortcuts import redirect from django.template.loader import get_template from django.utils.timezone import now from django.utils.translation import gettext_lazy as _ from django.views.generic import ListView from pretix.base.datasync.datasync import datasync_providers from pretix.base.models import Event, Order from pretix.base.models.datasync import OrderSyncQueue from pretix.base.services.datasync import sync_single from pretix.control.permissions import ( AdministratorPermissionRequiredMixin, EventPermissionRequiredMixin, OrganizerPermissionRequiredMixin, ) from pretix.control.signals import order_info from pretix.control.views.orders import OrderView @receiver(order_info, dispatch_uid="datasync_control_order_info") def on_control_order_info(sender: Event, request, order: Order, **kwargs): providers = [provider for provider, meta in datasync_providers.filter(active_in=sender)] if not providers: return "" queued = {p.sync_provider: p for p in order.queued_sync_jobs.all()} objects = { provider: list(objects) for (provider, objects) in groupby(order.sync_results.order_by('sync_provider').all(), key=lambda o: o.sync_provider) } providers = [(provider.identifier, provider.display_name, queued.get(provider.identifier), objects.get(provider.identifier)) for provider in providers] template = get_template("pretixcontrol/datasync/control_order_info.html") ctx = { "order": order, "request": request, "event": sender, "providers": providers, "now": now(), } return template.render(ctx, request=request) class ControlSyncJob(OrderView): permission = 'can_change_orders' def post(self, request, provider, *args, **kwargs): prov, meta = datasync_providers.get(active_in=self.request.event, identifier=provider) if self.request.POST.get("queue_sync") == "true": prov.enqueue_order(self.order, 'user') messages.success(self.request, _('The sync job has been enqueued and will run in the next minutes.')) elif self.request.POST.get("cancel_job"): job = self.order.queued_sync_jobs.get(pk=self.request.POST.get("cancel_job")) if job.in_flight: messages.warning(self.request, _('The sync job is already in progress.')) else: job.delete() messages.success(self.request, _('The sync job has been canceled.')) elif self.request.POST.get("run_job_now"): job = self.order.queued_sync_jobs.get(pk=self.request.POST.get("run_job_now")) if job.in_flight: messages.success(self.request, _('The sync job is already in progress.')) else: job.not_before = now() job.need_manual_retry = None job.save() sync_single.apply_async(args=(job.pk,)) messages.success(self.request, _('The sync job has been set to run as soon as possible.')) return redirect(self.get_order_url()) def get(self, *args, **kwargs): return HttpResponseNotAllowed(['POST']) class FailedSyncJobsView(ListView): template_name = 'pretixcontrol/datasync/failed_jobs.html' model = OrderSyncQueue context_object_name = 'queue_items' paginate_by = 100 ordering = ('triggered',) def get_queryset(self): return super().get_queryset().filter( Q(need_manual_retry__isnull=False) | Q(failed_attempts__gt=0) ).select_related( 'order' ) def post(self, request, *args, **kwargs): items = self.get_queryset().filter(pk__in=request.POST.getlist('idlist')) if self.request.POST.get("action") == "retry": for item in items: item.not_before = now() item.need_manual_retry = None item.save() messages.success(self.request, _('The selected jobs have been set to run as soon as possible.')) elif self.request.POST.get("action") == "cancel": items.delete() messages.success(self.request, _('The selected jobs have been canceled.')) return redirect(request.get_full_path()) class GlobalFailedSyncJobsView(AdministratorPermissionRequiredMixin, FailedSyncJobsView): pass class OrganizerFailedSyncJobsView(OrganizerPermissionRequiredMixin, FailedSyncJobsView): permission = "can_change_organizer_settings" def get_queryset(self): return super().get_queryset().filter( event__organizer=self.request.organizer ) class EventFailedSyncJobsView(EventPermissionRequiredMixin, FailedSyncJobsView): permission = "can_change_event_settings" def get_queryset(self): return super().get_queryset().filter( event=self.request.event )