Skip to content

Commit

Permalink
Eventyay Common: Create a dashboard and enhance settings page (#498)
Browse files Browse the repository at this point in the history
* Add settings into basic box

* Change bootstrap model, add dashboard settings url property for event

* Add extra space for modal

* Refactor code

* Remove 'Settings for' in label

* Add more type hint

* Add base class for celery task, add button enable disable component, refactor code

* Add default empty string for get

* eventyay-common: enhance status button in settings page

* Revert "Add default empty string for get"

This reverts commit 8ef4ec9.

* Add default empty str

* Change modal style, correct spelling

* Fix urljoin

* Fix urljoin

* Increase padding

---------

Co-authored-by: nttduong <[email protected]>
  • Loading branch information
HungNgien and duong-dsg authored Jan 18, 2025
1 parent 27aa5ed commit 876c402
Show file tree
Hide file tree
Showing 15 changed files with 1,092 additions and 275 deletions.
21 changes: 15 additions & 6 deletions src/pretix/base/models/event.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@
from ..settings import settings_hierarkey
from .organizer import Organizer, OrganizerBillingModel, Team

TALK_HOSTNAME = settings.TALK_HOSTNAME


class EventMixin:

Expand Down Expand Up @@ -1134,20 +1136,27 @@ def has_paid_things(self):

@property
def talk_schedule_url(self):
talk_host = settings.TALK_HOSTNAME
url = urljoin(talk_host, f"{self.slug}/schedule")
url = urljoin(TALK_HOSTNAME, f"{self.slug}/schedule")
return url

@property
def talk_session_url(self):
talk_host = settings.TALK_HOSTNAME
url = urljoin(talk_host, f"{self.slug}/talk")
url = urljoin(TALK_HOSTNAME, f"{self.slug}/talk")
return url

@property
def talk_speaker_url(self):
talk_host = settings.TALK_HOSTNAME
url = urljoin(talk_host, f"{self.slug}/speaker")
url = urljoin(TALK_HOSTNAME, f"{self.slug}/speaker")
return url

@property
def talk_dashboard_url(self):
url = urljoin(TALK_HOSTNAME, f"orga/event/{self.slug}")
return url

@property
def talk_settings_url(self):
url = urljoin(TALK_HOSTNAME, f"orga/event/{self.slug}/settings")
return url

@cached_property
Expand Down
2 changes: 1 addition & 1 deletion src/pretix/control/context.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
get_organizer_navigation,
)

from ..eventyay_common.views.event import EventCreatedFor
from ..eventyay_common.utils import EventCreatedFor
from ..helpers.i18n import (
get_javascript_format, get_javascript_output_format, get_moment_locale,
)
Expand Down
8 changes: 0 additions & 8 deletions src/pretix/control/forms/event.py
Original file line number Diff line number Diff line change
Expand Up @@ -369,10 +369,6 @@ class Meta:


class EventUpdateForm(I18nModelForm):
is_video_creation = forms.BooleanField(
required=False,
)

def __init__(self, *args, **kwargs):
self.change_slug = kwargs.pop("change_slug", False)
self.domain = kwargs.pop("domain", False)
Expand Down Expand Up @@ -411,9 +407,6 @@ def __init__(self, *args, **kwargs):
),
widget=forms.CheckboxSelectMultiple,
)
self.is_video_creation = self.initial.get("is_video_creation")
if self.is_video_creation:
self.fields["is_video_creation"].disabled = True

def clean_domain(self):
d = self.cleaned_data["domain"]
Expand Down Expand Up @@ -484,7 +477,6 @@ class Meta:
"geo_lat",
"geo_lon",
"sales_channels",
"is_video_creation",
]
field_classes = {
"date_from": SplitDateTimeField,
Expand Down
228 changes: 228 additions & 0 deletions src/pretix/eventyay_common/base_tasks.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,228 @@
import logging
from importlib import import_module
from importlib.util import find_spec
from typing import Any

from celery import Task
from django_scopes import scopes_disabled
from pretix_venueless.views import VenuelessSettingsForm

from ..base.models import Event
from .utils import EventCreatedFor

logger = logging.getLogger(__name__)


class CreateWorldTask(Task):
"""
A base class for handling video platform creation tasks for events.
This class is responsible for:
- Setting up video plugins for events
- Managing plugin attachments
- Configuring video settings and JWT authentication
- Handling post-task success operations
Raises:
ValueError: When event doesn't exist or validation fails
ConfigurationError: When plugin setup fails
"""

def add_plugin(self, event: Event, plugin_name: str) -> str:
"""
Add a plugin to an event's plugin list.
Args:
event: The event instance to add the plugin to
plugin_name: Name of the plugin to add
Returns:
str: Comma-separated string of all plugins including the new one
"""
if not event.plugins:
return plugin_name
plugins = set(event.plugins.split(","))
plugins.add(plugin_name)
return ",".join(plugins)

def attach_plugin_to_event(self, plugin_name: str, event_slug: str) -> None:
"""
Attach a plugin to an event by updating its plugins list.
Args:
plugin_name: Name of the plugin to attach
event_slug: Unique slug identifier of the event
Raises:
ValueError: If the event does not exist
"""
try:
with scopes_disabled():
event = Event.objects.get(slug=event_slug)
event.plugins = self.add_plugin(event, plugin_name)
event.save()
except Event.DoesNotExist:
logger.error("Event does not exist: %s", event_slug)
raise ValueError(f"Event with slug '{event_slug}' does not exist")

def save_video_settings_information(
self, event_id: str, video_settings: dict
) -> None:
"""
Save video configuration settings for an event.
Args:
event_id: The event identifier
video_settings: Dictionary containing video configuration parameters
Raises:
ValueError: If event doesn't exist or settings validation fails
"""
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:
errors = video_config_form.errors
logger.error(
"Video integration configuration failed - Validation errors: %s",
errors,
)
raise ValueError(
f"Failed to validate video integration settings: {errors}"
)
except Event.DoesNotExist:
logger.error("Event does not exist: %s", event_id)
raise ValueError(f"Event with ID '{event_id}' does not exist")

def check_installed_plugin(self, plugin_name: str) -> bool:
"""
Check if a plugin is installed.
Args:
plugin_name: Name of the plugin to check
Returns:
Boolean: True if installed, False otherwise
"""
try:
if find_spec(plugin_name) is not None:
import_module(plugin_name)
return True
return False
except ImportError:
logger.warning("Failed to import plugin: %s", plugin_name)
return False

def extract_jwt_config(self, world_data: dict) -> dict:
"""
Extract JWT configuration from world data.
Args:
world_data: Dictionary containing world configuration data
Returns:
dict: JWT configuration with secret, issuer, and audience
"""
try:
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", ""),
}
except (KeyError, IndexError) as e:
logger.warning("Failed to extract JWT config: %s", e)
return {"secret": "", "issuer": "", "audience": ""}

def setup_video_plugin(self, world_data: dict) -> None:
"""
Setup and configure the video plugin for an event.
Args:
world_data: Dictionary containing world configuration data
Raises:
ValueError: If plugin is not installed or configuration fails
"""
plugin_name = "pretix_venueless"
if not self.check_installed_plugin(plugin_name):
logger.error(
"Video integration configuration failed - Plugin not installed"
)
raise ValueError(f"Plugin '{plugin_name}' is not installed")

event_id = world_data.get("id")
if not event_id:
raise ValueError("World data missing event ID")

# Setup plugin
self.attach_plugin_to_event(plugin_name, event_id)

# Configure video settings
jwt_config = self.extract_jwt_config(world_data)
video_settings = {
"venueless_url": world_data.get("domain", ""),
"venueless_secret": jwt_config["secret"],
"venueless_issuer": jwt_config["issuer"],
"venueless_audience": jwt_config["audience"],
"venueless_all_items": True,
"venueless_items": [],
"venueless_questions": [],
}

self.save_video_settings_information(event_id, video_settings)

def on_success(self, retval: Any, task_id: str, args: tuple, kwargs: dict) -> Any:
"""
Handle successful task completion by setting up the video plugin.
Enable video plugin for the event
Args:
retval: Task return value containing world data
task_id: ID of the completed task
args: Original task arguments
kwargs: Original task keyword arguments
Returns:
Any: Result from parent class on_success method
"""
if isinstance(retval, dict):
try:
self.setup_video_plugin(retval)
except ValueError as e:
logger.error("Video integration configuration failed: %s", e)
return super().on_success(retval, task_id, args, kwargs)


class SendEventTask(Task):
def on_success(self, retval: Any, task_id: str, args: tuple, kwargs: dict) -> Any:
"""
Handle successful task completion by updating event settings.
Set the event settings to "create_for" both:
indicate that the event is created for both tickets and talk
Args:
retval: Task return value
task_id: ID of the completed task
args: Original task arguments
kwargs: Original task keyword arguments
Returns:
Any: Result from parent class on_success method
"""
event = kwargs.get("event", {}).get("slug", "")
try:
event = Event.objects.get(slug=event)
event.settings.set("create_for", EventCreatedFor.BOTH.value)
event.save()
except Event.DoesNotExist:
logger.error("Event with slug %s does not exist", event)
return super().on_success(retval, task_id, args, kwargs)
Loading

0 comments on commit 876c402

Please sign in to comment.