Project

General

Profile

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

calebasse / calebasse / dossiers / models.py @ 44a0fb10

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

    
3
import logging
4
import os
5

    
6
from datetime import datetime, date
7
from dateutil.relativedelta import relativedelta
8

    
9
from django import forms
10
from django.conf import settings
11
from django.db import models
12
from django.db.models import Min, Max, Q
13
from django.contrib.auth.models import User
14
from django.core.validators import MinValueValidator
15

    
16
import reversion
17

    
18
from calebasse.choices import LARGE_REGIME_CHOICES, TYPE_OF_CONTRACT_CHOICES
19
from calebasse.models import PhoneNumberField, ZipCodeField
20
from calebasse.personnes.models import People
21
from calebasse.ressources.models import (ServiceLinkedAbstractModel,
22
        NamedAbstractModel, Service)
23
from calebasse.actes.validation import are_all_acts_of_the_day_locked
24
from calebasse.actes.models import Act
25

    
26
DEFAULT_ACT_NUMBER_DIAGNOSTIC = 6
27
DEFAULT_ACT_NUMBER_TREATMENT = 30
28
DEFAULT_ACT_NUMBER_PROLONGATION = 10
29
VALIDITY_PERIOD_TREATMENT_HEALTHCARE_DAYS = 0
30
VALIDITY_PERIOD_TREATMENT_HEALTHCARE_MONTHS = 0
31
VALIDITY_PERIOD_TREATMENT_HEALTHCARE_YEARS = 1
32

    
33
logger = logging.getLogger('calebasse.dossiers')
34

    
35

    
36
class HealthCare(models.Model):
37

    
38
    class Meta:
39
        app_label = 'dossiers'
40

    
41
    start_date = models.DateField(verbose_name=u"Date de début")
42
    request_date = models.DateField(verbose_name=u"Date de demande",
43
        blank=True, null=True)
44
    agree_date = models.DateField(verbose_name=u"Date d'accord",
45
        blank=True, null=True)
46
    insist_date = models.DateField(verbose_name=u"Date de relance",
47
        blank=True, null=True)
48
    patient = models.ForeignKey('dossiers.PatientRecord',
49
        verbose_name=u'Dossier patient')
50
    created = models.DateTimeField(u'Création', auto_now_add=True)
51
    author = \
52
        models.ForeignKey(User,
53
        verbose_name=u'Auteur', blank=True, null=True)
54
    comment = models.TextField(max_length=3000, blank=True, null=True, verbose_name=u"Commentaire")
55

    
56
    def get_nb_acts_cared(self):
57
        return len(self.act_set.all())
58

    
59

    
60
class CmppHealthCareDiagnostic(HealthCare):
61

    
62
    class Meta:
63
        app_label = 'dossiers'
64

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

    
67
    def get_act_number(self):
68
        return self.act_number
69

    
70
    def set_act_number(self, value):
71
        if value < self.get_nb_acts_cared():
72
            raise Exception("La valeur doit être supérieur au "
73
                "nombre d'actes déjà pris en charge")
74
        self.act_number = value
75
        self.save()
76

    
77
    def save(self, **kwargs):
78
        self.start_date = \
79
            datetime(self.start_date.year, self.start_date.month,
80
                self.start_date.day)
81
        super(CmppHealthCareDiagnostic, self).save(**kwargs)
82

    
83

    
84
class CmppHealthCareTreatment(HealthCare):
85

    
86
    class Meta:
87
        app_label = 'dossiers'
88

    
89
    act_number = models.IntegerField(default=DEFAULT_ACT_NUMBER_TREATMENT,
90
            verbose_name=u"Nombre d'actes couverts")
91
    end_date = models.DateField(verbose_name=u"Date de fin",
92
        blank=True, null=True)
93
    prolongation = models.IntegerField(default=0,
94
            verbose_name=u'Prolongation')
95

    
96
    def get_act_number(self):
97
        if self.is_extended():
98
            return self.act_number + self.prolongation
99
        return self.act_number
100

    
101
    def set_act_number(self, value):
102
        if value < self.get_nb_acts_cared():
103
            raise Exception("La valeur doit être supérieur au "
104
                "nombre d'actes déjà pris en charge")
105
        self.act_number = value
106
        self.save()
107

    
108
    def is_extended(self):
109
        if self.prolongation > 0:
110
            return True
111
        return False
112

    
113
    def add_prolongation(self, value=None):
114
        if not value:
115
            value = DEFAULT_ACT_NUMBER_PROLONGATION
116
        if self.is_extended():
117
            raise Exception(u'Prise en charge déja prolongée')
118
        self.prolongation = value
119
        self.save()
120

    
121
    def del_prolongation(self):
122
        pass
123

    
124
    def save(self, **kwargs):
125
        self.start_date = \
126
            datetime(self.start_date.year, self.start_date.month,
127
                self.start_date.day)
128
        if not self.end_date:
129
            self.end_date = self.start_date + \
130
                relativedelta(years=VALIDITY_PERIOD_TREATMENT_HEALTHCARE_YEARS) + \
131
                relativedelta(months=VALIDITY_PERIOD_TREATMENT_HEALTHCARE_MONTHS) + \
132
                relativedelta(days=VALIDITY_PERIOD_TREATMENT_HEALTHCARE_DAYS-1)
133
        super(CmppHealthCareTreatment, self).save(**kwargs)
134

    
135

    
136
class SessadHealthCareNotification(HealthCare):
137

    
138
    class Meta:
139
        app_label = 'dossiers'
140

    
141
    end_date = models.DateField(verbose_name=u"Date de fin",
142
        blank=True, null=True)
143

    
144
    def save(self, **kwargs):
145
        self.start_date = \
146
            datetime(self.start_date.year, self.start_date.month,
147
                self.start_date.day)
148
        if self.end_date:
149
            self.end_date = \
150
                datetime(self.end_date.year, self.end_date.month,
151
                    self.end_date.day)
152
        super(SessadHealthCareNotification, self).save(**kwargs)
153

    
154
reversion.register(CmppHealthCareDiagnostic, follow=['healthcare_ptr'])
155
reversion.register(CmppHealthCareTreatment, follow=['healthcare_ptr'])
156
reversion.register(SessadHealthCareNotification, follow=['healthcare_ptr'])
157

    
158
class Status(NamedAbstractModel):
159

    
160
    class Meta:
161
        app_label = 'dossiers'
162
        verbose_name = u"Statut d'un état"
163
        verbose_name_plural = u"Statuts d'un état"
164

    
165
    type = models.CharField(max_length=80)
166
    services = models.ManyToManyField('ressources.Service')
167

    
168

    
169
class FileState(models.Model):
170

    
171
    class Meta:
172
        app_label = 'dossiers'
173
        verbose_name = u'Etat du dossier patient'
174
        verbose_name_plural = u'Etats du dossier patient'
175

    
176
    patient = models.ForeignKey('dossiers.PatientRecord',
177
        verbose_name=u'Dossier patient')
178
    status = models.ForeignKey('dossiers.Status', verbose_name=u'Statut')
179
    created = models.DateTimeField(u'Création', auto_now_add=True)
180
    date_selected = models.DateTimeField()
181
    author = \
182
        models.ForeignKey(User,
183
        verbose_name=u'Auteur')
184
    comment = models.TextField(max_length=3000, blank=True, null=True)
185
    previous_state = models.ForeignKey('FileState',
186
            on_delete=models.SET_NULL,
187
            verbose_name=u'Etat précédent',
188
            blank=True, null=True)
189

    
190
    def get_next_state(self):
191
        try:
192
            return FileState.objects.get(previous_state=self)
193
        except:
194
            return None
195

    
196
    def save(self, **kwargs):
197
        self.date_selected = \
198
                datetime(self.date_selected.year,
199
                        self.date_selected.month, self.date_selected.day)
200
        super(FileState, self).save(**kwargs)
201

    
202
    def __unicode__(self):
203
        return self.status.name + ' ' + str(self.date_selected)
204

    
205
    def delete(self, *args, **kwargs):
206
        next_state = self.get_next_state()
207
        if next_state and self.previous_state:
208
            next_state.previous_state = self.previous_state
209
            next_state.save()
210
        if self.patient.last_state == self:
211
            self.patient.last_state = self.previous_state
212
            self.patient.save()
213
        super(FileState, self).delete(*args, **kwargs)
214

    
215
class PatientAddress(models.Model):
216

    
217
    display_name = models.CharField(max_length=276,
218
            verbose_name=u'Adresse complète', editable=False)
219
    phone = PhoneNumberField(verbose_name=u"Téléphone", blank=True, null=True)
220
    fax = PhoneNumberField(verbose_name=u"Fax", blank=True, null=True)
221
    place_of_life = models.BooleanField(verbose_name=u"Lieu de vie")
222
    number = models.CharField(max_length=12,
223
            verbose_name=u"Numéro", blank=True, null=True)
224
    street = models.CharField(max_length=100,
225
            verbose_name=u"Rue", blank=True, null=True)
226
    address_complement = models.CharField(max_length=100,
227
            blank=True, null=True,
228
            verbose_name=u"Complément d'adresse")
229
    zip_code = ZipCodeField(verbose_name=u"Code postal", blank=True, null=True)
230
    city = models.CharField(max_length=60,
231
            verbose_name=u"Ville", blank=True, null=True)
232
    comment = models.TextField(verbose_name=u"Commentaire",
233
            null=True, blank=True)
234

    
235
    def __unicode__(self):
236
        return self.display_name
237

    
238
    def save(self, **kwargs):
239
        self.display_name = ''
240
        if self.number:
241
            self.display_name += self.number + ' '
242
        if self.street:
243
            self.display_name += self.street + ' '
244
        if self.address_complement:
245
            self.display_name += self.address_complement + ' '
246
        if self.zip_code:
247
            self.display_name += self.zip_code + ' '
248
        if self.city:
249
            self.display_name += self.city + ' '
250
        super(PatientAddress, self).save(**kwargs)
251

    
252

    
253
class PatientContact(People):
254
    class Meta:
255
        verbose_name = u'Contact patient'
256
        verbose_name_plural = u'Contacts patient'
257

    
258
    mobile = PhoneNumberField(verbose_name=u"Téléphone mobile", blank=True, null=True)
259
    # carte vitale
260
    social_security_id = models.CharField(max_length=13, verbose_name=u"NIR",
261
            null=True, blank=True)
262
    birthdate = models.DateField(verbose_name=u"Date de naissance",
263
            null=True, blank=True)
264
    birthplace = models.CharField(max_length=100, verbose_name=u"Lieu de naissance",
265
            null=True, blank=True)
266
    twinning_rank = models.IntegerField(verbose_name=u"Rang (gémellité)", default=1,
267
            validators=[MinValueValidator(1)])
268
    thirdparty_payer = models.BooleanField(verbose_name=u'Tiers-payant',
269
            default=False)
270
    begin_rights = models.DateField(verbose_name=u"Début de droits",
271
            null=True, blank=True)
272
    end_rights = models.DateField(verbose_name=u"Fin de droits",
273
            null=True, blank=True)
274
    health_center = models.ForeignKey('ressources.HealthCenter',
275
            verbose_name=u"Centre d'assurance maladie",
276
            null=True, blank=True)
277
    other_health_center = models.CharField(verbose_name=u"Centre spécifique",
278
            max_length=4,
279
            null=True, blank=True)
280
    type_of_contract = models.CharField(max_length=2,
281
            verbose_name=u"Type de contrat spécifique",
282
            choices=TYPE_OF_CONTRACT_CHOICES,
283
            null=True, blank=True)
284
    management_code = models.ForeignKey('ressources.ManagementCode',
285
            verbose_name=u"Code de gestion",
286
            null=True, blank=True)
287
    job = models.ForeignKey('ressources.Job',
288
            related_name="job",
289
            verbose_name=u"Profession",
290
            null=True, blank=True, default=None)
291
    parente = models.ForeignKey('ressources.PatientRelatedLink',
292
            verbose_name=u"Lien avec le patient (Parenté)",
293
            null=True, blank=True, default=None)
294

    
295
    addresses = models.ManyToManyField('PatientAddress', verbose_name=u"Adresses")
296
    contact_comment = models.TextField(verbose_name=u"Commentaire",
297
            null=True, blank=True)
298

    
299
    old_contact_id = models.CharField(max_length=256,
300
            verbose_name=u'Ancien ID du contact', blank=True, null=True)
301

    
302
    def get_control_key(self):
303
        if self.social_security_id:
304
            nir = self.social_security_id
305
            try:
306
                # Corse dpt 2A et 2B
307
                minus = 0
308
                if nir[6] in ('A', 'a'):
309
                    nir = [c for c in nir]
310
                    nir[6] = '0'
311
                    nir = ''.join(nir)
312
                    minus = 1000000
313
                elif nir[6] in ('B', 'b'):
314
                    nir = [c for c in nir]
315
                    nir[6] = '0'
316
                    nir = ''.join(nir)
317
                    minus = 2000000
318
                nir = int(nir) - minus
319
                return (97 - (nir % 97))
320
            except Exception, e:
321
                print str(e)
322
                return None
323
        return None
324

    
325

    
326
class PatientRecordManager(models.Manager):
327
    def for_service(self, service):
328
        return self.filter(service=service)
329

    
330
class PatientRecord(ServiceLinkedAbstractModel, PatientContact):
331
    objects = PatientRecordManager()
332

    
333
    class Meta:
334
        verbose_name = u'Dossier'
335
        verbose_name_plural = u'Dossiers'
336

    
337
    created = models.DateTimeField(u'création', auto_now_add=True)
338
    creator = \
339
        models.ForeignKey(User,
340
        verbose_name=u'Créateur dossier patient',
341
        editable=True)
342
    policyholder = models.ForeignKey('PatientContact',
343
            null=True, blank=True,
344
            verbose_name="Assuré", related_name="+",
345
            on_delete=models.SET_NULL)
346
    contacts = models.ManyToManyField('PatientContact',
347
            related_name='contact_of')
348
    nationality = models.CharField(verbose_name=u"Nationalité",
349
            max_length=70, null=True, blank=True)
350
    paper_id = models.CharField(max_length=6,
351
            verbose_name=u"N° dossier papier",
352
            null=True, blank=True)
353
    last_state = models.ForeignKey(FileState, related_name='+',
354
            null=True, on_delete=models.SET_NULL)
355
    comment = models.TextField(verbose_name=u"Commentaire",
356
            null=True, blank=True, default=None)
357
    pause = models.BooleanField(verbose_name=u"Pause facturation",
358
            default=False)
359
    confidential = models.BooleanField(verbose_name=u"Confidentiel",
360
            default=False)
361
    socialisation_durations = models.ManyToManyField('ressources.SocialisationDuration',
362
            related_name='socialisation_duration_of')
363
    mdph_requests = models.ManyToManyField('ressources.MDPHRequest',
364
            related_name='mdph_requests_of')
365
    mdph_responses = models.ManyToManyField('ressources.MDPHResponse',
366
            related_name='mdph_responses_of')
367

    
368
    # Physiology and health data
369
    size = models.IntegerField(verbose_name=u"Taille (cm)",
370
            null=True, blank=True, default=None)
371
    weight = models.IntegerField(verbose_name=u"Poids (g)",
372
            null=True, blank=True, default=None)
373
    pregnancy_term = models.IntegerField(verbose_name=u"Terme en semaines",
374
            null=True, blank=True, default=None)
375
    cranium_perimeter = models.DecimalField(verbose_name=u"Périmètre cranien", max_digits=5, decimal_places=2,
376
            null=True, blank=True, default=None)
377
    chest_perimeter = models.DecimalField(verbose_name=u"Périmètre thoracique", max_digits=5, decimal_places=2,
378
            null=True, blank=True, default=None)
379
    apgar_score_one = models.IntegerField(verbose_name=u"Test d'Apgar (1)",
380
            null=True, blank=True, default=None)
381
    apgar_score_two = models.IntegerField(verbose_name=u"Test d'Apgar (5)",
382
            null=True, blank=True, default=None)
383
    mises_1 = models.ManyToManyField('ressources.CodeCFTMEA', related_name="mises1",
384
            verbose_name=u"Axe I : catégories cliniques",
385
            null=True, blank=True, default=None)
386
    mises_2 = models.ManyToManyField('ressources.CodeCFTMEA', related_name="mises2",
387
            verbose_name=u"Axe II : facteurs organiques",
388
            null=True, blank=True, default=None)
389
    mises_3 = models.ManyToManyField('ressources.CodeCFTMEA', related_name="mises3",
390
            verbose_name=u"Axe II : facteurs environnementaux",
391
            null=True, blank=True, default=None)
392

    
393
    # Inscription motive
394
    analysemotive = models.ForeignKey('ressources.AnalyseMotive',
395
            verbose_name=u"Motif (analysé)",
396
            null=True, blank=True, default=None)
397
    familymotive = models.ForeignKey('ressources.FamilyMotive',
398
            verbose_name=u"Motif (famille)",
399
            null=True, blank=True, default=None)
400
    provenance = models.ForeignKey('ressources.Provenance',
401
            verbose_name=u"Conseilleur",
402
            null=True, blank=True, default=None)
403
    advicegiver = models.ForeignKey('ressources.AdviceGiver',
404
            verbose_name=u"Demandeur",
405
            null=True, blank=True, default=None)
406

    
407
    # Out motive
408
    outmotive = models.ForeignKey('ressources.OutMotive',
409
            verbose_name=u"Motif de sortie",
410
            null=True, blank=True, default=None)
411
    outto = models.ForeignKey('ressources.OutTo',
412
            verbose_name=u"Orientation",
413
            null=True, blank=True, default=None)
414

    
415
    # Family
416
    sibship_place = models.IntegerField(verbose_name=u"Place dans la fratrie",
417
            null=True, blank=True, default=None)
418
    nb_children_family = models.IntegerField(verbose_name=u"Nombre d'enfants dans la fratrie",
419
            null=True, blank=True, default=None)
420
    parental_authority = models.ForeignKey('ressources.ParentalAuthorityType',
421
            verbose_name=u"Autorité parentale",
422
            null=True, blank=True, default=None)
423
    family_situation = models.ForeignKey('ressources.FamilySituationType',
424
            verbose_name=u"Situation familiale",
425
            null=True, blank=True, default=None)
426
    child_custody = models.ForeignKey('ressources.ParentalCustodyType',
427
            verbose_name=u"Garde parentale",
428
            null=True, blank=True, default=None)
429
    job_mother = models.ForeignKey('ressources.Job',
430
            related_name="job_mother",
431
            verbose_name=u"Profession de la mère",
432
            null=True, blank=True, default=None)
433
    job_father = models.ForeignKey('ressources.Job',
434
            related_name="job_father",
435
            verbose_name=u"Profession du père",
436
            null=True, blank=True, default=None)
437
    rm_mother = models.ForeignKey('ressources.MaritalStatusType',
438
            related_name="rm_mother",
439
            verbose_name=u"Régime matrimonial de la mère",
440
            null=True, blank=True, default=None)
441
    rm_father = models.ForeignKey('ressources.MaritalStatusType',
442
            related_name="rm_father",
443
            verbose_name=u"Régime matrimonial du père",
444
            null=True, blank=True, default=None)
445
    family_comment = models.TextField(verbose_name=u"Commentaire",
446
            null=True, blank=True, default=None)
447

    
448
    # Transport
449
    transporttype = models.ForeignKey('ressources.TransportType',
450
            verbose_name=u"Type de transport",
451
            null=True, blank=True, default=None)
452
    transportcompany = models.ForeignKey('ressources.TransportCompany',
453
            verbose_name=u"Compagnie de transport",
454
            null=True, blank=True, default=None)
455

    
456
    # FollowUp
457
    coordinators = models.ManyToManyField('personnes.Worker',
458
            verbose_name=u"Coordinateurs",
459
            null=True, blank=True, default=None)
460
    externaldoctor = models.ForeignKey('personnes.ExternalTherapist',
461
            verbose_name=u"Médecin extérieur",
462
            null=True, blank=True, default=None)
463
    externalintervener = models.ForeignKey('personnes.ExternalWorker',
464
            verbose_name=u"Intervenant extérieur",
465
            null=True, blank=True, default=None)
466

    
467
    old_id = models.CharField(max_length=256,
468
            verbose_name=u'Ancien ID', blank=True, null=True)
469
    old_old_id = models.CharField(max_length=256,
470
            verbose_name=u'Ancien ancien ID', blank=True, null=True)
471

    
472
    def save(self, *args, **kwargs):
473
        if not getattr(self, 'service', None):
474
            raise Exception('The field service is mandatory.')
475
        super(PatientRecord, self).save(*args, **kwargs)
476

    
477
    def get_state(self):
478
        return self.last_state
479

    
480
    def get_initial_state(self):
481
        return self.filestate_set.order_by('date_selected')[0]
482

    
483
    def get_current_state(self):
484
        today = date.today()
485
        return self.get_state_at_day(today)
486

    
487
    def get_state_at_day(self, date):
488
        state = self.get_state()
489
        while(state):
490
            if datetime(state.date_selected.year,
491
                    state.date_selected.month, state.date_selected.day) <= \
492
                    datetime(date.year, date.month, date.day):
493
                return state
494
            state = state.previous_state
495
        return self.get_state()
496

    
497
    def was_in_state_at_day(self, date, status_type):
498
        state_at_day = self.get_state_at_day(date)
499
        if state_at_day and state_at_day.status.type == status_type:
500
            return True
501
        return False
502

    
503
    def get_states_history(self):
504
        return self.filestate_set.order_by('date_selected')
505

    
506
    def can_be_deleted(self):
507
        for act in self.act_set.all():
508
            if act.is_state('VALIDE'):
509
                return False
510
        return True
511

    
512
    def delete(self, *args, **kwargs):
513
        if self.can_be_deleted():
514
            super(PatientRecord, self).delete(*args, **kwargs)
515

    
516
    def get_ondisk_directory(self, service):
517
        if not settings.PATIENT_FILES_BASE_DIRECTORY:
518
            return None
519

    
520
        dirnames = []
521
        dirname = self.last_name.upper()
522
        dirnames.append(dirname)
523
        if self.first_name:
524
            dirname = '%s %s' % (dirname, self.first_name)
525
            dirnames.append(dirname)
526
        if self.paper_id:
527
            dirname = '%s %s' % (dirname, self.paper_id)
528
            dirnames.append(dirname)
529

    
530
        for i, dirname in enumerate(dirnames):
531
            fullpath = os.path.join(settings.PATIENT_FILES_BASE_DIRECTORY, service, dirname)
532
            try:
533
                next_fullpath = os.path.join(settings.PATIENT_FILES_BASE_DIRECTORY, service, dirnames[i+1])
534
            except IndexError:
535
                pass
536
            else:
537
                 if os.path.exists(fullpath) and not os.path.exists(next_fullpath):
538
                     os.rename(fullpath, next_fullpath)
539
                 continue
540
            if not os.path.exists(fullpath):
541
                os.makedirs(fullpath)
542
            for subdir in settings.PATIENT_SUBDIRECTORIES:
543
                subdir_fullpath = os.path.join(fullpath, subdir)
544
                if not os.path.exists(subdir_fullpath):
545
                    os.makedirs(subdir_fullpath)
546
        return fullpath
547

    
548
    def get_client_side_directory(self, service):
549
        directory = self.get_ondisk_directory(service)
550
        if not directory:
551
            return None
552
        if not settings.CLIENT_SIDE_PATIENT_FILES_BASE_DIRECTORY:
553
            return None
554
        return os.path.join(settings.CLIENT_SIDE_PATIENT_FILES_BASE_DIRECTORY,
555
                            directory[len(settings.PATIENT_FILES_BASE_DIRECTORY)+1:])
556

    
557
    def set_state(self, status, author, date_selected=None, comment=None):
558
        if not author:
559
            raise Exception('Missing author to set state')
560
        if not date_selected:
561
            date_selected = datetime.now()
562
        current_state = self.get_state()
563
        if not current_state:
564
            raise Exception('Invalid patient record. '
565
                'Missing current state.')
566
        if isinstance(date_selected, date):
567
            date_selected = datetime(year=date_selected.year,
568
                month=date_selected.month, day=date_selected.day)
569
        if date_selected < current_state.date_selected:
570
            raise Exception('You cannot set a state starting the %s that '
571
                'is before the previous state starting at day %s.' % \
572
                (str(date_selected), str(current_state.date_selected)))
573
        filestate = FileState.objects.create(patient=self, status=status,
574
            date_selected=date_selected, author=author, comment=comment,
575
            previous_state=current_state)
576
        self.last_state = filestate
577
        self.save()
578

    
579
    def change_day_selected_of_state(self, state, new_date):
580
        if state.previous_state:
581
            if new_date < state.previous_state.date_selected:
582
                raise Exception('You cannot set a state starting the %s '
583
                    'before the previous state starting at day %s.' % \
584
                    (str(new_date), str(state.previous_state.date_selected)))
585
        next_state = state.get_next_state()
586
        if next_state:
587
            if new_date > next_state.date_selected:
588
                raise Exception('You cannot set a state starting the %s '
589
                    'after the following state starting at day %s.' % \
590
                    (str(new_date), str(next_state.date_selected)))
591
        state.date_selected = new_date
592
        state.save()
593

    
594
    def remove_state(self, state):
595
        if state.patient.id != self.id:
596
            raise Exception('The state given is not about this patient '
597
                'record but about %s' % state.patient)
598
        next_state = state.get_next_state()
599
        if not next_state:
600
            self.remove_last_state()
601
        else:
602
            next_state.previous_state = state.previous_state
603
            next_state.save()
604
            state.delete()
605

    
606
    def remove_last_state(self):
607
        try:
608
            self.get_state().delete()
609
        except:
610
            pass
611

    
612
    # START Specific to sessad healthcare
613
    def get_last_notification(self):
614
        return SessadHealthCareNotification.objects.filter(patient=self, ).\
615
            latest('end_date')
616

    
617
    def days_before_notification_expiration(self):
618
        today = datetime.today()
619
        notification = self.get_last_notification(self)
620
        if not notification:
621
            return 0
622
        if notification.end_date < today:
623
            return 0
624
        else:
625
            return notification.end_date - today
626
    # END Specific to sessad healthcare
627

    
628
    # START Specific to cmpp healthcare
629
    def create_diag_healthcare(self, modifier):
630
        """
631
            Gestion de l'inscription automatique.
632

    
633
            Si un premier acte est validé alors une prise en charge
634
            diagnostique est ajoutée. Cela fera basculer le dossier dans l'état
635
            en diagnostic.
636

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

    
643
        """
644
        acts = self.act_set.order_by('date')
645
        hcds = CmppHealthCareDiagnostic.objects.filter(patient=self).order_by('-start_date')
646
        if not hcds:
647
            # Pas de prise en charge, on recherche l'acte facturable le plus
648
            # ancien, on crée une pc diag à la même date.
649
            for act in acts:
650
                if act.is_state('VALIDE') and act.is_billable():
651
                    CmppHealthCareDiagnostic(patient=self, author=modifier,
652
                        start_date=act.date).save()
653
                    break
654
        else:
655
            # On recherche l'acte facturable non facturé le plus ancien et
656
            # l'on regarde s'il a plus d'un an
657
            try:
658
                last_billed_act = self.act_set.filter(is_billed=True).\
659
                    latest('date')
660
                if last_billed_act:
661
                    for act in acts:
662
                        if act.is_state('VALIDE') and \
663
                                act.is_billable() and \
664
                                (act.date - last_billed_act.date).days >= 365:
665
                            return True
666
            except:
667
                pass
668
        return False
669

    
670
    def automated_switch_state(self, modifier):
671
        # Quel est le dernier acte facturable.
672
        acts = self.act_set.order_by('-date')
673
        # Si cet acte peut-être pris en charge en diagnostic c'est un acte de
674
        # diagnostic, sinon c'est un acte de traitement.
675
        last_state_services = self.last_state.status.\
676
                services.values_list('name', flat=True)
677
        cmpp = Service.objects.get(name='CMPP')
678
        for act in acts:
679
            if act.is_state('VALIDE') and act.is_billable() and \
680
                    act.date >= self.get_state().date_selected.date() and \
681
                    are_all_acts_of_the_day_locked(act.date):
682
                cared, hc = act.is_act_covered_by_diagnostic_healthcare()
683
                if hc:
684
                    if (self.last_state.status.type == "ACCUEIL" \
685
                            or self.last_state.status.type == "TRAITEMENT") \
686
                            and "CMPP" in last_state_services:
687
                        status = Status.objects.filter(type="DIAGNOSTIC").\
688
                                filter(services__name='CMPP')[0]
689
                        try:
690
                            self.set_state(status, modifier,
691
                                date_selected=act.date)
692
                        except:
693
                            pass
694
                # Sinon, si le dossier est en diag, s'il ne peut être couvert
695
                # en diag, il est en traitement.
696
                elif self.last_state.status.type == "DIAGNOSTIC" and \
697
                        "CMPP" in last_state_services:
698
                    status = Status.objects.filter(type="TRAITEMENT").\
699
                            filter(services__name='CMPP')[0]
700
                    try:
701
                        self.set_state(status, modifier,
702
                                date_selected=act.date)
703
                    except:
704
                        pass
705
                break
706

    
707
    def get_healthcare_status(self):
708
        today = date.today()
709
        current_hc_trait = None
710
        try:
711
            current_hc_trait = CmppHealthCareTreatment.objects.filter(patient=self,start_date__lte=today, end_date__gte=today).latest('start_date')
712
        except:
713
            pass
714
        if not current_hc_trait:
715
            current_hc_diag = None
716
            try:
717
                current_hc_diag = CmppHealthCareDiagnostic.objects.filter(patient=self, start_date__lte=today).latest('start_date')
718
            except:
719
                pass
720
            if current_hc_diag and current_hc_diag.get_act_number() > len(current_hc_diag.act_set.all()):
721

    
722
                #Plus simple et a changer dans la facturation, s'il y une pc de traitemant avec une start date > a la pc diag alors cette pc de diag est finie
723
                # Non parce que je peux ajouter une pc de traitement alors que je veux encore facturer sur la diag precedente.
724
                # Donc si j'ai un acte facturer en traitement alors la diag ne fonctionne plus.
725
                # Dans le fonctionnement normal, si j'ai encore une diag dispo et que je veux fact duirectement en trait, je reduit le nombre d'acte pris en charge.
726
                lasts_billed = Act.objects.filter(patient=self, is_billed = True, healthcare__isnull=False).order_by('-date')
727
                last_hc_date = None
728
                if lasts_billed:
729
                    last_hc_date = lasts_billed[0].healthcare.start_date
730
                if not last_hc_date or last_hc_date <= current_hc_diag.start_date:
731
                    # Prise en charge disponible
732
                    return (0, len(current_hc_diag.act_set.all()), current_hc_diag.get_act_number())
733
            last_hc_trait = None
734
            try:
735
                last_hc_trait = CmppHealthCareTreatment.objects.filter(patient=self).latest('start_date')
736
            except:
737
                pass
738
            if not last_hc_trait:
739
                if not current_hc_diag:
740
                    # Aucune PC
741
                    return (1, None)
742
                else:
743
                    # PC diag full, demander PC trait
744
                    return (2, current_hc_diag.get_act_number())
745
            if last_hc_trait.end_date < today:
746
                # Expirée
747
                #Test if rediagable
748
                return (3, last_hc_trait.end_date)
749
            if last_hc_trait.start_date > today:
750
                # N'a pas encore pris effet
751
                return (4, last_hc_trait.start_date)
752
            return (-1,)
753
        if current_hc_trait.get_act_number() > len(current_hc_trait.act_set.all()):
754
            # Pris en charge disponible
755
            return (5, len(current_hc_trait.act_set.all()), current_hc_trait.get_act_number())
756
        # Prise en charge au quota
757
        if not current_hc_trait.is_extended():
758
            # Peut être prolongée
759
            return (6, current_hc_trait.get_act_number())
760
        # Prise en charge saturée
761
        return (7, current_hc_trait.get_act_number(), current_hc_trait.end_date())
762
    # END Specific to cmpp healthcare
763

    
764

    
765
    @property
766
    def entry_date(self):
767
        d = self.filestate_set.filter(
768
                Q(status__type='DIAGNOSTIC') |
769
                Q(status__type='TRAITEMENT') |
770
                Q(status__type='SUIVI')). \
771
                        aggregate(Min('date_selected'))['date_selected__min']
772
        return d and d.date()
773

    
774

    
775
    @property
776
    def exit_date(self):
777
        if self.last_state.status.type != 'CLOS':
778
            return None
779
        d = self.filestate_set.filter(status__type='CLOS'). \
780
                    aggregate(Max('date_selected'))['date_selected__max']
781
        return d and d.date()
782

    
783

    
784
reversion.register(PatientRecord, follow=['people_ptr'])
785

    
786

    
787
def create_patient(first_name, last_name, service, creator,
788
        date_selected=None):
789
    logger.debug('create_patient: creation for patient %s %s in service %s '
790
        'by %s' % (first_name, last_name, service, creator))
791
    if not (first_name and last_name and service and creator):
792
        raise Exception('Missing parameter to create a patient record.')
793
    status = Status.objects.filter(type="ACCUEIL").filter(services=service)
794
    if not status:
795
        raise Exception('%s has no ACCEUIL status' % service.name)
796
    patient = PatientRecord.objects.create(first_name=first_name,
797
            last_name=last_name, service=service,
798
            creator=creator)
799
    fs = FileState(status=status[0], author=creator, previous_state=None)
800
    if not date_selected:
801
        date_selected = patient.created
802
    fs.patient = patient
803
    fs.date_selected = date_selected
804
    fs.save()
805
    patient.last_state = fs
806
    patient.save()
807
    patient.policyholder = patient.patientcontact
808
    patient.save()
809
    return patient
(5-5/9)