Project

General

Profile

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

calebasse / calebasse / dossiers / models.py @ 3cd1c742

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
    prolongation = models.IntegerField(default=0,
93
            verbose_name=u'Prolongation')
94

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

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

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

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

    
120
    def del_prolongation(self):
121
        pass
122

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

    
133

    
134
class SessadHealthCareNotification(HealthCare):
135

    
136
    class Meta:
137
        app_label = 'dossiers'
138

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

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

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

    
156
class Status(NamedAbstractModel):
157

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

    
163
    type = models.CharField(max_length=80)
164
    services = models.ManyToManyField('ressources.Service')
165

    
166

    
167
class FileState(models.Model):
168

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

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

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

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

    
200
    def __unicode__(self):
201
        return self.status.name + ' ' + str(self.date_selected)
202

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

    
213
class PatientAddress(models.Model):
214

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

    
233
    def __unicode__(self):
234
        return self.display_name
235

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

    
250

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

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

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

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

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

    
323

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

    
328
class PatientRecord(ServiceLinkedAbstractModel, PatientContact):
329
    objects = PatientRecordManager()
330

    
331
    class Meta:
332
        verbose_name = u'Dossier'
333
        verbose_name_plural = u'Dossiers'
334

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

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

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

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

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

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

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

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

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

    
475
    def get_state(self):
476
        return self.last_state
477

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

    
481
    def get_current_state(self):
482
        today = date.today()
483
        return self.get_state_at_day(today)
484

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

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

    
501
    def get_states_history(self):
502
        return self.filestate_set.order_by('date_selected')
503

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
762

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

    
772

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

    
781

    
782
reversion.register(PatientRecord, follow=['people_ptr'])
783

    
784

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