introduce max search horizon config

Adds `MAX_SEARCH_HORIZON` environment variable to control the
maximum range of days for search operations.

Splits longer search periods into multiple requests, preventing
potential performance issues or timeouts with large datasets.
This commit is contained in:
Marc Koch 2025-08-19 17:04:24 +02:00
parent 13dc40302c
commit 46e62a94ed
Signed by: marc
GPG Key ID: 12406554CFB028B9
2 changed files with 38 additions and 20 deletions

View File

@ -16,6 +16,7 @@ services:
- SMTP_SERVER=your.smtp.server # adjust this
- SMTP_PORT=587 # adjust this if necessary
- SMTP_SENDER_NAME=Room Booking System # adjust this if you want
- MAX_SEARCH_HORIZON=14 # adjust maximal range of days (longer search periods will get split into multiple requests)
volumes:
app-data:

View File

@ -12,10 +12,12 @@ from pprint import pprint
import caldav
import pytz
from caldav import CalendarObjectResource, Principal, Calendar
from caldav import CalendarObjectResource, Principal, Calendar, DAVObject
from jinja2 import Template
from django.core.exceptions import ObjectDoesNotExist
tz = pytz.timezone(os.getenv("TIME_ZONE", "Europe/Berlin"))
MAX_SEARCH_HORIZON = int(os.getenv("MAX_SEARCH_HORIZON", 14))
class DavEvent:
"""
@ -29,7 +31,7 @@ class DavEvent:
created: datetime
status: str
organizer: str
calendar: Calendar
calendar: DAVObject
obj: CalendarObjectResource
missing_required_fields: list
@ -119,11 +121,10 @@ class DavEvent:
if match:
token = match.group()
print(f"Priority token found in event description: {token}")
try:
token_obj = PriorityEventToken.objects.get(token=token)
# Check if the token object exists in the database
if not token_obj:
print(f"Priority token '{token}' not found in database.")
except ObjectDoesNotExist:
print(f"Priority token could not be found in database: {token}")
return False
# If token is already used, verify signature
@ -263,17 +264,34 @@ def clear(target_calendars: list, is_test: bool=False) -> dict:
print(f"--- Clearing cancelled bookings and overlaps in calendar: {calendar.id}")
horizon = tcal_by_name[calendar.id].auto_clear_overlap_horizon_days
# Split horizon search into multiple requests if horizon is bigger
# than MAX_SEARCH_HORIZON
horizons = []
while horizon > 0:
if horizon >= MAX_SEARCH_HORIZON:
horizons.append(MAX_SEARCH_HORIZON)
else:
horizons.append(horizon)
horizon -= MAX_SEARCH_HORIZON
events_fetched = []
start_delta = 0
end_delta = 0
today = datetime.now(tz=tz).date()
for h in horizons:
end_delta += h
try:
events_fetched = calendar.search(
start=datetime.now(),
end=date.today() + timedelta(days=horizon),
events_fetched.extend(calendar.search(
start=today + timedelta(days=start_delta),
end=today + timedelta(days=end_delta),
event=True,
expand=True,
split_expanded=True,
)
))
except Exception as e:
print(f"--- Failed to fetch events for calendar: {calendar.id}: {e}")
continue
start_delta += h
# Create DavEvent objects from fetched events
events = []
@ -348,7 +366,6 @@ def clear(target_calendars: list, is_test: bool=False) -> dict:
elif not event.is_cancelled:
if not is_test:
event.cancel()
is_cancelled = True
result["cancellation_type"] = "cancelled"
results["cancelled_overlapping_recurring_events"].append(result)
print("Cancelled overlapping recurring event:")