0
# Journal & FreeBusy
1
2
Journal entry management and free/busy scheduling functionality for calendar coordination, availability management, and personal journaling within CalDAV systems.
3
4
## Capabilities
5
6
### Journal Class
7
8
Represents CalDAV journal entries (VJOURNAL components) for personal notes, meeting minutes, and diary-style calendar entries.
9
10
```python { .api }
11
class Journal(CalendarObjectResource):
12
"""
13
Journal entry class for VJOURNAL components.
14
15
Inherits all methods from CalendarObjectResource including:
16
- load(), save(), delete()
17
- copy(), move()
18
- change_uid()
19
- expand() (for recurring journals)
20
"""
21
```
22
23
**Usage Examples:**
24
25
```python
26
import caldav
27
from datetime import datetime, timezone
28
import uuid
29
30
# Get existing journals
31
calendar = principal.calendars()[0]
32
journals = calendar.journals()
33
34
if journals:
35
journal = journals[0]
36
37
# Get journal information
38
component = journal.icalendar_component
39
summary = component.get('SUMMARY', 'No title')
40
description = component.get('DESCRIPTION', 'No content')
41
dtstart = component.get('DTSTART')
42
43
print(f"Journal: {summary}")
44
print(f"Date: {dtstart}")
45
print(f"Content: {description}")
46
47
# Modify journal
48
component['SUMMARY'] = 'Updated Journal Entry'
49
component['DESCRIPTION'] = 'Updated content with new insights'
50
journal.save()
51
print("Journal updated successfully")
52
```
53
54
### Journal Creation
55
56
Create new journal entries with comprehensive VJOURNAL support for documentation, meeting notes, and personal journaling.
57
58
```python { .api }
59
# Journal creation through calendar.save_journal() - see calendar-management.md
60
# Additional journal-specific creation patterns:
61
62
def create_simple_journal(summary, content, date=None):
63
"""
64
Helper function to create simple journal iCalendar data.
65
66
Parameters:
67
- summary: str, journal entry title
68
- content: str, journal entry content/description
69
- date: datetime, entry date (default: now)
70
71
Returns:
72
str: iCalendar data for the journal entry
73
"""
74
```
75
76
**Usage Examples:**
77
78
```python
79
# Create a meeting minutes journal entry
80
def create_meeting_minutes(meeting_title, attendees, notes, meeting_date=None):
81
if meeting_date is None:
82
meeting_date = datetime.now(timezone.utc)
83
84
journal_uid = f"{uuid.uuid4()}@example.com"
85
86
attendee_list = ", ".join(attendees) if attendees else "No attendees recorded"
87
full_content = f"""Meeting Attendees: {attendee_list}
88
89
Meeting Notes:
90
{notes}"""
91
92
ical_data = f"""BEGIN:VCALENDAR
93
VERSION:2.0
94
PRODID:-//My Journal App//My Journal App//EN
95
BEGIN:VJOURNAL
96
UID:{journal_uid}
97
DTSTART:{meeting_date.strftime('%Y%m%dT%H%M%SZ')}
98
SUMMARY:{meeting_title} - Meeting Minutes
99
DESCRIPTION:{full_content}
100
CATEGORIES:meeting-minutes
101
DTSTAMP:{datetime.now(timezone.utc).strftime('%Y%m%dT%H%M%SZ')}
102
END:VJOURNAL
103
END:VCALENDAR"""
104
105
return ical_data
106
107
# Create and save meeting minutes
108
meeting_date = datetime(2025, 9, 15, 14, 0, tzinfo=timezone.utc)
109
minutes_ical = create_meeting_minutes(
110
meeting_title="Weekly Team Sync",
111
attendees=["alice@example.com", "bob@example.com", "charlie@example.com"],
112
notes="""
113
- Reviewed sprint progress: 80% complete
114
- Discussed upcoming deadlines for Q4
115
- Alice raised concerns about resource allocation
116
- Action items:
117
* Bob to follow up on server migration
118
* Charlie to update documentation
119
* Schedule architecture review for next week
120
""",
121
meeting_date=meeting_date
122
)
123
124
journal = calendar.save_journal(minutes_ical)
125
print(f"Created meeting minutes: {journal.id}")
126
127
# Create personal journal entry
128
personal_ical = """BEGIN:VCALENDAR
129
VERSION:2.0
130
PRODID:-//My App//My App//EN
131
BEGIN:VJOURNAL
132
UID:daily-reflection-20250915@example.com
133
DTSTART:20250915T200000Z
134
SUMMARY:Daily Reflection - September 15, 2025
135
DESCRIPTION:Today was productive. Made good progress on the CalDAV integration project. The documentation is coming along well and the team is aligned on the technical approach. Tomorrow I'll focus on testing the authentication flows.
136
CATEGORIES:personal,reflection
137
END:VJOURNAL
138
END:VCALENDAR"""
139
140
personal_journal = calendar.save_journal(personal_ical)
141
142
# Create project log entry
143
project_log_ical = """BEGIN:VCALENDAR
144
VERSION:2.0
145
PRODID:-//My App//My App//EN
146
BEGIN:VJOURNAL
147
UID:project-log-001@example.com
148
DTSTART:20250915T170000Z
149
SUMMARY:CalDAV Integration - Development Log
150
DESCRIPTION:Completed implementation of event creation and modification. All unit tests passing. Next steps: implement recurring event support and add error handling for edge cases. Estimated completion: end of week.
151
CATEGORIES:project-log,development
152
END:VJOURNAL
153
END:VCALENDAR"""
154
155
project_journal = calendar.save_journal(project_log_ical)
156
```
157
158
### Journal Queries
159
160
Search and filter journal entries by date, category, content, and other properties.
161
162
```python { .api }
163
# Journal search patterns using calendar.search() - see calendar-management.md
164
165
def find_journals_by_category(calendar, category):
166
"""Find journal entries with specific category."""
167
journals = calendar.search(comp_filter="VJOURNAL")
168
matching = []
169
170
for journal in journals:
171
categories = journal.icalendar_component.get('CATEGORIES', [])
172
if isinstance(categories, str):
173
categories = [categories]
174
if category.lower() in [cat.lower() for cat in categories]:
175
matching.append(journal)
176
177
return matching
178
179
def find_journals_by_date_range(calendar, start_date, end_date):
180
"""Find journal entries within date range."""
181
return calendar.date_search(
182
start=start_date,
183
end=end_date,
184
compfilter="VJOURNAL"
185
)
186
187
def find_journals_by_text(calendar, search_text):
188
"""Find journal entries containing specific text."""
189
return calendar.search(
190
comp_filter="VJOURNAL",
191
text_match=search_text
192
)
193
```
194
195
**Usage Examples:**
196
197
```python
198
from datetime import datetime, timedelta
199
200
# Find meeting minutes from last week
201
last_week = datetime.now() - timedelta(days=7)
202
this_week = datetime.now()
203
204
recent_journals = find_journals_by_date_range(calendar, last_week, this_week)
205
print(f"Journal entries from last week: {len(recent_journals)}")
206
207
# Find all meeting minutes
208
meeting_journals = find_journals_by_category(calendar, "meeting-minutes")
209
print(f"Meeting minutes entries: {len(meeting_journals)}")
210
211
# Search for specific project mentions
212
project_journals = find_journals_by_text(calendar, "CalDAV integration")
213
print(f"Project-related journals: {len(project_journals)}")
214
215
# List all journal categories
216
all_journals = calendar.journals()
217
categories = set()
218
for journal in all_journals:
219
journal_categories = journal.icalendar_component.get('CATEGORIES', [])
220
if isinstance(journal_categories, str):
221
journal_categories = [journal_categories]
222
categories.update(journal_categories)
223
224
print(f"Journal categories: {', '.join(sorted(categories))}")
225
```
226
227
### FreeBusy Class
228
229
Represents CalDAV free/busy objects (VFREEBUSY components) for availability coordination and scheduling support.
230
231
```python { .api }
232
class FreeBusy(CalendarObjectResource):
233
"""
234
Free/busy information class for VFREEBUSY components.
235
236
Inherits all methods from CalendarObjectResource including:
237
- load(), save(), delete()
238
- copy(), move()
239
- change_uid()
240
"""
241
```
242
243
**Usage Examples:**
244
245
```python
246
# Get existing free/busy objects
247
freebusy_objects = calendar.freebusy()
248
249
if freebusy_objects:
250
fb = freebusy_objects[0]
251
252
# Get free/busy information
253
component = fb.icalendar_component
254
dtstart = component.get('DTSTART')
255
dtend = component.get('DTEND')
256
organizer = component.get('ORGANIZER')
257
258
print(f"Free/busy period: {dtstart} to {dtend}")
259
print(f"Organizer: {organizer}")
260
261
# Get free/busy periods
262
freebusy_periods = component.get('FREEBUSY', [])
263
if not isinstance(freebusy_periods, list):
264
freebusy_periods = [freebusy_periods]
265
266
for period in freebusy_periods:
267
print(f"Busy period: {period}")
268
```
269
270
### Free/Busy Requests
271
272
Request free/busy information for scheduling coordination and meeting planning.
273
274
```python { .api }
275
# Free/busy requests are typically made through Principal or Calendar objects
276
# See principal-calendar.md and calendar-management.md for details
277
278
def request_freebusy_info(principal, start_time, end_time, attendees):
279
"""
280
Request free/busy information for attendees.
281
282
Parameters:
283
- principal: Principal object
284
- start_time: datetime, start of time range
285
- end_time: datetime, end of time range
286
- attendees: list[str], attendee email addresses
287
288
Returns:
289
FreeBusy: Free/busy information object
290
"""
291
return principal.freebusy_request(start_time, end_time, attendees)
292
```
293
294
**Usage Examples:**
295
296
```python
297
from datetime import datetime, timedelta, timezone
298
299
# Request free/busy information for meeting planning
300
meeting_start = datetime(2025, 9, 20, 14, 0, tzinfo=timezone.utc) # 2 PM UTC
301
meeting_end = meeting_start + timedelta(hours=2) # 2 hour meeting
302
303
attendees = [
304
"alice@example.com",
305
"bob@example.com",
306
"charlie@example.com"
307
]
308
309
# Request through principal
310
freebusy_info = principal.freebusy_request(
311
start=meeting_start,
312
end=meeting_end,
313
attendees=attendees
314
)
315
316
if freebusy_info:
317
print("Free/busy information retrieved")
318
319
# Analyze the response (format depends on server implementation)
320
component = freebusy_info.icalendar_component
321
organizer = component.get('ORGANIZER', 'Unknown')
322
323
# Check for busy periods
324
busy_periods = component.get('FREEBUSY', [])
325
if not isinstance(busy_periods, list):
326
busy_periods = [busy_periods]
327
328
print(f"Found {len(busy_periods)} busy periods")
329
for i, period in enumerate(busy_periods):
330
print(f" Busy period {i+1}: {period}")
331
332
# Request through calendar
333
calendar_freebusy = calendar.freebusy_request(
334
start=meeting_start,
335
end=meeting_end,
336
attendees=attendees
337
)
338
```
339
340
### Free/Busy Creation
341
342
Create free/busy objects for publishing availability information.
343
344
```python { .api }
345
def create_freebusy_object(start_time, end_time, busy_periods=None, organizer=None):
346
"""
347
Helper function to create free/busy iCalendar data.
348
349
Parameters:
350
- start_time: datetime, start of free/busy period
351
- end_time: datetime, end of free/busy period
352
- busy_periods: list[tuple], list of (start, end) busy time tuples
353
- organizer: str, organizer email address
354
355
Returns:
356
str: iCalendar data for the free/busy object
357
"""
358
```
359
360
**Usage Examples:**
361
362
```python
363
# Create a free/busy object manually
364
def create_weekly_freebusy(week_start, busy_times, organizer_email):
365
week_end = week_start + timedelta(days=7)
366
fb_uid = f"freebusy-{week_start.strftime('%Y%m%d')}@example.com"
367
368
ical_data = f"""BEGIN:VCALENDAR
369
VERSION:2.0
370
PRODID:-//My App//My App//EN
371
BEGIN:VFREEBUSY
372
UID:{fb_uid}
373
DTSTART:{week_start.strftime('%Y%m%dT%H%M%SZ')}
374
DTEND:{week_end.strftime('%Y%m%dT%H%M%SZ')}
375
ORGANIZER:mailto:{organizer_email}
376
DTSTAMP:{datetime.now(timezone.utc).strftime('%Y%m%dT%H%M%SZ')}
377
"""
378
379
# Add busy periods
380
for start, end in busy_times:
381
ical_data += f"FREEBUSY;FBTYPE=BUSY:{start.strftime('%Y%m%dT%H%M%SZ')}/{end.strftime('%Y%m%dT%H%M%SZ')}\n"
382
383
ical_data += """END:VFREEBUSY
384
END:VCALENDAR"""
385
386
return ical_data
387
388
# Create free/busy for current week
389
week_start = datetime.now(timezone.utc).replace(hour=0, minute=0, second=0, microsecond=0)
390
# Subtract days to get to Monday
391
week_start -= timedelta(days=week_start.weekday())
392
393
busy_times = [
394
(week_start + timedelta(days=0, hours=9), week_start + timedelta(days=0, hours=17)), # Monday 9-5
395
(week_start + timedelta(days=1, hours=9), week_start + timedelta(days=1, hours=17)), # Tuesday 9-5
396
(week_start + timedelta(days=2, hours=9), week_start + timedelta(days=2, hours=12)), # Wednesday 9-12
397
(week_start + timedelta(days=3, hours=14), week_start + timedelta(days=3, hours=17)), # Thursday 2-5
398
(week_start + timedelta(days=4, hours=9), week_start + timedelta(days=4, hours=15)), # Friday 9-3
399
]
400
401
freebusy_ical = create_weekly_freebusy(
402
week_start=week_start,
403
busy_times=busy_times,
404
organizer_email="user@example.com"
405
)
406
407
# Note: Free/busy objects are typically created by the server automatically
408
# Manual creation is mainly for testing or special use cases
409
print("Free/busy object created (for testing purposes)")
410
```
411
412
## Journal Properties
413
414
```python { .api }
415
# Common journal properties that can be accessed via icalendar_component
416
JOURNAL_PROPERTIES = {
417
"SUMMARY": "str", # Journal entry title
418
"DESCRIPTION": "str", # Journal entry content
419
"DTSTART": "datetime", # Entry date/time
420
"CATEGORIES": "list[str]", # Entry categories/tags
421
"CLASS": "str", # PUBLIC, PRIVATE, CONFIDENTIAL
422
"STATUS": "str", # DRAFT, FINAL, CANCELLED
423
"ORGANIZER": "vCalAddress", # Entry creator
424
"ATTENDEE": "list[vCalAddress]", # Associated attendees (for meeting minutes)
425
}
426
427
# Common journal categories
428
JOURNAL_CATEGORIES = [
429
"meeting-minutes",
430
"personal",
431
"reflection",
432
"project-log",
433
"development",
434
"notes",
435
"diary",
436
"daily-standup",
437
"retrospective"
438
]
439
```
440
441
## FreeBusy Properties
442
443
```python { .api }
444
# Common free/busy properties that can be accessed via icalendar_component
445
FREEBUSY_PROPERTIES = {
446
"DTSTART": "datetime", # Start of free/busy period
447
"DTEND": "datetime", # End of free/busy period
448
"ORGANIZER": "vCalAddress", # Free/busy publisher
449
"ATTENDEE": "list[vCalAddress]", # Attendees the free/busy applies to
450
"FREEBUSY": "list[period]", # Free/busy time periods
451
"URL": "str", # URL for free/busy information
452
}
453
454
# Free/busy types (FBTYPE parameter)
455
FREEBUSY_TYPES = {
456
"FREE": "Time is free",
457
"BUSY": "Time is busy (default)",
458
"BUSY-UNAVAILABLE": "Time is busy and unavailable",
459
"BUSY-TENTATIVE": "Time is tentatively busy"
460
}
461
```