or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

calendar-management.mdclient-auth.mdevent-operations.mdindex.mdjournal-freebusy.mdprincipal-calendar.mdtask-management.md

journal-freebusy.mddocs/

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

```