Project

General

Profile

« Previous | Next » 

Revision 76974b6f

Added by Benjamin Dauvergne almost 12 years ago

agenda/actes/dossiers: move Occurence fields into Event, add recurring events support

View differences:

calebasse/actes/admin.py
2 2

  
3 3
import reversion
4 4

  
5
from models import Act, ActValidationState, EventAct
5
from models import Act, ActValidationState
6 6

  
7 7
admin.site.register(Act, reversion.VersionAdmin)
8 8
admin.site.register(ActValidationState, reversion.VersionAdmin)
9
admin.site.register(EventAct, reversion.VersionAdmin)
calebasse/actes/models.py
1 1
# -*- coding: utf-8 -*-
2
from datetime import date
3

  
2 4
from django.db import models
3 5
from django.contrib.auth.models import User
4 6

  
......
40 42
        return VALIDATION_STATES[self.state_name]
41 43

  
42 44

  
45
class ActManager(models.Manager):
46
    def create_act(self, author=None, **kwargs):
47
        act = self.create(**kwargs)
48
        ActValidationState.objects.create(act=act,state_name='NON_VALIDE',
49
            author=author, previous_state=None)
50
        return act
51

  
52
    def next_acts(self, patient_record, today=None):
53
        today = today or date.today()
54
        return self.filter(date__gte=today) \
55
                .filter(patient=patient_record) \
56
                .order_by('date')
57

  
58
    def last_acts(self, patient_record, today=None):
59
        today = today or date.today()
60
        return self.filter(date__lte=today) \
61
                .filter(patient=patient_record) \
62
                .order_by('-date')
63

  
64

  
43 65
class Act(models.Model):
66
    objects = ActManager()
67

  
44 68
    patient = models.ForeignKey('dossiers.PatientRecord')
45
    date = models.DateTimeField()
69
    date = models.DateField()
70
    _duration = models.IntegerField(u'Durée en minutes', default=0)
46 71
    act_type = models.ForeignKey('ressources.ActType',
47 72
            verbose_name=u'Type d\'acte')
48 73
    validation_locked = models.BooleanField(default=False,
......
68 93
            verbose_name=u'Intervenants')
69 94
    pause = models.BooleanField(default=False,
70 95
            verbose_name=u'Pause facturation')
96
    parent_event = models.ForeignKey('agenda.Event', 
97
            verbose_name=u'Rendez-vous lié',
98
            blank=True, null=True)
99
    VALIDATION_CODE_CHOICES = (
100
            ('absent', u'Absent'),
101
            ('present', u'Présent'),
102
            )
103
    attendance = models.CharField(max_length=16,
104
            choices=VALIDATION_CODE_CHOICES,
105
            default='absent',
106
            verbose_name=u'Présence')
107
    convocation_sent = models.BooleanField(blank=True,
108
            verbose_name=u'Convoqué')
109

  
110
    @property
111
    def event(self):
112
        if self.parent_event:
113
            return self.parent_event.today_occurence(self.date)
114
        return None
115

  
116
    @property
117
    def start_time(self):
118
        event = self.event
119
        if event:
120
            return event.start_datetime.timetz()
121
        return None
71 122

  
72 123
    def get_hc_tag(self):
73 124
        if self.healthcare:
......
155 206
        if not hc:
156 207
            # On pourrait ici créer une prise en charge de diagnostic
157 208
            return (False, None)
158
        if self.date.date() < hc.start_date:
209
        if self.date < hc.start_date:
159 210
            return (False, None)
160 211
        # Les acts facturés déja couvert par la prise en charge sont pointés
161 212
        # dans hc.act_set.all()
......
172 223
        for a in acts_billable:
173 224
            if nb_acts_billed + count >= hc.get_act_number():
174 225
                return (False, None)
175
            if a.date.date() >= hc.start_date:
226
            if a.date >= hc.start_date:
176 227
                if a.id == self.id:
177 228
                    return (True, hc)
178 229
                count = count + 1
......
225 276
#                    return (True, hc)
226 277
#                count = count + 1
227 278
#        return (False, None)
228
    # END Specific to cmpp healthcare
279
# END Specific to cmpp healthcare
280

  
281
    def duration(self):
282
        '''Return a displayable duration for this field.'''
283
        hours, remainder = divmod(self._duration, 60)
284
        return '%02d:%02d' % (hours, remainder)
229 285

  
230 286
    def __unicode__(self):
231 287
        return u'{0} le {1} pour {2} avec {3}'.format(
......
285 341
                end_datetime=end_datetime,
286 342
                room=room, note=note, **rrule_params)
287 343

  
288
    def modify_patient_appointment(self, creator, title, patient, participants,
289
            act_type, service, start_datetime, end_datetime, description='',
290
            room=None, note=None, **rrule_params):
291
        """
292
        This method allow you to create a new patient appointment quickly
293

  
294
        Args:
295
            creator: author of the modification
296
            title: patient appointment title (str)
297
            patient: Patient object
298
            participants: List of CalebasseUser (therapists)
299
            act_type: ActType object
300
            service: Service object. Use session service by defaut
301
            start_datetime: datetime with the start date and time
302
            end_datetime: datetime with the end date and time
303
            description: description of the event
304
            room: room where the event will take place
305
            freq, count, until, byweekday, rrule_params:
306
            follow the ``dateutils`` API (see http://labix.org/python-dateutil)
307

  
308
        Example:
309
            Look at calebasse.agenda.tests.EventTest (test_create_appointments
310
            method)
311
        """
312

  
313
        event_type, created = EventType.objects.get_or_create(
314
                label=u"Rendez-vous patient"
315
                )
316

  
317
        act_event = EventAct.objects.create(
318
                title=title,
319
                event_type=event_type,
320
                patient=patient,
321
                act_type=act_type,
322
                date=start_datetime,
323
                )
324
        act_event.doctors = participants
325
        ActValidationState(act=act_event, state_name=NON_VALIDE,
326
            author=creator, previous_state=None).save()
327

  
328
        return self._set_event(act_event, participants, description,
329
                services=[service], start_datetime=start_datetime,
330
                end_datetime=end_datetime,
331
                room=room, note=note, **rrule_params)
332

  
333
class EventAct(Event, Act):
334
    objects = EventActManager()
335

  
336
    VALIDATION_CODE_CHOICES = (
337
            ('absent', u'Absent'),
338
            ('present', u'Présent'),
339
            )
340
    attendance = models.CharField(max_length=16,
341
            choices=VALIDATION_CODE_CHOICES,
342
            default='absent',
343
            verbose_name=u'Présence')
344
    convocation_sent = models.BooleanField(blank=True,
345
            verbose_name=u'Convoqué')
346

  
347
    def __unicode__(self):
348
        return u'Rdv le {0} de {1} avec {2} pour {3}'.format(
349
                self.occurrence_set.all()[0].start_time, self.patient,
350
                ', '.join(map(unicode, self.participants.all())),
351
                self.act_type)
352

  
353
    def __repr__(self):
354
        return (u'<%s %r %r>' % (self.__class__.__name__, unicode(self),
355
            self.id)).encode('utf-8')
356

  
357
    def start_time(self):
358
        return self.occurrence_set.all()[0].start_time
359

  
360
    def duration(self):
361
        o = self.occurrence_set.all()[0]
362
        td = o.end_time - o.start_time
363
        hours, remainder = divmod(td.seconds, 3600)
364
        minutes, remainder = divmod(remainder, 60)
365
        return '%02d:%02d' % (hours, minutes)
366

  
367
    class Meta:
368
        verbose_name = 'Rendez-vous patient'
369
        verbose_name_plural = 'Rendez-vous patient'
370
        ordering = ['-date', 'patient']
371

  
372
reversion.register(EventAct, follow=['act_ptr', 'event_ptr'])
373 344

  
374 345
class ValidationMessage(ServiceLinkedAbstractModel):
375 346
    validation_date = models.DateTimeField()
calebasse/actes/templates/actes/act_listing.html
123 123
            {% endif %}
124 124
            {% endfor %}
125 125
        </td>
126
        <td>{{ act.start_time|date:"H:i" }}</td>
126
        <td>{{ act.parent_event.start_time|date:"H:i" }}</td>
127 127
        <td>{{ act.duration }}</td>
128 128
      </tr>
129 129
    {% endfor %}
calebasse/actes/tests.py
1 1
# -*- coding: utf-8 -*-
2
from datetime import datetime
3
from dateutil import rrule
2
from datetime import datetime, date
4 3

  
5 4
from django.test import TestCase
6 5
from django.contrib.auth.models import User
7 6

  
8 7
from validation import automated_validation, are_all_acts_of_the_day_locked, \
9 8
    get_days_with_acts_not_locked
10
from calebasse.actes.models import EventAct
11 9
from calebasse.dossiers.models import create_patient
10
from calebasse.agenda.models import EventWithAct
12 11
from calebasse.ressources.models import ActType, Service, WorkerType
13 12
from calebasse.personnes.models import Worker
14 13

  
......
28 27
        patient2 = create_patient('Jimmy', 'Claff', service, creator)
29 28
        patient3 = create_patient('Bouba', 'Lourson', service, creator)
30 29

  
31
        act_event = EventAct.objects.create_patient_appointment(creator, 'RDV avec M X', patient,
30
        act_event = EventWithAct.objects.create_patient_appointment(creator, 'RDV avec M X', patient,
32 31
                [therapist1, therapist2], act_type, service,
33 32
                start_datetime=datetime(2020, 10, 2, 7, 15),
34 33
                end_datetime=datetime(2020, 10, 2, 9, 20),
35
                freq=rrule.WEEKLY, byweekday=rrule.FR, until=datetime(2040, 10, 2))
36
        act_event2 = EventAct.objects.create_patient_appointment(creator, 'RDV avec M Y', patient, [therapist3],
34
                periodicity=1, until=date(2020, 10, 2))
35
        act_event2 = EventWithAct.objects.create_patient_appointment(creator, 'RDV avec M Y', patient, [therapist3],
37 36
                act_type, service, start_datetime=datetime(2020, 10, 2, 10, 15),
38 37
                end_datetime=datetime(2020, 10, 2, 12, 20),
39
                freq=rrule.WEEKLY, byweekday=rrule.FR, until=datetime(2021, 10, 2))
40
        act_event3 = EventAct.objects.create_patient_appointment(creator, 'RDV avec M X', patient2,
38
                periodicity=1, until=date(2020, 10, 2))
39
        act_event3 = EventWithAct.objects.create_patient_appointment(creator, 'RDV avec M X', patient2,
41 40
                [therapist1, therapist2], act_type, service,
42 41
                start_datetime=datetime(2020, 10, 2, 7, 15),
43 42
                end_datetime=datetime(2020, 10, 2, 9, 20),
44
                freq=rrule.WEEKLY, byweekday=rrule.FR, until=datetime(2040, 10, 2))
45
        act_event4 = EventAct.objects.create_patient_appointment(creator, 'RDV avec M Y', patient3, [therapist3],
43
                periodicity=1, until=date(2020, 10, 2))
44
        act_event4 = EventWithAct.objects.create_patient_appointment(creator, 'RDV avec M Y', patient3, [therapist3],
46 45
                act_type, service, start_datetime=datetime(2020, 10, 2, 10, 15),
47 46
                end_datetime=datetime(2020, 10, 2, 12, 20),
48
                freq=rrule.WEEKLY, byweekday=rrule.FR, until=datetime(2021, 10, 2))
49
        act_event5 = EventAct.objects.create_patient_appointment(creator, 'RDV avec M Y', patient3, [therapist3],
47
                periodicity=1, until=date(2020, 10, 2))
48
        act_event5 = EventWithAct.objects.create_patient_appointment(creator, 'RDV avec M Y', patient3, [therapist3],
50 49
                act_type, service, start_datetime=datetime(2020, 10, 3, 10, 15),
51 50
                end_datetime=datetime(2020, 10, 3, 12, 20),
52
                freq=rrule.WEEKLY, byweekday=rrule.FR, until=datetime(2021, 10, 2))
53
        act_event6 = EventAct.objects.create_patient_appointment(creator, 'RDV avec M Z', patient, [therapist3],
51
                periodicity=1, until=date(2020, 10, 2))
52
        act_event6 = EventWithAct.objects.create_patient_appointment(creator, 'RDV avec M Z', patient, [therapist3],
54 53
                act_type, service, start_datetime=datetime(2020, 10, 2, 10, 15),
55 54
                end_datetime=datetime(2020, 10, 2, 12, 20),
56
                freq=rrule.WEEKLY, byweekday=rrule.FR, until=datetime(2021, 10, 2))
57
        act_event7 = EventAct.objects.create_patient_appointment(creator, 'RDV avec M Z', patient, [therapist3],
55
                periodicity=1, until=date(2020, 10, 2))
56
        act_event7 = EventWithAct.objects.create_patient_appointment(creator, 'RDV avec M Z', patient, [therapist3],
58 57
                act_type, service, start_datetime=datetime(2020, 10, 4, 10, 15),
59 58
                end_datetime=datetime(2020, 10, 4, 12, 20),
60
                freq=rrule.WEEKLY, byweekday=rrule.FR, until=datetime(2021, 10, 2))
59
                periodicity=1, until=date(2020, 10, 4))
61 60

  
62
        act_event3.set_state('ABS_EXC', creator)
63
        act_event6.set_state('VALIDE', creator)
61
        act_event3.act.set_state('ABS_EXC', creator)
62
        act_event6.act.set_state('VALIDE', creator)
64 63

  
65
        result = automated_validation(datetime(2020, 10, 2, 12, 20), service, creator, commit=False)
64
        result = automated_validation(date(2020, 10, 2), service, creator, commit=False)
66 65
        self.assertEqual(result, (5,2,2,0,1,0,0,0,0,0))
67
        self.assertTrue(act_event.is_state('NON_VALIDE'))
68
        self.assertTrue(act_event2.is_state('NON_VALIDE'))
69
        self.assertTrue(act_event3.is_state('ABS_EXC'))
70
        self.assertTrue(act_event4.is_state('NON_VALIDE'))
71
        self.assertTrue(act_event5.is_state('NON_VALIDE'))
72
        self.assertTrue(act_event6.is_state('VALIDE'))
66
        self.assertTrue(act_event.act.is_state('NON_VALIDE'))
67
        self.assertTrue(act_event2.act.is_state('NON_VALIDE'))
68
        self.assertTrue(act_event3.act.is_state('ABS_EXC'))
69
        self.assertTrue(act_event4.act.is_state('NON_VALIDE'))
70
        self.assertTrue(act_event5.act.is_state('NON_VALIDE'))
71
        self.assertTrue(act_event6.act.is_state('VALIDE'))
73 72

  
74
        result = automated_validation(datetime(2020, 10, 2, 12, 20), service, creator)
73
        result = automated_validation(date(2020, 10, 2), service, creator)
75 74
        self.assertEqual(result, (5,2,2,0,1,0,0,0,0,0))
76 75

  
77
        self.assertTrue(act_event.is_state('VALIDE'))
78
        self.assertTrue(act_event2.is_state('ACT_DOUBLE'))
79
        self.assertTrue(act_event3.is_state('ABS_EXC'))
80
        self.assertTrue(act_event4.is_state('VALIDE'))
81
        self.assertTrue(act_event5.is_state('NON_VALIDE'))
82
        self.assertTrue(act_event6.is_state('ACT_DOUBLE'))
76
        self.assertTrue(act_event.act.is_state('VALIDE'))
77
        self.assertTrue(act_event2.act.is_state('ACT_DOUBLE'))
78
        self.assertTrue(act_event3.act.is_state('ABS_EXC'))
79
        self.assertTrue(act_event4.act.is_state('VALIDE'))
80
        self.assertTrue(act_event5.act.is_state('NON_VALIDE'))
81
        self.assertTrue(act_event6.act.is_state('ACT_DOUBLE'))
83 82

  
84 83
        self.assertTrue(are_all_acts_of_the_day_locked(datetime(2020, 10, 2, 12, 20)))
85 84
        self.assertFalse(are_all_acts_of_the_day_locked(datetime(2020, 10, 3, 12, 20)))
86 85
        self.assertFalse(are_all_acts_of_the_day_locked(datetime(2020, 10, 4, 12, 20)))
87 86
        self.assertEqual(get_days_with_acts_not_locked(datetime(2020, 10, 2), datetime(2020, 10, 4)),
88
            [datetime(2020, 10, 3, 0, 0), datetime(2020, 10, 4, 0, 0)])
87
            [date(2020, 10, 3), date(2020, 10, 4)])
89 88

  
90 89
        result = automated_validation(datetime(2020, 10, 2, 12, 20), service, creator, commit=False)
91 90
        self.assertEqual(result, (5,2,2,0,1,0,0,0,0,0))
92
        self.assertTrue(act_event.is_state('VALIDE'))
93
        self.assertTrue(act_event2.is_state('ACT_DOUBLE'))
94
        self.assertTrue(act_event3.is_state('ABS_EXC'))
95
        self.assertTrue(act_event4.is_state('VALIDE'))
96
        self.assertTrue(act_event5.is_state('NON_VALIDE'))
97
        self.assertTrue(act_event6.is_state('ACT_DOUBLE'))
91
        self.assertTrue(act_event.act.is_state('VALIDE'))
92
        self.assertTrue(act_event2.act.is_state('ACT_DOUBLE'))
93
        self.assertTrue(act_event3.act.is_state('ABS_EXC'))
94
        self.assertTrue(act_event4.act.is_state('VALIDE'))
95
        self.assertTrue(act_event5.act.is_state('NON_VALIDE'))
96
        self.assertTrue(act_event6.act.is_state('ACT_DOUBLE'))
98 97

  
99 98
        result = automated_validation(datetime(2020, 10, 2, 12, 20), service, creator)
100 99
        self.assertEqual(result, (5,2,2,0,1,0,0,0,0,0))
101
        self.assertTrue(act_event.is_state('VALIDE'))
102
        self.assertTrue(act_event2.is_state('ACT_DOUBLE'))
103
        self.assertTrue(act_event3.is_state('ABS_EXC'))
104
        self.assertTrue(act_event4.is_state('VALIDE'))
105
        self.assertTrue(act_event5.is_state('NON_VALIDE'))
106
        self.assertTrue(act_event6.is_state('ACT_DOUBLE'))
100
        self.assertTrue(act_event.act.is_state('VALIDE'))
101
        self.assertTrue(act_event2.act.is_state('ACT_DOUBLE'))
102
        self.assertTrue(act_event3.act.is_state('ABS_EXC'))
103
        self.assertTrue(act_event4.act.is_state('VALIDE'))
104
        self.assertTrue(act_event5.act.is_state('NON_VALIDE'))
105
        self.assertTrue(act_event6.act.is_state('ACT_DOUBLE'))
107 106

  
108 107
        result = automated_validation(datetime(2020, 10, 2, 12, 20), service, creator, commit=False)
109 108
        self.assertEqual(result, (5,2,2,0,1,0,0,0,0,0))
110
        self.assertTrue(act_event.is_state('VALIDE'))
111
        self.assertTrue(act_event2.is_state('ACT_DOUBLE'))
112
        self.assertTrue(act_event3.is_state('ABS_EXC'))
113
        self.assertTrue(act_event4.is_state('VALIDE'))
114
        self.assertTrue(act_event5.is_state('NON_VALIDE'))
115
        self.assertTrue(act_event6.is_state('ACT_DOUBLE'))
109
        self.assertTrue(act_event.act.is_state('VALIDE'))
110
        self.assertTrue(act_event2.act.is_state('ACT_DOUBLE'))
111
        self.assertTrue(act_event3.act.is_state('ABS_EXC'))
112
        self.assertTrue(act_event4.act.is_state('VALIDE'))
113
        self.assertTrue(act_event5.act.is_state('NON_VALIDE'))
114
        self.assertTrue(act_event6.act.is_state('ACT_DOUBLE'))
calebasse/actes/views.py
14 14
            service=service)
15 15

  
16 16
class ActListingView(ListView):
17
    model=models.EventAct
17
    model=models.Act
18 18
    template_name='actes/act_listing.html'
19 19

  
20 20
    def get_queryset(self):
21 21
        qs = super(ActListingView, self).get_queryset()
22 22
        self.get_search_form()
23
        qs = qs.filter(services=self.service)
23
        qs = qs.filter(patient__service=self.service)
24 24
        qs = qs.filter(date=self.date)
25 25
        if self.request.method == 'POST' and self.search_form.is_valid():
26 26
            cd = self.search_form.cleaned_data
calebasse/agenda/admin.py
2 2

  
3 3
import reversion
4 4

  
5
from models import Event, EventType, Note, Occurrence
5
from models import Event, EventType, EventWithAct
6 6

  
7 7
admin.site.register(Event, reversion.VersionAdmin)
8
admin.site.register(Occurrence, reversion.VersionAdmin)
9 8
admin.site.register(EventType, reversion.VersionAdmin)
10
admin.site.register(Note, reversion.VersionAdmin)
9
admin.site.register(EventWithAct, reversion.VersionAdmin)
calebasse/agenda/appointments.py
23 23
        self.patient_record_paper_id = None
24 24
        self.event_id = None
25 25
        self.event_type = None
26
        self.occurrence_id = None
26
        self.event_id = None
27 27
        self.workers = None
28 28
        self.workers_initial = None
29 29
        self.workers_codes = None
......
41 41
        else:
42 42
            self.begin_hour = None
43 43

  
44
    def init_from_occurrence(self, occurrence, service):
44
    def init_from_event(self, event, service):
45 45
        """ """
46
        delta = occurrence.end_time - occurrence.start_time
47
        self.occurrence_id = occurrence.id
46
        delta = event.end_datetime - event.start_datetime
47
        self.event_id = event.id
48 48
        self.length = delta.seconds / 60
49
        self.title = occurrence.title
50
        services = occurrence.event.services.all()
51
        self.date = occurrence.start_time.date()
52
        self.__set_time(time(occurrence.start_time.hour, occurrence.start_time.minute))
49
        self.title = event.title
50
        services = event.services.all()
51
        self.date = event.start_datetime.date()
52
        self.__set_time(time(event.start_datetime.hour, event.start_datetime.minute))
53 53
        for e_service in services:
54 54
            if e_service != service:
55 55
                name = e_service.name.lower().replace(' ', '-')
......
58 58
            self.type = "busy-here"
59 59
        else:
60 60
            self.type = "busy-elsewhere"
61
        self.event_id = occurrence.event.id
62
        if occurrence.event.room:
63
            self.room = occurrence.event.room.name
64
        self.description = occurrence.event.description
65
        if occurrence.event.event_type.id == 1:
66
            event_act = occurrence.event.eventact
67
            workers = event_act.participants.all()
61
        self.event_id = event.id
62
        if event.room:
63
            self.room = event.room.name
64
        self.description = event.description
65
        if event.event_type.id == 1:
66
            event_act = event.act
67
            workers = event_act.doctors.all()
68 68
            self.convocation_sent = event_act.convocation_sent
69 69
            self.patient = event_act.patient
70 70
            self.patient_record_id = event_act.patient.id
......
93 93
                    validation_states.pop('ACT_DOUBLE')
94 94
            self.validation = (event_act, state, display_name, validation_states)
95 95
        else:
96
            self.event_type = occurrence.event.event_type
96
            self.event_type = event.event_type
97 97

  
98 98
    def init_free_time(self, length, begin_time):
99 99
        """ """
......
116 116
        self.title = title
117 117
        self.__set_time(time)
118 118

  
119
def get_daily_appointments(date, worker, service, time_tables, occurrences, holidays):
119
def get_daily_appointments(date, worker, service, time_tables, events, holidays):
120 120
    """
121 121
    """
122 122
    appointments = []
123 123

  
124 124
    timetables_set = IntervalSet((t.to_interval(date) for t in time_tables))
125
    occurrences_set = IntervalSet((o.to_interval() for o in occurrences))
125
    events_set = IntervalSet((o.to_interval() for o in events))
126 126
    holidays_set = IntervalSet((h.to_interval(date) for h in holidays))
127
    busy_occurrences_set = IntervalSet((o.to_interval() for o in occurrences if not o.is_event_absence()))
127
    busy_occurrences_set = IntervalSet((o.to_interval() for o in events_set if not o.is_event_absence()))
128 128
    for free_time in timetables_set - (busy_occurrences_set+holidays_set):
129 129
        if free_time:
130 130
            delta = free_time.upper_bound - free_time.lower_bound
......
133 133
            appointment.init_free_time(delta_minutes,
134 134
                    time(free_time.lower_bound.hour, free_time.lower_bound.minute))
135 135
            appointments.append(appointment)
136
    for occurrence in occurrences:
136
    for event in events:
137 137
        appointment = Appointment()
138
        appointment.init_from_occurrence(occurrence, service)
138
        appointment.init_from_event(event, service)
139 139
        appointments.append(appointment)
140 140
    for holiday in holidays:
141 141
        interval = holiday.to_interval(date)
calebasse/agenda/conf/default.py
27 27

  
28 28
# Indicate the default length in time for a new occurrence, specifed by using
29 29
# a datetime.timedelta object
30
DEFAULT_OCCURRENCE_DURATION = datetime.timedelta(hours=+1)
30
DEFAULT_EVENT_DURATION = datetime.timedelta(hours=+1)
31 31

  
32 32
# If not None, passed to the calendar module's setfirstweekday function.
33 33
CALENDAR_FIRST_WEEKDAY = 1
calebasse/agenda/forms.py
4 4

  
5 5
from django import forms
6 6

  
7
from calebasse.dossiers.models import PatientRecord
8
from calebasse.personnes.models import Worker
9
from calebasse.actes.models import EventAct
10
from calebasse.agenda.models import Event, EventType
11
from calebasse.ressources.models import ActType
12
from calebasse.middleware.request import get_request
7
from ..dossiers.models import PatientRecord
8
from ..personnes.models import Worker
9
from ..ressources.models import ActType
10
from ..middleware.request import get_request
13 11

  
14 12
from ajax_select import make_ajax_field
13
from models import Event, EventWithAct, EventType
15 14

  
16 15
class NewAppointmentForm(forms.ModelForm):
17 16
    date = forms.DateField(label=u'Date')
......
19 18
    duration = forms.CharField(label=u'Durée',
20 19
            help_text=u'en minutes; vous pouvez utiliser la roulette de votre souris.')
21 20

  
22
    participants = make_ajax_field(EventAct, 'participants', 'worker-or-group', True)
23
    patient = make_ajax_field(EventAct, 'patient', 'patientrecord', False)
21
    participants = make_ajax_field(EventWithAct, 'participants', 'worker-or-group', True)
22
    patient = make_ajax_field(EventWithAct, 'patient', 'patientrecord', False)
24 23

  
25 24
    class Meta:
26
        model = EventAct
25
        model = EventWithAct
27 26
        fields = (
28 27
                'date',
29 28
                'time',
......
52 51
        duration = self.cleaned_data['duration']
53 52
        try:
54 53
            return int(duration)
55
        except:
56
            return None
54
        except ValueError:
55
            return 0
57 56

  
58
    def save(self, commit=False):
59
        start_datetime = datetime.combine(self.cleaned_data['date'],
57
    def save(self, commit=True):
58
        appointment = super(NewAppointmentForm, self).save(commit=False)
59
        appointment.start_datetime = datetime.combine(self.cleaned_data['date'],
60 60
                    self.cleaned_data['time'])
61
        end_datetime = start_datetime + timedelta(
61
        appointment.end_datetime = appointment.start_datetime + timedelta(
62 62
                minutes=self.cleaned_data['duration'])
63
        patient = self.cleaned_data['patient']
64
        creator = get_request().user
65
        self.instance = EventAct.objects.create_patient_appointment(
66
                creator=creator,
67
                title=patient.display_name,
68
                patient=patient,
69
                participants=self.cleaned_data['participants'],
70
                act_type=self.cleaned_data['act_type'],
71
                service=self.service,
72
                start_datetime=start_datetime,
73
                end_datetime=end_datetime,
74
                description='',
75
                room=self.cleaned_data['room'],
76
                note=None,)
77
        return self.instance
78

  
79
class UpdateAppointmentForm(NewAppointmentForm):
63
        appointment.recurrence_end_date = appointment.start_datetime.date()
64
        appointment.creator = get_request().user
65
        appointment.title = appointment.patient.display_name
66
        appointment.service = self.service
67
        appointment.clean()
68
        if commit:
69
            appointment.save()
70
        return appointment
80 71

  
81
    def __init__(self, instance, service=None, occurrence=None, **kwargs):
82
        super(UpdateAppointmentForm, self).__init__(instance=instance,
83
                                                    service=service, **kwargs)
84
        self.occurrence = occurrence
85 72

  
86

  
87
    def save(self):
88
        self.occurrence.start_time = datetime.combine(
89
                self.cleaned_data['date'],
90
                self.cleaned_data['time'])
91
        self.occurrence.end_time = self.occurrence.start_time + timedelta(
92
                minutes=self.cleaned_data['duration'])
93
        self.occurrence.save()
94
        patient = self.cleaned_data['patient']
95
        creator = get_request().user
96
        self.instance.title = patient.display_name
97
        self.instance.participants = self.cleaned_data['participants']
98
        self.instance.save()
99
        return self.instance
73
class UpdateAppointmentForm(NewAppointmentForm):
74
    pass
100 75

  
101 76

  
102 77
class NewEventForm(forms.ModelForm):
......
106 81
    time = forms.TimeField(label=u'Heure de début')
107 82
    duration = forms.CharField(label=u'Durée',
108 83
            help_text=u'en minutes; vous pouvez utiliser la roulette de votre souris.')
109
    participants = make_ajax_field(EventAct, 'participants', 'worker-or-group', True)
84

  
85
    participants = make_ajax_field(Event, 'participants', 'worker-or-group', True)
110 86

  
111 87
    class Meta:
112 88
        model = Event
113
        widgets = {'services': forms.CheckboxSelectMultiple}
114 89
        fields = (
115 90
                'title',
116 91
                'date',
......
118 93
                'duration',
119 94
                'room',
120 95
                'participants',
121
                'services',
122 96
                'event_type'
123 97
        )
124 98

  
......
135 109
        except:
136 110
            return None
137 111

  
138
    def save(self, commit=False):
139
        start_datetime = datetime.combine(self.cleaned_data['date'],
112
    def save(self, commit=True):
113
        event = super(NewEventForm, self).save(commit=False)
114
        event.start_datetime = datetime.combine(self.cleaned_data['date'],
140 115
                    self.cleaned_data['time'])
141
        end_datetime = start_datetime + timedelta(
116
        event.end_datetime = event.start_datetime + timedelta(
142 117
                minutes=self.cleaned_data['duration'])
143
        self.instance = Event.objects.create_event(
144
                title=self.cleaned_data['title'],
145
                event_type=self.cleaned_data['event_type'],
146
                participants=self.cleaned_data['participants'],
147
                services=self.cleaned_data['services'],
148
                start_datetime=start_datetime,
149
                end_datetime=end_datetime,
150
                description='',
151
                room=self.cleaned_data['room'],
152
                note=None,)
153
        return self.instance
118
        event.recurrence_end_date = event.start_datetime.date()
119
        event.creator = get_request().user
120
        event.service = self.service
121
        event.clean()
122
        if commit:
123
            event.save()
124
        return event
154 125

  
155 126
    def clean(self):
156 127
        cleaned_data = super(NewEventForm, self).clean()
......
159 130
            self._errors['title'] = self.error_class([
160 131
                            u"Ce champ est obligatoire pour les événements de type « Autre »."])
161 132
        return cleaned_data
162

  
163

  
164
class UpdateEventForm(NewEventForm):
165

  
166
    def __init__(self, instance, occurrence=None, **kwargs):
167
        super(UpdateEventForm, self).__init__(instance=instance, **kwargs)
168
        self.occurrence = occurrence
169

  
170
    def save(self):
171
        self.occurrence.start_time = datetime.combine(
172
                self.cleaned_data['date'],
173
                self.cleaned_data['time'])
174
        self.occurrence.end_time = self.occurrence.start_time + timedelta(
175
                minutes=self.cleaned_data['duration'])
176
        self.occurrence.save()
177
        creator = get_request().user
178
        self.instance.participants = self.cleaned_data['participants']
179
        self.instance.services = self.cleaned_data['services']
180
        self.instance.save()
181
        return self.instance
calebasse/agenda/managers.py
1 1

  
2
from datetime import datetime, timedelta, date
2
from datetime import datetime, timedelta, date, time
3 3
from interval import IntervalSet
4 4

  
5
from django.db import models
6
from model_utils.managers import InheritanceManager
5
from django.db.models import Q
6
from model_utils.managers import InheritanceManager, PassThroughManager, InheritanceQuerySet
7 7

  
8 8
from calebasse.agenda.conf import default
9
from calebasse.utils import weeks_since_epoch
9 10
from calebasse import agenda
10 11

  
11 12
__all__ = (
12 13
    'EventManager',
13
    'OccurrenceManager',
14 14
)
15 15

  
16
class EventManager(InheritanceManager):
17
    """ This class allows you to manage events, appointment, ...
18
    """
19

  
20
    def _set_event(self, event, participants=[], description='', services=[],
21
            start_datetime=None, end_datetime=None, note=None, room=None, **rrule_params):
22
        """ Private method to configure an Event or an EventAct
23
        """
24
        event.description = description
25
        event.participants = participants
26
        event.services = services
27
        event.room = room
28
        if note is not None:
29
            event.notes.create(note=note)
30
        start_datetime = start_datetime or datetime.now().replace(
31
            minute=0, second=0, microsecond=0
32
        )
33
        occurence_duration = default.DEFAULT_OCCURRENCE_DURATION
34
        end_datetime = end_datetime or start_datetime + occurence_duration
35
        event.add_occurrences(start_datetime, end_datetime, **rrule_params)
36
        event.save()
37

  
38
        return event
39 16

  
40

  
41
    def create_event(self, title, event_type, participants=[], description='',
42
        services=[], start_datetime=None, end_datetime=None, room=None, note=None,
43
        **rrule_params):
44
        """
45
        Convenience function to create an ``Event``, optionally create an
46
        ``EventType``, and associated ``Occurrence``s. ``Occurrence`` creation
47
        rules match those for ``Event.add_occurrences``.
48

  
49
        Args:
50
            event_type: can be either an ``EventType`` object or the label
51
            is either created or retrieved.
52
            participants: List of CalebasseUser
53
            start_datetime: will default to the current hour if ``None``
54
            end_datetime: will default to ``start_datetime`` plus
55
            default.DEFAULT_OCCURRENCE_DURATION hour if ``None``
56
            freq, count, rrule_params:
57
            follow the ``dateutils`` API (see http://labix.org/python-dateutil)
58
        Returns:
59
            Event object
60
        """
61

  
62
        if isinstance(event_type, str):
63
            event_type, created = agenda.models.EventType.objects.get_or_create(
64
                label=event_type
65
            )
66
        event = self.create(title=title, event_type=event_type)
67

  
68
        return self._set_event(event, participants, services = services,
69
                start_datetime = start_datetime, end_datetime = end_datetime,
70
                room=room, **rrule_params)
71

  
72
    def create_holiday(self, start_date, end_date, peoples=[], services=[], motive=''):
73
        event_type, created = agenda.models.EventType.objects.get_or_create(
74
                label=u"Vacances"
75
                )
76
        event = self.create(title="Conge", event_type=event_type)
77
        start_datetime = datetime(start_date.year, start_date.month, start_date.day)
78
        end_datetime = datetime(end_date.year, end_date.month, end_date.day, 23, 59)
79
        return self._set_event(event, peoples, motive, services, start_datetime, end_datetime)
80

  
81
class OccurrenceManager(models.Manager):
82

  
83
    def daily_occurrences(self, date=None, participants=None, services=None,
84
            event_type=None):
85
        '''
86
        Returns a queryset of for instances that have any overlap with a
87
        particular day.
88

  
89
        Args:
90
            date: may be either a datetime.datetime, datetime.date object, or
91
            ``None``. If ``None``, default to the current day.
92
            participants: a list of CalebasseUser
93
            services: a list of services
94
            event_type: a single, or a list of, event types
95
        '''
96
        date = date or datetime.now()
97
        start = datetime(date.year, date.month, date.day)
98
        end = start.replace(hour=23, minute=59, second=59)
99
        qs = self.select_related('event').filter(
100
            models.Q(
101
                start_time__gte=start,
102
                start_time__lte=end,
103
            ) |
104
            models.Q(
105
                end_time__gte=start,
106
                end_time__lte=end,
107
            ) |
108
            models.Q(
109
                start_time__lt=start,
110
                end_time__gt=end,
111
            )
112
        )
113

  
114
        if participants:
115
            qs = qs.filter(event__participants__in=participants)
116
        if services:
117
            qs = qs.filter(event__services__in=services)
118
        if event_type:
119
            if type(event_type) is list:
120
                qs = qs.filter(event__event_type__in=event_type)
121
            else:
122
                qs = qs.filter(event__event_type=event_type)
17
class EventQuerySet(InheritanceQuerySet):
18
    def for_today(self, today=None):
19
        today = today or date.today()
20
        weeks = weeks_since_epoch(today)
21
        filters = [Q(start_datetime__gte=datetime.combine(today, time()),
22
               start_datetime__lte=datetime.combine(today, time(23,59,59))) ]
23
        base_q = Q(start_datetime__lte=datetime.combine(today, time(23,59,59))) & \
24
                (Q(recurrence_end_date__gte=today) |
25
                    Q(recurrence_end_date__isnull=True))
26
        for period in range(1, 6):
27
            filters.append(base_q & Q(recurrence_week_offset=weeks % period,
28
                recurrence_week_period=period, recurrence_week_day=today.weekday()))
29
        qs = self.filter(reduce(Q.__or__, filters))
123 30
        return qs
124 31

  
125
    def daily_disponibility(self, date, occurrences, participants, time_tables, holidays):
32
    def today_occurences(self, today=None):
33
        today = today or date.today()
34
        self = self.for_today(today)
35
        occurences = ( e.today_occurence(today) for e in self )
36
        return sorted(occurences, key=lambda e: e.start_datetime)
37

  
38
    def daily_disponibilities(self, date, events, participants, time_tables,
39
            holidays):
40
        '''Slice the day into quarters between 8:00 and 19:00, and returns the
41
           list of particpants with their status amon free, busy and away for each
42
           hour and quarters.
43

  
44
           date - the date of day we slice
45
           occurences - a dictionnary of iterable of events indexed by participants
46
           participants - an iterable of participants
47
           time_tables - a dictionnaty of timetable applying for this day indexed by participants
48
           holidays - a dictionnary of holidays applying for this day indexed by participants
49
        '''
126 50
        result = dict()
127 51
        quarter = 0
128
        occurrences_set = {}
52
        events_set = {}
129 53
        timetables_set = {}
130 54
        holidays_set = {}
131 55
        for participant in participants:
132
            occurrences_set[participant.id] = IntervalSet((o.to_interval() for o in occurrences[participant.id] if not o.is_event_absence()))
56
            events_set[participant.id] = IntervalSet((o.to_interval() for o in events[participant.id] if not o.is_event_absence()))
133 57
            timetables_set[participant.id] = IntervalSet((t.to_interval(date) for t in time_tables[participant.id]))
134 58
            holidays_set[participant.id] = IntervalSet((h.to_interval(date) for h in holidays[participant.id]))
135 59
        start_datetime = datetime(date.year, date.month, date.day, 8, 0)
......
137 61
        while (start_datetime.hour <= 19):
138 62
            for participant in participants:
139 63
                if not result.has_key(start_datetime.hour):
140
                    result[start_datetime.hour] = [0, 1, 2, 3]
141
                    result[start_datetime.hour][0] = []
142
                    result[start_datetime.hour][1] = []
143
                    result[start_datetime.hour][2] = []
144
                    result[start_datetime.hour][3] = []
64
                    result[start_datetime.hour] = [[], [], [], []]
145 65
                    quarter = 0
146

  
147 66
                interval = IntervalSet.between(start_datetime, end_datetime, False)
148
                if interval.intersection(occurrences_set[participant.id]):
67
                if interval.intersection(events_set[participant.id]):
149 68
                    result[start_datetime.hour][quarter].append({'id': participant.id, 'dispo': 'busy'})
150 69
                elif interval.intersection(holidays_set[participant.id]):
151 70
                    result[start_datetime.hour][quarter].append({'id': participant.id, 'dispo': 'busy'})
......
158 77
            end_datetime += timedelta(minutes=15)
159 78
        return result
160 79

  
161
    def next_appoinment(self, patient_record):
162
        qs = self.next_appoinments(patient_record)
80

  
81
class EventManager(PassThroughManager.for_queryset_class(EventQuerySet),
82
        InheritanceManager):
83
    """ This class allows you to manage events, appointment, ...
84
    """
85

  
86
    def create_event(self, creator, title, event_type, participants=[], description='',
87
            services=[], start_datetime=None, end_datetime=None, room=None, note=None, periodicity=1, until=False, **kwargs):
88
        """
89
        Convenience function to create an ``Event``, optionally create an
90
        ``EventType``.
91

  
92
        Args:
93
            event_type: can be either an ``EventType`` object or the label
94
            is either created or retrieved.
95
            participants: List of CalebasseUser
96
            start_datetime: will default to the current hour if ``None``
97
            end_datetime: will default to ``start_datetime`` plus
98
            default.DEFAULT_EVENT_DURATION hour if ``None``
99
        Returns:
100
            Event object
101
        """
102
        if isinstance(event_type, str):
103
            event_type, created = agenda.models.EventType.objects.get_or_create(
104
                label=event_type
105
            )
106
        if not start_datetime:
107
            now = datetime.now()
108
            start_datetime = datetime.combine(now.date, time(now.hour))
109
        if not end_datetime:
110
            end_datetime = start_datetime + default.DEFAULT_EVENT_DURATION
111
        if until is False:
112
            until = start_datetime.date()
113
        event = self.create(creator=creator,
114
                title=title, start_datetime=start_datetime,
115
                end_datetime=end_datetime, event_type=event_type,
116
                room=room, recurrence_week_period=periodicity,
117
                recurrence_end_date=until,
118
                recurrence_week_day=start_datetime.weekday(), **kwargs)
119
        event.services = services
120
        event.participants = participants
121
        return event
122

  
123
    def next_appointment(self, patient_record):
124
        qs = self.next_appointments(patient_record)
163 125
        if qs:
164 126
            return qs[0]
165 127
        else:
166 128
            return None
167 129

  
168
    def next_appoinments(self, patient_record):
169
        today = date.today()
170
        return self.filter(start_time__gt=datetime(today.year, today.month, today.day)).\
171
                filter(event__event_type__id=1).\
172
                filter(event__eventact__patient=patient_record).\
173
                prefetch_related('event__eventact').\
174
                order_by('start_time')
130
    def next_appointments(self, patient_record, today=None):
131
        from calebasse.actes.models import Act
132
        acts = Act.objects.next_acts(patient_record, today=today) \
133
                .filter(parent_event__isnull=False) \
134
                .select_related()
135
        return [ a.parent_event.today_occurence(a.date) for a in acts ]
175 136

  
176
    def last_appoinment(self, patient_record):
177
        qs = self.last_appoinments(patient_record)
137
    def last_appointment(self, patient_record):
138
        qs = self.last_appointments(patient_record)
178 139
        if qs:
179 140
            return qs[0]
180 141
        else:
181 142
            return None
182 143

  
183
    def last_appoinments(self, patient_record):
184
        return self.filter(start_time__lt=datetime.now()).\
185
                filter(event__event_type__id=1).\
186
                filter(event__eventact__patient=patient_record).\
187
                prefetch_related('event__eventact').\
188
                order_by('-start_time')
144
    def last_appointments(self, patient_record, today=None):
145
        from calebasse.actes.models import Act
146
        acts = Act.objects.last_acts(patient_record, today=today) \
147
                .filter(parent_event__isnull=False) \
148
                .select_related()
149
        return [ a.parent_event.today_occurence(a.date) for a in acts ]
calebasse/agenda/models.py
1 1
# -*- coding: utf-8 -*-
2 2

  
3
from datetime import datetime
4

  
5
from dateutil import rrule
3
from datetime import datetime, date, timedelta
4
from copy import copy
6 5

  
7 6
from django.utils.translation import ugettext_lazy as _
8
from django.contrib.contenttypes.models import ContentType
9
from django.contrib.contenttypes import generic
7
from django.contrib.auth.models import User
10 8
from django.db import models
11 9

  
10
from ..middleware.request import get_request
12 11
from calebasse.agenda import managers
12
from calebasse.utils import weeks_since_epoch
13 13
from interval import Interval
14 14

  
15 15
__all__ = (
16
    'Note',
17 16
    'EventType',
18 17
    'Event',
19
    'Occurrence',
18
    'EventWithAct',
20 19
)
21 20

  
22
class Note(models.Model):
23
    '''
24
    A generic model for adding simple, arbitrary notes to other models such as
25
    ``Event`` or ``Occurrence``.
26
    '''
27

  
28
    class Meta:
29
        verbose_name = u'Note'
30
        verbose_name_plural = u'Notes'
31

  
32
    def __unicode__(self):
33
        return self.note
34

  
35
    note = models.TextField(_('note'))
36
    created = models.DateTimeField(_('created'), auto_now_add=True)
37
    content_type = models.ForeignKey(ContentType)
38
    object_id = models.IntegerField()
39

  
40

  
41 21
class EventType(models.Model):
42 22
    '''
43 23
    Simple ``Event`` classifcation.
......
55 35

  
56 36
class Event(models.Model):
57 37
    '''
58
    Container model for general metadata and associated ``Occurrence`` entries.
38
    Container model for general agenda events
59 39
    '''
60 40
    objects = managers.EventManager()
61 41

  
62 42
    title = models.CharField(_('Title'), max_length=32, blank=True)
63 43
    description = models.TextField(_('Description'), max_length=100)
64 44
    event_type = models.ForeignKey(EventType, verbose_name=u"Type d'événement")
65
    notes = generic.GenericRelation(Note, verbose_name=_('notes'))
45
    creator = models.ForeignKey(User, verbose_name=_(u'Créateur'), blank=True, null=True)
46
    create_date = models.DateTimeField(_(u'Date de création'), auto_now_add=True)
66 47

  
67 48
    services = models.ManyToManyField('ressources.Service',
68 49
            null=True, blank=True, default=None)
......
71 52
    room = models.ForeignKey('ressources.Room', blank=True, null=True,
72 53
            verbose_name=u'Salle')
73 54

  
55
    start_datetime = models.DateTimeField(_('Début'), blank=True, null=True, db_index=True)
56
    end_datetime = models.DateTimeField(_('Fin'), blank=True, null=True)
57

  
58
    PERIODS = (
59
            (1, u'Toutes les semaines'),
60
            (2, u'Une semaine sur deux'),
61
            (3, u'Une semaine sur trois'),
62
            (4, 'Une semaine sur quatre'),
63
            (5, 'Une semaine sur cinq')
64
    )
65
    OFFSET = range(0,4)
66
    recurrence_week_day = models.PositiveIntegerField(default=0)
67
    recurrence_week_offset = models.PositiveIntegerField(
68
            choices=zip(OFFSET, OFFSET),
69
            verbose_name=u"Décalage en semaines par rapport au 1/1/1970 pour le calcul de période",
70
            default=0,
71
            db_index=True)
72
    recurrence_week_period = models.PositiveIntegerField(
73
            choices=PERIODS,
74
            verbose_name=u"Période en semaines",
75
            default=None,
76
            blank=True,
77
            null=True,
78
            db_index=True)
79
    recurrence_end_date = models.DateField(
80
            verbose_name=_(u'Fin de la récurrence'),
81
            blank=True, null=True,
82
            db_index=True)
83

  
74 84
    class Meta:
75 85
        verbose_name = u'Evénement'
76 86
        verbose_name_plural = u'Evénements'
77
        ordering = ('title', )
78

  
79

  
80
    def __unicode__(self):
81
        return self.title
82

  
83
    def add_occurrences(self, start_time, end_time, room=None, **rrule_params):
87
        ordering = ('start_datetime', 'end_datetime', 'title')
88

  
89
    def __init__(self, *args, **kwargs):
90
        if kwargs.get('start_datetime') and not kwargs.has_key('recurrence_end_date'):
91
            kwargs['recurrence_end_date'] = kwargs.get('start_datetime').date()
92
        super(Event, self).__init__(*args, **kwargs)
93

  
94
    def clean(self):
95
        '''Initialize recurrence fields if they are not.'''
96
        if self.start_datetime:
97
            if self.recurrence_week_period:
98
                if self.recurrence_end_date and self.recurrence_end_date < self.start_datetime.date():
99
                    self.recurrence_end_date = self.start_datetime.date()
100
                self.recurrence_week_day = self.start_datetime.weekday()
101
                self.recurrence_week_offset = weeks_since_epoch(self.start_datetime) % self.recurrence_week_period
102

  
103
    def timedelta(self):
104
        '''Distance between start and end of the event'''
105
        return self.end_datetime - self.start_datetime
106

  
107
    def today_occurence(self, today=None):
108
        '''For a recurring event compute the today 'Event'.
109

  
110
           The computed event is the fake one that you cannot save to the database.
84 111
        '''
85
        Add one or more occurences to the event using a comparable API to 
86
        ``dateutil.rrule``. 
87

  
88
        If ``rrule_params`` does not contain a ``freq``, one will be defaulted
89
        to ``rrule.DAILY``.
90

  
91
        Because ``rrule.rrule`` returns an iterator that can essentially be
92
        unbounded, we need to slightly alter the expected behavior here in order
93
        to enforce a finite number of occurrence creation.
94

  
95
        If both ``count`` and ``until`` entries are missing from ``rrule_params``,
96
        only a single ``Occurrence`` instance will be created using the exact
97
        ``start_time`` and ``end_time`` values.
112
        today = today or date.today()
113
        if not self.is_recurring():
114
            if today == self.start_datetime.date():
115
                return self
116
            else:
117
                return None
118
        if today.weekday() != self.recurrence_week_day:
119
            return None
120
        if weeks_since_epoch(today) % self.recurrence_week_period != self.recurrence_week_offset:
121
            return None
122
        if not (self.start_datetime.date() <= today <= self.recurrence_end_date):
123
            return None
124
        start_datetime = datetime.combine(today, self.start_datetime.timetz())
125
        end_datetime = start_datetime + self.timedelta()
126
        event = copy(self)
127
        event.start_datetime = start_datetime
128
        event.end_datetime = end_datetime
129
        event.recurrence_end_date = None
130
        event.recurrence_week_offset = None
131
        event.recurrence_week_period = None
132
        event.parent = self
133
        def save(*args, **kwarks): 
134
            raise RuntimeError()
135
        event.save = save
136
        return event
137

  
138
    def next_occurence(self, today=None):
139
        '''Returns the next occurence after today.'''
140
        today = today or date.today()
141
        for occurence in self.all_occurences():
142
            if occurence.start_datetime.date() > today:
143
                return occurence
144

  
145
    def is_recurring(self):
146
        '''Is this event multiple ?'''
147
        return self.recurrence_week_period is not None
148

  
149
    def all_occurences(self, limit=90):
150
        '''Returns all occurences of this event as a list or pair (start_datetime,
151
           end_datetime).
152

  
153
           limit - compute occurrences until limit days in the future
154

  
155
           Default is to limit to 90 days.
98 156
        '''
99
        rrule_params.setdefault('freq', rrule.DAILY)
100

  
101
        if 'count' not in rrule_params and 'until' not in rrule_params:
102
            self.occurrence_set.create(start_time=start_time, end_time=end_time)
157
        if self.recurrence_week_period:
158
            day = self.start_datetime.date()
159
            max_end_date = max(date.today(), self.start_datetime.date()) + timedelta(days=limit)
160
            end_date = min(self.recurrence_end_date or max_end_date, max_end_date)
161
            delta = timedelta(days=self.recurrence_week_period*7)
162
            while day <= end_date:
163
                yield self.today_occurence(day)
164
                day += delta
103 165
        else:
104
            delta = end_time - start_time
105
            for ev in rrule.rrule(dtstart=start_time, **rrule_params):
106
                self.occurrence_set.create(start_time=ev, end_time=ev + delta)
166
            yield self
107 167

  
108
    def upcoming_occurrences(self):
109
        '''
110
        Return all occurrences that are set to start on or after the current
111
        time.
112
        '''
113
        return self.occurrence_set.filter(start_time__gte=datetime.now())
114

  
115
    def next_occurrence(self):
116
        '''
117
        Return the single occurrence set to start on or after the current time
118
        if available, otherwise ``None``.
119
        '''
120
        upcoming = self.upcoming_occurrences()
121
        return upcoming and upcoming[0] or None
122

  
123
    def daily_occurrences(self, dt=None):
124
        '''
125
        Convenience method wrapping ``Occurrence.objects.daily_occurrences``.
126
        '''
127
        return Occurrence.objects.daily_occurrences(dt=dt, event=self)
128

  
129

  
130
class Occurrence(models.Model):
131
    '''
132
    Represents the start end time for a specific occurrence of a master ``Event``
133
    object.
134
    '''
135
    start_time = models.DateTimeField()
136
    end_time = models.DateTimeField()
137
    event = models.ForeignKey('Event', verbose_name=_('event'), editable=False)
138
    notes = models.ManyToManyField('Note', verbose_name=_('notes'),
139
            null=True, blank=True, default=None)
140

  
141
    objects = managers.OccurrenceManager()
168
    def to_interval(self):
169
        return Interval(self.start_datetime, self.end_datetime)
142 170

  
143
    class Meta:
144
        verbose_name = u'Occurrence'
145
        verbose_name_plural = u'Occurrences'
146
        ordering = ('start_time', 'end_time')
171
    def is_event_absence(self):
172
        return False
147 173

  
148 174
    def __unicode__(self):
149
        return u'%s: %s' % (self.title, self.start_time.isoformat())
175
        return self.title
150 176

  
151
    def __cmp__(self, other):
152
        return cmp(self.start_time, other.start_time)
153 177

  
154
    @property
155
    def title(self):
156
        return self.event.title
178
class EventWithActManager(managers.EventManager):
179
    def create_patient_appointment(self, creator, title, patient,
180
            doctors=[], act_type=None, service=None, start_datetime=None, end_datetime=None,
181
            room=None, periodicity=1, until=False):
182
        appointment = self.create_event(creator=creator,
183
                title=title,
184
                event_type=EventType(id=1),
185
                participants=doctors,
186
                services=[service],
187
                start_datetime=start_datetime,
188
                end_datetime=end_datetime,
189
                room=room,
190
                periodicity=periodicity,
191
                until=until,
192
                act_type=act_type,
193
                patient=patient)
194
        return appointment
195

  
196

  
197
class EventWithAct(Event):
198
    '''An event corresponding to an act.'''
199
    objects = EventWithActManager()
200
    act_type = models.ForeignKey('ressources.ActType',
201
        verbose_name=u'Type d\'acte')
202
    patient = models.ForeignKey('dossiers.PatientRecord')
157 203

  
158 204
    @property
159
    def event_type(self):
160
        return self.event.event_type
161

  
162
    def to_interval(self):
163
        return Interval(self.start_time, self.end_time)
205
    def act(self):
206
        return self.get_or_create_act()
207

  
208
    def get_or_create_act(self, today=None):
209
        from ..actes.models import Act, ActValidationState
210
        today = today or self.start_datetime.date()
211
        act, created = Act.objects.get_or_create(patient=self.patient,
212
                parent_event=getattr(self, 'parent', self),
213
                date=today,
214
                act_type=self.act_type)
215
        self.update_act(act)
216
        if created:
217
            ActValidationState.objects.create(act=act, state_name='NON_VALIDE',
218
                author=self.creator, previous_state=None)
219
        return act
220

  
221
    def update_act(self, act):
222
        '''Update an act to match details of the meeting'''
223
        delta = self.timedelta()
224
        duration = delta.seconds // 60
225
        act._duration = duration
226
        act.doctors = self.participants.select_subclasses()
227
        act.act_type = self.act_type
228
        act.patient = self.patient
229
        act.date = self.start_datetime.date()
230
        act.save()
231

  
232
    def save(self, *args, **kwargs):
233
        '''Force event_type to be patient meeting.'''
234
        self.clean()
235
        self.event_type = EventType(id=1)
236
        super(EventWithAct, self).save(*args, **kwargs)
237
        # list of occurences may have changed
238
        from ..actes.models import Act
239
        occurences = list(self.all_occurences())
240
        acts = Act.objects.filter(parent_event=self)
241
        occurences_by_date = dict((o.start_datetime.date(), o) for o in occurences)
242
        acts_by_date = dict()
243
        for a in acts:
244
            # sanity check
245
            assert a.date not in acts_by_date
246
            acts_by_date[a.date] = a
247
        for a in acts:
248
            o = occurences_by_date.get(a.date)
249
            if o:
250
                o.update_act(a)
251
            else:
252
                if len(a.actvalidationstate_set.all()) > 1:
253
                    a.parent_event = None
254
                    a.save()
255
                else:
256
                    a.delete()
257
        participants = self.participants.select_subclasses()
258
        for o in occurences:
259
            if o.start_datetime.date() in acts_by_date:
260
                continue
261
            o.get_or_create_act()
164 262

  
165 263
    def is_event_absence(self):
166
        if self.event.event_type.id != 1:
167
            return False
168
        event_act = self.event.eventact
169
        return event_act.is_absent()
264
        return self.act.is_absent()
265

  
266
    def __unicode__(self):
267
        kwargs = {
268
                'patient': self.patient,
269
                'start_datetime': self.start_datetime,
270
                'act_type': self.act_type
271
        }
272
        kwargs['doctors'] = ', '.join(map(unicode, self.participants.all())) if self.id else ''
273
        return u'Rdv le {start_datetime} de {patient} avec {doctors} pour ' \
274
            '{act_type} ({act_type.id})'.format(**kwargs)
calebasse/agenda/templates/agenda/ajax-worker-tab.html
23 23
        {% endif %}
24 24
        {% if appointment.event_id %}
25 25
         {% if appointment.patient_record_id %}
26
           <button title="Éditer un rendez-vous" class="edit-appointment icon-edit" data-occurrence-id="{{ appointment.occurrence_id }}"></button>
26
           <button title="Éditer un rendez-vous" class="edit-appointment icon-edit" data-event-id="{{ appointment.event_id }}"></button>
27 27
         {% else %}
28
           <button title="Éditer un événement" class="edit-event icon-edit" data-occurrence-id="{{ appointment.occurrence_id }}">
28
           <button title="Éditer un événement" class="edit-event icon-edit" data-event-id="{{ appointment.event_id }}">
29 29
         {% endif %}
30
        <button class="remove-appointment icon-remove-sign" title="Supprimer un rendez-vous" data-occurrence-id="{{ appointment.occurrence_id }}" data-rdv="{{ appointment.title }}"></button>
30
        <button class="remove-appointment icon-remove-sign" title="Supprimer un rendez-vous" data-event-id="{{ appointment.event_id }}" data-rdv="{{ appointment.title }}"></button>
31 31
        {% endif %}
32 32
   </span>
33 33
  </h3>
......
69 69
    </p>
70 70
    <a href="/{{ service }}/dossiers/{{ appointment.patient_record_id }}/view" target="_blank">Dossier patient</a> -
71 71
    <a href="/{{ service }}/dossiers/{{ appointment.patient_record_id }}/view#tab=5" target="_blank">Prochains rendez-vous</a> -
72
    <a href="#" class="generate-mail-btn" data-dossier-id="{{ appointment.patient_record_id }}" data-occurence-id="{{ appointment.occurrence_id }}" data-next-url="{{ request.get_full_path }}">Courrier</a>
72
    <a href="#" class="generate-mail-btn" data-dossier-id="{{ appointment.patient_record_id }}" data-date="{{date}}" data-occurence-id="{{ appointment.occurrence_id }}" data-next-url="{{ request.get_full_path }}">Courrier</a>
73 73
    {% endif %}
74 74
    {% if appointment.validation %}
75 75
      <div><span>{% if appointment.validation.1 %}<strong>{{ appointment.validation.2 }}</strong>, le {{ appointment.validation.1.created }} par {{ appointment.validation.1.author }}
calebasse/agenda/templates/agenda/ressources.html
70 70
                 {% endif %}
71 71
                 {% if appointment.event_id %}
72 72
                  {% if appointment.patient_record_id %}
73
                    <button title="Éditer un rendez-vous" class="edit-appointment icon-edit" data-occurrence-id="{{ appointment.occurrence_id }}"></button>
73
                    <button title="Éditer un rendez-vous" class="edit-appointment icon-edit" data-event-id="{{ appointment.event_id }}"></button>
74 74
                  {% else %}
75
                    <button title="Éditer un événement" class="edit-event icon-edit" data-occurrence-id="{{ appointment.occurrence_id }}">
75
                    <button title="Éditer un événement" class="edit-event icon-edit" data-event-id="{{ appointment.event_id }}">
76 76
                  {% endif %}
77
                 <button class="remove-appointment icon-remove-sign" title="Supprimer un rendez-vous" data-occurrence-id="{{ appointment.occurrence_id }}" data-rdv="{{ appointment.title }}"></button>
77
                 <button class="remove-appointment icon-remove-sign" title="Supprimer un rendez-vous" data-event-id="{{ appointment.event_id }}" data-rdv="{{ appointment.title }}"></button>
78 78
                 {% endif %}
79 79
            </span>
80 80
           </h3>
......
112 112
             </p>
113 113
             <a href="/{{ service }}/dossiers/{{ appointment.patient_record_id }}/view" target="_blank">Dossier patient</a> -
114 114
             <a href="/{{ service }}/dossiers/{{ appointment.patient_record_id }}/view#tab=5" target="_blank">Prochains rendez-vous</a> -
115
             <a href="#" class="generate-mail-btn" data-dossier-id="{{ appointment.patient_record_id }}" data-occurence-id="{{ appointment.occurrence_id }}" data-next-url="{{ request.get_full_path }}">Courrier</a>
115
             <a href="#" class="generate-mail-btn" data-dossier-id="{{ appointment.patient_record_id }}" data-date="{{date}}" data-occurence-id="{{ appointment.occurrence_id }}" data-next-url="{{ request.get_full_path }}">Courrier</a>
116 116
             {% endif %}
117 117
             {% if appointment.validation %}
118 118
               <div><span>{% if appointment.validation.1 %}<strong>{{ appointment.validation.2 }}</strong>, le {{ appointment.validation.1.created }} par {{ appointment.validation.1.author }}
calebasse/agenda/tests.py
4 4

  
5 5
Replace this with more appropriate tests for your application.
6 6
"""
7
from datetime import datetime
7
from datetime import datetime, date, timedelta
8 8

  
9 9
from django.test import TestCase
10 10
from django.contrib.auth.models import User
11
from datetime import datetime, date
12
from dateutil import rrule
13 11

  
14
from calebasse.agenda.models import Occurrence, Event
15
from calebasse.actes.models import EventAct
16
from calebasse.dossiers.models import PatientRecord
12
from calebasse.agenda.models import Event, EventType, EventWithAct
17 13
from calebasse.dossiers.models import create_patient
18 14
from calebasse.ressources.models import ActType, Service, WorkerType
19 15
from calebasse.personnes.models import Worker
16
from calebasse.actes.models import Act
20 17

  
21 18
class EventTest(TestCase):
22 19
    fixtures = ['services', 'filestates']
23 20
    def setUp(self):
24 21
        self.creator = User.objects.create(username='John')
25 22

  
26
    def test_create_events(self):
27
        event = Event.objects.create_event("test 1", 'un test', description='42 42',
28
            start_datetime=datetime(2012, 10, 20, 13), end_datetime=datetime(2012, 10, 20, 14),
29
            freq=rrule.WEEKLY, count=3)
30
        self.assertEqual(str(event), 'test 1')
23
    def test_recurring_events(self):
24
        event = Event.objects.create(start_datetime=datetime(2012, 10, 20, 13),
25
                end_datetime=datetime(2012, 10, 20, 13, 30), event_type=EventType(id=1),
26
                recurrence_week_period=3, recurrence_end_date=date(2012, 12, 1))
27
        event.clean()
28
        event.save()
29
        # Test model
30
        self.assertEqual(event.timedelta(), timedelta(minutes=30))
31
        occurences = list(event.all_occurences())
32
        self.assertEqual(occurences[0].start_datetime,
33
                datetime(2012, 10, 20, 13))
34
        self.assertEqual(occurences[0].end_datetime,
35
                datetime(2012, 10, 20, 13, 30))
36
        self.assertEqual(occurences[1].start_datetime,
37
                datetime(2012, 11, 10, 13))
38
        self.assertEqual(occurences[1].end_datetime,
39
                datetime(2012, 11, 10, 13, 30))
40
        self.assertEqual(occurences[2].start_datetime,
41
                datetime(2012, 12,  1, 13))
42
        self.assertEqual(occurences[2].end_datetime,
43
                datetime(2012, 12,  1, 13, 30))
44
        self.assertEqual(len(occurences), 3)
45
        self.assertTrue(event.is_recurring())
46
        self.assertEqual(event.next_occurence(today=date(2012, 10, 21)).start_datetime,
47
            datetime(2012, 11, 10, 13))
48
        self.assertEqual(event.next_occurence(today=date(2012, 11,  30)).start_datetime,
49
            datetime(2012, 12,  1, 13))
50
        self.assertEqual(event.next_occurence(today=date(2012, 12,  2)), None)
51
        # Test manager
52
        i = datetime(2012, 10, 1)
53
        while i < datetime(2013, 2, 1):
54
            d = i.date()
55
            if d in (date(2012, 10, 20), date(2012, 11, 10), date(2012, 12, 1)):
56
                self.assertEqual(list(Event.objects.for_today(d)), [event], d)
57
            else:
58
                self.assertEqual(list(Event.objects.for_today(d)), [], d)
59
            i += timedelta(days=1)
60

  
31 61

  
32 62
    def test_create_appointments(self):
33
        service_camsp = Service.objects.create(name='CAMSP')
63
        service_camsp = Service.objects.get(name='CAMSP')
34 64

  
35
        patient = create_patient('Jean', 'Lepoulpe', service_camsp, self.creator, date_selected=datetime(2020, 10, 5))
65
        patient = create_patient('Jean', 'LEPOULPE', service_camsp, self.creator, date_selected=datetime(2020, 10, 5))
36 66
        wtype = WorkerType.objects.create(name='ElDoctor', intervene=True)
37 67
        therapist1 = Worker.objects.create(first_name='Bob', last_name='Leponge', type=wtype)
38 68
        therapist2 = Worker.objects.create(first_name='Jean', last_name='Valjean', type=wtype)
39
        therapist3 = Worker.objects.create(first_name='Pierre', last_name='PaulJacques', type=wtype)
40 69
        act_type = ActType.objects.create(name='trepanation')
41 70
        service = Service.objects.create(name='CMPP')
42
        act_event = EventAct.objects.create_patient_appointment(self.creator, 'RDV avec M X', patient,
71
        appointment1 = EventWithAct.objects.create_patient_appointment(self.creator, 'RDV avec M X', patient,
43 72
                [therapist1, therapist2], act_type, service,
44 73
                start_datetime=datetime(2020, 10, 2, 7, 15),
45 74
                end_datetime=datetime(2020, 10, 2, 9, 20),
46
                freq=rrule.WEEKLY, byweekday=rrule.FR, until=datetime(2040, 10, 2))
47
        act_event2 = EventAct.objects.create_patient_appointment(self.creator, 'RDV avec M Y', patient, [therapist3],
75
                periodicity=None, until=None)
76
        self.assertEqual(unicode(appointment1), u'Rdv le 2020-10-02 07:15:00 de Jean LEPOULPE avec Bob LEPONGE, Jean VALJEAN pour trepanation (1)')
77

  
78
    def test_create_recurring_appointment(self):
79
        service_camsp = Service.objects.get(name='CAMSP')
80
        patient = create_patient('Jean', 'LEPOULPE', service_camsp, self.creator, date_selected=datetime(2020, 10, 5))
81
        wtype = WorkerType.objects.create(name='ElDoctor', intervene=True)
82
        therapist3 = Worker.objects.create(first_name='Pierre', last_name='PaulJacques', type=wtype)
83
        act_type = ActType.objects.create(name='trepanation')
84
        service = Service.objects.create(name='CMPP')
85
        appointment2 = EventWithAct.objects.create_patient_appointment(self.creator, 'RDV avec M Y', patient, [therapist3],
48 86
                act_type, service, start_datetime=datetime(2020, 10, 2, 10, 15),
49 87
                end_datetime=datetime(2020, 10, 2, 12, 20),
50
                freq=rrule.WEEKLY, byweekday=rrule.FR, until=datetime(2021, 10, 2))
51
        self.assertEqual(str(act_event), 'Rdv le 2020-10-02 07:15:00 de Jean Lepoulpe avec Bob Leponge, Jean Valjean pour trepanation (1)')
52
        self.assertEqual(
53
                str(Occurrence.objects.daily_occurrences(datetime(2020, 10, 8), [therapist2])),
54
                '[]'
55
                )
56
        self.assertEqual(
57
                str(Occurrence.objects.daily_occurrences(datetime(2020, 10, 9), [therapist2])),
58
                '[<Occurrence: RDV avec M X: 2020-10-09T07:15:00>]'
59
                )
60
        self.assertEqual(
61
                str(Occurrence.objects.daily_occurrences(datetime(2020, 10, 9), [therapist3])),
62
                '[<Occurrence: RDV avec M Y: 2020-10-09T10:15:00>]'
63
                )
88
                periodicity=2, until=date(2020, 10, 16))
89
        occurences = list(appointment2.all_occurences())
90
        self.assertEqual(len(occurences), 2)
91
        self.assertEqual(Act.objects.filter(parent_event=appointment2).count(),
92
                2)
93
        self.assertEqual(occurences[0].act.date, occurences[0].start_datetime.date())
94
        self.assertEqual(occurences[1].act.date, occurences[1].start_datetime.date())
95
        self.assertEqual(Act.objects.filter(parent_event=appointment2).count(),
96
                2)
97
        appointment2.recurrence_week_period = None
98
        appointment2.save()
99
        self.assertEqual(Act.objects.filter(parent_event=appointment2).count(),
100
                1)
101
        appointment2.recurrence_week_period = 2
102
        appointment2.recurrence_end_date = date(2020, 10, 16)
103
        appointment2.save()
104
        occurences = list(appointment2.all_occurences())
105
        self.assertEqual(len(occurences), 2)
106
        self.assertEqual(Act.objects.filter(parent_event=appointment2).count(), 2)
107
        occurences[1].act.set_state('ANNUL_NOUS', self.creator)
108
        appointment2.recurrence_week_period = None
109
        appointment2.save()
110
        occurences = list(appointment2.all_occurences())
111
        self.assertEqual(len(occurences), 1)
112
        self.assertEqual(Act.objects.filter(parent_event=appointment2).count(), 1)
113
        self.assertEqual(Act.objects.count(), 2)
64 114

  
65
    def test_create_holiday(self):
66
        """docstring for test_create_event"""
67
        wtype = WorkerType.objects.create(name='ElProfessor', intervene=True)
68
        user = Worker.objects.create(first_name='Jean-Claude', last_name='Van Damme', type=wtype)
69
        event = Event.objects.create_holiday(date(2012, 12, 12), date(2012,12,30),
70
                [user], motive='tournage d\'un film')
71
        self.assertEqual(str(event), 'Conge')
72
        #event = user.event.occurrence_set.all()[0]
73
        #self.assertEqual(event.end_time - event.start_time, timedelta(0, 7200))
calebasse/agenda/urls.py
16 16
            url(r'^nouveau-rdv/$',
17 17
                NewAppointmentView.as_view(),
18 18
                name='nouveau-rdv'),
19
            url(r'^update-rdv/(?P<id>\d+)$',
19
            url(r'^update-rdv/(?P<pk>\d+)$',
20 20
                UpdateAppointmentView.as_view(),
21 21
                name='update-rdv'),
22 22
            url(r'^new-event/$',
23 23
                NewEventView.as_view(),
24 24
                name='new-event'),
25
            url(r'^update-event/(?P<id>\d+)$',
25
            url(r'^update-event/(?P<pk>\d+)$',
26 26
                UpdateEventView.as_view(),
27 27
                name='update-event'),
28 28
            url(r'^activite-du-service/$',
calebasse/agenda/views.py
7 7
from django.http import HttpResponseRedirect
8 8

  
9 9
from calebasse.cbv import TemplateView, CreateView, UpdateView
10
from calebasse.agenda.models import Occurrence, Event, EventType
10
from calebasse.agenda.models import Event, EventType, EventWithAct
11 11
from calebasse.personnes.models import TimeTable, Holiday
12
from calebasse.actes.models import EventAct
13 12
from calebasse.agenda.appointments import get_daily_appointments, get_daily_usage
14 13
from calebasse.personnes.models import Worker
15 14
from calebasse.ressources.models import WorkerType, Room
16
from calebasse.actes.validation import get_acts_of_the_day
15
from calebasse.actes.validation import (get_acts_of_the_day,
16
        get_days_with_acts_not_locked)
17 17
from calebasse.actes.validation_states import VALIDATION_STATES
18 18
from calebasse.actes.models import Act, ValidationMessage
19
from calebasse.actes.validation import (automated_validation,
20
    unlock_all_acts_of_the_day, are_all_acts_of_the_day_locked)
19
from calebasse.actes.validation import (automated_validation, unlock_all_acts_of_the_day)
21 20
from calebasse import cbv
22 21

  
23
from forms import (NewAppointmentForm, NewEventForm,
24
        UpdateAppointmentForm, UpdateEventForm)
22
from forms import (NewAppointmentForm, NewEventForm, UpdateAppointmentForm)
25 23

  
26 24
def redirect_today(request, service):
27 25
    '''If not date is given we redirect on the agenda for today'''
......
70 68
        appointments_times = dict()
71 69
        appoinment_type = EventType.objects.get(id=1)
72 70
        meeting_type = EventType.objects.get(id=2)
73
        occurrences = Occurrence.objects.daily_occurrences(context['date'],
74
                services=[self.service],
75
                event_type=[appoinment_type, meeting_type])
76
        for occurrence in occurrences:
77
            start_time = occurrence.start_time.strftime("%H:%M")
78
            if not appointments_times.has_key(start_time):
79
                appointments_times[start_time] = dict()
80
                appointments_times[start_time]['row'] = 0
81
                appointments_times[start_time]['appointments'] = []
71
        plain_events = Event.objects.for_today(self.date) \
72
                .filter(services=self.service,
73
                        event_type__in=[appoinment_type, meeting_type])
74
        events = [ e.today_occurence(self.date) for e in plain_events ]
75
        for event in events:
76
            start_datetime = event.start_datetime.strftime("%H:%M")
77
            if not appointments_times.has_key(start_datetime):
78
                appointments_times[start_datetime] = dict()
79
                appointments_times[start_datetime]['row'] = 0
80
                appointments_times[start_datetime]['appointments'] = []
82 81
            appointment = dict()
83
            length = occurrence.end_time - occurrence.start_time
82
            length = event.end_datetime - event.start_datetime
84 83
            if length.seconds:
85 84
                length = length.seconds / 60
86 85
                appointment['length'] = "%sm" % length
87
            if occurrence.event.event_type == EventType.objects.get(id=1):
86
            if event.event_type == EventType.objects.get(id=1):
88 87
                appointment['type'] = 1
89
                event_act = occurrence.event.eventact
88
                event_act = event.act
90 89
                appointment['label'] = event_act.patient.display_name
91 90
                appointment['act'] = event_act.act_type.name
92
            elif occurrence.event.event_type == EventType.objects.get(id=2):
91
            elif event.event_type == EventType.objects.get(id=2):
93 92
                appointment['type'] = 2
94
                appointment['label'] = '%s - %s' % (occurrence.event.event_type.label,
95
                                                    occurrence.event.title)
93
                appointment['label'] = '%s - %s' % (event.event_type.label,
94
                                                    event.title)
96 95
            else:
97 96
                appointment['type'] = 0
98 97
                appointment['label'] = '???'
99
            appointment['participants'] = occurrence.event.participants.all()
100
            appointments_times[start_time]['row'] += 1
101
            appointments_times[start_time]['appointments'].append(appointment)
98
            appointment['participants'] = event.participants.all()
99
            appointments_times[start_datetime]['row'] += 1
100
            appointments_times[start_datetime]['appointments'].append(appointment)
102 101
        context['appointments_times'] = sorted(appointments_times.items())
103 102
        return context
104 103

  
105 104

  
106 105
class NewAppointmentView(cbv.ReturnToObjectMixin, cbv.ServiceFormMixin, CreateView):
107
    model = EventAct
106
    model = EventWithAct
108 107
    form_class = NewAppointmentForm
109 108
    template_name = 'agenda/nouveau-rdv.html'
110 109
    success_url = '..'
......
119 118

  
120 119

  
121 120
class UpdateAppointmentView(UpdateView):
122
    model = EventAct
121
    model = EventWithAct
123 122
    form_class = UpdateAppointmentForm
124 123
    template_name = 'agenda/update-rdv.html'
125 124
    success_url = '..'
126 125

  
127
    def get_object(self, queryset=None):
128
        self.occurrence = Occurrence.objects.get(id=self.kwargs['id'])
129
        if self.occurrence.event.eventact:
130
            return self.occurrence.event.eventact
131

  
132 126
    def get_initial(self):
133 127
        initial = super(UpdateView, self).get_initial()
134
        initial['date'] = self.object.date
135
        initial['time'] = self.occurrence.start_time.strftime("%H:%M")
136
        time = self.occurrence.end_time - self.occurrence.start_time
128
        initial['date'] = self.object.start_datetime.strftime("%Y-%m-%d")
129
        initial['time'] = self.object.start_datetime.strftime("%H:%M")
130
        time = self.object.end_datetime - self.object.start_datetime
137 131
        if time:
138 132
            time = time.seconds / 60
139 133
        else:
......
144 138

  
145 139
    def get_form_kwargs(self):
146 140
        kwargs = super(UpdateAppointmentView, self).get_form_kwargs()
147
        kwargs['occurrence'] = self.occurrence
148 141
        kwargs['service'] = self.service
149 142
        return kwargs
150 143

  
......
165 158
        initial['room'] = self.request.GET.get('room')
166 159
        return initial
167 160

  
161

  
168 162
class UpdateEventView(UpdateView):
169 163
    model = Event
170
    form_class = UpdateEventForm
164
    form_class = NewEventForm
171 165
    template_name = 'agenda/update-event.html'
172 166
    success_url = '..'
173 167

  
174
    def get_object(self, queryset=None):
175
        self.occurrence = Occurrence.objects.get(id=self.kwargs['id'])
176
        return self.occurrence.event
177

  
178 168
    def get_initial(self):
179 169
        initial = super(UpdateEventView, self).get_initial()
180
        initial['date'] = self.occurrence.start_time.date()
181
        initial['time'] = self.occurrence.start_time.strftime("%H:%M")
182
        time = self.occurrence.end_time - self.occurrence.start_time
170
        initial['date'] = self.object.start_datetime.strftime("%Y-%m-%d")
171
        initial['time'] = self.object.start_datetime.strftime("%H:%M")
172
        time = self.object.end_datetime - self.object.start_datetime
183 173
        if time:
184 174
            time = time.seconds / 60
185 175
        else:
......
188 178
        initial['participants'] = self.object.participants.values_list('id', flat=True)
189 179
        return initial
190 180

  
191
    def get_form_kwargs(self):
192
        kwargs = super(UpdateEventView, self).get_form_kwargs()
193
        kwargs['occurrence'] = self.occurrence
194
        return kwargs
195

  
196
def new_appointment(request):
197
    pass
198 181

  
199 182
class AgendaServiceActValidationView(TemplateView):
200

  
201 183
    template_name = 'agenda/act-validation.html'
202 184

  
203 185
    def acts_of_the_day(self):
......
299 281

  
300 282

  
301 283
class AgendasTherapeutesView(AgendaHomepageView):
302

  
303 284
    template_name = 'agenda/agendas-therapeutes.html'
304 285

  
305 286
    def get_context_data(self, **kwargs):
......
312 293
        holidays = Holiday.objects.select_related('worker'). \
313 294
                for_period(self.date, self.date). \
314 295
                order_by('start_date')
315
        occurrences = Occurrence.objects.daily_occurrences(context['date']).order_by('start_time')
296
        plain_events = Event.objects.for_today(self.date) \
297
                .order_by('start_datetime').select_subclasses()
298
        events = [ e.today_occurence(self.date) for e in plain_events ]
316 299

  
317
        occurrences_workers = {}
300
        events_workers = {}
318 301
        time_tables_workers = {}
319 302
        holidays_workers = {}
320 303
        context['workers_agenda'] = []
321 304
        for worker in context['workers']:
322 305
            time_tables_worker = [tt for tt in time_tables if tt.worker.id == worker.id]
323
            occurrences_worker = [o for o in occurrences if worker.id in o.event.participants.values_list('id', flat=True)]
306
            events_worker = [o for o in events if worker.id in o.participants.values_list('id', flat=True)]
324 307
            holidays_worker = [h for h in holidays if h.worker_id in (None, worker.id)]
325
            occurrences_workers[worker.id] = occurrences_worker
308
            events_workers[worker.id] = events_worker
326 309
            time_tables_workers[worker.id] = time_tables_worker
327 310
            holidays_workers[worker.id] = holidays_worker
328 311
            context['workers_agenda'].append({'worker': worker,
329 312
                    'appointments': get_daily_appointments(context['date'], worker, self.service,
330
                        time_tables_worker, occurrences_worker, holidays_worker)})
313
                        time_tables_worker, events_worker, holidays_worker)})
331 314

  
332 315
        for worker_agenda in context.get('workers_agenda', []):
333 316
            patient_appointments = [x for x in worker_agenda['appointments'] if x.patient_record_id]
......
342 325
        return context
343 326

  
344 327
class JoursNonVerrouillesView(TemplateView):
345

  
346 328
    template_name = 'agenda/days-not-locked.html'
347 329

  
348 330
    def get_context_data(self, **kwargs):
349 331
        context = super(JoursNonVerrouillesView, self).get_context_data(**kwargs)
350
        acts = EventAct.objects.filter(is_billed=False,
332
        acts = Act.objects.filter(is_billed=False,
351 333
            patient__service=self.service).order_by('date')
352
        days_not_locked = []
353
        for act in acts:
354
            current_day = datetime.datetime(act.date.year, act.date.month, act.date.day)
355
            if not current_day in days_not_locked:
356
                locked = are_all_acts_of_the_day_locked(current_day, self.service)
357
                if not locked:
358
                    days_not_locked.append(current_day)
359
        context['days_not_locked'] = days_not_locked
334
        days = set(acts.values_list('date', flat=True))
335
        max_day, min_day = max(days), min(days)
336
        days &= set(get_days_with_acts_not_locked(min_day, max_day, self.service))
337
        context['days_not_locked'] = days
360 338
        return context
361 339

  
362 340
class RessourcesView(TemplateView):
......
366 344
    def get_context_data(self, **kwargs):
367 345
        context = super(RessourcesView, self).get_context_data(**kwargs)
368 346

  
369
        occurrences = Occurrence.objects.daily_occurrences(context['date']).order_by('start_time')
347
        plain_events = Event.objects.for_today(self.date) \
348
                .order_by('start_datetime').select_subclasses()
349
        events = [ e.today_occurence(self.date) for e in plain_events ]
370 350

  
371 351
        context['ressources_types'] = []
372 352
        context['ressources_agenda'] = []
......
376 356
        context['ressources_types'].append(data)
377 357
        ressources.extend(data['ressources'])
378 358

  
379
        occurrences_ressources = {}
359
        events_ressources = {}
380 360
        for ressource in ressources:
381
            occurrences_ressource = [o for o in occurrences if ressource == o.event.room]
382
            occurrences_ressources[ressource.id] = occurrences_ressource
361
            events_ressource = [e for e in events if ressource == e.room]
362
            events_ressources[ressource.id] = events_ressource
383 363
            context['ressources_agenda'].append({'ressource': ressource,
384 364
                    'appointments': get_daily_usage(context['date'], ressource,
385
                        self.service, occurrences_ressource)})
365
                        self.service, events_ressource)})
386 366

  
387 367
        return context
388 368

  
......
401 381
        holidays_worker = Holiday.objects.for_worker_id(worker_id). \
402 382
                for_period(self.date, self.date). \
403 383
                order_by('start_date')
404
        occurrences = Occurrence.objects.daily_occurrences(context['date']).order_by('start_time')
405
        occurrences_worker = [o for o in occurrences if worker_id in o.event.participants.values_list('id', flat=True)]
384
        plain_events = Event.objects.for_today(self.date) \
385
                .order_by('start_datetime').select_subclasses()
386
        events = [ e.today_occurence(self.date) for e in plain_events ]
387
        events_worker = [e for e in events if worker_id in e.participants.values_list('id', flat=True)]
406 388

  
407 389
        worker = Worker.objects.get(pk=worker_id)
408 390
        context['worker_agenda'] = {'worker': worker,
409 391
                    'appointments': get_daily_appointments(context['date'], worker, self.service,
410
                        time_tables_worker, occurrences_worker, holidays_worker)}
392
                        time_tables_worker, events_worker, holidays_worker)}
411 393
        return context
412 394

  
413 395
class AjaxWorkerDisponibilityColumnView(TemplateView):
......
425 407
        holidays_worker = Holiday.objects.for_worker_id(worker_id). \
426 408
                for_period(self.date, self.date). \
427 409
                order_by('start_date')
428
        occurrences = Occurrence.objects.daily_occurrences(context['date']).order_by('start_time')
429
        occurrences_worker = [o for o in occurrences if worker_id in o.event.participants.values_list('id', flat=True)]
410
        events = Event.objects.today_occurences(self.date)
411
        events_worker = [e for e in events if worker_id in e.participants.values_list('id', flat=True)]
430 412

  
431 413
        worker = Worker.objects.get(pk=worker_id)
432 414
        time_tables_workers = {worker.id: time_tables_worker}
433
        occurrences_workers = {worker.id: occurrences_worker}
415
        events_workers = {worker.id: events_worker}
434 416
        holidays_workers = {worker.id: holidays_worker}
435 417

  
436 418
        context['initials'] = worker.get_initials()
437 419
        context['worker_id'] = worker.id
438
        context['disponibility'] = Occurrence.objects.daily_disponibility(context['date'],
439
                occurrences_workers, [worker], time_tables_workers, holidays_workers)
420
        context['disponibility'] = Event.objects.daily_disponibilities(self.date,
421
                events_workers, [worker], time_tables_workers, holidays_workers)
440 422
        return context
calebasse/api.py
1 1

  
2 2
from tastypie.authorization import DjangoAuthorization
3 3
from tastypie.resources import ModelResource
4
from calebasse.agenda.models import Event, Occurrence
4
from calebasse.agenda.models import Event
5 5
from calebasse.dossiers.models import PatientRecord, PatientAddress
6 6

  
7 7

  
......
11 11
        resource_name = 'event'
12 12
        authorization = DjangoAuthorization()
13 13

  
14
class OccurrenceResource(ModelResource):
15
    class Meta:
16
        queryset = Occurrence.objects.all()
17
        resource_name = 'occurrence'
18
        authorization = DjangoAuthorization()
19

  
20 14
class PatientRecordRessource(ModelResource):
21 15
    class Meta:
22 16
        queryset = PatientRecord.objects.all()
......
31 25

  
32 26
patientaddress_ressource = PatientAddressRessource()
33 27
event_resource = EventResource()
34
occurrence_resource = OccurrenceResource()
35 28
patientrecord_resource = PatientRecordRessource()
36 29

  
calebasse/dossiers/models.py
578 578
        cmpp = Service.objects.get(name='CMPP')
579 579
        for act in acts:
580 580
            if act.is_state('VALIDE') and act.is_billable() and \
581
                    act.date >= self.get_state().date_selected and \
581
                    act.date >= self.get_state().date_selected.date() and \
582 582
                    are_all_acts_of_the_day_locked(act.date):
583 583
                cared, hc = act.is_act_covered_by_diagnostic_healthcare()
584 584
                if hc:
calebasse/dossiers/views.py
15 15
from calebasse import cbv
16 16
from calebasse.doc_templates import make_doc_from_template
17 17
from calebasse.dossiers import forms
18
from calebasse.agenda.models import Occurrence
18
from calebasse.agenda.models import Event, EventWithAct
19
from calebasse.actes.models import Act
19 20
from calebasse.agenda.appointments import Appointment
20 21
from calebasse.dossiers.models import (PatientRecord, PatientContact,
21 22
        PatientAddress, Status, FileState, create_patient, CmppHealthCareTreatment,
......
27 28

  
28 29
def get_next_rdv(patient_record):
29 30
    next_rdv = {}
30
    occurrence = Occurrence.objects.next_appoinment(patient_record)
31
    if occurrence:
32
        next_rdv['start_datetime'] = occurrence.start_time
33
        next_rdv['participants'] = occurrence.event.participants.all()
34
        next_rdv['act_type'] = occurrence.event.eventact.act_type
31
    event = Event.objects.next_appointment(patient_record)
32
    if event:
33
        next_rdv['start_datetime'] = event.start_time
34
        next_rdv['participants'] = event.participants.all()
35
        next_rdv['act_type'] = event.eventact.act_type
35 36
    return next_rdv
36 37

  
37 38
def get_last_rdv(patient_record):
38 39
    last_rdv = {}
39
    occurrence = Occurrence.objects.last_appoinment(patient_record)
40
    if occurrence:
41
        last_rdv['start_datetime'] = occurrence.start_time
42
        last_rdv['participants'] = occurrence.event.participants.all()
43
        last_rdv['act_type'] = occurrence.event.eventact.act_type
40
    event = Event.objects.last_appointment(patient_record)
41
    if event:
42
        last_rdv['start_datetime'] = event.start_time
43
        last_rdv['participants'] = event.participants.all()
44
        last_rdv['act_type'] = event.eventact.act_type
44 45
    return last_rdv
45 46

  
46 47
class NewPatientRecordView(cbv.FormView, cbv.ServiceViewMixin):
......
254 255
        ctx['service_id'] = self.service.id
255 256
        ctx['states'] = FileState.objects.filter(patient=self.object).filter(status__services=self.service)
256 257
        ctx['next_rdvs'] = []
257
        for rdv in Occurrence.objects.next_appoinments(ctx['object']):
258
            state = rdv.event.eventact.get_state()
258
        for act in Act.objects.next_acts(ctx['object']).select_related():
259
            state = act.get_state()
259 260
            if not state.previous_state:
260 261
                state = None
261
            ctx['next_rdvs'].append((rdv, state))
262
            ctx['next_rdvs'].append((act.parent_event, state))
262 263
        ctx['last_rdvs'] = []
263
        for rdv in Occurrence.objects.last_appoinments(ctx['object']):
264
            state = rdv.event.eventact.get_state()
264
        for act in Act.objects.last_acts(ctx['object']):
265
            state = act.get_state()
265 266
            if not state.previous_state:
266 267
                state = None
267
            ctx['last_rdvs'].append((rdv, state))
268
            ctx['last_rdvs'].append((act.parent_event, state))
268 269
        ctx['status'] = []
269 270
        if ctx['object'].service.name == "CMPP":
270 271
            if ctx['object'].last_state.status.type == "ACCUEIL":
......
577 578
        ctx['object'] = PatientRecord.objects.get(id=self.kwargs['patientrecord_id'])
578 579
        ctx['service_id'] = self.service.id
579 580
        if self.request.GET.get('event-id'):
581
            date = self.request.GET.get('date')
582
            date = datetime.strptime(date, '%Y-%m-%d').date()
580 583
            appointment = Appointment()
581
            occurrence = Occurrence.objects.get(id=self.request.GET.get('event-id'))
582
            appointment.init_from_occurrence(occurrence, self.service)
584
            event = Event.objects.get(id=self.request.GET.get('event-id'))
585
            event = event.today_occurence(date)
586
            appointment.init_from_event(event, self.service)
583 587
            ctx['appointment'] = appointment
584 588
        return ctx
585 589

  
......
632 636
        else:
633 637
            qs = qs.filter(last_state__status__type__in='')
634 638

  
635
        qs_events = Occurrence.objects.select_related('event')
636 639
        try:
637 640
            date_actes_start = datetime.strptime(form.data['date_actes_start'], "%d/%m/%Y")
638
            qs_events = qs_events.filter(models.Q(start_time__gte=date_actes_start))
641
            qs = qs.filter(act_set__date__gte=date_actes_start.date())
639 642
        except (ValueError, KeyError):
640
            date_actes_start = None
643
            pass
641 644
        try:
642 645
            date_actes_end = datetime.strptime(form.data['date_actes_end'], "%d/%m/%Y")
643
            qs_events = qs_events.filter(models.Q(end_time__lte=date_actes_end))
646
            qs = qs.filter(act_set__date__lte=date_actes_end.date())
644 647
        except (ValueError, KeyError):
645
            date_actes_end = None
646

  
647
        if date_actes_start or date_actes_end:
648
            qs = qs.filter(id__in=[x.event.eventact.patient.id for x in qs_events])
649

  
648
            pass
650 649
        qs = qs.filter(service=self.service).order_by('last_name')
651 650
        return qs
652 651

  
calebasse/facturation/tests.py
10 10
from calebasse.actes.validation import automated_validation, \
11 11
    are_all_acts_of_the_day_locked, \
12 12
    get_days_with_acts_not_locked
13
from calebasse.actes.models import EventAct
13
from calebasse.actes.models import Act
14
from calebasse.agenda.models import EventWithAct
14 15
from calebasse.dossiers.models import create_patient, PatientRecord, \
15 16
    SessadHealthCareNotification, CmppHealthCareTreatment, CmppHealthCareDiagnostic
16 17
from calebasse.dossiers.models import Status
......
39 40
        service_camsp = Service.objects.get(name='CAMSP')
40 41

  
41 42
        patient_a = create_patient('a', 'A', service_camsp, self.creator, date_selected=datetime(2020, 10, 5))
42
        act0 = EventAct.objects.create_patient_appointment(self.creator, 'RDV', patient_a, [self.therapist3],
43
        act0 = EventWithAct.objects.create_patient_appointment(self.creator, 'RDV', patient_a, [self.therapist3],
43 44
                self.act_type, service_camsp, start_datetime=datetime(2020, 10, 6, 10, 15),
44 45
                end_datetime=datetime(2020, 10, 6, 12, 20))
45 46
        status_suivi = Status.objects.filter(services__name='CAMSP').filter(type='SUIVI')[0]
46 47
        patient_a.set_state(status_suivi, self.creator, date_selected=datetime(2020, 10, 7))
47
        act1 = EventAct.objects.create_patient_appointment(self.creator, 'RDV', patient_a, [self.therapist3],
48
        act1 = EventWithAct.objects.create_patient_appointment(self.creator, 'RDV', patient_a, [self.therapist3],
48 49
                self.act_type, service_camsp, start_datetime=datetime(2020, 10, 7, 10, 15),
49 50
                end_datetime=datetime(2020, 10, 7, 12, 20))
50
        act2 = EventAct.objects.create_patient_appointment(self.creator, 'RDV', patient_a, [self.therapist3],
51
        act2 = EventWithAct.objects.create_patient_appointment(self.creator, 'RDV', patient_a, [self.therapist3],
51 52
                self.act_type, service_camsp, start_datetime=datetime(2020, 10, 7, 14, 15),
52 53
                end_datetime=datetime(2020, 10, 7, 16, 20))
53
        act3 = EventAct.objects.create_patient_appointment(self.creator, 'RDV', patient_a, [self.therapist3],
54
        act3 = EventWithAct.objects.create_patient_appointment(self.creator, 'RDV', patient_a, [self.therapist3],
54 55
                self.act_type, service_camsp, start_datetime=datetime(2020, 10, 7, 16, 20),
55 56
                end_datetime=datetime(2020, 10, 7, 17, 20))
56 57
        status_clos = Status.objects.filter(services__name='CAMSP').filter(type='CLOS')[0]
57 58
        patient_a.set_state(status_clos, self.creator, date_selected=datetime(2020, 10, 8))
58
        act4 = EventAct.objects.create_patient_appointment(self.creator, 'RDV', patient_a, [self.therapist3],
59
        act4 = EventWithAct.objects.create_patient_appointment(self.creator, 'RDV', patient_a, [self.therapist3],
59 60
                self.act_type, service_camsp, start_datetime=datetime(2020, 10, 8, 10, 15),
60 61
                end_datetime=datetime(2020, 10, 8, 12, 20))
61 62

  
62 63
        patient_b = create_patient('b', 'B', service_camsp, self.creator, date_selected=datetime(2020, 10, 4))
63
        act5 = EventAct.objects.create_patient_appointment(self.creator, 'RDV', patient_b, [self.therapist3],
64
        act5 = EventWithAct.objects.create_patient_appointment(self.creator, 'RDV', patient_b, [self.therapist3],
64 65
                self.act_type, service_camsp, start_datetime=datetime(2020, 10, 4, 10, 15),
65 66
                end_datetime=datetime(2020, 10, 4, 12, 20))
66
        act6 = EventAct.objects.create_patient_appointment(self.creator, 'RDV', patient_b, [self.therapist3],
67
        act6 = EventWithAct.objects.create_patient_appointment(self.creator, 'RDV', patient_b, [self.therapist3],
67 68
                self.act_type, service_camsp, start_datetime=datetime(2020, 10, 5, 10, 15),
68 69
                end_datetime=datetime(2020, 10, 5, 12, 20))
69 70
        act6.set_state('ABS_EXC', self.creator)
70
        act7 = EventAct.objects.create_patient_appointment(self.creator, 'RDV', patient_b, [self.therapist3],
71
        act7 = EventWithAct.objects.create_patient_appointment(self.creator, 'RDV', patient_b, [self.therapist3],
71 72
                self.act_type, service_camsp, start_datetime=datetime(2020, 10, 5, 10, 15),
72 73
                end_datetime=datetime(2020, 10, 5, 12, 20))
73 74
        act7.switch_billable = True
74 75
        act7.save()
75 76
        patient_b.set_state(status_suivi, self.creator, date_selected=datetime(2020, 10, 6))
76
        act8 = EventAct.objects.create_patient_appointment(self.creator, 'RDV', patient_b, [self.therapist3],
77
        act8 = EventWithAct.objects.create_patient_appointment(self.creator, 'RDV', patient_b, [self.therapist3],
77 78
                self.act_type, service_camsp, start_datetime=datetime(2020, 10, 7, 10, 15),
78 79
                end_datetime=datetime(2020, 10, 7, 12, 20))
79
        act9 = EventAct.objects.create_patient_appointment(self.creator, 'RDV', patient_b, [self.therapist3],
80
        act9 = EventWithAct.objects.create_patient_appointment(self.creator, 'RDV', patient_b, [self.therapist3],
80 81
                self.act_type, service_camsp, start_datetime=datetime(2020, 10, 7, 14, 15),
81 82
                end_datetime=datetime(2020, 10, 7, 16, 20))
82
        act10 = EventAct.objects.create_patient_appointment(self.creator, 'RDV', patient_b, [self.therapist3],
83
        act10 = EventWithAct.objects.create_patient_appointment(self.creator, 'RDV', patient_b, [self.therapist3],
83 84
                self.act_type, service_camsp, start_datetime=datetime(2020, 10, 7, 16, 20),
84 85
                end_datetime=datetime(2020, 10, 7, 17, 20))
85
        act11 = EventAct.objects.create_patient_appointment(self.creator, 'RDV', patient_b, [self.therapist3],
86
        act11 = EventWithAct.objects.create_patient_appointment(self.creator, 'RDV', patient_b, [self.therapist3],
86 87
                self.act_type, service_camsp, start_datetime=datetime(2020, 10, 8, 10, 15),
87 88
                end_datetime=datetime(2020, 10, 8, 12, 20))
88 89
        patient_b.set_state(status_clos, self.creator, date_selected=datetime(2020, 10, 9))
......
149 150
        service_sessad = Service.objects.get(name='SESSAD DYS')
150 151

  
151 152
        patient_a = create_patient('a', 'A', service_sessad, self.creator, date_selected=datetime(2020, 10, 5))
152
        act0 = EventAct.objects.create_patient_appointment(self.creator, 'RDV', patient_a, [self.therapist3],
153
        act0 = EventWithAct.objects.create_patient_appointment(self.creator, 'RDV', patient_a, [self.therapist3],
153 154
                self.act_type, service_sessad, start_datetime=datetime(2020, 10, 6, 10, 15),
154 155
                end_datetime=datetime(2020, 10, 6, 12, 20))
155 156
        status_traitement = Status.objects.filter(services__name='SESSAD DYS').filter(type='TRAITEMENT')[0]
156 157
        patient_a.set_state(status_traitement, self.creator, date_selected=datetime(2020, 10, 7))
157
        act1 = EventAct.objects.create_patient_appointment(self.creator, 'RDV', patient_a, [self.therapist3],
158
        act1 = EventWithAct.objects.create_patient_appointment(self.creator, 'RDV', patient_a, [self.therapist3],
158 159
                self.act_type, service_sessad, start_datetime=datetime(2020, 10, 7, 10, 15),
159 160
                end_datetime=datetime(2020, 10, 7, 12, 20))
160
        act2 = EventAct.objects.create_patient_appointment(self.creator, 'RDV', patient_a, [self.therapist3],
161
        act2 = EventWithAct.objects.create_patient_appointment(self.creator, 'RDV', patient_a, [self.therapist3],
161 162
                self.act_type, service_sessad, start_datetime=datetime(2020, 10, 7, 14, 15),
162 163
                end_datetime=datetime(2020, 10, 7, 16, 20))
163
        act3 = EventAct.objects.create_patient_appointment(self.creator, 'RDV', patient_a, [self.therapist3],
164
        act3 = EventWithAct.objects.create_patient_appointment(self.creator, 'RDV', patient_a, [self.therapist3],
164 165
                self.act_type, service_sessad, start_datetime=datetime(2020, 10, 7, 16, 20),
165 166
                end_datetime=datetime(2020, 10, 7, 17, 20))
166 167
        status_clos = Status.objects.filter(services__name='SESSAD DYS').filter(type='CLOS')[0]
167 168
        patient_a.set_state(status_clos, self.creator, date_selected=datetime(2020, 10, 8))
168
        act4 = EventAct.objects.create_patient_appointment(self.creator, 'RDV', patient_a, [self.therapist3],
169
        act4 = EventWithAct.objects.create_patient_appointment(self.creator, 'RDV', patient_a, [self.therapist3],
169 170
                self.act_type, service_sessad, start_datetime=datetime(2020, 10, 8, 10, 15),
170 171
                end_datetime=datetime(2020, 10, 8, 12, 20))
171 172

  
......
196 197
        service_cmpp = Service.objects.get(name='CMPP')
197 198

  
198 199
        patient_a = create_patient('a', 'A', service_cmpp, self.creator, date_selected=datetime(2020, 10, 1))
199
        acts = [ EventAct.objects.create_patient_appointment(self.creator, 'RDV', patient_a, [self.therapist3],
200
        acts = [ EventWithAct.objects.create_patient_appointment(self.creator, 'RDV', patient_a, [self.therapist3],
200 201
                self.act_type, service_cmpp, start_datetime=datetime(2020, 10, i, 10, 15),
201 202
                end_datetime=datetime(2020, 10, i, 12, 20)) for i in range (1, 32)]
202 203
        status_accueil = Status.objects.filter(services__name='CMPP').filter(type='ACCUEIL')[0]
......
236 237
            patient_a = PatientRecord.objects.get(id=patient_a.id)
237 238
            self.assertEqual(patient_a.get_state().status, status_traitement)
238 239

  
239
        acts_2 = [ EventAct.objects.create_patient_appointment(self.creator, 'RDV', patient_a, [self.therapist3],
240
        acts_2 = [ EventWithAct.objects.create_patient_appointment(self.creator, 'RDV', patient_a, [self.therapist3],
240 241
                self.act_type, service_cmpp, start_datetime=datetime(2020, 11, i, 10, 15),
241 242
                end_datetime=datetime(2020, 11, i, 12, 20)) for i in range (1, 31)]
242 243
        for i in range(1, 31):
......
281 282
        patients = []
282 283
        for j in range(2):
283 284
            patients.append(create_patient(str(j), str(j), service_cmpp, self.creator, date_selected=datetime(2012, 10, 1)))
284
            acts = [ EventAct.objects.create_patient_appointment(self.creator, 'RDV', patients[j], [self.therapist3],
285
            acts = [ EventWithAct.objects.create_patient_appointment(self.creator, 'RDV', patients[j], [self.therapist3],
285 286
                    self.act_type, service_cmpp, start_datetime=datetime(2012, 10, i, 10, 15),
286 287
                    end_datetime=datetime(2012, 10, i, 12, 20)) for i in range (1, 32)]
287
            acts_2 = [ EventAct.objects.create_patient_appointment(self.creator, 'RDV', patients[j], [self.therapist3],
288
            acts_2 = [ EventWithAct.objects.create_patient_appointment(self.creator, 'RDV', patients[j], [self.therapist3],
288 289
                    self.act_type, service_cmpp, start_datetime=datetime(2012, 11, i, 10, 15),
289 290
                    end_datetime=datetime(2012, 11, i, 12, 20)) for i in range (1, 31)]
290 291
            hct = CmppHealthCareTreatment(patient=patients[j], start_date=datetime(2012, 10, 7), author=self.creator)
......
376 377

  
377 378

  
378 379
        patient = create_patient('A', 'a', service_cmpp, self.creator, date_selected=datetime(2012, 10, 1))
379
        acts = [ EventAct.objects.create_patient_appointment(self.creator, 'RDV', patient, [self.therapist3],
380
        acts = [ EventWithAct.objects.create_patient_appointment(self.creator, 'RDV', patient, [self.therapist3],
380 381
                self.act_type, service_cmpp, start_datetime=datetime(2012, 10, i, 10, 15),
381 382
                end_datetime=datetime(2012, 10, i, 12, 20)) for i in range (1, 32)]
382 383
        hct = CmppHealthCareTreatment(patient=patient, start_date=datetime(2011, 11, 7), author=self.creator)
......
407 408
        self.assertEqual(len_patient_with_lost_acts, 0)
408 409

  
409 410

  
410
        acts_2 = [ EventAct.objects.create_patient_appointment(self.creator, 'RDV', patient, [self.therapist3],
411
        acts_2 = [ EventWithAct.objects.create_patient_appointment(self.creator, 'RDV', patient, [self.therapist3],
411 412
                self.act_type, service_cmpp, start_datetime=datetime(2012, 11, i, 10, 15),
412 413
                end_datetime=datetime(2012, 11, i, 12, 20)) for i in range (1, 31)]
413 414

  
......
447 448

  
448 449
        self.assertEqual(patient.last_state.status.type, "ACCUEIL")
449 450

  
450
        EventAct.objects.create_patient_appointment(self.creator, 'RDV', patient, [self.therapist3],
451
        EventWithAct.objects.create_patient_appointment(self.creator, 'RDV', patient, [self.therapist3],
451 452
                self.act_type, service_cmpp, start_datetime=datetime(2012, 10, 1, 10, 15),
452 453
                end_datetime=datetime(2012, 10, 1, 12, 20))
453 454

  
......
457 458
        self.assertEqual(patient.last_state.status.type, "DIAGNOSTIC")
458 459
        self.assertEqual(patient.last_state.date_selected, datetime(2012, 10, 1, 0, 0))
459 460

  
460
        acts = [ EventAct.objects.create_patient_appointment(self.creator, 'RDV', patient, [self.therapist3],
461
        acts = [ EventWithAct.objects.create_patient_appointment(self.creator, 'RDV', patient, [self.therapist3],
461 462
                self.act_type, service_cmpp, start_datetime=datetime(2012, 10, i, 10, 15),
462 463
                end_datetime=datetime(2012, 10, i, 12, 20)) for i in range (2, 32)]
463 464

  
......
472 473

  
473 474
        self.assertEqual(patient.last_state.status.type, "ACCUEIL")
474 475

  
475
        EventAct.objects.create_patient_appointment(self.creator, 'RDV', patient, [self.therapist3],
476
        EventWithAct.objects.create_patient_appointment(self.creator, 'RDV', patient, [self.therapist3],
476 477
                self.act_type, service_cmpp, start_datetime=datetime(2012, 10, 1, 10, 15),
477 478
                end_datetime=datetime(2012, 10, 1, 12, 20))
478 479

  
......
489 490
        self.assertEqual(patient.last_state.status.type, "CLOS")
490 491
        self.assertEqual(patient.last_state.date_selected, datetime(2012, 12, 9, 0, 0))
491 492

  
492
        acts = [ EventAct.objects.create_patient_appointment(self.creator, 'RDV', patient, [self.therapist3],
493
        acts = [ EventWithAct.objects.create_patient_appointment(self.creator, 'RDV', patient, [self.therapist3],
493 494
                self.act_type, service_cmpp, start_datetime=datetime(2012, 10, i, 10, 15),
494 495
                end_datetime=datetime(2012, 10, i, 12, 20)) for i in range (2, 32)]
495 496

  
......
506 507
        price_o = add_price(120, date(2012, 10, 1))
507 508

  
508 509
        patient = create_patient('A', 'a', service_cmpp, self.creator, date_selected=datetime(2012, 10, 1))
509
        acts = [ EventAct.objects.create_patient_appointment(self.creator, 'RDV', patient, [self.therapist3],
510
        acts = [ EventWithAct.objects.create_patient_appointment(self.creator, 'RDV', patient, [self.therapist3],
510 511
                self.act_type, service_cmpp, start_datetime=datetime(2012, 10, i, 10, 15),
511 512
                end_datetime=datetime(2012, 10, i, 12, 20)) for i in range (1, 4)]
512 513

  
......
545 546

  
546 547
        hcd.set_act_number(5)
547 548

  
548
        acts = [ EventAct.objects.create_patient_appointment(self.creator, 'RDV', patient, [self.therapist3],
549
        acts = [ EventWithAct.objects.create_patient_appointment(self.creator, 'RDV', patient, [self.therapist3],
549 550
                self.act_type, service_cmpp, start_datetime=datetime(2012, 10, i, 10, 15),
550 551
                end_datetime=datetime(2012, 10, i, 12, 20)) for i in range (4, 32)]
551 552

  
......
601 602

  
602 603
        hct.set_act_number(28)
603 604

  
604
        acts = [ EventAct.objects.create_patient_appointment(self.creator, 'RDV', patient, [self.therapist3],
605
        acts = [ EventWithAct.objects.create_patient_appointment(self.creator, 'RDV', patient, [self.therapist3],
605 606
                self.act_type, service_cmpp, start_datetime=datetime(2012, 11, i, 10, 15),
606 607
                end_datetime=datetime(2012, 11, i, 12, 20)) for i in range (1, 4)]
607 608

  
calebasse/fixtures/tests/test_actes.json
2 2
    {
3 3
        "fields": {
4 4
            "act_type": 97,
5
            "date": "2012-10-30T12:00:00",
5
            "date": "2012-10-30",
6 6
            "patient": 8,
7 7
            "transport_company": null,
8 8
            "transport_type": null,
9
            "validation_locked": false
9
            "validation_locked": false,
10
            "doctors": [
11
                5
12
            ],
13
            "parent_event": 1
10 14
        },
11 15
        "model": "actes.act",
12 16
        "pk": 1
......
14 18
    {
15 19
        "fields": {
16 20
            "act_type": 10,
17
            "date": "2012-10-30T00:00:00",
21
            "date": "2012-10-30",
18 22
            "patient": 9,
19 23
            "transport_company": null,
20 24
            "transport_type": null,
21
            "validation_locked": false
22
        },
23
        "model": "actes.act",
24
        "pk": 2
25
    },
26
    {
27
        "fields": {
28
            "attendance": "absent",
29
            "convocation_sent": false,
30
            "event_ptr": 1,
31
            "participants": [
25
            "validation_locked": false,
26
            "doctors": [
32 27
                5
33 28
            ],
34
            "services": [
35
                1
36
            ]
29
            "parent_event": 2
37 30
        },
38
        "model": "actes.eventact",
39
        "pk": 1
40
    },
41
    {
42
        "fields": {
43
            "attendance": "absent",
44
            "convocation_sent": false,
45
            "event_ptr": 2,
46
            "participants": [
47
                5
48
            ],
49
            "services": [
50
                1
51
            ]
52
        },
53
        "model": "actes.eventact",
31
        "model": "actes.act",
54 32
        "pk": 2
55 33
    },
56 34
    {
calebasse/fixtures/tests/test_agenda.json
105 105
        1,
106 106
        2
107 107
      ],
108
      "room": 2
108
      "room": 2,
109
      "create_date": "2012-10-20T11:30:00",
110
      "start_datetime": "2012-10-30T11:30:00",
111
      "end_datetime": "2012-10-30T12:00:00"
109 112
    }
110 113
  },
111 114
  {
......
121 124
      "services": [
122 125
        1
123 126
      ],
124
      "room": 1
127
      "room": 1,
128
      "create_date": "2012-10-20T11:30:00",
129
      "start_datetime": "2012-10-30T10:45:00",
130
      "end_datetime": "2012-10-30T11:30:00"
125 131
    }
126 132
  },
127 133
  {
......
137 143
      "services": [
138 144
        1
139 145
      ],
140
      "room": 2
146
      "room": 2,
147
      "create_date": "2012-10-20T11:30:00",
148
      "start_datetime": "2012-10-30T10:00:00",
149
      "end_datetime": "2012-10-30T10:45:00"
141 150
    }
142 151
  },
143 152
  {
144
    "pk": 1,
145
    "model": "agenda.occurrence",
146
    "fields": {
147
      "notes": [],
148
      "start_time": "2012-10-30T10:00:00",
149
      "end_time": "2012-10-30T10:45:00",
150
      "event": 1
151
    }
153
      "fields": {
154
          "act_type": 97,
155
          "patient": 10,
156
          "event_ptr": 1
157
      },
158
      "model": "agenda.eventwithact",
159
      "pk": 1
152 160
  },
153 161
  {
154
    "pk": 2,
155
    "model": "agenda.occurrence",
156
    "fields": {
157
      "notes": [],
158
      "start_time": "2012-10-30T10:45:00",
159
      "end_time": "2012-10-30T11:30:00",
160
      "event": 2
161
    }
162
  },
163
  {
164
    "pk": 3,
165
    "model": "agenda.occurrence",
166
    "fields": {
167
      "notes": [],
168
      "start_time": "2012-10-30T11:30:00",
169
      "end_time": "2012-10-30T12:00:00",
170
      "event": 3
171
    }
162
      "fields": {
163
          "act_type": 10,
164
          "patient": 9,
165
          "event_ptr": 2
166
      },
167
      "model": "agenda.eventwithact",
168
      "pk": 2
172 169
  }
173 170
]
calebasse/static/js/calebasse.agenda.js
26 26
          if (r == true)
27 27
          {
28 28
            $.ajax({
29
              url: '/api/occurrence/' + $(this).data('occurrence-id') + '/',
29
              url: '/api/event/' + $(this).data('event-id') + '/',
30 30
              type: 'DELETE',
31 31
              success: function(data) {
32 32
                  window.location.reload(true);
......
45 45
          event_dialog(new_appointment_url, 'Nouveau rendez-vous', '850px', 'Ajouter');
46 46
      });
47 47
      $(base).find('.edit-appointment').click(function() {
48
          event_dialog("update-rdv/" + $(this).data('occurrence-id') , 'Modifier rendez-vous', '850px', 'Modifier');
48
          event_dialog("update-rdv/" + $(this).data('event-id') , 'Modifier rendez-vous', '850px', 'Modifier');
49 49
          return false;
50 50
      });
51 51
      $(base).find('.newevent').click(function() {
......
56 56
          event_dialog($(this).data('url') + "?" + qs, 'Nouvel événement', '850px', 'Ajouter');
57 57
      });
58 58
      $(base).find('.edit-event').click(function() {
59
          event_dialog("update-event/" + $(this).data('occurrence-id') , 'Modifier un événement', '850px', 'Modifier');
59
          event_dialog("update-event/" + $(this).data('event-id') , 'Modifier un événement', '850px', 'Modifier');
60 60
          return false;
61 61
      });
62 62
      $(base).find('#print-button').click(function() { window.print(); });
63 63

  
64 64
      $('.generate-mail-btn', base).click(function() {
65
        var url = '../../dossiers/' + $(this).data('dossier-id') + '/generate?event-id=' + $(this).data('occurence-id');
65
        var url = '../../dossiers/' + $(this).data('dossier-id') + '/generate?event-id=' + $(this).data('occurence-id' + '&date=' + $(this).data('date'));
66 66
        $('#ajax-dlg').load(url,
67 67
          function () {
68 68
            $(this).dialog({title: 'Générer un courrier', width: '500px',
calebasse/urls.py
5 5

  
6 6
from urls_utils import decorated_includes
7 7

  
8
from calebasse.api import (event_resource, occurrence_resource,
8
from calebasse.api import (event_resource,
9 9
        patientrecord_resource, patientaddress_ressource)
10 10

  
11 11
admin.autodiscover()
......
34 34
    url(r'^accounts/', include('django.contrib.auth.urls')),
35 35
    url(r'^api/',
36 36
        decorated_includes(login_required, include(event_resource.urls))),
37
    url(r'^api/',
38
        decorated_includes(login_required, include(occurrence_resource.urls)),
39
        ),
40 37
    url(r'^api/',
41 38
        decorated_includes(login_required, include(patientrecord_resource.urls)),
42 39
        ),

Also available in: Unified diff