From a7cce43e7e7b64885468c40665aa6af87fa92b7b Mon Sep 17 00:00:00 2001 From: odkhang Date: Sat, 23 Nov 2024 15:29:09 +0700 Subject: [PATCH] Automatically Configure Video When Video is created by an Event (#432) * auto config create video * Update code * Fix flake8 in pipeline * modify variable name * Update code * Update code * Update code * Update code --- src/pretix/eventyay_common/tasks.py | 126 +++++++++++++++++++++- src/pretix/eventyay_common/views/event.py | 114 +++++++++++--------- 2 files changed, 184 insertions(+), 56 deletions(-) diff --git a/src/pretix/eventyay_common/tasks.py b/src/pretix/eventyay_common/tasks.py index 9037a50a0..7148a202e 100644 --- a/src/pretix/eventyay_common/tasks.py +++ b/src/pretix/eventyay_common/tasks.py @@ -1,7 +1,9 @@ import base64 +import importlib.util import logging from datetime import datetime, timezone as tz from decimal import Decimal +from importlib import import_module import pytz import requests @@ -13,6 +15,7 @@ from django.db import DatabaseError from django.db.models import Q from django_scopes import scopes_disabled +from pretix_venueless.views import VenuelessSettingsForm from pretix.helpers.stripe_utils import ( confirm_payment_intent, process_auto_billing_charge_stripe, @@ -169,20 +172,135 @@ def create_world(self, is_video_creation, event_data): if is_video_creation and has_permission: try: - requests.post( + response = requests.post( "{}/api/v1/create-world/".format(settings.VIDEO_SERVER_HOSTNAME), json=payload, headers=headers, ) + setup_video_plugin(response.json()) except requests.exceptions.ConnectionError as e: logger.error("Connection error: %s", str(e)) - raise self.retry(exc=e) + self.retry(exc=e) except requests.exceptions.Timeout as e: logger.error("Request timed out: %s", str(e)) - raise self.retry(exc=e) + self.retry(exc=e) except requests.exceptions.RequestException as e: logger.error("Request failed: %s", str(e)) - raise self.retry(exc=e) + self.retry(exc=e) + except ValueError as e: + logger.error("Value error: %s", str(e)) + + +def extract_jwt_config(world_data): + """ + Extract the JWT configuration from the world data. + @param world_data: A dictionary containing the world data. + @return: A dictionary containing the JWT configuration. + """ + config = world_data.get('config', {}) + jwt_secrets = config.get('JWT_secrets', []) + jwt_config = jwt_secrets[0] if jwt_secrets else {} + return { + 'secret': jwt_config.get('secret'), + 'issuer': jwt_config.get('issuer'), + 'audience': jwt_config.get('audience') + } + + +def setup_video_plugin(world_data): + """ + Setup the video plugin for the event. + @param world_data: A dictionary containing the world data. + if the plugin is installed, add the plugin to the event and save the video settings information. + """ + jwt_config = extract_jwt_config(world_data) + video_plugin = get_installed_plugin('pretix_venueless') + event_id = world_data.get("id") + if video_plugin: + attach_plugin_to_event('pretix_venueless', event_id) + video_settings = { + 'venueless_url': world_data.get('domain', ""), + 'venueless_secret': jwt_config.get('secret', ""), + 'venueless_issuer': jwt_config.get('issuer', ""), + 'venueless_audience': jwt_config.get('audience', ""), + 'venueless_all_items': True, + 'venueless_items': [], + 'venueless_questions': [], + } + save_video_settings_information(event_id, video_settings) + else: + logger.error("Video integration configuration failed - Plugin not installed") + raise ValueError("Failed to configure video integration") + + +def save_video_settings_information(event_id, video_settings): + """ + Save the video settings information to the event. + @param event_id: A string representing the unique identifier for the event. + @param video_settings: A dictionary containing the video settings information. + @return: The video configuration form. + """ + try: + with scopes_disabled(): + event_instance = Event.objects.get(slug=event_id) + video_config_form = VenuelessSettingsForm( + data=video_settings, + obj=event_instance + ) + if video_config_form.is_valid(): + video_config_form.save() + else: + logger.error("Video integration configuration failed - Validation errors: %s", video_config_form.errors) + raise ValueError("Failed to validate video integration settings") + return video_config_form + except Event.DoesNotExist: + logger.error("Event does not exist: %s", event_id) + raise ValueError("Event does not exist") + + +def get_installed_plugin(plugin_name): + """ + Check if a plugin is installed. + @param plugin_name: A string representing the name of the plugin to check. + @return: The installed plugin if it exists, otherwise None. + """ + if importlib.util.find_spec(plugin_name) is not None: + installed_plugin = import_module(plugin_name) + else: + installed_plugin = None + return installed_plugin + + +def attach_plugin_to_event(plugin_name, event_slug): + """ + Attach a plugin to an event. + @param plugin_name: A string representing the name of the plugin to add. + @param event_slug: A string representing the slug of the event to which the plugin should be added. + @return: The event instance with the added plugin. + """ + try: + with scopes_disabled(): + event = Event.objects.get(slug=event_slug) + event.plugins = add_plugin(event, plugin_name) + event.save() + return event + except Event.DoesNotExist: + logger.error("Event does not exist: %s", event_slug) + raise ValueError("Event does not exist") + + +def add_plugin(event, plugin_name): + """ + Add a plugin + @param event: The event instance to which the plugin should be added. + @param plugin_name: A string representing the name of the plugin to add. + @return: The updated list of plugins for the event. + """ + if not event.plugins: + return plugin_name + plugins = set(event.plugins.split(',')) + plugins.add(plugin_name) + return ','.join(plugins) def get_header_token(user_id): diff --git a/src/pretix/eventyay_common/views/event.py b/src/pretix/eventyay_common/views/event.py index b0f234cbe..aed250410 100644 --- a/src/pretix/eventyay_common/views/event.py +++ b/src/pretix/eventyay_common/views/event.py @@ -9,6 +9,7 @@ from django.db import transaction from django.db.models import F, Max, Min, Prefetch from django.db.models.functions import Coalesce, Greatest +from django.http import HttpResponseRedirect from django.shortcuts import redirect from django.urls import reverse from django.utils.functional import cached_property @@ -31,7 +32,9 @@ from pretix.control.views.event import DecoupleMixin, EventSettingsViewMixin from pretix.control.views.item import MetaDataEditorMixin from pretix.eventyay_common.forms.event import EventCommonSettingsForm -from pretix.eventyay_common.tasks import create_world, send_event_webhook +from pretix.eventyay_common.tasks import ( + add_plugin, create_world, send_event_webhook, +) from pretix.eventyay_common.utils import ( check_create_permission, encode_email, generate_token, ) @@ -333,39 +336,43 @@ def get_context_data(self, *args, **kwargs) -> dict: return context def handle_video_creation(self, form): - if check_create_permission(self.request): - form.instance.is_video_creation = form.cleaned_data.get("is_video_creation") - elif not check_create_permission(self.request) and form.cleaned_data.get( - "is_video_creation" - ): - form.instance.is_video_creation = True - else: - form.instance.is_video_creation = False - - event_data = dict( - id=form.cleaned_data.get("slug"), - title=form.cleaned_data.get("name").data, - timezone=self.sform.cleaned_data.get("timezone"), - locale=self.sform.cleaned_data.get("locale"), - has_permission=check_create_permission(self.request), - token=generate_token(self.request), - ) - - create_world.delay( - is_video_creation=form.cleaned_data.get("is_video_creation"), event_data=event_data - ) + has_permission = check_create_permission(self.request) + is_video_creation = form.cleaned_data.get("is_video_creation", False) + + if is_video_creation and not has_permission: + messages.error(self.request, _("You do not have permission to create videos.")) + return None + + form.instance.is_video_creation = is_video_creation and has_permission + + if form.instance.is_video_creation: + form.event.plugins = add_plugin(form.event, "pretix_venueless") + event_data = { + "id": form.cleaned_data.get("slug"), + "title": form.cleaned_data.get("name").data, + "timezone": self.sform.cleaned_data.get("timezone"), + "locale": self.sform.cleaned_data.get("locale"), + "has_permission": has_permission, + "token": generate_token(self.request) + } + create_world.delay( + is_video_creation=True, + event_data=event_data + ) + messages.success(self.request, _("Your changes have been saved.")) + return form @transaction.atomic def form_valid(self, form): + processed_form = self.handle_video_creation(form) + if processed_form is None: + return HttpResponseRedirect(self.request.path) self._save_decoupled(self.sform) self.sform.save() tickets.invalidate_cache.apply_async(kwargs={"event": self.request.event.pk}) - self.handle_video_creation(form) - - messages.success(self.request, _("Your changes have been saved.")) - return super().form_valid(form) + return super().form_valid(processed_form) def get_success_url(self) -> str: return reverse( @@ -379,39 +386,42 @@ def get_success_url(self) -> str: def get_form_kwargs(self): kwargs = super().get_form_kwargs() if self.request.user.has_active_staff_session(self.request.session.session_key): - kwargs["change_slug"] = True kwargs["domain"] = True return kwargs def post(self, request, *args, **kwargs): form = self.get_form() - form.instance.sales_channels = ["web"] - if form.is_valid() and self.sform.is_valid(): - zone = timezone(self.sform.cleaned_data["timezone"]) - event = form.instance - event.date_from = self.reset_timezone(zone, event.date_from) - event.date_to = self.reset_timezone(zone, event.date_to) - if event.settings.create_for and event.settings.create_for == EventCreatedFor.BOTH.value: - event_dict = { - "organiser_slug": event.organizer.slug, - "name": event.name.data, - "slug": event.slug, - "date_from": str(event.date_from), - "date_to": str(event.date_to), - "timezone": str(event.settings.timezone), - "locale": event.settings.locale, - "locales": event.settings.locales, - } - send_event_webhook.delay( - user_id=self.request.user.id, event=event_dict, action="update" + if form.changed_data or self.sform.changed_data: + form.instance.sales_channels = ["web"] + if form.is_valid() and self.sform.is_valid(): + zone = timezone(self.sform.cleaned_data["timezone"]) + event = form.instance + event.date_from = self.reset_timezone(zone, event.date_from) + event.date_to = self.reset_timezone(zone, event.date_to) + if event.settings.create_for and event.settings.create_for == EventCreatedFor.BOTH.value: + event_dict = { + "organiser_slug": event.organizer.slug, + "name": event.name.data, + "slug": event.slug, + "date_from": str(event.date_from), + "date_to": str(event.date_to), + "timezone": str(event.settings.timezone), + "locale": event.settings.locale, + "locales": event.settings.locales, + } + send_event_webhook.delay( + user_id=self.request.user.id, event=event_dict, action="update" + ) + return self.form_valid(form) + else: + messages.error( + self.request, + _("We could not save your changes. See below for details."), ) - return self.form_valid(form) + return self.form_invalid(form) else: - messages.error( - self.request, - _("We could not save your changes. See below for details."), - ) - return self.form_invalid(form) + messages.warning(self.request, _("You haven't made any changes.")) + return HttpResponseRedirect(self.request.path) @staticmethod def reset_timezone(tz, dt):