Scheduled exports (#3033)

This commit is contained in:
Raphael Michel
2023-01-19 11:46:30 +01:00
committed by GitHub
parent 0bb5af191b
commit 19d1a8de71
36 changed files with 2461 additions and 293 deletions

View File

@@ -0,0 +1,362 @@
#
# This file is part of pretix (Community Edition).
#
# Copyright (C) 2014-2020 Raphael Michel and contributors
# Copyright (C) 2020-2021 rami.io GmbH and contributors
#
# This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General
# Public License as published by the Free Software Foundation in version 3 of the License.
#
# ADDITIONAL TERMS APPLY: Pursuant to Section 7 of the GNU Affero General Public License, additional terms are
# applicable granting you additional permissions and placing additional restrictions on your usage of this software.
# Please refer to the pretix LICENSE file to obtain the full terms applicable to this work. If you did not receive
# this file, see <https://pretix.eu/about/en/license>.
#
# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied
# warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
# details.
#
# You should have received a copy of the GNU Affero General Public License along with this program. If not, see
# <https://www.gnu.org/licenses/>.
#
from datetime import datetime, time, timedelta
import pytest
import pytz
from django.core import mail as djmail
from django.utils.timezone import now
from django_scopes import scope
from freezegun import freeze_time
from pretix.base.models import (
Event, Organizer, ScheduledEventExport, ScheduledOrganizerExport, User,
)
from pretix.base.services.export import run_scheduled_exports
@pytest.fixture(scope='function')
def event():
o = Organizer.objects.create(name='Dummy', slug='dummy')
event = Event.objects.create(
organizer=o, name='Dummy', slug='dummy',
date_from=datetime(2023, 1, 19, 2, 30, 0, tzinfo=pytz.UTC),
plugins='pretix.plugins.banktransfer'
)
with scope(organizer=o):
yield event
@pytest.fixture
def team(event):
return event.organizer.teams.create(all_events=True, can_view_orders=True)
@pytest.fixture
def user(team):
user = User.objects.create_user('dummy@dummy.dummy', 'dummy')
team.members.add(user)
return user
@pytest.mark.django_db
@freeze_time("2023-01-18 03:00:00+01:00")
def test_event_run_sets_new_time(event, user):
s = ScheduledEventExport(event=event, owner=user)
s.schedule_rrule = "DTSTART:20230118T000000\nRRULE:FREQ=DAILY;INTERVAL=1;WKST=MO"
s.schedule_rrule_time = time(2, 30, 0)
s.schedule_next_run = now() - timedelta(minutes=5)
s.save()
run_scheduled_exports(None)
s.refresh_from_db()
assert s.schedule_next_run == event.timezone.localize(datetime(2023, 1, 19, 2, 30, 0))
@pytest.mark.django_db
@freeze_time("2023-01-18 03:00:00+01:00")
def test_event_not_run_when_failed_5_times(event, user):
s = ScheduledEventExport(event=event, owner=user)
s.schedule_rrule = "DTSTART:20230118T000000\nRRULE:FREQ=DAILY;INTERVAL=1;WKST=MO"
s.schedule_rrule_time = time(2, 30, 0)
s.schedule_next_run = event.timezone.localize(datetime(2023, 1, 18, 2, 30, 0))
s.error_counter = 5
s.save()
run_scheduled_exports(None)
s.refresh_from_db()
assert s.schedule_next_run == event.timezone.localize(datetime(2023, 1, 18, 2, 30, 0))
@pytest.mark.django_db
@freeze_time("2023-01-18 03:00:00+01:00")
def test_event_fail_invalid_config(event, user):
djmail.outbox = []
s = ScheduledEventExport(event=event, owner=user)
s.export_identifier = " invalid "
s.schedule_rrule = "DTSTART:20230118T000000\nRRULE:FREQ=DAILY;INTERVAL=1;WKST=MO"
s.schedule_rrule_time = time(2, 30, 0)
s.schedule_next_run = now() - timedelta(minutes=5)
s.error_counter = 0
s.save()
run_scheduled_exports(None)
s.refresh_from_db()
assert s.schedule_next_run > now()
assert s.error_counter == 1
assert len(djmail.outbox) == 1
assert djmail.outbox[0].subject == "Export failed"
assert "Reason: Export type not found." in djmail.outbox[0].body
assert djmail.outbox[0].to == [user.email]
@pytest.mark.django_db
@freeze_time("2023-01-18 03:00:00+01:00")
def test_event_fail_user_inactive(event, user):
djmail.outbox = []
s = ScheduledEventExport(event=event, owner=user)
s.export_identifier = "orderlist"
s.schedule_rrule = "DTSTART:20230118T000000\nRRULE:FREQ=DAILY;INTERVAL=1;WKST=MO"
s.schedule_rrule_time = time(2, 30, 0)
s.schedule_next_run = now() - timedelta(minutes=5)
s.error_counter = 0
s.save()
user.is_active = False
user.save()
run_scheduled_exports(None)
s.refresh_from_db()
assert s.schedule_next_run > now()
assert s.error_counter == 1
assert len(djmail.outbox) == 0 # no mails sent to inactive user
@pytest.mark.django_db
@freeze_time("2023-01-18 03:00:00+01:00")
def test_event_fail_user_no_permission(event, user, team):
djmail.outbox = []
s = ScheduledEventExport(event=event, owner=user)
s.export_identifier = "orderlist"
s.schedule_rrule = "DTSTART:20230118T000000\nRRULE:FREQ=DAILY;INTERVAL=1;WKST=MO"
s.schedule_rrule_time = time(2, 30, 0)
s.schedule_next_run = now() - timedelta(minutes=5)
s.error_counter = 0
s.save()
team.can_view_orders = False
team.save()
run_scheduled_exports(None)
s.refresh_from_db()
assert s.schedule_next_run > now()
assert s.error_counter == 1
assert len(djmail.outbox) == 1
assert djmail.outbox[0].subject == "Export failed"
assert "Reason: Permission denied." in djmail.outbox[0].body
assert djmail.outbox[0].to == [user.email]
@pytest.mark.django_db
@freeze_time("2023-01-18 03:00:00+01:00")
def test_event_ok(event, user, team):
djmail.outbox = []
s = ScheduledEventExport(event=event, owner=user)
s.export_identifier = "orderlist"
s.export_form_data = {"_format": "xlsx", "paid_only": False}
s.mail_additional_recipients = "boss@example.org,boss@example.net"
s.mail_additional_recipients_cc = "assistant@example.net"
s.mail_additional_recipients_bcc = "archive@example.net"
s.mail_subject = "Report 1"
s.mail_template = "Here is the report."
s.schedule_rrule = "DTSTART:20230118T000000\nRRULE:FREQ=DAILY;INTERVAL=1;WKST=MO"
s.schedule_rrule_time = time(2, 30, 0)
s.schedule_next_run = now() - timedelta(minutes=5)
s.error_counter = 1
s.save()
run_scheduled_exports(None)
s.refresh_from_db()
assert s.schedule_next_run > now()
assert s.error_counter == 0
assert len(djmail.outbox) == 1
assert djmail.outbox[0].subject == "Report 1"
assert "Here is the report." in djmail.outbox[0].body
assert djmail.outbox[0].to == [user.email, "boss@example.org", "boss@example.net"]
assert djmail.outbox[0].cc == ["assistant@example.net"]
assert djmail.outbox[0].bcc == ["archive@example.net"]
assert len(djmail.outbox[0].attachments) == 1
assert djmail.outbox[0].attachments[0][0] == "dummy_orders.xlsx"
@pytest.mark.django_db
@freeze_time("2023-01-18 03:00:00+01:00")
def test_organizer_run_sets_new_time(event, user):
s = ScheduledOrganizerExport(organizer=event.organizer, owner=user)
s.schedule_rrule = "DTSTART:20230118T000000\nRRULE:FREQ=DAILY;INTERVAL=1;WKST=MO"
s.schedule_rrule_time = time(2, 30, 0)
s.schedule_next_run = now() - timedelta(minutes=5)
s.save()
run_scheduled_exports(None)
s.refresh_from_db()
assert s.schedule_next_run == event.timezone.localize(datetime(2023, 1, 19, 2, 30, 0))
@pytest.mark.django_db
@freeze_time("2023-01-18 03:00:00+01:00")
def test_organizer_not_run_when_failed_5_times(event, user):
s = ScheduledOrganizerExport(organizer=event.organizer, owner=user)
s.schedule_rrule = "DTSTART:20230118T000000\nRRULE:FREQ=DAILY;INTERVAL=1;WKST=MO"
s.schedule_rrule_time = time(2, 30, 0)
s.schedule_next_run = event.timezone.localize(datetime(2023, 1, 18, 2, 30, 0))
s.error_counter = 5
s.save()
run_scheduled_exports(None)
s.refresh_from_db()
assert s.schedule_next_run == event.timezone.localize(datetime(2023, 1, 18, 2, 30, 0))
@pytest.mark.django_db
@freeze_time("2023-01-18 03:00:00+01:00")
def test_organizer_fail_invalid_config(event, user):
djmail.outbox = []
s = ScheduledOrganizerExport(organizer=event.organizer, owner=user)
s.export_identifier = " invalid "
s.schedule_rrule = "DTSTART:20230118T000000\nRRULE:FREQ=DAILY;INTERVAL=1;WKST=MO"
s.schedule_rrule_time = time(2, 30, 0)
s.schedule_next_run = now() - timedelta(minutes=5)
s.error_counter = 0
s.save()
run_scheduled_exports(None)
s.refresh_from_db()
assert s.schedule_next_run > now()
assert s.error_counter == 1
assert len(djmail.outbox) == 1
assert djmail.outbox[0].subject == "Export failed"
assert "Reason: Export type not found." in djmail.outbox[0].body
assert djmail.outbox[0].to == [user.email]
@pytest.mark.django_db
@freeze_time("2023-01-18 03:00:00+01:00")
def test_organizer_fail_user_inactive(event, user):
djmail.outbox = []
s = ScheduledOrganizerExport(organizer=event.organizer, owner=user)
s.export_identifier = "orderlist"
s.schedule_rrule = "DTSTART:20230118T000000\nRRULE:FREQ=DAILY;INTERVAL=1;WKST=MO"
s.schedule_rrule_time = time(2, 30, 0)
s.schedule_next_run = now() - timedelta(minutes=5)
s.error_counter = 0
s.save()
user.is_active = False
user.save()
run_scheduled_exports(None)
s.refresh_from_db()
assert s.schedule_next_run > now()
assert s.error_counter == 1
assert len(djmail.outbox) == 0 # no mails sent to inactive user
@pytest.mark.django_db
@freeze_time("2023-01-18 03:00:00+01:00")
def test_organizer_fail_user_does_not_have_specific_permission(event, user, team):
djmail.outbox = []
s = ScheduledOrganizerExport(organizer=event.organizer, owner=user)
s.export_identifier = "customerlist"
s.schedule_rrule = "DTSTART:20230118T000000\nRRULE:FREQ=DAILY;INTERVAL=1;WKST=MO"
s.schedule_rrule_time = time(2, 30, 0)
s.schedule_next_run = now() - timedelta(minutes=5)
s.error_counter = 0
s.save()
team.can_manage_customers = False
team.save()
run_scheduled_exports(None)
s.refresh_from_db()
assert s.schedule_next_run > now()
assert s.error_counter == 1
assert len(djmail.outbox) == 1
assert djmail.outbox[0].subject == "Export failed"
assert "Reason: Permission denied." in djmail.outbox[0].body
assert djmail.outbox[0].to == [user.email]
@pytest.mark.django_db
@freeze_time("2023-01-18 03:00:00+01:00")
def test_organizer_limited_to_events(event, user, team):
djmail.outbox = []
s = ScheduledOrganizerExport(organizer=event.organizer, owner=user)
s.export_identifier = "eventdata"
s.export_form_data = {"_format": "default", "all_events": True}
s.mail_subject = "Report 1"
s.mail_template = "Here is the report."
s.schedule_rrule = "DTSTART:20230118T000000\nRRULE:FREQ=DAILY;INTERVAL=1;WKST=MO"
s.schedule_rrule_time = time(2, 30, 0)
s.schedule_next_run = now() - timedelta(minutes=5)
s.error_counter = 0
s.save()
event2 = Event.objects.create(
organizer=event.organizer, name='Dummy', slug='dummy2',
date_from=datetime(2023, 1, 19, 2, 30, 0, tzinfo=pytz.UTC),
plugins='pretix.plugins.banktransfer'
)
team.all_events = False
team.save()
team.limit_events.add(event2)
run_scheduled_exports(None)
s.refresh_from_db()
assert s.schedule_next_run > now()
assert s.error_counter == 0
assert len(djmail.outbox) == 1
assert djmail.outbox[0].subject == "Report 1"
assert "Here is the report." in djmail.outbox[0].body
assert djmail.outbox[0].to == [user.email]
assert len(djmail.outbox[0].attachments) == 1
assert djmail.outbox[0].attachments[0][0] == "dummy_events.csv"
assert len(djmail.outbox[0].attachments[0][1].splitlines()) == 2
@pytest.mark.django_db
@freeze_time("2023-01-18 03:00:00+01:00")
def test_organizer_ok(event, user, team):
djmail.outbox = []
s = ScheduledOrganizerExport(organizer=event.organizer, owner=user)
s.export_identifier = "eventdata"
s.export_form_data = {"_format": "default", "all_events": True}
s.mail_additional_recipients = "boss@example.org,boss@example.net"
s.mail_additional_recipients_cc = "assistant@example.net"
s.mail_additional_recipients_bcc = "archive@example.net"
s.mail_subject = "Report 1"
s.mail_template = "Here is the report."
s.schedule_rrule = "DTSTART:20230118T000000\nRRULE:FREQ=DAILY;INTERVAL=1;WKST=MO"
s.schedule_rrule_time = time(2, 30, 0)
s.schedule_next_run = now() - timedelta(minutes=5)
s.error_counter = 1
s.save()
Event.objects.create(
organizer=event.organizer, name='Dummy', slug='dummy2',
date_from=datetime(2023, 1, 19, 2, 30, 0, tzinfo=pytz.UTC),
plugins='pretix.plugins.banktransfer'
)
run_scheduled_exports(None)
s.refresh_from_db()
assert s.schedule_next_run > now()
assert s.error_counter == 0
assert len(djmail.outbox) == 1
assert djmail.outbox[0].subject == "Report 1"
assert "Here is the report." in djmail.outbox[0].body
assert djmail.outbox[0].to == [user.email, "boss@example.org", "boss@example.net"]
assert djmail.outbox[0].cc == ["assistant@example.net"]
assert djmail.outbox[0].bcc == ["archive@example.net"]
assert len(djmail.outbox[0].attachments) == 1
assert djmail.outbox[0].attachments[0][0] == "dummy_events.csv"
assert len(djmail.outbox[0].attachments[0][1].splitlines()) == 3

View File

@@ -48,12 +48,14 @@ from django.core.files.uploadedfile import SimpleUploadedFile
from django.test import TestCase
from django.utils.timezone import now
from django_scopes import scope, scopes_disabled
from freezegun import freeze_time
from pretix.base.i18n import language
from pretix.base.models import (
CachedFile, CartPosition, Checkin, CheckinList, Event, Item, ItemCategory,
ItemVariation, Order, OrderFee, OrderPayment, OrderPosition, OrderRefund,
Organizer, Question, Quota, SeatingPlan, User, Voucher, WaitingListEntry,
Organizer, Question, Quota, ScheduledEventExport, SeatingPlan, User,
Voucher, WaitingListEntry,
)
from pretix.base.models.event import SubEvent
from pretix.base.models.items import (
@@ -2766,3 +2768,44 @@ def test_subevent_date_updates_order_date():
assert order1.last_modified > o1lm
assert order2.last_modified == o2lm
class ScheduledExportTestCase(TestCase):
@scopes_disabled()
def setUp(self):
self.organizer = Organizer.objects.create(name='Dummy', slug='dummy')
self.event = Event.objects.create(
organizer=self.organizer, name='Dummy', slug='dummy',
date_from=now(), date_to=now() - timedelta(hours=1), has_subevents=True
)
self.event.settings.timezone = 'Europe/Berlin'
@classscope(attr='organizer')
def test_compute_next_time(self):
s = ScheduledEventExport(event=self.event)
s.schedule_rrule = "DTSTART:20230118T000000\nRRULE:FREQ=WEEKLY;COUNT=30;INTERVAL=1;WKST=MO;BYDAY=TU,TH"
s.schedule_rrule_time = datetime.time(6, 30, 0)
with freeze_time("2023-01-18 15:08:00+01:00"):
s.compute_next_run()
assert s.schedule_next_run == self.event.timezone.localize(datetime.datetime(2023, 1, 19, 6, 30, 0))
with freeze_time("2024-01-18 15:08:00+01:00"):
s.compute_next_run()
assert s.schedule_next_run is None
@classscope(attr='organizer')
def test_compute_next_time_handle_dst(self):
s = ScheduledEventExport(event=self.event)
s.schedule_rrule = "DTSTART:20230118T000000\nRRULE:FREQ=DAILY;INTERVAL=1;WKST=MO"
s.schedule_rrule_time = datetime.time(2, 30, 0)
with freeze_time("2023-03-25 18:00:00+01:00"):
s.compute_next_run()
assert s.schedule_next_run == self.event.timezone.localize(datetime.datetime(2023, 3, 26, 3, 30, 0))
with freeze_time("2023-03-26 18:00:00+01:00"):
s.compute_next_run()
assert s.schedule_next_run == self.event.timezone.localize(datetime.datetime(2023, 3, 27, 2, 30, 0))
with freeze_time("2023-10-28 18:00:00+01:00"):
s.compute_next_run()
assert s.schedule_next_run == self.event.timezone.localize(datetime.datetime(2023, 10, 29, 2, 30, 0, fold=0))

View File

@@ -0,0 +1,380 @@
#
# This file is part of pretix (Community Edition).
#
# Copyright (C) 2014-2020 Raphael Michel and contributors
# Copyright (C) 2020-2021 rami.io GmbH and contributors
#
# This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General
# Public License as published by the Free Software Foundation in version 3 of the License.
#
# ADDITIONAL TERMS APPLY: Pursuant to Section 7 of the GNU Affero General Public License, additional terms are
# applicable granting you additional permissions and placing additional restrictions on your usage of this software.
# Please refer to the pretix LICENSE file to obtain the full terms applicable to this work. If you did not receive
# this file, see <https://pretix.eu/about/en/license>.
#
# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied
# warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
# details.
#
# You should have received a copy of the GNU Affero General Public License along with this program. If not, see
# <https://www.gnu.org/licenses/>.
#
import datetime
import json
import pytest
from django.utils.timezone import now
from pretix.base.models import (
Event, Item, Organizer, ScheduledEventExport, ScheduledOrganizerExport,
Team, User,
)
@pytest.fixture
def env():
o = Organizer.objects.create(name="Dummy", slug="dummy")
event = Event.objects.create(
organizer=o, name="Dummy", slug="dummy",
date_from=now(), plugins="pretix.plugins.banktransfer,pretix.plugins.stripe,tests.testdummy"
)
event.settings.set("ticketoutput_testdummy__enabled", True)
user = User.objects.create_user("dummy@dummy.dummy", "dummy")
t = Team.objects.create(organizer=o, can_view_orders=True, can_change_orders=True, can_manage_customers=True,
can_change_event_settings=True)
t.members.add(user)
t.limit_events.add(event)
Item.objects.create(
event=event, name="Early-bird ticket", category=None, default_price=23,
admission=True, personalized=True
)
return event, user, t
@pytest.mark.django_db
def test_event_export(client, env):
client.login(email="dummy@dummy.dummy", password="dummy")
response = client.get("/control/event/dummy/dummy/orders/export/?identifier=itemdata")
assert b"Export format" in response.content
response = client.post("/control/event/dummy/dummy/orders/export/do", {
"exporter": "itemdata",
"itemdata-_format": "default",
"ajax": "1"
})
d = json.loads(response.content)
assert d["ready"]
assert d["success"]
response = client.get(d["redirect"])
assert len(b"".join(response.streaming_content).split(b"\n")) == 3
@pytest.mark.django_db
def test_event_export_schedule(client, env):
client.login(email="dummy@dummy.dummy", password="dummy")
response = client.get("/control/event/dummy/dummy/orders/export/?identifier=itemdata")
assert b"Export format" in response.content
assert b"Repetition schedule" not in response.content
# Start editing
response = client.post("/control/event/dummy/dummy/orders/export/?identifier=itemdata", {
"schedule": "yes",
"exporter": "itemdata",
"itemdata-_format": "default",
})
assert b"Export format" in response.content
assert b"Repetition schedule" in response.content
# Create schedule
response = client.post("/control/event/dummy/dummy/orders/export/?identifier=itemdata", {
"schedule": "save",
"exporter": "itemdata",
"itemdata-_format": "default",
"schedule-schedule_rrule_time": "03:30",
"rrule-dtstart": "2023-01-19",
"rrule-interval": "1",
"rrule-freq": "weekly",
"rrule-end": "forever",
"rrule-until": "2022-01-01", # ignored
"rrule-count": "10", # ignored
"rrule-monthly_same": "on", # ignored
"rrule-yearly_same": "on", # ignored
"schedule-mail_additional_recipients": "boss@example.net, friend@example.com",
"schedule-locale": "en",
"schedule-mail_subject": "Product data, my friend!",
"schedule-mail_template": "Mail body"
}, follow=True)
assert b"Your export schedule has been saved. The next export will start around" in response.content
s = env[0].scheduled_exports.get()
assert s.owner == env[1]
assert s.schedule_rrule == "DTSTART:20230119T000000\nRRULE:FREQ=WEEKLY"
assert s.schedule_rrule_time == datetime.time(3, 30, 0)
assert s.schedule_next_run > now()
assert s.export_identifier == "itemdata"
assert s.export_form_data == {"_format": "default"}
assert s.locale == "en"
assert s.mail_additional_recipients == "boss@example.net,friend@example.com"
assert s.mail_subject == "Product data, my friend!"
assert s.mail_template == "Mail body"
# Schedule is in list
response = client.get("/control/event/dummy/dummy/orders/export/")
assert b"Product data, my friend!" in response.content
# Edit schedule
response = client.get(f"/control/event/dummy/dummy/orders/export/?identifier=itemdata&scheduled={s.pk}")
assert b"Mail body" in response.content
# Submit edited schedule
response = client.post(f"/control/event/dummy/dummy/orders/export/?identifier=itemdata&scheduled={s.pk}", {
"schedule": "save",
"exporter": "itemdata",
"itemdata-_format": "xlsx",
"schedule-schedule_rrule_time": "03:30",
"rrule-dtstart": "2023-01-10",
"rrule-interval": "1",
"rrule-freq": "weekly",
"rrule-end": "until",
"rrule-until": "2022-01-01", # ignored
"rrule-count": "1",
"rrule-monthly_same": "on", # ignored
"rrule-yearly_same": "on", # ignored
"schedule-mail_additional_recipients": "boss@example.net, friend@example.com",
"schedule-locale": "en",
"schedule-mail_subject": "Product data, my friend!",
"schedule-mail_template": "Mail body"
}, follow=True)
assert b"Your export schedule has been saved, but no next export is planned" in response.content
s.refresh_from_db()
assert s.schedule_next_run is None
assert s.export_form_data == {"_format": "xlsx"}
# Run
response = client.post(f"/control/event/dummy/dummy/orders/export/{s.pk}/run")
assert response.status_code == 302
# Delete schedule
response = client.get(f"/control/event/dummy/dummy/orders/export/{s.pk}/delete")
assert b"Product data, my friend!" in response.content
client.post(f"/control/event/dummy/dummy/orders/export/{s.pk}/delete")
assert env[0].scheduled_exports.count() == 0
@pytest.mark.django_db
def test_event_limited_permission(client, env):
env[2].can_change_event_settings = False
env[2].save()
user2 = User.objects.create_user("dummy2@dummy.dummy", "dummy")
s1 = ScheduledEventExport(event=env[0], owner=env[1])
s1.schedule_rrule = "DTSTART:20230118T000000\nRRULE:FREQ=DAILY;INTERVAL=1;WKST=MO"
s1.schedule_rrule_time = datetime.time(2, 30, 0)
s1.error_counter = 5
s1.mail_subject = "RULE1"
s1.save()
s2 = ScheduledEventExport(event=env[0], owner=user2)
s2.schedule_rrule = "DTSTART:20230118T000000\nRRULE:FREQ=DAILY;INTERVAL=1;WKST=MO"
s2.schedule_rrule_time = datetime.time(2, 30, 0)
s2.error_counter = 5
s2.mail_subject = "RULE2"
s2.save()
client.login(email="dummy@dummy.dummy", password="dummy")
response = client.get("/control/event/dummy/dummy/orders/export/")
assert b"RULE1" in response.content
assert b"RULE2" not in response.content
response = client.get(f"/control/event/dummy/dummy/orders/export/?identifier=itemdata&scheduled={s1.pk}")
assert response.status_code == 200
response = client.get(f"/control/event/dummy/dummy/orders/export/?identifier=itemdata&scheduled={s2.pk}")
assert response.status_code == 404
response = client.post(f"/control/event/dummy/dummy/orders/export/{s1.pk}/run")
assert response.status_code == 302
response = client.get(f"/control/event/dummy/dummy/orders/export/{s1.pk}/delete")
assert response.status_code == 200
response = client.post(f"/control/event/dummy/dummy/orders/export/{s2.pk}/run")
assert response.status_code == 404
response = client.get(f"/control/event/dummy/dummy/orders/export/{s2.pk}/delete")
assert response.status_code == 404
env[2].can_change_event_settings = True
env[2].save()
response = client.get("/control/event/dummy/dummy/orders/export/")
assert b"RULE1" in response.content
assert b"RULE2" in response.content
response = client.get(f"/control/event/dummy/dummy/orders/export/?identifier=itemdata&scheduled={s2.pk}")
assert response.status_code == 200
response = client.get(f"/control/event/dummy/dummy/orders/export/{s2.pk}/delete")
assert response.status_code == 200
response = client.post(f"/control/event/dummy/dummy/orders/export/{s2.pk}/run")
assert response.status_code == 302
@pytest.mark.django_db
def test_organizer_export(client, env):
client.login(email="dummy@dummy.dummy", password="dummy")
response = client.get("/control/organizer/dummy/export/?identifier=eventdata")
assert b"Export format" in response.content
response = client.post("/control/organizer/dummy/export/do", {
"exporter": "eventdata",
"eventdata-_format": "default",
"eventdata-all_events": "on",
"ajax": "1"
})
d = json.loads(response.content)
assert d["ready"]
assert d["success"]
response = client.get(d["redirect"])
assert len(b"".join(response.streaming_content).split(b"\n")) == 3
@pytest.mark.django_db
def test_organizer_export_schedule(client, env):
client.login(email="dummy@dummy.dummy", password="dummy")
response = client.get("/control/organizer/dummy/export/?identifier=eventdata")
assert b"Export format" in response.content
assert b"Repetition schedule" not in response.content
# Start editing
response = client.post("/control/organizer/dummy/export/?identifier=eventdata", {
"schedule": "yes",
"exporter": "eventdata",
"eventdata-_format": "default",
"eventdata-all_events": "on",
})
assert b"Export format" in response.content
assert b"Repetition schedule" in response.content
# Create schedule
response = client.post("/control/organizer/dummy/export/?identifier=eventdata", {
"schedule": "save",
"exporter": "eventdata",
"eventdata-_format": "default",
"eventdata-all_events": "on",
"schedule-schedule_rrule_time": "03:30",
"schedule-timezone": "Australia/Sydney",
"rrule-dtstart": "2023-01-19",
"rrule-interval": "1",
"rrule-freq": "weekly",
"rrule-end": "forever",
"rrule-until": "2022-01-01", # ignored
"rrule-count": "10", # ignored
"rrule-monthly_same": "on", # ignored
"rrule-yearly_same": "on", # ignored
"schedule-mail_additional_recipients": "boss@example.net, friend@example.com",
"schedule-locale": "en",
"schedule-mail_subject": "Product data, my friend!",
"schedule-mail_template": "Mail body"
}, follow=True)
assert b"Your export schedule has been saved. The next export will start around" in response.content
s = env[0].organizer.scheduled_exports.get()
assert s.owner == env[1]
assert s.schedule_rrule == "DTSTART:20230119T000000\nRRULE:FREQ=WEEKLY"
assert s.schedule_rrule_time == datetime.time(3, 30, 0)
assert s.schedule_next_run > now()
assert s.export_identifier == "eventdata"
assert s.export_form_data == {"_format": "default", "all_events": True, "events": []}
assert s.locale == "en"
assert s.timezone == "Australia/Sydney"
assert s.mail_additional_recipients == "boss@example.net,friend@example.com"
assert s.mail_subject == "Product data, my friend!"
assert s.mail_template == "Mail body"
# Schedule is in list
response = client.get("/control/organizer/dummy/export/")
assert b"Product data, my friend!" in response.content
# Edit schedule
response = client.get(f"/control/organizer/dummy/export/?identifier=eventdata&scheduled={s.pk}")
assert b"Mail body" in response.content
# Submit edited schedule
response = client.post(f"/control/organizer/dummy/export/?identifier=eventdata&scheduled={s.pk}", {
"schedule": "save",
"exporter": "eventdata",
"eventdata-all_events": "on",
"eventdata-_format": "xlsx",
"schedule-schedule_rrule_time": "03:30",
"schedule-timezone": "Australia/Sydney",
"rrule-dtstart": "2023-01-10",
"rrule-interval": "1",
"rrule-freq": "weekly",
"rrule-end": "until",
"rrule-until": "2022-01-01", # ignored
"rrule-count": "1",
"rrule-monthly_same": "on", # ignored
"rrule-yearly_same": "on", # ignored
"schedule-mail_additional_recipients": "boss@example.net, friend@example.com",
"schedule-locale": "en",
"schedule-mail_subject": "Product data, my friend!",
"schedule-mail_template": "Mail body"
}, follow=True)
print(response.content)
assert b"Your export schedule has been saved, but no next export is planned" in response.content
s.refresh_from_db()
assert s.schedule_next_run is None
assert s.export_form_data == {"_format": "xlsx", "all_events": True, "events": []}
# Delete schedule
response = client.post(f"/control/organizer/dummy/export/{s.pk}/run")
assert response.status_code == 302
# Delete schedule
response = client.get(f"/control/organizer/dummy/export/{s.pk}/delete")
assert b"Product data, my friend!" in response.content
client.post(f"/control/organizer/dummy/export/{s.pk}/delete")
assert env[0].organizer.scheduled_exports.count() == 0
@pytest.mark.django_db
def test_organizer_limited_permission(client, env):
env[2].can_change_organizer_settings = False
env[2].save()
user2 = User.objects.create_user("dummy2@dummy.dummy", "dummy")
s1 = ScheduledOrganizerExport(organizer=env[0].organizer, owner=env[1])
s1.schedule_rrule = "DTSTART:20230118T000000\nRRULE:FREQ=DAILY;INTERVAL=1;WKST=MO"
s1.schedule_rrule_time = datetime.time(2, 30, 0)
s1.error_counter = 5
s1.mail_subject = "RULE1"
s1.save()
s2 = ScheduledOrganizerExport(organizer=env[0].organizer, owner=user2)
s2.schedule_rrule = "DTSTART:20230118T000000\nRRULE:FREQ=DAILY;INTERVAL=1;WKST=MO"
s2.schedule_rrule_time = datetime.time(2, 30, 0)
s2.error_counter = 5
s2.mail_subject = "RULE2"
s2.save()
client.login(email="dummy@dummy.dummy", password="dummy")
response = client.get("/control/organizer/dummy/export/")
assert b"RULE1" in response.content
assert b"RULE2" not in response.content
response = client.get(f"/control/organizer/dummy/export/?identifier=eventdata&scheduled={s1.pk}")
assert response.status_code == 200
response = client.get(f"/control/organizer/dummy/export/?identifier=eventdata&scheduled={s2.pk}")
assert response.status_code == 404
response = client.get(f"/control/organizer/dummy/export/{s1.pk}/delete")
assert response.status_code == 200
response = client.post(f"/control/organizer/dummy/export/{s1.pk}/run")
assert response.status_code == 302
response = client.get(f"/control/organizer/dummy/export/{s2.pk}/delete")
assert response.status_code == 404
response = client.post(f"/control/organizer/dummy/export/{s2.pk}/run")
assert response.status_code == 404
env[2].can_change_organizer_settings = True
env[2].save()
response = client.get("/control/organizer/dummy/export/")
assert b"RULE1" in response.content
assert b"RULE2" in response.content
response = client.get(f"/control/organizer/dummy/export/?identifier=eventdata&scheduled={s2.pk}")
assert response.status_code == 200
response = client.get(f"/control/organizer/dummy/export/{s2.pk}/delete")
assert response.status_code == 200
response = client.post(f"/control/organizer/dummy/export/{s2.pk}/run")
assert response.status_code == 302

View File

@@ -352,6 +352,7 @@ event_permission_urls = [
("can_change_event_settings", "subevents/add", 200, HTTP_GET),
("can_view_orders", "orders/overview/", 200, HTTP_GET),
("can_view_orders", "orders/export/", 200, HTTP_GET),
("can_view_orders", "orders/export/do", 302, HTTP_POST),
("can_view_orders", "orders/", 200, HTTP_GET),
("can_view_orders", "orders/FOO/", 200, HTTP_GET),
("can_change_orders", "orders/FOO/extend", 200, HTTP_GET),