Project

General

Profile

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

calebasse / calebasse / dossiers / models.py @ d694f19f

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

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

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

    
246

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
415
    def remove_last_state(self):
416
        try:
417
            self.get_state().delete()
418
        except:
419
            pass
420

    
421
    # START Specific to sessad healthcare
422
    def get_last_notification(self):
423
        return SessadHealthCareNotification.objects.filter(patient=self, ).\
424
            latest('end_date')
425

    
426
    def days_before_notification_expiration(self):
427
        today = datetime.today()
428
        notification = self.get_last_notification(self)
429
        if not notification:
430
            return 0
431
        if notification.end_date < today:
432
            return 0
433
        else:
434
            return notification.end_date - today
435
    # END Specific to sessad healthcare
436

    
437
    # START Specific to cmpp healthcare
438
    def create_diag_healthcare(self, modifier):
439
        """
440
            Gestion de l'inscription automatique.
441

    
442
            Si un premier acte est validé alors une prise en charge
443
            diagnostique est ajoutée. Cela fera basculer le dossier dans l'état
444
            en diagnostic.
445

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

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

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

    
517
reversion.register(PatientRecord, follow=['people_ptr'])
518

    
519

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