From 9ba3227837bc611702caa467bbab01d64bf204b7 Mon Sep 17 00:00:00 2001 From: Mira Date: Thu, 24 Aug 2023 17:06:47 +0200 Subject: [PATCH] Checkout: Prefill is_business heuristically (Z#23126061) (#3533) --- src/pretix/base/cache.py | 10 ++++---- src/pretix/presale/checkoutflow.py | 40 +++++++++++++++++++++++++++--- 2 files changed, 42 insertions(+), 8 deletions(-) diff --git a/src/pretix/base/cache.py b/src/pretix/base/cache.py index 2a0e59f09..e2702628c 100644 --- a/src/pretix/base/cache.py +++ b/src/pretix/base/cache.py @@ -62,27 +62,27 @@ class NamespacedCache: prefix = int(time.time()) self.cache.set(self.prefixkey, prefix) - def set(self, key: str, value: str, timeout: int=300): + def set(self, key: str, value: any, timeout: int=300): return self.cache.set(self._prefix_key(key), value, timeout) - def get(self, key: str) -> str: + def get(self, key: str) -> any: return self.cache.get(self._prefix_key(key, known_prefix=self._last_prefix)) - def get_or_set(self, key: str, default: Callable, timeout=300) -> str: + def get_or_set(self, key: str, default: Callable, timeout=300) -> any: return self.cache.get_or_set( self._prefix_key(key, known_prefix=self._last_prefix), default=default, timeout=timeout ) - def get_many(self, keys: List[str]) -> Dict[str, str]: + def get_many(self, keys: List[str]) -> Dict[str, any]: values = self.cache.get_many([self._prefix_key(key) for key in keys]) newvalues = {} for k, v in values.items(): newvalues[self._strip_prefix(k)] = v return newvalues - def set_many(self, values: Dict[str, str], timeout=300): + def set_many(self, values: Dict[str, any], timeout=300): newvalues = {} for k, v in values.items(): newvalues[self._prefix_key(k)] = v diff --git a/src/pretix/presale/checkoutflow.py b/src/pretix/presale/checkoutflow.py index 7c53bbbb4..083a91a94 100644 --- a/src/pretix/presale/checkoutflow.py +++ b/src/pretix/presale/checkoutflow.py @@ -39,10 +39,13 @@ from decimal import Decimal from django.conf import settings from django.contrib import messages +from django.core.cache import caches from django.core.exceptions import ImproperlyConfigured, ValidationError from django.core.signing import BadSignature, loads from django.core.validators import EmailValidator -from django.db.models import F, Q +from django.db import models +from django.db.models import Count, F, Q, Sum +from django.db.models.functions import Cast from django.http import HttpResponseNotAllowed, JsonResponse from django.shortcuts import redirect from django.utils import translation @@ -62,12 +65,14 @@ from pretix.base.services.cart import ( ) from pretix.base.services.memberships import validate_memberships_in_order from pretix.base.services.orders import perform_order +from pretix.base.services.tasks import EventTask from pretix.base.settings import PERSON_NAME_SCHEMES from pretix.base.signals import validate_cart_addons from pretix.base.templatetags.money import money_filter from pretix.base.templatetags.phone_format import phone_format from pretix.base.templatetags.rich_text import rich_text_snippet from pretix.base.views.tasks import AsyncAction +from pretix.celery_app import app from pretix.multidomain.urlreverse import eventreverse from pretix.presale.forms.checkout import ( ContactForm, InvoiceAddressForm, InvoiceNameForm, MembershipForm, @@ -802,7 +807,9 @@ class QuestionsStep(QuestionsViewMixin, CartMixin, TemplateFlowStep): @cached_property def invoice_form(self): wd = self.cart_session.get('widget_data', {}) - if not self.invoice_address.pk: + if self.invoice_address.pk: + wd_initial = {} + elif wd: wd_initial = { 'name_parts': { k[21:].replace('-', '_'): v @@ -817,7 +824,9 @@ class QuestionsStep(QuestionsViewMixin, CartMixin, TemplateFlowStep): 'country': wd.get('invoice-address-country', ''), } else: - wd_initial = {} + wd_initial = { + 'is_business': self._get_is_business_heuristic(), + } initial = dict(wd_initial) if self.cart_customer: @@ -1133,6 +1142,31 @@ class QuestionsStep(QuestionsViewMixin, CartMixin, TemplateFlowStep): ctx['profiles_data'] = profiles_list return ctx + def _get_is_business_heuristic(self): + key = 'checkout_heuristic_is_business:' + str(self.event.pk) + cached_result = caches['default'].get(key) + if cached_result is None: + if caches['default'].add(key, False, timeout=10): # return False while query is running + QuestionsStep._update_is_business_heuristic.apply_async(args=(self.event.pk,)) + return False + else: + return cached_result + + @staticmethod + @app.task(base=EventTask) + def _update_is_business_heuristic(event): + result = InvoiceAddress.objects.filter(order__event=event).aggregate( + total=Count('*'), business=Sum(Cast('is_business', output_field=models.IntegerField()))) + if result['total'] < 100: + result = InvoiceAddress.objects.filter(order__event__organizer=event.organizer).aggregate( + total=Count('*'), business=Sum(Cast('is_business', output_field=models.IntegerField()))) + if result['business'] and result['total']: + is_business = result['business'] / result['total'] >= 0.6 + else: + is_business = False + key = 'checkout_heuristic_is_business:' + str(event.pk) + caches['default'].set(key, is_business, timeout=12 * 3600) # 12 hours + class PaymentStep(CartMixin, TemplateFlowStep): priority = 200