Project

General

Profile

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

calebasse / calebasse / dossiers / models.py @ ee47faa1

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

    
3
import logging
4

    
5
from datetime import datetime, date
6
from dateutil.relativedelta import relativedelta
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.choices import LARGE_REGIME_CHOICES
15
from calebasse.models import PhoneNumberField, ZipCodeField
16
from calebasse.personnes.models import People
17
from calebasse.ressources.models import (ServiceLinkedAbstractModel,
18
        NamedAbstractModel, Service)
19
from calebasse.actes.validation import are_all_acts_of_the_day_locked
20

    
21
DEFAULT_ACT_NUMBER_DIAGNOSTIC = 6
22
DEFAULT_ACT_NUMBER_TREATMENT = 30
23
DEFAULT_ACT_NUMBER_PROLONGATION = 10
24
VALIDITY_PERIOD_TREATMENT_HEALTHCARE_DAYS = 0
25
VALIDITY_PERIOD_TREATMENT_HEALTHCARE_MONTHS = 0
26
VALIDITY_PERIOD_TREATMENT_HEALTHCARE_YEARS = 1
27

    
28
logger = logging.getLogger('calebasse.dossiers')
29

    
30

    
31
class HealthCare(models.Model):
32

    
33
    class Meta:
34
        app_label = 'dossiers'
35

    
36
    start_date = models.DateField(verbose_name=u"Date de début")
37
    patient = models.ForeignKey('dossiers.PatientRecord',
38
        verbose_name=u'Dossier patient')
39
    created = models.DateTimeField(u'Création', auto_now_add=True)
40
    author = \
41
        models.ForeignKey(User,
42
        verbose_name=u'Auteur')
43
    comment = models.TextField(max_length=3000, blank=True, null=True, verbose_name=u"Commentaire")
44

    
45
    def get_nb_acts_cared(self):
46
        return len(self.act_set.all())
47

    
48

    
49
class CmppHealthCareDiagnostic(HealthCare):
50

    
51
    class Meta:
52
        app_label = 'dossiers'
53

    
54
    act_number = models.IntegerField(default=DEFAULT_ACT_NUMBER_DIAGNOSTIC, verbose_name=u"Nombre d'actes couverts")
55

    
56
    def get_act_number(self):
57
        return self.act_number
58

    
59
    def set_act_number(self, value):
60
        if value < self.get_nb_acts_cared():
61
            raise Exception("La valeur doit être supérieur au "
62
                "nombre d'actes déjà pris en charge")
63
        self.act_number = value
64
        self.save()
65

    
66
    def save(self, **kwargs):
67
        self.start_date = \
68
            datetime(self.start_date.year, self.start_date.month,
69
                self.start_date.day)
70
        super(CmppHealthCareDiagnostic, self).save(**kwargs)
71

    
72

    
73
class CmppHealthCareTreatment(HealthCare):
74

    
75
    class Meta:
76
        app_label = 'dossiers'
77

    
78
    act_number = models.IntegerField(default=DEFAULT_ACT_NUMBER_TREATMENT,
79
            verbose_name=u"Nombre d'actes couverts")
80
    end_date = models.DateField(verbose_name=u"Date de fin")
81
    prolongation = models.IntegerField(default=0,
82
            verbose_name=u'Prolongation')
83

    
84
    def get_act_number(self):
85
        if self.is_extended():
86
            return self.act_number + self.prolongation
87
        return self.act_number
88

    
89
    def set_act_number(self, value):
90
        if value < self.get_nb_acts_cared():
91
            raise Exception("La valeur doit être supérieur au "
92
                "nombre d'actes déjà pris en charge")
93
        self.act_number = value
94
        self.save()
95

    
96
    def is_extended(self):
97
        if self.prolongation > 0:
98
            return True
99
        return False
100

    
101
    def add_prolongation(self, value=None):
102
        if not value:
103
            value = DEFAULT_ACT_NUMBER_PROLONGATION
104
        if self.is_extended():
105
            raise Exception(u'Prise en charge déja prolongée')
106
        self.prolongation = value
107
        self.save()
108

    
109
    def save(self, **kwargs):
110
        self.start_date = \
111
            datetime(self.start_date.year, self.start_date.month,
112
                self.start_date.day)
113
        self.end_date = self.start_date + \
114
            relativedelta(years=VALIDITY_PERIOD_TREATMENT_HEALTHCARE_YEARS) + \
115
            relativedelta(months=VALIDITY_PERIOD_TREATMENT_HEALTHCARE_MONTHS) + \
116
            relativedelta(days=VALIDITY_PERIOD_TREATMENT_HEALTHCARE_DAYS)
117
        super(CmppHealthCareTreatment, self).save(**kwargs)
118

    
119

    
120
class SessadHealthCareNotification(HealthCare):
121

    
122
    class Meta:
123
        app_label = 'dossiers'
124

    
125
    end_date = models.DateField()
126

    
127
    def save(self, **kwargs):
128
        self.start_date = \
129
            datetime(self.start_date.year, self.start_date.month,
130
                self.start_date.day)
131
        self.end_date = \
132
            datetime(self.end_date.year, self.end_date.month,
133
                self.end_date.day)
134
        super(SessadHealthCareNotification, self).save(**kwargs)
135

    
136
reversion.register(CmppHealthCareDiagnostic, follow=['healthcare_ptr'])
137
reversion.register(CmppHealthCareTreatment, follow=['healthcare_ptr'])
138
reversion.register(SessadHealthCareNotification, follow=['healthcare_ptr'])
139

    
140
class Status(NamedAbstractModel):
141

    
142
    class Meta:
143
        app_label = 'dossiers'
144
        verbose_name = u"Statut d'un état"
145
        verbose_name_plural = u"Statuts d'un état"
146

    
147
    type = models.CharField(max_length=80)
148
    services = models.ManyToManyField('ressources.Service')
149

    
150

    
151
class FileState(models.Model):
152

    
153
    class Meta:
154
        app_label = 'dossiers'
155
        verbose_name = u'Etat du dossier patient'
156
        verbose_name_plural = u'Etats du dossier patient'
157

    
158
    patient = models.ForeignKey('dossiers.PatientRecord',
159
        verbose_name=u'Dossier patient')
160
    status = models.ForeignKey('dossiers.Status')
161
    created = models.DateTimeField(u'Création', auto_now_add=True)
162
    date_selected = models.DateTimeField()
163
    author = \
164
        models.ForeignKey(User,
165
        verbose_name=u'Auteur')
166
    comment = models.TextField(max_length=3000, blank=True, null=True)
167
    previous_state = models.ForeignKey('FileState',
168
        verbose_name=u'Etat précédent', blank=True, null=True)
169

    
170
    def get_next_state(self):
171
        try:
172
            return FileState.objects.get(previous_state=self)
173
        except:
174
            return None
175

    
176
    def save(self, **kwargs):
177
        self.date_selected = \
178
                datetime(self.date_selected.year,
179
                        self.date_selected.month, self.date_selected.day)
180
        super(FileState, self).save(**kwargs)
181

    
182
    def __unicode__(self):
183
        return self.status.name + ' ' + str(self.date_selected)
184

    
185
class PatientAddress(models.Model):
186

    
187
    display_name = models.CharField(max_length=276,
188
            verbose_name=u'Adresse complète', editable=False)
189
    phone = PhoneNumberField(verbose_name=u"Téléphone", blank=True, null=True)
190
    fax = PhoneNumberField(verbose_name=u"Fax", blank=True, null=True)
191
    place_of_life = models.BooleanField(verbose_name=u"Lieu de vie")
192
    number = models.CharField(max_length=12,
193
            verbose_name=u"Numéro", blank=True, null=True)
194
    street = models.CharField(max_length=100,
195
            verbose_name=u"Rue", blank=True, null=True)
196
    address_complement = models.CharField(max_length=100,
197
            blank=True, null=True,
198
            verbose_name=u"Complément d'adresse")
199
    zip_code = ZipCodeField(verbose_name=u"Code postal", blank=True, null=True)
200
    city = models.CharField(max_length=60,
201
            verbose_name=u"Ville", blank=True, null=True)
202
    comment = models.TextField(verbose_name=u"Commentaire",
203
            null=True, blank=True)
204

    
205
    def __unicode__(self):
206
        return self.display_name
207

    
208
    def save(self, **kwargs):
209
        self.display_name = "%s %s, %s %s" % (self.number, self.street,  self.zip_code, self.city)
210
        super(PatientAddress, self).save(**kwargs)
211

    
212

    
213
class PatientContact(People):
214
    class Meta:
215
        verbose_name = u'Contact patient'
216
        verbose_name_plural = u'Contacts patient'
217

    
218
    mobile = PhoneNumberField(verbose_name=u"Téléphone mobile", blank=True, null=True)
219
    # carte vitale
220
    social_security_id = models.CharField(max_length=13, verbose_name=u"NIR",
221
            null=True, blank=True)
222
    birthdate = models.DateField(verbose_name=u"Date de naissance",
223
            null=True, blank=True)
224
    twinning_rank = models.IntegerField(verbose_name=u"Rang (gémellité)",
225
            null=True, blank=True)
226
    thirdparty_payer = models.BooleanField(verbose_name=u'Tiers-payant',
227
            default=False)
228
    begin_rights = models.DateField(verbose_name=u"Début de droits",
229
            null=True, blank=True)
230
    end_rights = models.DateField(verbose_name=u"Fin de droits",
231
            null=True, blank=True)
232
    health_center = models.ForeignKey('ressources.HealthCenter',
233
            verbose_name=u"Centre d'assurance maladie",
234
            null=True, blank=True)
235

    
236
    addresses = models.ManyToManyField('PatientAddress', verbose_name=u"Adresses")
237
    contact_comment = models.TextField(verbose_name=u"Commentaire",
238
            null=True, blank=True)
239

    
240
    def get_control_key(self):
241
        if self.social_security_id:
242
            return (97 - (int(self.social_security_id) % 97))
243
        return None
244

    
245

    
246
class PatientRecordManager(models.Manager):
247
    def for_service(self, service):
248
        return self.filter(service=service)
249

    
250
class PatientRecord(ServiceLinkedAbstractModel, PatientContact):
251
    objects = PatientRecordManager()
252

    
253
    class Meta:
254
        verbose_name = u'Dossier'
255
        verbose_name_plural = u'Dossiers'
256

    
257
    created = models.DateTimeField(u'création', auto_now_add=True)
258
    creator = \
259
        models.ForeignKey(User,
260
        verbose_name=u'Créateur dossier patient',
261
        editable=True)
262
    policyholder = models.ForeignKey('PatientContact',
263
            null=True, blank=True,
264
            verbose_name="Assuré", related_name="+")
265
    contacts = models.ManyToManyField('PatientContact',
266
            related_name='contact_of')
267
    nationality = models.CharField(verbose_name=u"Nationalité",
268
            max_length=70, null=True, blank=True)
269
    paper_id = models.CharField(max_length=6,
270
            verbose_name=u"N° dossier papier",
271
            null=True, blank=True)
272
    last_state = models.ForeignKey(FileState, related_name='+',
273
            null=True)
274
    school = models.ForeignKey('ressources.School',
275
            null=True, blank=True, default=None)
276
    comment = models.TextField(verbose_name=u"Commentaire",
277
            null=True, blank=True, default=None)
278
    pause = models.BooleanField(verbose_name=u"Pause facturation",
279
            default=False)
280

    
281
    # Physiology
282
    size = models.IntegerField(verbose_name=u"Taille (cm)",
283
            null=True, blank=True, default=None)
284
    weight = models.IntegerField(verbose_name=u"Poids (kg)",
285
            null=True, blank=True, default=None)
286
    pregnancy_term = models.IntegerField(verbose_name=u"Terme en semaines",
287
            null=True, blank=True, default=None)
288

    
289
    # Inscription motive
290
    analysemotive = models.ForeignKey('ressources.AnalyseMotive',
291
            verbose_name=u"Motif (analysé)",
292
            null=True, blank=True, default=None)
293
    familymotive = models.ForeignKey('ressources.FamilyMotive',
294
            verbose_name=u"Motif (famille)",
295
            null=True, blank=True, default=None)
296
    advicegiver = models.ForeignKey('ressources.AdviceGiver',
297
            verbose_name=u"Conseilleur",
298
            null=True, blank=True, default=None)
299

    
300
    # Family
301
    sibship_place = models.IntegerField(verbose_name=u"Place dans la fratrie",
302
            null=True, blank=True, default=None)
303
    nb_children_family = models.IntegerField(verbose_name=u"Nombre d'enfants dans la fratrie",
304
            null=True, blank=True, default=None)
305
    parental_authority = models.ForeignKey('ressources.ParentalAuthorityType',
306
            verbose_name=u"Autorité parentale",
307
            null=True, blank=True, default=None)
308
    family_situation = models.ForeignKey('ressources.FamilySituationType',
309
            verbose_name=u"Situation familiale",
310
            null=True, blank=True, default=None)
311
    child_custody = models.ForeignKey('ressources.ParentalCustodyType',
312
            verbose_name=u"Garde parentale",
313
            null=True, blank=True, default=None)
314

    
315
    # Transport
316
    transporttype = models.ForeignKey('ressources.TransportType',
317
            verbose_name=u"Type de transport",
318
            null=True, blank=True, default=None)
319
    transportcompany = models.ForeignKey('ressources.TransportCompany',
320
            verbose_name=u"Compagnie de transport",
321
            null=True, blank=True, default=None)
322

    
323
    # FollowUp
324
    coordinators = models.ManyToManyField('personnes.Worker',
325
            verbose_name=u"Coordinateurs",
326
            null=True, blank=True, default=None)
327
    externaldoctor = models.ForeignKey('personnes.ExternalDoctor',
328
            verbose_name=u"Médecin extérieur",
329
            null=True, blank=True, default=None)
330
    externalintervener = models.ForeignKey('personnes.ExternalIntervener',
331
            verbose_name=u"Intervenant extérieur",
332
            null=True, blank=True, default=None)
333

    
334
    def __init__(self, *args, **kwargs):
335
        super(PatientRecord, self).__init__(*args, **kwargs)
336
        if not hasattr(self, 'service'):
337
            raise Exception('The field service is mandatory.')
338

    
339
    def get_state(self):
340
        return self.last_state
341

    
342
    def get_initial_state(self):
343
        return self.filestate_set.order_by('date_selected')[0]
344

    
345
    def get_current_state(self):
346
        today = date.today()
347
        return self.get_state_at_day(today)
348

    
349
    def get_state_at_day(self, date):
350
        state = self.get_state()
351
        while(state):
352
            if datetime(state.date_selected.year,
353
                    state.date_selected.month, state.date_selected.day) <= \
354
                    datetime(date.year, date.month, date.day):
355
                return state
356
            state = state.previous_state
357
        return self.get_state()
358

    
359
    def was_in_state_at_day(self, date, status_type):
360
        state_at_day = self.get_state_at_day(date)
361
        if state_at_day and state_at_day.status.type == status_type:
362
            return True
363
        return False
364

    
365
    def get_states_history(self):
366
        return self.filestate_set.order_by('date_selected')
367

    
368
    def can_be_deleted(self):
369
        for act in self.act_set.all():
370
            if act.is_state('VALIDE'):
371
                return False
372
        return True
373

    
374
    def set_state(self, status, author, date_selected=None, comment=None):
375
        if not author:
376
            raise Exception('Missing author to set state')
377
        if not date_selected:
378
            date_selected = datetime.now()
379
        current_state = self.get_state()
380
        if not current_state:
381
            raise Exception('Invalid patient record. '
382
                'Missing current state.')
383
        if date_selected < current_state.date_selected:
384
            raise Exception('You cannot set a state starting the %s that '
385
                'is before the previous state starting at day %s.' % \
386
                (str(date_selected), str(current_state.date_selected)))
387
        filestate = FileState.objects.create(patient=self, status=status,
388
            date_selected=date_selected, author=author, comment=comment,
389
            previous_state=current_state)
390
        self.last_state = filestate
391
        self.save()
392

    
393
    def change_day_selected_of_state(self, state, new_date):
394
        if state.previous_state:
395
            if new_date < state.previous_state.date_selected:
396
                raise Exception('You cannot set a state starting the %s '
397
                    'before the previous state starting at day %s.' % \
398
                    (str(new_date), str(state.previous_state.date_selected)))
399
        next_state = state.get_next_state()
400
        if next_state:
401
            if new_date > next_state.date_selected:
402
                raise Exception('You cannot set a state starting the %s '
403
                    'after the following state starting at day %s.' % \
404
                    (str(new_date), str(next_state.date_selected)))
405
        state.date_selected = new_date
406
        state.save()
407

    
408
    def remove_state(self, state):
409
        if state.patient.id != self.id:
410
            raise Exception('The state given is not about this patient '
411
                'record but about %s' % state.patient)
412
        next_state = state.get_next_state()
413
        if not next_state:
414
            self.remove_last_state()
415
        else:
416
            next_state.previous_state = state.previous_state
417
            next_state.save()
418
            state.delete()
419

    
420
    def remove_last_state(self):
421
        try:
422
            self.get_state().delete()
423
        except:
424
            pass
425

    
426
    # START Specific to sessad healthcare
427
    def get_last_notification(self):
428
        return SessadHealthCareNotification.objects.filter(patient=self, ).\
429
            latest('end_date')
430

    
431
    def days_before_notification_expiration(self):
432
        today = datetime.today()
433
        notification = self.get_last_notification(self)
434
        if not notification:
435
            return 0
436
        if notification.end_date < today:
437
            return 0
438
        else:
439
            return notification.end_date - today
440
    # END Specific to sessad healthcare
441

    
442
    # START Specific to cmpp healthcare
443
    def create_diag_healthcare(self, modifier):
444
        """
445
            Gestion de l'inscription automatique.
446

    
447
            Si un premier acte est validé alors une prise en charge
448
            diagnostique est ajoutée. Cela fera basculer le dossier dans l'état
449
            en diagnostic.
450

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

    
457
        """
458
        acts = self.act_set.order_by('date')
459
        hcds = CmppHealthCareDiagnostic.objects.filter(patient=self).order_by('-start_date')
460
        if not hcds:
461
            # Pas de prise en charge, on recherche l'acte facturable le plus
462
            # ancien, on crée une pc diag à la même date.
463
            for act in acts:
464
                if act.is_state('VALIDE') and act.is_billable():
465
                    CmppHealthCareDiagnostic(patient=self, author=modifier,
466
                        start_date=act.date).save()
467
                    break
468
        else:
469
            # On recherche l'acte facturable non facturé le plus ancien et
470
            # l'on regarde s'il a plus d'un an
471
            try:
472
                last_billed_act = self.act_set.filter(is_billed=True).\
473
                    latest('date')
474
                if last_billed_act:
475
                    for act in acts:
476
                        if act.is_state('VALIDE') and \
477
                                act.is_billable() and \
478
                                (act.date - last_billed_act.date).days >= 365:
479
                            return True
480
            except:
481
                pass
482
        return False
483

    
484
    def automated_switch_state(self, modifier):
485
        # Quel est le dernier acte facturable.
486
        acts = self.act_set.order_by('-date')
487
        # Si cet acte peut-être pris en charge en diagnostic c'est un acte de
488
        # diagnostic, sinon c'est un acte de traitement.
489
        last_state_services = self.last_state.status.\
490
                services.values_list('name', flat=True)
491
        cmpp = Service.objects.get(name='CMPP')
492
        for act in acts:
493
            if act.is_state('VALIDE') and act.is_billable() and \
494
                    act.date >= self.get_state().date_selected and \
495
                    are_all_acts_of_the_day_locked(act.date):
496
                cared, hc = act.is_act_covered_by_diagnostic_healthcare()
497
                if hc:
498
                    if (self.last_state.status.type == "ACCUEIL" \
499
                            or self.last_state.status.type == "TRAITEMENT") \
500
                            and "CMPP" in last_state_services:
501
                        status = Status.objects.filter(type="DIAGNOSTIC").\
502
                                filter(services__name='CMPP')[0]
503
                        try:
504
                            self.set_state(status, modifier,
505
                                date_selected=act.date)
506
                        except:
507
                            pass
508
                # Sinon, si le dossier est en diag, s'il ne peut être couvert
509
                # en diag, il est en traitement.
510
                elif self.last_state.status.type == "DIAGNOSTIC" and \
511
                        "CMPP" in last_state_services:
512
                    status = Status.objects.filter(type="TRAITEMENT").\
513
                            filter(services__name='CMPP')[0]
514
                    try:
515
                        self.set_state(status, modifier,
516
                                date_selected=act.date)
517
                    except:
518
                        pass
519
                break
520
    # END Specific to cmpp healthcare
521

    
522
reversion.register(PatientRecord, follow=['people_ptr'])
523

    
524

    
525
def create_patient(first_name, last_name, service, creator,
526
        date_selected=None):
527
    logger.debug('create_patient: creation for patient %s %s in service %s '
528
        'by %s' % (first_name, last_name, service, creator))
529
    if not (first_name and last_name and service and creator):
530
        raise Exception('Missing parameter to create a patient record.')
531
    status = Status.objects.filter(type="ACCUEIL").filter(services=service)
532
    if not status:
533
        raise Exception('%s has no ACCEUIL status' % service.name)
534
    patient = PatientRecord.objects.create(first_name=first_name,
535
            last_name=last_name, service=service,
536
            creator=creator)
537
    fs = FileState(status=status[0], author=creator, previous_state=None)
538
    if not date_selected:
539
        date_selected = patient.created
540
    fs.patient = patient
541
    fs.date_selected = date_selected
542
    fs.save()
543
    patient.last_state = fs
544
    patient.save()
545
    return patient
(5-5/9)