Project

General

Profile

Download (18.5 KB) Statistics
| Branch: | Tag: | Revision:

calebasse / calebasse / dossiers / models.py @ 428081e0

1
# -*- coding: utf-8 -*-
2

    
3
import logging
4

    
5
from datetime import datetime, date
6
from datetime import timedelta
7

    
8
from django import forms
9
from django.db import models
10
from django.contrib.auth.models import User
11

    
12
import reversion
13

    
14
from calebasse.models import PhoneNumberField, ZipCodeField
15
from calebasse.personnes.models import People
16
from calebasse.ressources.models import (ServiceLinkedAbstractModel,
17
        NamedAbstractModel, Service)
18
from calebasse.actes.validation import are_all_acts_of_the_day_locked
19

    
20
DEFAULT_ACT_NUMBER_DIAGNOSTIC = 6
21
DEFAULT_ACT_NUMBER_TREATMENT = 30
22
DEFAULT_ACT_NUMBER_PROLONGATION = 10
23
VALIDITY_PERIOD_TREATMENT_HEALTHCARE = 365
24

    
25
logger = logging.getLogger('calebasse.dossiers')
26

    
27

    
28
class HealthCare(models.Model):
29

    
30
    class Meta:
31
        app_label = 'dossiers'
32

    
33
    patient = models.ForeignKey('dossiers.PatientRecord',
34
        verbose_name=u'Dossier patient', editable=False)
35
    created = models.DateTimeField(u'Création', auto_now_add=True)
36
    author = \
37
        models.ForeignKey(User,
38
        verbose_name=u'Auteur', editable=False)
39
    comment = models.TextField(max_length=3000, blank=True, null=True)
40
    start_date = models.DateTimeField()
41

    
42

    
43
class CmppHealthCareDiagnostic(HealthCare):
44

    
45
    class Meta:
46
        app_label = 'dossiers'
47

    
48
    _act_number = models.IntegerField(default=DEFAULT_ACT_NUMBER_DIAGNOSTIC)
49

    
50
    def get_act_number(self):
51
        return self._act_number
52

    
53
    def save(self, **kwargs):
54
        self.start_date = \
55
            datetime(self.start_date.year, self.start_date.month,
56
                self.start_date.day)
57
        super(CmppHealthCareDiagnostic, self).save(**kwargs)
58

    
59

    
60
class CmppHealthCareTreatment(HealthCare):
61

    
62
    class Meta:
63
        app_label = 'dossiers'
64

    
65
    _act_number = models.IntegerField(default=DEFAULT_ACT_NUMBER_TREATMENT)
66
    end_date = models.DateTimeField()
67
    prolongation = models.IntegerField(default=0,
68
            verbose_name=u'Prolongation')
69

    
70
    def get_act_number(self):
71
        if self.is_extended():
72
            return self._act_number + self.prolongation
73
        return self._act_number
74

    
75
    def is_extended(self):
76
        if self.prolongation > 0:
77
            return True
78
        return False
79

    
80
    def add_prolongation(self, value=None):
81
        if not value:
82
            value = DEFAULT_ACT_NUMBER_PROLONGATION
83
        if self.is_extended():
84
            raise Exception(u'Prise en charge déja prolongée')
85
        self.prolongation = value
86
        self.save()
87

    
88
    def save(self, **kwargs):
89
        self.start_date = \
90
            datetime(self.start_date.year, self.start_date.month,
91
                self.start_date.day)
92
        self.end_date = self.start_date + \
93
            timedelta(days=VALIDITY_PERIOD_TREATMENT_HEALTHCARE)
94

    
95
        super(CmppHealthCareTreatment, self).save(**kwargs)
96

    
97

    
98
class SessadHealthCareNotification(HealthCare):
99

    
100
    class Meta:
101
        app_label = 'dossiers'
102

    
103
    end_date = models.DateTimeField()
104

    
105
    def save(self, **kwargs):
106
        self.start_date = \
107
            datetime(self.start_date.year, self.start_date.month,
108
                self.start_date.day)
109
        self.end_date = \
110
            datetime(self.end_date.year, self.end_date.month,
111
                self.end_date.day)
112
        super(SessadHealthCareNotification, self).save(**kwargs)
113

    
114
reversion.register(CmppHealthCareDiagnostic, follow=['healthcare_ptr'])
115
reversion.register(CmppHealthCareTreatment, follow=['healthcare_ptr'])
116
reversion.register(SessadHealthCareNotification, follow=['healthcare_ptr'])
117

    
118
class Status(NamedAbstractModel):
119

    
120
    class Meta:
121
        app_label = 'dossiers'
122
        verbose_name = u"Statut d'un état"
123
        verbose_name_plural = u"Statuts d'un état"
124

    
125
    type = models.CharField(max_length=80)
126
    services = models.ManyToManyField('ressources.Service')
127

    
128

    
129
class FileState(models.Model):
130

    
131
    class Meta:
132
        app_label = 'dossiers'
133
        verbose_name = u'Etat du dossier patient'
134
        verbose_name_plural = u'Etats du dossier patient'
135

    
136
    patient = models.ForeignKey('dossiers.PatientRecord',
137
        verbose_name=u'Dossier patient')
138
    status = models.ForeignKey('dossiers.Status')
139
    created = models.DateTimeField(u'Création', auto_now_add=True)
140
    date_selected = models.DateTimeField()
141
    author = \
142
        models.ForeignKey(User,
143
        verbose_name=u'Auteur')
144
    comment = models.TextField(max_length=3000, blank=True, null=True)
145
    previous_state = models.ForeignKey('FileState',
146
        verbose_name=u'Etat précédent', blank=True, null=True)
147

    
148
    def get_next_state(self):
149
        try:
150
            return FileState.objects.get(previous_state=self)
151
        except:
152
            return None
153

    
154
    def save(self, **kwargs):
155
        self.date_selected = \
156
                datetime(self.date_selected.year,
157
                        self.date_selected.month, self.date_selected.day)
158
        super(FileState, self).save(**kwargs)
159

    
160
    def __unicode__(self):
161
        return self.status.name + ' ' + str(self.date_selected)
162

    
163
class PatientAddress(models.Model):
164

    
165
    def __unicode__(self):
166
        return self.address + ', ' + self.city
167

    
168
    phone = PhoneNumberField(verbose_name=u"Téléphone", blank=True, null=True)
169
    fax = PhoneNumberField(verbose_name=u"Fax", blank=True, null=True)
170
    address = models.CharField(max_length=120,
171
            verbose_name=u"Adresse")
172
    address_complement = models.CharField(max_length=120,
173
            blank=True,
174
            null=True,
175
            default=None,
176
            verbose_name=u"Complément d'addresse")
177
    zip_code = ZipCodeField(verbose_name=u"Code postal")
178
    city = models.CharField(max_length=80,
179
            verbose_name=u"Ville")
180

    
181
class PatientContact(People):
182
    class Meta:
183
        verbose_name = u'Contact patient'
184
        verbose_name_plural = u'Contacts patient'
185

    
186
    mobile = PhoneNumberField(verbose_name=u"Téléphone mobile", blank=True, null=True)
187
    email = models.EmailField(blank=True, null=True)
188
    social_security_id = models.CharField(max_length=13, verbose_name=u"Numéro de sécurité sociale")
189
    addresses = models.ManyToManyField('PatientAddress', verbose_name=u"Adresses",
190
            blank=True, null=True)
191

    
192

    
193
class PatientRecordManager(models.Manager):
194
    def for_service(self, service):
195
        return self.filter(service=service)
196

    
197
class PatientRecord(ServiceLinkedAbstractModel, PatientContact):
198
    objects = PatientRecordManager()
199

    
200
    class Meta:
201
        verbose_name = u'Dossier'
202
        verbose_name_plural = u'Dossiers'
203

    
204
    created = models.DateTimeField(u'création', auto_now_add=True)
205
    creator = \
206
        models.ForeignKey(User,
207
        verbose_name=u'Créateur dossier patient',
208
        editable=True)
209
    contacts = models.ManyToManyField('personnes.People',
210
            related_name='contact_of')
211
    birthdate = models.DateField(verbose_name=u"Date de naissance",
212
            null=True, blank=True)
213
    nationality = models.CharField(verbose_name=u"Nationalité",
214
            max_length=70, null=True, blank=True)
215
    paper_id = models.CharField(max_length=12,
216
            null=True, blank=True)
217
    last_state = models.ForeignKey(FileState, related_name='+',
218
            null=True)
219
    school = models.ForeignKey('ressources.School',
220
            null=True, blank=True, default=None)
221
    comment = models.TextField(verbose_name=u"Commentaire",
222
            null=True, blank=True, default=None)
223
    pause = models.BooleanField(verbose_name=u"Pause facturation",
224
            default=False)
225

    
226
    # Physiology
227
    size = models.IntegerField(verbose_name=u"Taille (cm)",
228
            null=True, blank=True, default=None)
229
    weight = models.IntegerField(verbose_name=u"Poids (kg)",
230
            null=True, blank=True, default=None)
231
    pregnancy_term = models.IntegerField(verbose_name=u"Terme en semaines",
232
            null=True, blank=True, default=None)
233

    
234
    # Inscription motive
235
    analysemotive = models.ForeignKey('ressources.AnalyseMotive',
236
            verbose_name=u"Motif (analysé)",
237
            null=True, blank=True, default=None)
238
    familymotive = models.ForeignKey('ressources.FamilyMotive',
239
            verbose_name=u"Motif (famille)",
240
            null=True, blank=True, default=None)
241
    advicegiver = models.ForeignKey('ressources.AdviceGiver',
242
            verbose_name=u"Conseilleur",
243
            null=True, blank=True, default=None)
244

    
245
    # Family
246
    sibship_place = models.IntegerField(verbose_name=u"Place dans la fratrie",
247
            null=True, blank=True, default=None)
248
    nb_children_family = models.IntegerField(verbose_name=u"Nombre d'enfants dans la fratrie",
249
            null=True, blank=True, default=None)
250
    twinning_rank = models.IntegerField(verbose_name=u"Rang (gémellité)",
251
            null=True, blank=True, default=None)
252
    parental_authority = models.ForeignKey('ressources.ParentalAuthorityType',
253
            verbose_name=u"Autorité parentale",
254
            null=True, blank=True, default=None)
255
    family_situation = models.ForeignKey('ressources.FamilySituationType',
256
            verbose_name=u"Situation familiale",
257
            null=True, blank=True, default=None)
258
    child_custody = models.ForeignKey('ressources.ParentalCustodyType',
259
            verbose_name=u"Garde parentale",
260
            null=True, blank=True, default=None)
261

    
262
    # Transport
263
    transporttype = models.ForeignKey('ressources.TransportType',
264
            verbose_name=u"Type de transport",
265
            null=True, blank=True, default=None)
266
    transportcompany = models.ForeignKey('ressources.TransportCompany',
267
            verbose_name=u"Compagnie de transport",
268
            null=True, blank=True, default=None)
269

    
270
    # FollowUp
271
    coordinators = models.ManyToManyField('personnes.Worker',
272
            verbose_name=u"Coordinateurs",
273
            null=True, blank=True, default=None)
274
    externaldoctor = models.ForeignKey('personnes.ExternalDoctor',
275
            verbose_name=u"Médecin extérieur",
276
            null=True, blank=True, default=None)
277
    externalintervener = models.ForeignKey('personnes.ExternalIntervener',
278
            verbose_name=u"Intervenant extérieur",
279
            null=True, blank=True, default=None)
280

    
281
    def __init__(self, *args, **kwargs):
282
        super(PatientRecord, self).__init__(*args, **kwargs)
283
        if not hasattr(self, 'service'):
284
            raise Exception('The field service is mandatory.')
285

    
286
    def get_state(self):
287
        return self.last_state
288

    
289
    def get_current_state(self):
290
        today = date.today()
291
        return self.get_state_at_day(today)
292

    
293
    def get_state_at_day(self, date):
294
        state = self.get_state()
295
        while(state):
296
            if datetime(state.date_selected.year,
297
                    state.date_selected.month, state.date_selected.day) <= \
298
                    datetime(date.year, date.month, date.day):
299
                return state
300
            state = state.previous_state
301
        return self.get_state()
302

    
303
    def was_in_state_at_day(self, date, status_type):
304
        state_at_day = self.get_state_at_day(date)
305
        if state_at_day and state_at_day.status.type == status_type:
306
            return True
307
        return False
308

    
309
    def get_states_history(self):
310
        return self.filestate_set.order_by('date_selected')
311

    
312
    def set_state(self, status, author, date_selected=None, comment=None):
313
        if not author:
314
            raise Exception('Missing author to set state')
315
        if not date_selected:
316
            date_selected = datetime.now()
317
        current_state = self.get_state()
318
        if not current_state:
319
            raise Exception('Invalid patient record. '
320
                'Missing current state.')
321
        if date_selected < current_state.date_selected:
322
            raise Exception('You cannot set a state starting the %s that '
323
                'is before the previous state starting at day %s.' % \
324
                (str(date_selected), str(current_state.date_selected)))
325
        filestate = FileState.objects.create(patient=self, status=status,
326
            date_selected=date_selected, author=author, comment=comment,
327
            previous_state=current_state)
328
        self.last_state = filestate
329
        self.save()
330

    
331
    def change_day_selected_of_state(self, state, new_date):
332
        if state.previous_state:
333
            if new_date < state.previous_state.date_selected:
334
                raise Exception('You cannot set a state starting the %s '
335
                    'before the previous state starting at day %s.' % \
336
                    (str(new_date), str(state.previous_state.date_selected)))
337
        next_state = state.get_next_state()
338
        if next_state:
339
            if new_date > next_state.date_selected:
340
                raise Exception('You cannot set a state starting the %s '
341
                    'after the following state starting at day %s.' % \
342
                    (str(new_date), str(next_state.date_selected)))
343
        state.date_selected = new_date
344
        state.save()
345

    
346
    def remove_state(self, state):
347
        if state.patient.id != self.id:
348
            raise Exception('The state given is not about this patient '
349
                'record but about %s' % state.patient)
350
        next_state = state.get_next_state()
351
        if not next_state:
352
            self.remove_last_state()
353
        else:
354
            next_state.previous_state = state.previous_state
355
            next_state.save()
356
            state.delete()
357

    
358
    def remove_last_state(self):
359
        try:
360
            self.get_state().delete()
361
        except:
362
            pass
363

    
364
    # START Specific to sessad healthcare
365
    def get_last_notification(self):
366
        return SessadHealthCareNotification.objects.filter(patient=self, ).\
367
            latest('end_date')
368

    
369
    def days_before_notification_expiration(self):
370
        today = datetime.today()
371
        notification = self.get_last_notification(self)
372
        if not notification:
373
            return 0
374
        if notification.end_date < today:
375
            return 0
376
        else:
377
            return notification.end_date - today
378
    # END Specific to sessad healthcare
379

    
380
    # START Specific to cmpp healthcare
381
    def create_diag_healthcare(self, modifier):
382
        """
383
            Gestion de l'inscription automatique.
384

    
385
            Si un premier acte est validé alors une prise en charge
386
            diagnostique est ajoutée. Cela fera basculer le dossier dans l'état
387
            en diagnostic.
388

    
389
            A voir si auto ou manuel :
390
            Si ce n'est pas le premier acte validé mais que l'acte précédement
391
            facturé a plus d'un an, on peut créer une prise en charge
392
            diagnostique. Même s'il y a une prise en charge de traitement
393
            expirée depuis moins d'un an donc renouvelable.
394

    
395
        """
396
        acts = self.act_set.order_by('date')
397
        hcs = self.healthcare_set.order_by('-start_date')
398
        if not hcs:
399
            # Pas de prise en charge, on recherche l'acte facturable le plus
400
            # ancien, on crée une pc diag à la même date.
401
            for act in acts:
402
                if are_all_acts_of_the_day_locked(act.date) and \
403
                        act.is_state('VALIDE') and act.is_billable():
404
                    CmppHealthCareDiagnostic(patient=self, author=modifier,
405
                        start_date=act.date).save()
406
                    break
407
        else:
408
            # On recherche l'acte facturable non facturé le plus ancien et
409
            # l'on regarde s'il a plus d'un an
410
            try:
411
                last_billed_act = self.act_set.filter(is_billed=True).\
412
                    latest('date')
413
                if last_billed_act:
414
                    for act in acts:
415
                        if are_all_acts_of_the_day_locked(act.date) and \
416
                                act.is_state('VALIDE') and \
417
                                act.is_billable() and \
418
                                (act.date - last_billed_act.date).days >= 365:
419
                            CmppHealthCareDiagnostic(patient=self,
420
                                author=modifier, start_date=act.date).save()
421
                            break
422
            except:
423
                pass
424

    
425
    def automated_switch_state(self, modifier):
426
        # Quel est le dernier acte facturable.
427
        acts = self.act_set.order_by('-date')
428
        # Si cet acte peut-être pris en charge en diagnostic c'est un acte de
429
        # diagnostic, sinon c'est un acte de traitement.
430
        last_state_services = self.last_state.status.\
431
                services.values_list('name', flat=True)
432
        cmpp = Service.objects.get(name='CMPP')
433
        for act in acts:
434
            if act.is_state('VALIDE') and act.is_billable() and \
435
                    are_all_acts_of_the_day_locked(act.date):
436
                cared, hc = act.is_act_covered_by_diagnostic_healthcare()
437
                if hc:
438
                    if (self.last_state.status.type == "ACCUEIL" \
439
                            or self.last_state.status.type == "TRAITEMENT") \
440
                            and "CMPP" in last_state_services:
441
                        status = Status.objects.filter(type="DIAGNOSTIC").\
442
                                filter(services__name='CMPP')[0]
443
                        try:
444
                            self.set_state(status, modifier,
445
                                date_selected=act.date)
446
                        except:
447
                            pass
448
                # Sinon, si le dossier est en diag, s'il ne peut être couvert
449
                # en diag, il est en traitement.
450
                elif self.last_state.status.type == "DIAGNOSTIC" and \
451
                        "CMPP" in last_state_services:
452
                    status = Status.objects.filter(type="TRAITEMENT").\
453
                            filter(services__name='CMPP')[0]
454
                    try:
455
                        self.set_state(status, modifier,
456
                                date_selected=act.date)
457
                    except:
458
                        pass
459
                break
460
    # END Specific to cmpp healthcare
461

    
462
reversion.register(PatientRecord, follow=['people_ptr'])
463

    
464

    
465
def create_patient(first_name, last_name, service, creator,
466
        date_selected=None):
467
    logger.debug('create_patient: creation for patient %s %s in service %s '
468
        'by %s' % (first_name, last_name, service, creator))
469
    if not (first_name and last_name and service and creator):
470
        raise Exception('Missing parameter to create a patient record.')
471
    status = Status.objects.filter(type="ACCUEIL").filter(services=service)
472
    if not status:
473
        raise Exception('%s has no ACCEUIL status' % service.name)
474
    patient = PatientRecord.objects.create(first_name=first_name,
475
            last_name=last_name, service=service,
476
            creator=creator)
477
    fs = FileState(status=status[0], author=creator, previous_state=None)
478
    if not date_selected:
479
        date_selected = patient.created
480
    fs.patient = patient
481
    fs.date_selected = date_selected
482
    fs.save()
483
    patient.last_state = fs
484
    patient.save()
485
    return patient
(5-5/9)