Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Automatically Configure Video When Video is created by an Event #432

Merged
merged 13 commits into from
Nov 23, 2024
126 changes: 122 additions & 4 deletions src/pretix/eventyay_common/tasks.py
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -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,
Expand Down Expand Up @@ -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):
Expand Down
114 changes: 62 additions & 52 deletions src/pretix/eventyay_common/views/event.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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,
)
Expand Down Expand Up @@ -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(
Expand All @@ -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):
Expand Down
Loading