-
Notifications
You must be signed in to change notification settings - Fork 152
Fix: Immediately trigger waiting list notifications #1275
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
base: enext
Are you sure you want to change the base?
Fix: Immediately trigger waiting list notifications #1275
Conversation
Currently translated at 0.1% (3 of 4121 strings) Translation: eventyay/Eventyay Tickets Translate-URL: https://hosted.weblate.org/projects/open-event/eventyay-tickets/zh_Hant/
Currently translated at 0.2% (9 of 4121 strings) Translation: eventyay/Eventyay Tickets Translate-URL: https://hosted.weblate.org/projects/open-event/eventyay-tickets/zh_Hant/
Currently translated at 0.4% (18 of 4121 strings) Translation: eventyay/Eventyay Tickets Translate-URL: https://hosted.weblate.org/projects/open-event/eventyay-tickets/pl_INFORMAL/
Currently translated at 2.9% (123 of 4121 strings) Translation: eventyay/eventyay Translate-URL: https://hosted.weblate.org/projects/eventyay/eventyay/zh_Hant/
Currently translated at 2.9% (123 of 4121 strings) Translation: eventyay/eventyay Translate-URL: https://hosted.weblate.org/projects/eventyay/eventyay/zh_Hant/
The issue was caused by unconditional access to test_form.cleaned_data without checking if the form validation succeeded. When test_form.is_valid() returned False, accessing cleaned_data could raise AttributeError or return incomplete data, causing a 500 error. Solution: Added conditional check to only access cleaned_data when form is valid, otherwise use empty dict for initial values. This ensures the export page loads properly even when no valid GET parameters are provided. Changes: - Modified ExportMixin.exporters property in control/views/orders.py - Added validation check before accessing test_form.cleaned_data - Fallback to empty dict when form is invalid
Applied the same fix from issueto the organizer-level export
functionality. The ExportMixin in organizer.py had the identical issue
where test_form.cleaned_data was accessed without checking if the form
validation succeeded first.
This prevents potential HTTP 500 errors when accessing:
- /control/organizer/{organizer}/export/
Changes:
- Modified ExportMixin.exporters in control/views/organizer.py
- Added validation check before accessing test_form.cleaned_data
- Fallback to empty dict when form is invalid
- Use f-strings instead of string concatenation for better readability - Rename 'id' variable to 'identifier' to avoid shadowing builtin - Apply improvements to both orders.py and organizer.py Addresses Sourcery suggestions...
- Add JSON_FIELD_AVAILABLE setting based on database backend (postgresql = True) - Fix checkinlists exporter using old Event.items instead of Event.products - Resolves AttributeError when accessing export functionality
- Added signal receivers for order_canceled and order_changed events - Trigger waiting list assignment when orders are canceled - Trigger waiting list when quota size is increased - Trigger waiting list when quota is manually reopened - Fixes field name from 'item' to 'product' for compatibility This change ensures waiting list members receive voucher notifications immediately instead of waiting up to 30 minutes for the periodic task. Fixes fossasia#1253
Reviewer's GuideThis PR implements immediate triggers for waiting list assignments by adding signal receivers for order cancellations and modifications, integrating quota reopen hooks in product views, detecting quota size increases in form handling, and refactoring the waiting list processor to use the updated “product” field. Sequence diagram for immediate waiting list trigger on order cancellationsequenceDiagram
actor User
participant "Order Service"
participant "Signal Receiver (on_order_canceled)"
participant "Waiting List Processor"
User->>"Order Service": Cancel order
"Order Service"->>"Signal Receiver (on_order_canceled)": Emit order_canceled signal
"Signal Receiver (on_order_canceled)"->>"Waiting List Processor": assign_automatically.apply_async(event, subevent)
"Waiting List Processor"-->>User: Send waiting list notification email
Sequence diagram for immediate waiting list trigger on quota increase or reopensequenceDiagram
actor Organizer
participant "Quota Management View"
participant "Waiting List Processor"
Organizer->>"Quota Management View": Increase quota or reopen quota
"Quota Management View"->>"Waiting List Processor": assign_automatically.apply_async(event, user, subevent)
"Waiting List Processor"-->>Organizer: Waiting list assignment processed
Class diagram for WaitingListEntry field refactor (item → product)classDiagram
class WaitingListEntry {
+product
+variation
+subevent
+voucher
+priority
+created
}
class Product
class Variation
class Subevent
class Voucher
WaitingListEntry --> Product : product
WaitingListEntry --> Variation : variation
WaitingListEntry --> Subevent : subevent
WaitingListEntry --> Voucher : voucher
File-Level Changes
Assessment against linked issues
Possibly linked issues
Tips and commandsInteracting with Sourcery
Customizing Your ExperienceAccess your dashboard to:
Getting Help
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hey there - I've reviewed your changes - here's some feedback:
- The on_order_canceled and on_order_changed receivers share almost identical logic; consider extracting the common checks and subevent handling into a shared helper to reduce duplication.
- The waiting list trigger block in control/views/product.py is duplicated in both quota reopening branches; extract it into a single utility or decorator to keep the view code DRY.
- Relying on the view's form_valid to catch quota size increases may miss other update paths—consider moving the quota change detection into a model signal or service layer to centralize the logic.
Prompt for AI Agents
Please address the comments from this code review:
## Overall Comments
- The on_order_canceled and on_order_changed receivers share almost identical logic; consider extracting the common checks and subevent handling into a shared helper to reduce duplication.
- The waiting list trigger block in control/views/product.py is duplicated in both quota reopening branches; extract it into a single utility or decorator to keep the view code DRY.
- Relying on the view's form_valid to catch quota size increases may miss other update paths—consider moving the quota change detection into a model signal or service layer to centralize the logic.
## Individual Comments
### Comment 1
<location> `app/eventyay/base/services/waitinglist.py:135-136` </location>
<code_context>
+ assign_automatically.apply_async(args=(event.pk, None, subevent_id))
+
+
+@receiver(signal=order_changed, dispatch_uid='waitinglist_order_changed')
+def on_order_changed(sender, order, **kwargs):
+ """
+ When an order is modified (e.g., positions canceled), immediately trigger
</code_context>
<issue_to_address>
**suggestion:** The logic for subevent assignment is duplicated between order_canceled and order_changed.
Refactor the shared subevent extraction and assignment logic into a helper function to avoid duplication and enhance maintainability.
Suggested implementation:
```python
def trigger_waitinglist_assignment(event, subevents):
"""
Helper to trigger waiting list assignment for main event or subevents.
"""
if not subevents or not event.has_subevents:
assign_automatically.apply_async(args=(event.pk,))
else:
for subevent_id in subevents:
assign_automatically.apply_async(args=(event.pk, None, subevent_id))
```
```python
trigger_waitinglist_assignment(event, subevents)
```
You will need to update the corresponding logic in the `on_order_changed` function to use the new `trigger_waitinglist_assignment` helper.
If the subevent extraction logic is also duplicated in `on_order_changed`, refactor it to use the same approach as the other location and pass the subevents to the helper.
</issue_to_address>
### Comment 2
<location> `app/eventyay/base/services/waitinglist.py:121` </location>
<code_context>
@receiver(signal=order_canceled, dispatch_uid='waitinglist_order_canceled')
def on_order_canceled(sender, order, **kwargs):
"""
When an order is canceled, immediately trigger waiting list assignment
if automatic assignment is enabled for the event.
"""
event = sender
# Check if waiting list auto-assignment is enabled
if not event.settings.get('waiting_list_enabled', as_type=bool):
return
if not event.settings.get('waiting_list_auto', as_type=bool):
return
# Check if event is still selling tickets
if not (event.presale_is_running or event.has_subevents):
return
# Get unique subevents from canceled order positions
subevents = set()
for position in order.positions.all():
if position.subevent:
subevents.add(position.subevent.pk)
# Trigger assignment for the main event
if not subevents or not event.has_subevents:
assign_automatically.apply_async(args=(event.pk,))
else:
# Trigger assignment for each affected subevent
for subevent_id in subevents:
assign_automatically.apply_async(args=(event.pk, None, subevent_id))
</code_context>
<issue_to_address>
**issue (code-quality):** Convert for loop into set comprehension ([`set-comprehension`](https://docs.sourcery.ai/Reference/Default-Rules/refactorings/set-comprehension/))
</issue_to_address>
### Comment 3
<location> `app/eventyay/base/services/waitinglist.py:155` </location>
<code_context>
@receiver(signal=order_changed, dispatch_uid='waitinglist_order_changed')
def on_order_changed(sender, order, **kwargs):
"""
When an order is modified (e.g., positions canceled), immediately trigger
waiting list assignment if automatic assignment is enabled for the event.
"""
event = sender
# Check if waiting list auto-assignment is enabled
if not event.settings.get('waiting_list_enabled', as_type=bool):
return
if not event.settings.get('waiting_list_auto', as_type=bool):
return
# Check if event is still selling tickets
if not (event.presale_is_running or event.has_subevents):
return
# Get unique subevents from order positions
subevents = set()
for position in order.positions.all():
if position.subevent:
subevents.add(position.subevent.pk)
# Trigger assignment for the main event
if not subevents or not event.has_subevents:
assign_automatically.apply_async(args=(event.pk,))
else:
# Trigger assignment for each affected subevent
for subevent_id in subevents:
assign_automatically.apply_async(args=(event.pk, None, subevent_id))
</code_context>
<issue_to_address>
**issue (code-quality):** Convert for loop into set comprehension ([`set-comprehension`](https://docs.sourcery.ai/Reference/Default-Rules/refactorings/set-comprehension/))
</issue_to_address>
### Comment 4
<location> `app/eventyay/control/views/product.py:1047` </location>
<code_context>
def post(self, request, *args, **kwargs):
if not request.user.has_event_permission(request.organizer, request.event, 'can_change_items', request):
raise PermissionDenied()
quota = self.get_object()
if 'reopen' in request.POST:
quota.closed = False
quota.save(update_fields=['closed'])
quota.log_action('pretix.event.quota.opened', user=request.user)
messages.success(request, _('The quota has been re-opened.'))
# Trigger waiting list assignment when quota is reopened
event = request.event
if event.settings.get('waiting_list_enabled', as_type=bool) and \
event.settings.get('waiting_list_auto', as_type=bool) and \
(event.presale_is_running or event.has_subevents):
from eventyay.base.services.waitinglist import assign_automatically
if quota.subevent:
assign_automatically.apply_async(args=(event.pk, request.user.pk, quota.subevent.pk))
else:
assign_automatically.apply_async(args=(event.pk, request.user.pk))
if 'disable' in request.POST:
quota.closed = False
quota.close_when_sold_out = False
quota.save(update_fields=['closed', 'close_when_sold_out'])
quota.log_action('pretix.event.quota.opened', user=request.user)
quota.log_action(
'pretix.event.quota.changed',
user=self.request.user,
data={'close_when_sold_out': False},
)
messages.success(request, _('The quota has been re-opened and will not close again.'))
# Trigger waiting list assignment when quota is reopened
event = request.event
if event.settings.get('waiting_list_enabled', as_type=bool) and \
event.settings.get('waiting_list_auto', as_type=bool) and \
(event.presale_is_running or event.has_subevents):
from eventyay.base.services.waitinglist import assign_automatically
if quota.subevent:
assign_automatically.apply_async(args=(event.pk, request.user.pk, quota.subevent.pk))
else:
assign_automatically.apply_async(args=(event.pk, request.user.pk))
return redirect(
reverse(
'control:event.products.quotas.show',
kwargs={
'organizer': self.request.event.organizer.slug,
'event': self.request.event.slug,
'quota': quota.pk,
},
)
)
</code_context>
<issue_to_address>
**issue (code-quality):** We've found these issues:
- Extract duplicate code into method ([`extract-duplicate-method`](https://docs.sourcery.ai/Reference/Default-Rules/refactorings/extract-duplicate-method/))
- Extract code out into method ([`extract-method`](https://docs.sourcery.ai/Reference/Default-Rules/refactorings/extract-method/))
</issue_to_address>Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull Request Overview
This PR adds immediate triggers for waiting list processing to improve responsiveness when capacity becomes available. Previously, waiting list assignments were only processed periodically, but now they are triggered instantly in three key scenarios: order cancellations, order position modifications, and quota increases/reopening.
- Signal receivers added to trigger waiting list processing when orders are canceled or modified
- Quota reopening and size increases now immediately trigger waiting list assignment
- Field references updated from
itemtoproductfor schema compatibility
Reviewed Changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 2 comments.
| File | Description |
|---|---|
| app/eventyay/base/services/waitinglist.py | Adds signal receivers for order_canceled and order_changed events to trigger immediate waiting list assignment; updates field references from item to product |
| app/eventyay/control/views/product.py | Adds waiting list triggers when quotas are reopened or their size is increased in QuotaView.post() and QuotaUpdate.form_valid() |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
- Changed from if/else validation check to getattr() to preserve partial cleaned_data - This allows useful defaults even when form is partially invalid - Reverted unnecessary 'id' to 'identifier' rename in organizer.py - Renamed 'items' field to 'products' in checkinlists exporter for consistency - Updated form_data['items'] to form_data['products'] reference
Currently translated at 24.1% (995 of 4121 strings) Translation: eventyay/eventyay Translate-URL: https://hosted.weblate.org/projects/eventyay/eventyay/lv/
Currently translated at 36.1% (1989 of 5509 strings) Translation: eventyay/eventyay Translate-URL: https://hosted.weblate.org/projects/eventyay/eventyay/ca/
Currently translated at 17.6% (729 of 4121 strings) Translation: eventyay/eventyay Translate-URL: https://hosted.weblate.org/projects/eventyay/eventyay/fi/
Currently translated at 34.2% (1890 of 5523 strings) Translation: eventyay/eventyay Translate-URL: https://hosted.weblate.org/projects/eventyay/eventyay/pt_BR/
Currently translated at 0.4% (18 of 4121 strings) Translation: eventyay/eventyay Translate-URL: https://hosted.weblate.org/projects/eventyay/eventyay/pl_INFORMAL/
Currently translated at 92.5% (3815 of 4121 strings) Translation: eventyay/eventyay Translate-URL: https://hosted.weblate.org/projects/eventyay/eventyay/de_INFORMAL/
Currently translated at 28.0% (1531 of 5465 strings) Translation: eventyay/eventyay Translate-URL: https://hosted.weblate.org/projects/eventyay/eventyay/fa/
Currently translated at 35.1% (1934 of 5506 strings) Translation: eventyay/eventyay Translate-URL: https://hosted.weblate.org/projects/eventyay/eventyay/it/
Currently translated at 70.1% (3865 of 5508 strings) Translation: eventyay/eventyay Translate-URL: https://hosted.weblate.org/projects/eventyay/eventyay/ar/
Currently translated at 20.8% (1150 of 5509 strings) Translation: eventyay/eventyay Translate-URL: https://hosted.weblate.org/projects/eventyay/eventyay/ru/
Currently translated at 0.0% (0 of 4121 strings) Translation: eventyay/eventyay Translate-URL: https://hosted.weblate.org/projects/eventyay/eventyay/ms/
Currently translated at 2.4% (99 of 4121 strings) Translation: eventyay/eventyay Translate-URL: https://hosted.weblate.org/projects/eventyay/eventyay/hu/
Currently translated at 0.4% (18 of 4121 strings) Translation: eventyay/eventyay Translate-URL: https://hosted.weblate.org/projects/eventyay/eventyay/si/
Currently translated at 100.0% (1568 of 1568 strings) Translation: eventyay/eventyay Translate-URL: https://hosted.weblate.org/projects/eventyay/eventyay/de_FORMAL/
i18n(translations): update localized strings from Weblate
…speaker-acceptance-link-oof Fix Speaker Acceptance Confirmation Link Returning 500 Error
* fix(translations): Add missing languages * add(translation): Change language selection to drop down from check-box * fix(translation): Add changes suggested by ai comments * fix(translation): Updated Ukrainian to use the standard Django code `uk` --------- Co-authored-by: Mario Behling <mb@mariobehling.de>
…4.* (fossasia#1401) Updates the requirements on [beautifulsoup4](https://www.crummy.com/software/BeautifulSoup/bs4/) to permit the latest version. --- updated-dependencies: - dependency-name: beautifulsoup4 dependency-version: 4.14.3 dependency-type: direct:production ... Signed-off-by: Mario Behling <mb@mariobehling.de>
…sia#1400) Updates the requirements on [celery](https://github.com/celery/celery) to permit the latest version. - [Release notes](https://github.com/celery/celery/releases) - [Changelog](https://github.com/celery/celery/blob/main/Changelog.rst) - [Commits](celery/celery@v5.4.0rc1...v5.6.0) --- updated-dependencies: - dependency-name: celery dependency-version: 5.6.0 dependency-type: direct:production ... Signed-off-by: Mario Behling <mb@mariobehling.de>
…rder (fossasia#1375) Co-authored-by: Mario Behling <mb@mariobehling.de>
Fixes #1253
(Fix)Immediately trigger waiting list notifications
This PR adds immediate triggers for waiting list processing in three scenarios:
1. Order Cancellations
When an order is canceled, immediately trigger waiting list assignment via signal receiver.
Files Changed:
app/eventyay/base/services/waitinglist.py- Addedon_order_canceled()receiver2. Order Position Cancellations
When individual positions are canceled (order modified), immediately trigger waiting list.
Files Changed:
app/eventyay/base/services/waitinglist.py- Addedon_order_changed()receiver3. Quota Increases
When organizers increase quota size or reopen closed quotas, immediately trigger waiting list.
Technical Details
Signal Receivers
Quota Increase Detection
Fixed field name from
wle.itemtowle.productthroughoutwaitinglist.pyfor compatibility with current model schema.Email Generated
Summary by Sourcery
Implement immediate triggering of automated waiting list assignments on order cancellations/changes and quota updates, and update model references for consistency
New Features:
Enhancements: