Project

General

Profile

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

calebasse / calebasse / dossiers / models.py @ 158b10a6

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

    
15
import reversion
16

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

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

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

    
34

    
35
class HealthCare(models.Model):
36

    
37
    class Meta:
38
        app_label = 'dossiers'
39

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

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

    
58

    
59
class CmppHealthCareDiagnostic(HealthCare):
60

    
61
    class Meta:
62
        app_label = 'dossiers'
63

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

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

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

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

    
82

    
83
class CmppHealthCareTreatment(HealthCare):
84

    
85
    class Meta:
86
        app_label = 'dossiers'
87

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

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

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

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

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

    
119
    def del_prolongation(self):
120
        pass
121

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

    
132

    
133
class SessadHealthCareNotification(HealthCare):
134

    
135
    class Meta:
136
        app_label = 'dossiers'
137

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

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

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

    
155
class Status(NamedAbstractModel):
156

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

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

    
165

    
166
class FileState(models.Model):
167

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

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

    
185
    def get_next_state(self):
186
        try:
187
            return FileState.objects.get(previous_state=self)
188
        except:
189
            return None
190

    
191
    def save(self, **kwargs):
192
        self.date_selected = \
193
                datetime(self.date_selected.year,
194
                        self.date_selected.month, self.date_selected.day)
195
        super(FileState, self).save(**kwargs)
196

    
197
    def __unicode__(self):
198
        return self.status.name + ' ' + str(self.date_selected)
199

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

    
210
class PatientAddress(models.Model):
211

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

    
230
    def __unicode__(self):
231
        return self.display_name
232

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

    
247

    
248
class PatientContact(People):
249
    class Meta:
250
        verbose_name = u'Contact patient'
251
        verbose_name_plural = u'Contacts patient'
252

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

    
287
    addresses = models.ManyToManyField('PatientAddress', verbose_name=u"Adresses")
288
    contact_comment = models.TextField(verbose_name=u"Commentaire",
289
            null=True, blank=True)
290

    
291
    old_contact_id = models.CharField(max_length=256,
292
            verbose_name=u'Ancien ID du contact', blank=True, null=True)
293

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

    
317

    
318
class PatientRecordManager(models.Manager):
319
    def for_service(self, service):
320
        return self.filter(service=service)
321

    
322
class PatientRecord(ServiceLinkedAbstractModel, PatientContact):
323
    objects = PatientRecordManager()
324

    
325
    class Meta:
326
        verbose_name = u'Dossier'
327
        verbose_name_plural = u'Dossiers'
328

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

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

    
385
    # Inscription motive
386
    analysemotive = models.ForeignKey('ressources.AnalyseMotive',
387
            verbose_name=u"Motif (analysé)",
388
            null=True, blank=True, default=None)
389
    familymotive = models.ForeignKey('ressources.FamilyMotive',
390
            verbose_name=u"Motif (famille)",
391
            null=True, blank=True, default=None)
392
    provenance = models.ForeignKey('ressources.Provenance',
393
            verbose_name=u"Conseilleur",
394
            null=True, blank=True, default=None)
395
    advicegiver = models.ForeignKey('ressources.AdviceGiver',
396
            verbose_name=u"Demandeur",
397
            null=True, blank=True, default=None)
398

    
399
    # Out motive
400
    outmotive = models.ForeignKey('ressources.OutMotive',
401
            verbose_name=u"Motif de sortie",
402
            null=True, blank=True, default=None)
403
    outto = models.ForeignKey('ressources.OutTo',
404
            verbose_name=u"Orientation",
405
            null=True, blank=True, default=None)
406

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

    
440
    # Transport
441
    transporttype = models.ForeignKey('ressources.TransportType',
442
            verbose_name=u"Type de transport",
443
            null=True, blank=True, default=None)
444
    transportcompany = models.ForeignKey('ressources.TransportCompany',
445
            verbose_name=u"Compagnie de transport",
446
            null=True, blank=True, default=None)
447

    
448
    # FollowUp
449
    coordinators = models.ManyToManyField('personnes.Worker',
450
            verbose_name=u"Coordinateurs",
451
            null=True, blank=True, default=None)
452
    externaldoctor = models.ForeignKey('personnes.ExternalTherapist',
453
            verbose_name=u"Médecin extérieur",
454
            null=True, blank=True, default=None)
455
    externalintervener = models.ForeignKey('personnes.ExternalWorker',
456
            verbose_name=u"Intervenant extérieur",
457
            null=True, blank=True, default=None)
458

    
459
    old_id = models.CharField(max_length=256,
460
            verbose_name=u'Ancien ID', blank=True, null=True)
461
    old_old_id = models.CharField(max_length=256,
462
            verbose_name=u'Ancien ancien ID', blank=True, null=True)
463

    
464
    def save(self, *args, **kwargs):
465
        if not getattr(self, 'service', None):
466
            raise Exception('The field service is mandatory.')
467
        super(PatientRecord, self).save(*args, **kwargs)
468

    
469
    def get_state(self):
470
        return self.last_state
471

    
472
    def get_initial_state(self):
473
        return self.filestate_set.order_by('date_selected')[0]
474

    
475
    def get_current_state(self):
476
        today = date.today()
477
        return self.get_state_at_day(today)
478

    
479
    def get_state_at_day(self, date):
480
        state = self.get_state()
481
        while(state):
482
            if datetime(state.date_selected.year,
483
                    state.date_selected.month, state.date_selected.day) <= \
484
                    datetime(date.year, date.month, date.day):
485
                return state
486
            state = state.previous_state
487
        return self.get_state()
488

    
489
    def was_in_state_at_day(self, date, status_type):
490
        state_at_day = self.get_state_at_day(date)
491
        if state_at_day and state_at_day.status.type == status_type:
492
            return True
493
        return False
494

    
495
    def get_states_history(self):
496
        return self.filestate_set.order_by('date_selected')
497

    
498
    def can_be_deleted(self):
499
        for act in self.act_set.all():
500
            if act.is_state('VALIDE'):
501
                return False
502
        return True
503

    
504
    def delete(self, *args, **kwargs):
505
        if self.can_be_deleted():
506
            super(PatientRecord, self).delete(*args, **kwargs)
507

    
508
    def get_ondisk_directory(self, service):
509
        if not settings.PATIENT_FILES_BASE_DIRECTORY:
510
            return None
511

    
512
        dirnames = []
513
        dirname = self.last_name.upper()
514
        dirnames.append(dirname)
515
        if self.first_name:
516
            dirname = '%s %s' % (dirname, self.first_name)
517
            dirnames.append(dirname)
518
        if self.paper_id:
519
            dirname = '%s %s' % (dirname, self.paper_id)
520
            dirnames.append(dirname)
521

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

    
540
    def get_client_side_directory(self, service):
541
        directory = self.get_ondisk_directory(service)
542
        if not directory:
543
            return None
544
        if not settings.CLIENT_SIDE_PATIENT_FILES_BASE_DIRECTORY:
545
            return None
546
        return os.path.join(settings.CLIENT_SIDE_PATIENT_FILES_BASE_DIRECTORY,
547
                            directory[len(settings.PATIENT_FILES_BASE_DIRECTORY)+1:])
548

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

    
571
    def change_day_selected_of_state(self, state, new_date):
572
        if state.previous_state:
573
            if new_date < state.previous_state.date_selected:
574
                raise Exception('You cannot set a state starting the %s '
575
                    'before the previous state starting at day %s.' % \
576
                    (str(new_date), str(state.previous_state.date_selected)))
577
        next_state = state.get_next_state()
578
        if next_state:
579
            if new_date > next_state.date_selected:
580
                raise Exception('You cannot set a state starting the %s '
581
                    'after the following state starting at day %s.' % \
582
                    (str(new_date), str(next_state.date_selected)))
583
        state.date_selected = new_date
584
        state.save()
585

    
586
    def remove_state(self, state):
587
        if state.patient.id != self.id:
588
            raise Exception('The state given is not about this patient '
589
                'record but about %s' % state.patient)
590
        next_state = state.get_next_state()
591
        if not next_state:
592
            self.remove_last_state()
593
        else:
594
            next_state.previous_state = state.previous_state
595
            next_state.save()
596
            state.delete()
597

    
598
    def remove_last_state(self):
599
        try:
600
            self.get_state().delete()
601
        except:
602
            pass
603

    
604
    # START Specific to sessad healthcare
605
    def get_last_notification(self):
606
        return SessadHealthCareNotification.objects.filter(patient=self, ).\
607
            latest('end_date')
608

    
609
    def days_before_notification_expiration(self):
610
        today = datetime.today()
611
        notification = self.get_last_notification(self)
612
        if not notification:
613
            return 0
614
        if notification.end_date < today:
615
            return 0
616
        else:
617
            return notification.end_date - today
618
    # END Specific to sessad healthcare
619

    
620
    # START Specific to cmpp healthcare
621
    def create_diag_healthcare(self, modifier):
622
        """
623
            Gestion de l'inscription automatique.
624

    
625
            Si un premier acte est validé alors une prise en charge
626
            diagnostique est ajoutée. Cela fera basculer le dossier dans l'état
627
            en diagnostic.
628

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

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

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

    
699
    def get_healthcare_status(self):
700
        today = date.today()
701
        current_hc_trait = None
702
        try:
703
            current_hc_trait = CmppHealthCareTreatment.objects.filter(patient=self,start_date__lte=today, end_date__gte=today).latest('start_date')
704
        except:
705
            pass
706
        if not current_hc_trait:
707
            current_hc_diag = None
708
            try:
709
                current_hc_diag = CmppHealthCareDiagnostic.objects.filter(patient=self, start_date__lte=today).latest('start_date')
710
            except:
711
                pass
712
            if current_hc_diag and current_hc_diag.get_act_number() > len(current_hc_diag.act_set.all()):
713

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

    
756

    
757
    @property
758
    def entry_date(self):
759
        d = self.filestate_set.filter(
760
                Q(status__type='DIAGNOSTIC') |
761
                Q(status__type='TRAITEMENT')). \
762
                        aggregate(Min('date_selected'))['date_selected__min']
763
        return d and d.date()
764

    
765
    @property
766
    def exit_date(self):
767
        d = self.filestate_set.filter(status__type='CLOS'). \
768
                    aggregate(Max('date_selected'))['date_selected__max']
769
        return d and d.date()
770

    
771

    
772
reversion.register(PatientRecord, follow=['people_ptr'])
773

    
774

    
775
def create_patient(first_name, last_name, service, creator,
776
        date_selected=None):
777
    logger.debug('create_patient: creation for patient %s %s in service %s '
778
        'by %s' % (first_name, last_name, service, creator))
779
    if not (first_name and last_name and service and creator):
780
        raise Exception('Missing parameter to create a patient record.')
781
    status = Status.objects.filter(type="ACCUEIL").filter(services=service)
782
    if not status:
783
        raise Exception('%s has no ACCEUIL status' % service.name)
784
    patient = PatientRecord.objects.create(first_name=first_name,
785
            last_name=last_name, service=service,
786
            creator=creator)
787
    fs = FileState(status=status[0], author=creator, previous_state=None)
788
    if not date_selected:
789
        date_selected = patient.created
790
    fs.patient = patient
791
    fs.date_selected = date_selected
792
    fs.save()
793
    patient.last_state = fs
794
    patient.save()
795
    patient.policyholder = patient.patientcontact
796
    patient.save()
797
    return patient
(5-5/9)