docs
0
# Activity & Calendar System
1
2
Comprehensive activity management with calendar integration, task scheduling, meeting coordination, and reminder systems for organizing business activities and time management.
3
4
## Capabilities
5
6
### Activity Management
7
8
Central activity entity for managing tasks, meetings, calls, and events.
9
10
```python { .api }
11
class Activity(CremeEntity):
12
"""
13
Activity entity for scheduling and managing business tasks and events.
14
15
Attributes:
16
- title: str, activity title/subject
17
- description: TextField, detailed description
18
- minutes: TextField, meeting minutes or notes
19
- place: str, location or venue
20
- duration: int, duration in minutes
21
- start: DateTimeField, start date and time
22
- end: DateTimeField, end date and time
23
- is_all_day: bool, all-day event flag
24
- status: ForeignKey, activity status
25
- type: ForeignKey, activity type
26
- sub_type: ForeignKey, activity sub-type
27
- busy: bool, mark time as busy in calendar
28
- floating_type: int, floating activity type
29
30
Methods:
31
- get_absolute_url(): Get activity detail URL
32
- get_calendar_url(): Get calendar view URL
33
- is_popup(): Check if activity is a popup reminder
34
- get_duration_str(): Get formatted duration string
35
- collides_with(other_activity): Check for scheduling conflicts
36
- get_attendees(): Get list of participants
37
"""
38
39
def get_activity_model():
40
"""
41
Get the active Activity model class from settings.
42
43
Returns:
44
type[Activity]: The configured activity model class
45
46
Example:
47
ActivityModel = get_activity_model()
48
activity = ActivityModel.objects.create(
49
title="Client Meeting",
50
start=datetime.now(),
51
duration=60
52
)
53
"""
54
55
def activity_model_is_custom():
56
"""
57
Check if the activity model has been customized.
58
59
Returns:
60
bool: True if using custom activity model
61
"""
62
```
63
64
### Calendar Management
65
66
Calendar system for organizing and viewing activities.
67
68
```python { .api }
69
class Calendar(CremeModel):
70
"""
71
Calendar entity for organizing activities and events.
72
73
Attributes:
74
- name: str, calendar name
75
- is_default: bool, default calendar flag
76
- is_custom: bool, user-created calendar flag
77
- is_public: bool, public visibility flag
78
- user: ForeignKey, calendar owner
79
- color: str, calendar display color
80
81
Methods:
82
- get_absolute_url(): Get calendar view URL
83
- get_activities(start_date, end_date): Get activities in date range
84
- is_visible_to(user): Check if user can view calendar
85
- can_edit(user): Check if user can edit calendar
86
- __str__(): Return calendar name
87
"""
88
```
89
90
### Activity Types and Categorization
91
92
Classification system for different types of activities.
93
94
```python { .api }
95
class ActivityType(CremeModel):
96
"""
97
Main categorization for activities.
98
99
Attributes:
100
- name: str, type name
101
- default_day_duration: int, default duration in minutes for day-long activities
102
- default_hour_duration: int, default duration in minutes for timed activities
103
- color: str, display color for this type
104
- is_custom: bool, user-created type flag
105
106
Methods:
107
- __str__(): Return type name
108
- get_default_duration(is_all_day): Get appropriate default duration
109
110
Common Types:
111
- Meeting, Phone Call, Task, Email, Visit, Conference
112
"""
113
114
class ActivitySubType(CremeModel):
115
"""
116
Sub-categorization within activity types.
117
118
Attributes:
119
- name: str, sub-type name
120
- type: ForeignKey, parent activity type
121
- is_custom: bool, user-created sub-type flag
122
123
Methods:
124
- __str__(): Return sub-type name
125
126
Examples:
127
- Meeting: Internal, Client, Supplier, Board
128
- Phone Call: Inbound, Outbound, Follow-up
129
- Task: Administrative, Development, Research
130
"""
131
```
132
133
### Activity Status
134
135
Status tracking for activity lifecycle management.
136
137
```python { .api }
138
class Status(CremeModel):
139
"""
140
Status options for activities throughout their lifecycle.
141
142
Attributes:
143
- name: str, status name
144
- description: str, status description
145
- color: str, display color
146
- is_custom: bool, user-created status flag
147
148
Methods:
149
- __str__(): Return status name
150
151
Common Statuses:
152
- Planned, In Progress, Completed, Cancelled, Postponed
153
"""
154
```
155
156
### Participation and Attendees
157
158
Managing activity participants and attendance.
159
160
```python { .api }
161
class Relation(CremeModel):
162
"""
163
Relationship between activities and participants.
164
Uses the core relationship system to link activities with contacts/users.
165
166
Activity-specific relation types:
167
- 'activities-subject_participates': Contact participates in activity
168
- 'activities-subject_is_invited': Contact is invited to activity
169
- 'activities-subject_manages': Contact manages/organizes activity
170
171
Methods:
172
- get_participants(activity): Get list of activity participants
173
- add_participant(activity, contact, relation_type): Add participant
174
- remove_participant(activity, contact): Remove participant
175
"""
176
```
177
178
## Usage Examples
179
180
### Creating Activities
181
182
```python
183
from creme.activities import get_activity_model
184
from creme.activities.models import ActivityType, ActivitySubType, Status
185
from datetime import datetime, timedelta
186
187
ActivityModel = get_activity_model()
188
189
# Create a basic meeting
190
meeting_type = ActivityType.objects.get(name="Meeting")
191
meeting_status = Status.objects.get(name="Planned")
192
193
activity = ActivityModel.objects.create(
194
title="Weekly Team Meeting",
195
description="Discuss project progress and next steps",
196
start=datetime.now() + timedelta(days=1, hours=9),
197
duration=60, # 1 hour
198
type=meeting_type,
199
status=meeting_status,
200
place="Conference Room A"
201
)
202
203
# Create all-day event
204
conference_activity = ActivityModel.objects.create(
205
title="Industry Conference 2023",
206
start=datetime.now() + timedelta(days=30),
207
is_all_day=True,
208
duration=1440, # 24 hours
209
type=meeting_type,
210
status=meeting_status,
211
place="Convention Center"
212
)
213
```
214
215
### Calendar Operations
216
217
```python
218
from creme.activities.models import Calendar
219
220
# Create personal calendar
221
personal_cal = Calendar.objects.create(
222
name="Personal Schedule",
223
user=request.user,
224
is_default=True,
225
is_public=False,
226
color="#3498db"
227
)
228
229
# Create team calendar
230
team_cal = Calendar.objects.create(
231
name="Team Activities",
232
user=request.user,
233
is_public=True,
234
color="#e74c3c"
235
)
236
237
# Get activities for date range
238
from datetime import date
239
start_date = date.today()
240
end_date = start_date + timedelta(days=7)
241
242
week_activities = personal_cal.get_activities(start_date, end_date)
243
```
244
245
### Adding Participants
246
247
```python
248
from creme.creme_core.models import Relation, RelationType
249
from creme.persons import get_contact_model
250
251
ContactModel = get_contact_model()
252
253
# Get participants relation type
254
participates_rel = RelationType.objects.get(pk='activities-subject_participates')
255
256
# Add contacts as participants
257
participants = ContactModel.objects.filter(id__in=[1, 2, 3])
258
for contact in participants:
259
Relation.objects.create(
260
user=request.user,
261
subject_entity=contact,
262
object_entity=activity,
263
type=participates_rel
264
)
265
266
# Add meeting organizer
267
manages_rel = RelationType.objects.get(pk='activities-subject_manages')
268
organizer = ContactModel.objects.get(id=4)
269
Relation.objects.create(
270
user=request.user,
271
subject_entity=organizer,
272
object_entity=activity,
273
type=manages_rel
274
)
275
```
276
277
### Scheduling and Conflict Detection
278
279
```python
280
# Check for scheduling conflicts
281
def check_conflicts(activity, user):
282
"""Check if activity conflicts with existing schedule."""
283
overlapping = ActivityModel.objects.filter(
284
user=user,
285
start__lt=activity.end,
286
end__gt=activity.start,
287
busy=True
288
).exclude(id=activity.id if activity.id else None)
289
290
return overlapping
291
292
# Find available time slots
293
def find_available_slots(start_date, end_date, duration_minutes, user):
294
"""Find free time slots for scheduling."""
295
busy_times = ActivityModel.objects.filter(
296
user=user,
297
start__date__range=[start_date, end_date],
298
busy=True
299
).order_by('start')
300
301
available_slots = []
302
current_time = datetime.combine(start_date, datetime.min.time().replace(hour=9))
303
end_time = datetime.combine(end_date, datetime.min.time().replace(hour=17))
304
305
for activity in busy_times:
306
if current_time + timedelta(minutes=duration_minutes) <= activity.start:
307
available_slots.append({
308
'start': current_time,
309
'end': activity.start,
310
'duration': (activity.start - current_time).total_seconds() / 60
311
})
312
current_time = max(current_time, activity.end)
313
314
return available_slots
315
```
316
317
### Recurring Activities
318
319
```python
320
from dateutil.rrule import rrule, WEEKLY, DAILY
321
322
def create_recurring_activity(base_activity, recurrence_rule, count=None, until=None):
323
"""Create recurring activities based on a template."""
324
activities = []
325
326
# Generate dates based on recurrence rule
327
if count:
328
dates = list(rrule(recurrence_rule, dtstart=base_activity.start, count=count))
329
elif until:
330
dates = list(rrule(recurrence_rule, dtstart=base_activity.start, until=until))
331
else:
332
# Default to 10 occurrences
333
dates = list(rrule(recurrence_rule, dtstart=base_activity.start, count=10))
334
335
for occurrence_date in dates[1:]: # Skip first occurrence (original)
336
duration = timedelta(minutes=base_activity.duration)
337
recurring_activity = ActivityModel.objects.create(
338
title=base_activity.title,
339
description=base_activity.description,
340
start=occurrence_date,
341
end=occurrence_date + duration,
342
duration=base_activity.duration,
343
type=base_activity.type,
344
sub_type=base_activity.sub_type,
345
status=base_activity.status,
346
place=base_activity.place,
347
busy=base_activity.busy
348
)
349
activities.append(recurring_activity)
350
351
return activities
352
353
# Create weekly team meeting
354
weekly_meetings = create_recurring_activity(
355
base_activity=team_meeting,
356
recurrence_rule=WEEKLY,
357
count=12 # 12 weeks
358
)
359
```
360
361
### Activity Reporting
362
363
```python
364
from django.db.models import Count, Sum, Q
365
from datetime import datetime, timedelta
366
367
# Activity statistics for user
368
def get_activity_stats(user, start_date, end_date):
369
"""Get activity statistics for date range."""
370
activities = ActivityModel.objects.filter(
371
user=user,
372
start__date__range=[start_date, end_date]
373
)
374
375
stats = {
376
'total_activities': activities.count(),
377
'completed_activities': activities.filter(status__name='Completed').count(),
378
'total_duration': activities.aggregate(Sum('duration'))['duration__sum'] or 0,
379
'by_type': activities.values('type__name').annotate(count=Count('id')),
380
'by_status': activities.values('status__name').annotate(count=Count('id')),
381
}
382
383
return stats
384
385
# Overdue activities
386
overdue_activities = ActivityModel.objects.filter(
387
end__lt=datetime.now(),
388
status__name__in=['Planned', 'In Progress']
389
)
390
391
# Today's schedule
392
today_activities = ActivityModel.objects.filter(
393
start__date=date.today()
394
).order_by('start')
395
```
396
397
### Integration with Other Modules
398
399
```python
400
# Link activity to opportunity
401
from creme.opportunities import get_opportunity_model
402
from creme.creme_core.models import RelationType
403
404
OpportunityModel = get_opportunity_model()
405
opportunity = OpportunityModel.objects.get(id=1)
406
407
# Create follow-up call activity
408
followup_rel = RelationType.objects.get(pk='activities-subject_linked_2_activity')
409
followup_call = ActivityModel.objects.create(
410
title="Follow up on proposal",
411
start=datetime.now() + timedelta(days=3),
412
duration=30,
413
type=ActivityType.objects.get(name="Phone Call"),
414
status=Status.objects.get(name="Planned")
415
)
416
417
Relation.objects.create(
418
user=request.user,
419
subject_entity=followup_call,
420
object_entity=opportunity,
421
type=followup_rel
422
)
423
424
# Link activity to invoice (payment reminder)
425
from creme.billing import get_invoice_model
426
427
InvoiceModel = get_invoice_model()
428
invoice = InvoiceModel.objects.get(id=1)
429
430
payment_reminder = ActivityModel.objects.create(
431
title=f"Payment reminder for {invoice.name}",
432
start=invoice.expiration_date + timedelta(days=7),
433
duration=15,
434
type=ActivityType.objects.get(name="Phone Call"),
435
status=Status.objects.get(name="Planned")
436
)
437
438
Relation.objects.create(
439
user=request.user,
440
subject_entity=payment_reminder,
441
object_entity=invoice,
442
type=followup_rel
443
)
444
```
445
446
### Calendar Views and Export
447
448
```python
449
# Generate calendar data for frontend
450
def get_calendar_events(user, start_date, end_date):
451
"""Get calendar events in format suitable for calendar widgets."""
452
activities = ActivityModel.objects.filter(
453
user=user,
454
start__range=[start_date, end_date]
455
).select_related('type', 'status')
456
457
events = []
458
for activity in activities:
459
events.append({
460
'id': activity.id,
461
'title': activity.title,
462
'start': activity.start.isoformat(),
463
'end': activity.end.isoformat() if activity.end else None,
464
'allDay': activity.is_all_day,
465
'color': activity.type.color if activity.type else '#3498db',
466
'url': activity.get_absolute_url()
467
})
468
469
return events
470
471
# Export to iCal format
472
def export_calendar_ical(activities):
473
"""Export activities to iCal format."""
474
from icalendar import Calendar, Event
475
476
cal = Calendar()
477
cal.add('prodid', '-//Creme CRM//Calendar//EN')
478
cal.add('version', '2.0')
479
480
for activity in activities:
481
event = Event()
482
event.add('summary', activity.title)
483
event.add('dtstart', activity.start)
484
if activity.end:
485
event.add('dtend', activity.end)
486
if activity.description:
487
event.add('description', activity.description)
488
if activity.place:
489
event.add('location', activity.place)
490
491
cal.add_component(event)
492
493
return cal.to_ical()
494
```
495
496
The activity system provides comprehensive scheduling and time management capabilities that integrate seamlessly with all other CRM modules for complete business activity coordination.