Projet

Général

Profil

Télécharger (43,6 ko) Statistiques
| Branche: | Tag: | Révision:

calebasse / calebasse / dossiers / models.py @ a9520794

1 96f613c6 Benjamin Dauvergne
# -*- coding: utf-8 -*-
2
3 7bdb9ebf Mikaël Ates
import logging
4 68d5a646 Frédéric Péters
import os
5 7bdb9ebf Mikaël Ates
6 3665f853 Mikaël Ates
from datetime import datetime, date
7 fb4195eb Mikaël Ates
from dateutil.relativedelta import relativedelta
8 005f1264 Mikaël Ates
from cPickle import loads, dumps
9 70b65c8c Mikaël Ates
10 68d5a646 Frédéric Péters
from django.conf import settings
11 1097fd0a Benjamin Dauvergne
from django.db import models
12 5539237c Benjamin Dauvergne
from django.db.models import Min, Max, Q
13 6163f2ff Mikaël Ates
from django.contrib.auth.models import User
14 39ddd165 Jérôme Schneider
from django.core.validators import MinValueValidator
15 1097fd0a Benjamin Dauvergne
16 3c5df84d Jérôme Schneider
from calebasse.choices import TYPE_OF_CONTRACT_CHOICES, DEFICIENCY_CHOICES
17 3cab63be Jérôme Schneider
from calebasse.models import PhoneNumberField, ZipCodeField
18 96f613c6 Benjamin Dauvergne
from calebasse.personnes.models import People
19 7948e4a4 Jérôme Schneider
from calebasse.ressources.models import (ServiceLinkedAbstractModel,
20 3c5df84d Jérôme Schneider
        NamedAbstractModel)
21 81916a5f Mikaël Ates
from calebasse.actes.models import Act
22 1097fd0a Benjamin Dauvergne
23 a01d85be Serghei MIHAI
from ..middleware.request import get_request
24 1af9e727 Frédéric Péters
from ..utils import get_service_setting
25 a01d85be Serghei MIHAI
26 d489bf41 Mikaël Ates
DEFAULT_ACT_NUMBER_DIAGNOSTIC = 6
27
DEFAULT_ACT_NUMBER_TREATMENT = 30
28
DEFAULT_ACT_NUMBER_PROLONGATION = 10
29 fb4195eb Mikaël Ates
VALIDITY_PERIOD_TREATMENT_HEALTHCARE_DAYS = 0
30
VALIDITY_PERIOD_TREATMENT_HEALTHCARE_MONTHS = 0
31
VALIDITY_PERIOD_TREATMENT_HEALTHCARE_YEARS = 1
32 d489bf41 Mikaël Ates
33 7bdb9ebf Mikaël Ates
logger = logging.getLogger('calebasse.dossiers')
34
35
36 005f1264 Mikaël Ates
class TransportPrescriptionLog(models.Model):
37
    patient = models.ForeignKey('dossiers.PatientRecord',
38
        verbose_name=u'Dossier patient')
39
    created = models.DateTimeField(u'Création', auto_now_add=True)
40
    choices = models.CharField(max_length = 4096, null=True, blank=True)
41
42
    def get_choices(self):
43
        if not self.choices:
44
            return dict()
45 6fd15526 Serghei MIHAI
        return loads(str(self.choices), protocol = 1)
46 005f1264 Mikaël Ates
47
    def set_choices(self, choices=None):
48
        if choices and isinstance(choices, dict):
49 6fd15526 Serghei MIHAI
            self.choices = dumps(choices, protocol = 1)
50 005f1264 Mikaël Ates
51
52 d489bf41 Mikaël Ates
class HealthCare(models.Model):
53 2da0976b Jérôme Schneider
    start_date = models.DateField(verbose_name=u"Date de début")
54 15f8dbaa Mikaël Ates
    request_date = models.DateField(verbose_name=u"Date de demande",
55 d72af982 Mikaël Ates
        blank=True, null=True)
56 15f8dbaa Mikaël Ates
    agree_date = models.DateField(verbose_name=u"Date d'accord",
57 d72af982 Mikaël Ates
        blank=True, null=True)
58
    insist_date = models.DateField(verbose_name=u"Date de relance",
59
        blank=True, null=True)
60 d489bf41 Mikaël Ates
    patient = models.ForeignKey('dossiers.PatientRecord',
61 2da0976b Jérôme Schneider
        verbose_name=u'Dossier patient')
62 d489bf41 Mikaël Ates
    created = models.DateTimeField(u'Création', auto_now_add=True)
63
    author = \
64
        models.ForeignKey(User,
65 d72af982 Mikaël Ates
        verbose_name=u'Auteur', blank=True, null=True)
66 b464657d Mikaël Ates
    comment = models.TextField(max_length=3000, blank=True, null=True, verbose_name=u"Commentaire")
67 d489bf41 Mikaël Ates
68 c66c6fcd Mikaël Ates
    def get_nb_acts_cared(self):
69
        return len(self.act_set.all())
70
71 d489bf41 Mikaël Ates
72
class CmppHealthCareDiagnostic(HealthCare):
73 72219903 Mikaël Ates
    act_number = models.IntegerField(default=DEFAULT_ACT_NUMBER_DIAGNOSTIC, verbose_name=u"Nombre d'actes couverts")
74 3a72b7ac Mikaël Ates
    end_date = models.DateField(verbose_name=u"Date de fin",
75
        blank=True, null=True)
76 d489bf41 Mikaël Ates
77
    def get_act_number(self):
78 72219903 Mikaël Ates
        return self.act_number
79 d489bf41 Mikaël Ates
80 71a9768c Mikaël Ates
    def set_act_number(self, value):
81
        if value < self.get_nb_acts_cared():
82
            raise Exception("La valeur doit être supérieur au "
83
                "nombre d'actes déjà pris en charge")
84 72219903 Mikaël Ates
        self.act_number = value
85 71a9768c Mikaël Ates
        self.save()
86
87 d489bf41 Mikaël Ates
    def save(self, **kwargs):
88
        self.start_date = \
89
            datetime(self.start_date.year, self.start_date.month,
90
                self.start_date.day)
91
        super(CmppHealthCareDiagnostic, self).save(**kwargs)
92
93
94
class CmppHealthCareTreatment(HealthCare):
95 72219903 Mikaël Ates
    act_number = models.IntegerField(default=DEFAULT_ACT_NUMBER_TREATMENT,
96 2da0976b Jérôme Schneider
            verbose_name=u"Nombre d'actes couverts")
97 44a0fb10 Mikaël Ates
    end_date = models.DateField(verbose_name=u"Date de fin",
98
        blank=True, null=True)
99 d489bf41 Mikaël Ates
    prolongation = models.IntegerField(default=0,
100
            verbose_name=u'Prolongation')
101 61405b31 Mikaël Ates
    prolongation_date = models.DateField(verbose_name=u"Date de prolongation",
102
        blank=True, null=True)
103 d489bf41 Mikaël Ates
104
    def get_act_number(self):
105
        if self.is_extended():
106 72219903 Mikaël Ates
            return self.act_number + self.prolongation
107
        return self.act_number
108 d489bf41 Mikaël Ates
109 71a9768c Mikaël Ates
    def set_act_number(self, value):
110
        if value < self.get_nb_acts_cared():
111
            raise Exception("La valeur doit être supérieur au "
112
                "nombre d'actes déjà pris en charge")
113 72219903 Mikaël Ates
        self.act_number = value
114 71a9768c Mikaël Ates
        self.save()
115
116 d489bf41 Mikaël Ates
    def is_extended(self):
117
        if self.prolongation > 0:
118
            return True
119
        return False
120
121
    def add_prolongation(self, value=None):
122
        if not value:
123
            value = DEFAULT_ACT_NUMBER_PROLONGATION
124
        if self.is_extended():
125
            raise Exception(u'Prise en charge déja prolongée')
126
        self.prolongation = value
127
        self.save()
128
129 81916a5f Mikaël Ates
    def del_prolongation(self):
130
        pass
131
132 d489bf41 Mikaël Ates
    def save(self, **kwargs):
133
        self.start_date = \
134
            datetime(self.start_date.year, self.start_date.month,
135
                self.start_date.day)
136 44a0fb10 Mikaël Ates
        if not self.end_date:
137
            self.end_date = self.start_date + \
138
                relativedelta(years=VALIDITY_PERIOD_TREATMENT_HEALTHCARE_YEARS) + \
139
                relativedelta(months=VALIDITY_PERIOD_TREATMENT_HEALTHCARE_MONTHS) + \
140
                relativedelta(days=VALIDITY_PERIOD_TREATMENT_HEALTHCARE_DAYS-1)
141 d489bf41 Mikaël Ates
        super(CmppHealthCareTreatment, self).save(**kwargs)
142
143
144
class SessadHealthCareNotification(HealthCare):
145 27bc0d25 Mikaël Ates
    end_date = models.DateField(verbose_name=u"Date de fin",
146
        blank=True, null=True)
147 d489bf41 Mikaël Ates
148
    def save(self, **kwargs):
149
        self.start_date = \
150
            datetime(self.start_date.year, self.start_date.month,
151
                self.start_date.day)
152 27bc0d25 Mikaël Ates
        if self.end_date:
153
            self.end_date = \
154
                datetime(self.end_date.year, self.end_date.month,
155
                    self.end_date.day)
156 d489bf41 Mikaël Ates
        super(SessadHealthCareNotification, self).save(**kwargs)
157
158 0f2849e4 Mikaël Ates
class ProtectionStatus(NamedAbstractModel):
159
160
    class Meta:
161
        verbose_name = u"Statut d'une mesure de protection"
162
        verbose_name_plural = u"Statuts d'une mesure de protection"
163
164
class ProtectionState(models.Model):
165
166
    class Meta:
167
        verbose_name = u'Mesure de protection du dossier patient'
168
        verbose_name_plural = u'Mesure de protections du dossier patient'
169
        ordering = ['-start_date']
170
171
    patient = models.ForeignKey('dossiers.PatientRecord',
172
        verbose_name=u'Dossier patient')
173
    status = models.ForeignKey('dossiers.ProtectionStatus', verbose_name=u'Statut de protection')
174
    created = models.DateTimeField(u'Création', auto_now_add=True)
175
    start_date = models.DateTimeField()
176
    end_date = models.DateTimeField(blank=True, null=True)
177
    comment = models.TextField(max_length=3000, blank=True, null=True)
178
179
    def __unicode__(self):
180
        return self.status.name + ' ' + str(self.start_date)
181
182 7328a577 Jérôme Schneider
class Status(NamedAbstractModel):
183
184
    class Meta:
185 d994547b Jérôme Schneider
        verbose_name = u"Statut d'un état"
186
        verbose_name_plural = u"Statuts d'un état"
187 7328a577 Jérôme Schneider
188
    type = models.CharField(max_length=80)
189
    services = models.ManyToManyField('ressources.Service')
190
191 2773b4d4 Jérôme Schneider
192 2a928c61 Mikaël Ates
class FileState(models.Model):
193
194
    class Meta:
195 7328a577 Jérôme Schneider
        verbose_name = u'Etat du dossier patient'
196
        verbose_name_plural = u'Etats du dossier patient'
197 2a928c61 Mikaël Ates
198
    patient = models.ForeignKey('dossiers.PatientRecord',
199 536bab34 Mikaël Ates
        verbose_name=u'Dossier patient')
200 4a0ff5ea Frédéric Péters
    status = models.ForeignKey('dossiers.Status', verbose_name=u'Statut')
201 2a928c61 Mikaël Ates
    created = models.DateTimeField(u'Création', auto_now_add=True)
202
    date_selected = models.DateTimeField()
203
    author = \
204
        models.ForeignKey(User,
205 536bab34 Mikaël Ates
        verbose_name=u'Auteur')
206 2a928c61 Mikaël Ates
    comment = models.TextField(max_length=3000, blank=True, null=True)
207
    previous_state = models.ForeignKey('FileState',
208 9335a096 Jérôme Schneider
            on_delete=models.SET_NULL,
209
            verbose_name=u'Etat précédent',
210
            blank=True, null=True)
211 2a928c61 Mikaël Ates
212
    def get_next_state(self):
213
        try:
214
            return FileState.objects.get(previous_state=self)
215
        except:
216
            return None
217
218
    def save(self, **kwargs):
219 f3407c83 Jérôme Schneider
        self.date_selected = \
220
                datetime(self.date_selected.year,
221
                        self.date_selected.month, self.date_selected.day)
222 2a928c61 Mikaël Ates
        super(FileState, self).save(**kwargs)
223
224
    def __unicode__(self):
225 2d4b9891 Jérôme Schneider
        return self.status.name + ' ' + str(self.date_selected)
226 2a928c61 Mikaël Ates
227 4a0ff5ea Frédéric Péters
    def delete(self, *args, **kwargs):
228
        next_state = self.get_next_state()
229
        if next_state and self.previous_state:
230
            next_state.previous_state = self.previous_state
231
            next_state.save()
232
        if self.patient.last_state == self:
233
            self.patient.last_state = self.previous_state
234
            self.patient.save()
235 a01d85be Serghei MIHAI
        obj_id = self.id
236 4a0ff5ea Frédéric Péters
        super(FileState, self).delete(*args, **kwargs)
237
238 3cab63be Jérôme Schneider
class PatientAddress(models.Model):
239
240 29168b44 Jérôme Schneider
    display_name = models.CharField(max_length=276,
241
            verbose_name=u'Adresse complète', editable=False)
242 2d4b9891 Jérôme Schneider
    phone = PhoneNumberField(verbose_name=u"Téléphone", blank=True, null=True)
243
    fax = PhoneNumberField(verbose_name=u"Fax", blank=True, null=True)
244 1164bc89 Mikaël Ates
    place_of_life = models.BooleanField(verbose_name=u"Lieu de vie",
245
            default=True)
246 29168b44 Jérôme Schneider
    number = models.CharField(max_length=12,
247
            verbose_name=u"Numéro", blank=True, null=True)
248 61491834 Jérôme Schneider
    recipient = models.CharField(max_length=100,
249
            verbose_name=u"Destinataire", blank=True, null=True)
250 29168b44 Jérôme Schneider
    street = models.CharField(max_length=100,
251 49e4c841 Mikaël Ates
            verbose_name=u"Rue", blank=True, null=True)
252 29168b44 Jérôme Schneider
    address_complement = models.CharField(max_length=100,
253
            blank=True, null=True,
254 d0353961 Mikaël Ates
            verbose_name=u"Complément d'adresse")
255 49e4c841 Mikaël Ates
    zip_code = ZipCodeField(verbose_name=u"Code postal", blank=True, null=True)
256 29168b44 Jérôme Schneider
    city = models.CharField(max_length=60,
257 49e4c841 Mikaël Ates
            verbose_name=u"Ville", blank=True, null=True)
258 317bcb4c Jérôme Schneider
    comment = models.TextField(verbose_name=u"Commentaire",
259
            null=True, blank=True)
260 3cab63be Jérôme Schneider
261 29168b44 Jérôme Schneider
    def __unicode__(self):
262 724b950f Mikaël Ates
        return self.display_name or u"Non renseigné"
263 29168b44 Jérôme Schneider
264
    def save(self, **kwargs):
265 1b310da1 Mikaël Ates
        self.display_name = ''
266 61491834 Jérôme Schneider
        if self.recipient:
267
            self.display_name += self.recipient + ' '
268 1b310da1 Mikaël Ates
        if self.number:
269
            self.display_name += self.number + ' '
270
        if self.street:
271
            self.display_name += self.street + ' '
272
        if self.address_complement:
273
            self.display_name += self.address_complement + ' '
274
        if self.zip_code:
275
            self.display_name += self.zip_code + ' '
276
        if self.city:
277
            self.display_name += self.city + ' '
278 29168b44 Jérôme Schneider
        super(PatientAddress, self).save(**kwargs)
279
280 3cab63be Jérôme Schneider
class PatientContact(People):
281
    class Meta:
282
        verbose_name = u'Contact patient'
283
        verbose_name_plural = u'Contacts patient'
284
285 2d4b9891 Jérôme Schneider
    mobile = PhoneNumberField(verbose_name=u"Téléphone mobile", blank=True, null=True)
286 a8dd5599 Jérôme Schneider
    # carte vitale
287 bb57a8fe Mikaël Ates
    social_security_id = models.CharField(max_length=13, verbose_name=u"NIR",
288 2237043c Mikaël Ates
            null=True, blank=True)
289 a8dd5599 Jérôme Schneider
    birthdate = models.DateField(verbose_name=u"Date de naissance",
290
            null=True, blank=True)
291 afc55bb9 Mikaël Ates
    birthplace = models.CharField(max_length=100, verbose_name=u"Lieu de naissance",
292 28ff59f7 Mikaël Ates
            null=True, blank=True)
293 39ddd165 Jérôme Schneider
    twinning_rank = models.IntegerField(verbose_name=u"Rang (gémellité)", default=1,
294
            validators=[MinValueValidator(1)])
295 a8dd5599 Jérôme Schneider
    thirdparty_payer = models.BooleanField(verbose_name=u'Tiers-payant',
296
            default=False)
297
    begin_rights = models.DateField(verbose_name=u"Début de droits",
298
            null=True, blank=True)
299
    end_rights = models.DateField(verbose_name=u"Fin de droits",
300
            null=True, blank=True)
301 2237043c Mikaël Ates
    health_center = models.ForeignKey('ressources.HealthCenter',
302 0f4c0029 Jérôme Schneider
            verbose_name=u"Centre d'assurance maladie",
303 a8dd5599 Jérôme Schneider
            null=True, blank=True)
304 e949a412 Mikaël Ates
    other_health_center = models.CharField(verbose_name=u"Centre spécifique",
305
            max_length=4,
306
            null=True, blank=True)
307 158b10a6 Thomas NOEL
    type_of_contract = models.CharField(max_length=2,
308
            verbose_name=u"Type de contrat spécifique",
309
            choices=TYPE_OF_CONTRACT_CHOICES,
310
            null=True, blank=True)
311 a4bee9e6 Mikaël Ates
    management_code = models.ForeignKey('ressources.ManagementCode',
312
            verbose_name=u"Code de gestion",
313
            null=True, blank=True)
314 43b6f7ee Mikaël Ates
    job = models.ForeignKey('ressources.Job',
315
            related_name="job",
316
            verbose_name=u"Profession",
317
            null=True, blank=True, default=None)
318
    parente = models.ForeignKey('ressources.PatientRelatedLink',
319
            verbose_name=u"Lien avec le patient (Parenté)",
320
            null=True, blank=True, default=None)
321 c2e0cf72 Mikaël Ates
    ame = models.BooleanField(verbose_name=u"AME", default=False)
322 a8dd5599 Jérôme Schneider
323 317bcb4c Jérôme Schneider
    addresses = models.ManyToManyField('PatientAddress', verbose_name=u"Adresses")
324
    contact_comment = models.TextField(verbose_name=u"Commentaire",
325
            null=True, blank=True)
326 2a928c61 Mikaël Ates
327 e949a412 Mikaël Ates
    old_contact_id = models.CharField(max_length=256,
328
            verbose_name=u'Ancien ID du contact', blank=True, null=True)
329
330 97546feb Mikaël Ates
    def get_control_key(self):
331
        if self.social_security_id:
332 ddd531b6 Mikaël Ates
            nir = self.social_security_id
333
            try:
334
                # Corse dpt 2A et 2B
335
                minus = 0
336
                if nir[6] in ('A', 'a'):
337
                    nir = [c for c in nir]
338
                    nir[6] = '0'
339
                    nir = ''.join(nir)
340
                    minus = 1000000
341
                elif nir[6] in ('B', 'b'):
342
                    nir = [c for c in nir]
343
                    nir[6] = '0'
344
                    nir = ''.join(nir)
345
                    minus = 2000000
346
                nir = int(nir) - minus
347
                return (97 - (nir % 97))
348
            except Exception, e:
349 7c83c026 Jérôme Schneider
                logger.warning("%s" % str(e))
350 ddd531b6 Mikaël Ates
                return None
351 97546feb Mikaël Ates
        return None
352
353 1af9e727 Frédéric Péters
    def age(self, age_format=None):
354 9f237a5a Frédéric Péters
        if not self.birthdate:
355
            return 'inconnu'
356 1af9e727 Frédéric Péters
357
        if not age_format:
358
            age_format = get_service_setting('age_format')
359
360 9f237a5a Frédéric Péters
        now = datetime.today().date()
361
        age = relativedelta(now, self.birthdate)
362 1af9e727 Frédéric Péters
363
        # by default we return the number of months for children < 2 years, but
364
        # there's a service setting to have it always displayed that way.
365
        months = age.years * 12 + age.months
366
        if months == 0:
367
            components = []
368
        elif age.years < 2 or age_format == 'months_only':
369
            components = ['%s mois' % months]
370
        else:
371
            components = ['%s ans' % age.years]
372
            if age.months:
373
                components.append('%s mois' % age.months)
374
375
        # under three months, we also display the number of days
376
        if months < 3:
377
            if age.days == 1:
378
                components.append("%s jour" % age.days)
379
            elif age.days > 1:
380
                components.append('%s jours' % age.days)
381
382
        return ' et '.join(components)
383 9f237a5a Frédéric Péters
384 3cab63be Jérôme Schneider
385 3e915eb3 Jérôme Schneider
class PatientRecordManager(models.Manager):
386
    def for_service(self, service):
387
        return self.filter(service=service)
388
389 3cab63be Jérôme Schneider
class PatientRecord(ServiceLinkedAbstractModel, PatientContact):
390 3e915eb3 Jérôme Schneider
    objects = PatientRecordManager()
391
392 96f613c6 Benjamin Dauvergne
    class Meta:
393
        verbose_name = u'Dossier'
394
        verbose_name_plural = u'Dossiers'
395 1097fd0a Benjamin Dauvergne
396 6163f2ff Mikaël Ates
    created = models.DateTimeField(u'création', auto_now_add=True)
397
    creator = \
398
        models.ForeignKey(User,
399
        verbose_name=u'Créateur dossier patient',
400 8e6fe119 Jérôme Schneider
        editable=True)
401 c9db0256 Jérôme Schneider
    policyholder = models.ForeignKey('PatientContact',
402
            null=True, blank=True,
403 057e2737 Frédéric Péters
            verbose_name="Assuré", related_name="+",
404
            on_delete=models.SET_NULL)
405 a8dd5599 Jérôme Schneider
    contacts = models.ManyToManyField('PatientContact',
406 96f613c6 Benjamin Dauvergne
            related_name='contact_of')
407 6c5b2d74 Jérôme Schneider
    nationality = models.CharField(verbose_name=u"Nationalité",
408
            max_length=70, null=True, blank=True)
409 1ff91280 Mikaël Ates
    paper_id = models.CharField(max_length=6,
410 729480ef Jérôme Schneider
            verbose_name=u"N° dossier papier",
411 e37c8bd1 Jérôme Schneider
            null=True, blank=True)
412 586af040 Jérôme Schneider
    last_state = models.ForeignKey(FileState, related_name='+',
413 4a0ff5ea Frédéric Péters
            null=True, on_delete=models.SET_NULL)
414 67f7a6a5 Jérôme Schneider
    comment = models.TextField(verbose_name=u"Commentaire",
415
            null=True, blank=True, default=None)
416 e772c770 Jérôme Schneider
    pause = models.BooleanField(verbose_name=u"Pause facturation",
417
            default=False)
418 21fb4def Mikaël Ates
    pause_comment = models.TextField(verbose_name=u"Commentaire sur la pause facturation",
419
            null=True, blank=True, default=None)
420 08391712 Mikaël Ates
    confidential = models.BooleanField(verbose_name=u"Confidentiel",
421
            default=False)
422 af391557 Mikaël Ates
    socialisation_durations = models.ManyToManyField('ressources.SocialisationDuration',
423
            related_name='socialisation_duration_of')
424
    mdph_requests = models.ManyToManyField('ressources.MDPHRequest',
425
            related_name='mdph_requests_of')
426
    mdph_responses = models.ManyToManyField('ressources.MDPHResponse',
427
            related_name='mdph_responses_of')
428 2a03cc83 Mikaël Ates
    addresses_contacts_comment = models.TextField(verbose_name=u"Commentaire sur les adresses et contacts",
429
            null=True, blank=True, default=None)
430 d714d23e Mikaël Ates
431 1117d039 Mikaël Ates
    # Physiology and health data
432 c01be6e5 Mikaël Ates
    size = models.DecimalField(verbose_name=u"Taille (cm)", max_digits=5, decimal_places=1,
433 6c5b2d74 Jérôme Schneider
            null=True, blank=True, default=None)
434 437531fc Mikaël Ates
    weight = models.IntegerField(verbose_name=u"Poids (g)",
435 6c5b2d74 Jérôme Schneider
            null=True, blank=True, default=None)
436
    pregnancy_term = models.IntegerField(verbose_name=u"Terme en semaines",
437
            null=True, blank=True, default=None)
438 1117d039 Mikaël Ates
    cranium_perimeter = models.DecimalField(verbose_name=u"Périmètre cranien", max_digits=5, decimal_places=2,
439
            null=True, blank=True, default=None)
440
    chest_perimeter = models.DecimalField(verbose_name=u"Périmètre thoracique", max_digits=5, decimal_places=2,
441
            null=True, blank=True, default=None)
442 437531fc Mikaël Ates
    apgar_score_one = models.IntegerField(verbose_name=u"Test d'Apgar (1)",
443 1117d039 Mikaël Ates
            null=True, blank=True, default=None)
444 437531fc Mikaël Ates
    apgar_score_two = models.IntegerField(verbose_name=u"Test d'Apgar (5)",
445 1117d039 Mikaël Ates
            null=True, blank=True, default=None)
446
    mises_1 = models.ManyToManyField('ressources.CodeCFTMEA', related_name="mises1",
447
            verbose_name=u"Axe I : catégories cliniques",
448
            null=True, blank=True, default=None)
449
    mises_2 = models.ManyToManyField('ressources.CodeCFTMEA', related_name="mises2",
450
            verbose_name=u"Axe II : facteurs organiques",
451
            null=True, blank=True, default=None)
452
    mises_3 = models.ManyToManyField('ressources.CodeCFTMEA', related_name="mises3",
453 e0e4ab56 Frédéric Péters
            verbose_name=u"Axe II : facteurs environnementaux",
454 1117d039 Mikaël Ates
            null=True, blank=True, default=None)
455 fea9beeb Mikaël Ates
    deficiency_intellectual = models.IntegerField(max_length=1,
456
            verbose_name=u"Déficiences intellectuelles",
457
            choices=DEFICIENCY_CHOICES,
458
            default=0)
459
    deficiency_autism_and_other_ted = models.IntegerField(max_length=1,
460
            verbose_name=u"Autisme et autres TED",
461
            choices=DEFICIENCY_CHOICES,
462
            default=0)
463
    deficiency_mental_disorder = models.IntegerField(max_length=1,
464
            verbose_name=u"Troubles psychiques",
465
            choices=DEFICIENCY_CHOICES,
466
            default=0)
467
    deficiency_learning_disorder = models.IntegerField(max_length=1,
468
            verbose_name=u"Troubles du langage et des apprentissages",
469
            choices=DEFICIENCY_CHOICES,
470
            default=0)
471
    deficiency_auditory = models.IntegerField(max_length=1,
472
            verbose_name=u"Déficiences auditives",
473
            choices=DEFICIENCY_CHOICES,
474
            default=0)
475
    deficiency_visual = models.IntegerField(max_length=1,
476
            verbose_name=u"Déficiences visuelles",
477
            choices=DEFICIENCY_CHOICES,
478
            default=0)
479
    deficiency_motor = models.IntegerField(max_length=1,
480
            verbose_name=u"Déficiences motrices",
481
            choices=DEFICIENCY_CHOICES,
482
            default=0)
483
    deficiency_metabolic_disorder = models.IntegerField(max_length=1,
484
            verbose_name=u"Déficiences métaboliques",
485
            choices=DEFICIENCY_CHOICES,
486
            default=0)
487
    deficiency_brain_damage = models.IntegerField(max_length=1,
488
            verbose_name=u"Cérébro-lésions",
489
            choices=DEFICIENCY_CHOICES,
490
            default=0)
491
    deficiency_polyhandicap = models.BooleanField(verbose_name=u'Polyhandicap',
492
            default=False)
493
    deficiency_behavioral_disorder = models.IntegerField(max_length=1,
494 82ef5f72 Mikaël Ates
            verbose_name=u"Troubles de la conduite et du comportement",
495 fea9beeb Mikaël Ates
            choices=DEFICIENCY_CHOICES,
496
            default=0)
497
    deficiency_in_diagnostic = models.BooleanField(verbose_name=u'En diagnostic',
498
            default=False)
499
    deficiency_other_disorder = models.IntegerField(max_length=1,
500
            verbose_name=u"Autres types de déficience",
501
            choices=DEFICIENCY_CHOICES,
502
            default=0)
503 6c5b2d74 Jérôme Schneider
504 2773b4d4 Jérôme Schneider
    # Inscription motive
505 f5b8071c Jérôme Schneider
    analysemotive = models.ForeignKey('ressources.AnalyseMotive',
506 2773b4d4 Jérôme Schneider
            verbose_name=u"Motif (analysé)",
507
            null=True, blank=True, default=None)
508 428081e0 Jérôme Schneider
    familymotive = models.ForeignKey('ressources.FamilyMotive',
509 2773b4d4 Jérôme Schneider
            verbose_name=u"Motif (famille)",
510
            null=True, blank=True, default=None)
511 bcb16ce5 Mikaël Ates
    provenance = models.ForeignKey('ressources.Provenance',
512 4e0fb78c Mikaël Ates
            verbose_name=u"Conseilleur",
513 bcb16ce5 Mikaël Ates
            null=True, blank=True, default=None)
514 f5b8071c Jérôme Schneider
    advicegiver = models.ForeignKey('ressources.AdviceGiver',
515 4e0fb78c Mikaël Ates
            verbose_name=u"Demandeur",
516 2773b4d4 Jérôme Schneider
            null=True, blank=True, default=None)
517 a37b47e6 Mikaël Ates
    provenanceplace = models.ForeignKey('ressources.ProvenancePlace',
518
            verbose_name=u"Lieu de provenance",
519
            null=True, blank=True, default=None)
520 2773b4d4 Jérôme Schneider
521 dc1389b8 Mikaël Ates
    # Out motive
522
    outmotive = models.ForeignKey('ressources.OutMotive',
523
            verbose_name=u"Motif de sortie",
524
            null=True, blank=True, default=None)
525
    outto = models.ForeignKey('ressources.OutTo',
526
            verbose_name=u"Orientation",
527
            null=True, blank=True, default=None)
528
529 428081e0 Jérôme Schneider
    # Family
530 2773b4d4 Jérôme Schneider
    sibship_place = models.IntegerField(verbose_name=u"Place dans la fratrie",
531
            null=True, blank=True, default=None)
532
    nb_children_family = models.IntegerField(verbose_name=u"Nombre d'enfants dans la fratrie",
533
            null=True, blank=True, default=None)
534 cea6ec34 Jérôme Schneider
    parental_authority = models.ForeignKey('ressources.ParentalAuthorityType',
535 2773b4d4 Jérôme Schneider
            verbose_name=u"Autorité parentale",
536
            null=True, blank=True, default=None)
537 428081e0 Jérôme Schneider
    family_situation = models.ForeignKey('ressources.FamilySituationType',
538 2773b4d4 Jérôme Schneider
            verbose_name=u"Situation familiale",
539
            null=True, blank=True, default=None)
540 cea6ec34 Jérôme Schneider
    child_custody = models.ForeignKey('ressources.ParentalCustodyType',
541 2773b4d4 Jérôme Schneider
            verbose_name=u"Garde parentale",
542
            null=True, blank=True, default=None)
543 08391712 Mikaël Ates
    job_mother = models.ForeignKey('ressources.Job',
544
            related_name="job_mother",
545
            verbose_name=u"Profession de la mère",
546
            null=True, blank=True, default=None)
547
    job_father = models.ForeignKey('ressources.Job',
548
            related_name="job_father",
549
            verbose_name=u"Profession du père",
550
            null=True, blank=True, default=None)
551
    rm_mother = models.ForeignKey('ressources.MaritalStatusType',
552
            related_name="rm_mother",
553
            verbose_name=u"Régime matrimonial de la mère",
554
            null=True, blank=True, default=None)
555
    rm_father = models.ForeignKey('ressources.MaritalStatusType',
556
            related_name="rm_father",
557
            verbose_name=u"Régime matrimonial du père",
558
            null=True, blank=True, default=None)
559
    family_comment = models.TextField(verbose_name=u"Commentaire",
560
            null=True, blank=True, default=None)
561 6c5b2d74 Jérôme Schneider
562 cea6ec34 Jérôme Schneider
    # Transport
563 428081e0 Jérôme Schneider
    transporttype = models.ForeignKey('ressources.TransportType',
564 cea6ec34 Jérôme Schneider
            verbose_name=u"Type de transport",
565
            null=True, blank=True, default=None)
566 428081e0 Jérôme Schneider
    transportcompany = models.ForeignKey('ressources.TransportCompany',
567 cea6ec34 Jérôme Schneider
            verbose_name=u"Compagnie de transport",
568
            null=True, blank=True, default=None)
569 e2df6d5e Serghei MIHAI
    simple_appointment_transport = models.BooleanField(u'Afficher par défaut le transport sur les rendez-vous simples',
570
                                                       default=False)
571
    periodic_appointment_transport = models.BooleanField(u'Afficher par défaut le transport sur les rendez-vous réguliers',
572
                                                         default=False)
573 cea6ec34 Jérôme Schneider
574 428081e0 Jérôme Schneider
    # FollowUp
575
    coordinators = models.ManyToManyField('personnes.Worker',
576
            verbose_name=u"Coordinateurs",
577
            null=True, blank=True, default=None)
578 688c7fbe Mikaël Ates
    externaldoctor = models.ForeignKey('personnes.ExternalTherapist',
579 428081e0 Jérôme Schneider
            verbose_name=u"Médecin extérieur",
580
            null=True, blank=True, default=None)
581 688c7fbe Mikaël Ates
    externalintervener = models.ForeignKey('personnes.ExternalWorker',
582 428081e0 Jérôme Schneider
            verbose_name=u"Intervenant extérieur",
583
            null=True, blank=True, default=None)
584
585 437531fc Mikaël Ates
    old_id = models.CharField(max_length=256,
586
            verbose_name=u'Ancien ID', blank=True, null=True)
587
    old_old_id = models.CharField(max_length=256,
588
            verbose_name=u'Ancien ancien ID', blank=True, null=True)
589
590 2671f8ab Benjamin Dauvergne
    def save(self, *args, **kwargs):
591
        if not getattr(self, 'service', None):
592 d714d23e Mikaël Ates
            raise Exception('The field service is mandatory.')
593 2671f8ab Benjamin Dauvergne
        super(PatientRecord, self).save(*args, **kwargs)
594 d714d23e Mikaël Ates
595
    def get_state(self):
596 7328a577 Jérôme Schneider
        return self.last_state
597 d714d23e Mikaël Ates
598 d867d175 Mikaël Ates
    def get_initial_state(self):
599
        return self.filestate_set.order_by('date_selected')[0]
600
601 3665f853 Mikaël Ates
    def get_current_state(self):
602
        today = date.today()
603
        return self.get_state_at_day(today)
604
605 d714d23e Mikaël Ates
    def get_state_at_day(self, date):
606
        state = self.get_state()
607
        while(state):
608
            if datetime(state.date_selected.year,
609
                    state.date_selected.month, state.date_selected.day) <= \
610
                    datetime(date.year, date.month, date.day):
611
                return state
612
            state = state.previous_state
613 09970a47 Mikaël Ates
        return None
614 d714d23e Mikaël Ates
615 fc20476a Mikaël Ates
    def was_in_state_at_day(self, date, status_type):
616 d714d23e Mikaël Ates
        state_at_day = self.get_state_at_day(date)
617 fc20476a Mikaël Ates
        if state_at_day and state_at_day.status.type == status_type:
618 d714d23e Mikaël Ates
            return True
619
        return False
620
621
    def get_states_history(self):
622
        return self.filestate_set.order_by('date_selected')
623
624 573c0b66 Mikaël Ates
    def get_states_history_with_duration(self):
625
        '''
626
        Return the state history with for each state its duration.
627
        If the last state is in the past, the duration is counted until today.
628
        If the last state is in the future, the duration is not set.
629
        '''
630
        history = self.get_states_history()
631
        history_with_duration = list()
632
        today = datetime.today()
633
        i = 0
634
        for state in history:
635
            history_with_duration.append([state, None])
636
            if i != 0:
637
                history_with_duration[i-1][1] = state.date_selected - history_with_duration[i-1][0].date_selected
638
            if i == len(history)-1 and state.date_selected <= today:
639
                history_with_duration[i][1] = today - history_with_duration[i][0].date_selected
640
            i += 1
641
        return history_with_duration
642
643 c777d441 Mikaël Ates
    def can_be_deleted(self):
644
        for act in self.act_set.all():
645
            if act.is_state('VALIDE'):
646
                return False
647
        return True
648
649 055f8b05 Frédéric Péters
    def delete(self, *args, **kwargs):
650
        if self.can_be_deleted():
651 a01d85be Serghei MIHAI
            obj_id = self.id
652 055f8b05 Frédéric Péters
            super(PatientRecord, self).delete(*args, **kwargs)
653
654 0c33f9fd Frédéric Péters
    def get_ondisk_directory(self, service):
655 ef2de305 Frédéric Péters
        if not settings.PATIENT_FILES_BASE_DIRECTORY:
656
            return None
657
658
        dirnames = []
659
        dirname = self.last_name.upper()
660
        dirnames.append(dirname)
661
        if self.first_name:
662
            dirname = '%s %s' % (dirname, self.first_name)
663
            dirnames.append(dirname)
664
        if self.paper_id:
665
            dirname = '%s %s' % (dirname, self.paper_id)
666
            dirnames.append(dirname)
667
668
        for i, dirname in enumerate(dirnames):
669 0c33f9fd Frédéric Péters
            fullpath = os.path.join(settings.PATIENT_FILES_BASE_DIRECTORY, service, dirname)
670 ef2de305 Frédéric Péters
            try:
671 0c33f9fd Frédéric Péters
                next_fullpath = os.path.join(settings.PATIENT_FILES_BASE_DIRECTORY, service, dirnames[i+1])
672 ef2de305 Frédéric Péters
            except IndexError:
673
                pass
674
            else:
675 3c5df84d Jérôme Schneider
                if os.path.exists(fullpath) and not os.path.exists(next_fullpath):
676
                    os.rename(fullpath, next_fullpath)
677
                continue
678 ef2de305 Frédéric Péters
            if not os.path.exists(fullpath):
679
                os.makedirs(fullpath)
680
            for subdir in settings.PATIENT_SUBDIRECTORIES:
681
                subdir_fullpath = os.path.join(fullpath, subdir)
682
                if not os.path.exists(subdir_fullpath):
683
                    os.makedirs(subdir_fullpath)
684
        return fullpath
685 23937808 Frédéric Péters
686 ac0cd457 Frédéric Péters
    def get_client_side_directory(self, service):
687
        directory = self.get_ondisk_directory(service)
688
        if not directory:
689
            return None
690
        if not settings.CLIENT_SIDE_PATIENT_FILES_BASE_DIRECTORY:
691
            return None
692
        return os.path.join(settings.CLIENT_SIDE_PATIENT_FILES_BASE_DIRECTORY,
693
                            directory[len(settings.PATIENT_FILES_BASE_DIRECTORY)+1:])
694
695 7328a577 Jérôme Schneider
    def set_state(self, status, author, date_selected=None, comment=None):
696 d714d23e Mikaël Ates
        if not author:
697
            raise Exception('Missing author to set state')
698
        if not date_selected:
699
            date_selected = datetime.now()
700
        current_state = self.get_state()
701
        if not current_state:
702
            raise Exception('Invalid patient record. '
703
                'Missing current state.')
704 5030f816 Mikaël Ates
        if isinstance(date_selected, date):
705
            date_selected = datetime(year=date_selected.year,
706
                month=date_selected.month, day=date_selected.day)
707 d714d23e Mikaël Ates
        if date_selected < current_state.date_selected:
708
            raise Exception('You cannot set a state starting the %s that '
709
                'is before the previous state starting at day %s.' % \
710
                (str(date_selected), str(current_state.date_selected)))
711 7328a577 Jérôme Schneider
        filestate = FileState.objects.create(patient=self, status=status,
712 d714d23e Mikaël Ates
            date_selected=date_selected, author=author, comment=comment,
713 7328a577 Jérôme Schneider
            previous_state=current_state)
714
        self.last_state = filestate
715
        self.save()
716 d714d23e Mikaël Ates
717
    def change_day_selected_of_state(self, state, new_date):
718
        if state.previous_state:
719
            if new_date < state.previous_state.date_selected:
720
                raise Exception('You cannot set a state starting the %s '
721
                    'before the previous state starting at day %s.' % \
722
                    (str(new_date), str(state.previous_state.date_selected)))
723
        next_state = state.get_next_state()
724
        if next_state:
725
            if new_date > next_state.date_selected:
726
                raise Exception('You cannot set a state starting the %s '
727
                    'after the following state starting at day %s.' % \
728
                    (str(new_date), str(next_state.date_selected)))
729
        state.date_selected = new_date
730
        state.save()
731
732
    def remove_state(self, state):
733
        if state.patient.id != self.id:
734
            raise Exception('The state given is not about this patient '
735
                'record but about %s' % state.patient)
736
        next_state = state.get_next_state()
737
        if not next_state:
738
            self.remove_last_state()
739
        else:
740
            next_state.previous_state = state.previous_state
741
            next_state.save()
742
            state.delete()
743
744
    def remove_last_state(self):
745
        try:
746
            self.get_state().delete()
747
        except:
748
            pass
749 7b9948f9 Mikaël Ates
750 0f2849e4 Mikaël Ates
    def get_protection_state_at_date(self, date):
751
        try:
752
            return self.protectionstate_set.exclude(end_date__lt=date). \
753
                exclude(start_date__gt=date).latest('start_date')
754
        except:
755
            return None
756
757 efc6c4b8 Frédéric Péters
    def get_next_rdv(self):
758
        from views_utils import get_next_rdv
759
        return get_next_rdv(self)
760
761 e6a910a6 Mikaël Ates
    # START Specific to sessad healthcare
762
    def get_last_notification(self):
763
        return SessadHealthCareNotification.objects.filter(patient=self, ).\
764
            latest('end_date')
765
766
    def days_before_notification_expiration(self):
767
        today = datetime.today()
768 10294684 Benjamin Dauvergne
        notification = self.get_last_notification(self)
769 e6a910a6 Mikaël Ates
        if not notification:
770
            return 0
771
        if notification.end_date < today:
772
            return 0
773
        else:
774
            return notification.end_date - today
775
    # END Specific to sessad healthcare
776
777 0eec7968 Mikaël Ates
    # START Specific to cmpp healthcare
778
    def create_diag_healthcare(self, modifier):
779
        """
780
            Gestion de l'inscription automatique.
781
782
            Si un premier acte est validé alors une prise en charge
783
            diagnostique est ajoutée. Cela fera basculer le dossier dans l'état
784
            en diagnostic.
785
786
            A voir si auto ou manuel :
787
            Si ce n'est pas le premier acte validé mais que l'acte précédement
788
            facturé a plus d'un an, on peut créer une prise en charge
789
            diagnostique. Même s'il y a une prise en charge de traitement
790
            expirée depuis moins d'un an donc renouvelable.
791
792
        """
793 80fcd3e2 Mikaël Ates
        acts = Act.objects.filter(validation_locked=False,
794
            patient__service=self.service)
795
        days_not_locked = sorted(set(acts.values_list('date', flat=True)))
796
        acts = self.act_set.filter(validation_locked=True,
797
            valide=True, is_lost=False, is_billed=False)
798
        acts = acts.exclude(date__in=days_not_locked)
799
        acts = acts.order_by('date')
800
        pause_query = Q(pause=True)
801
        billable_query = Q(act_type__billable=True, switch_billable=False) | \
802
                Q(act_type__billable=False, switch_billable=True)
803
        billable_acts = acts.filter(~pause_query & billable_query)
804
805
        if not CmppHealthCareDiagnostic.objects.filter(patient=self).exists() \
806
                and billable_acts:
807 0eec7968 Mikaël Ates
            # Pas de prise en charge, on recherche l'acte facturable le plus
808
            # ancien, on crée une pc diag à la même date.
809 80fcd3e2 Mikaël Ates
            CmppHealthCareDiagnostic(patient=self, author=modifier,
810
                start_date=billable_acts[0].date).save()
811 0eec7968 Mikaël Ates
        else:
812 80fcd3e2 Mikaël Ates
            # On recherche l'acte facturable non facturé le plus ancien après
813
            # le dernier acte facturé et on regarde s'il a plus d'un an
814 0eec7968 Mikaël Ates
            try:
815
                last_billed_act = self.act_set.filter(is_billed=True).\
816
                    latest('date')
817 80fcd3e2 Mikaël Ates
                if last_billed_act and billable_acts:
818
                    billable_acts = billable_acts.filter(date__gte=last_billed_act.date)
819
                    if billable_acts and (billable_acts[0].date - last_billed_act.date).days >= 365:
820
                        return True
821
                    return False
822 0eec7968 Mikaël Ates
            except:
823
                pass
824 72219903 Mikaël Ates
        return False
825 0eec7968 Mikaël Ates
826
    def automated_switch_state(self, modifier):
827 3a72b7ac Mikaël Ates
        def state_switcher(diag, act):
828
            if diag and (self.last_state.status.type == "ACCUEIL" or
829
                    self.last_state.status.type == "TRAITEMENT"):
830
                status = Status.objects.get(type="DIAGNOSTIC",
831
                    services__name='CMPP')
832
                try:
833
                    self.set_state(status, modifier, date_selected=act.date)
834
                except:
835
                    pass
836
            elif not diag and (self.last_state.status.type == "ACCUEIL" or
837
                    self.last_state.status.type == "DIAGNOSTIC"):
838
                status = Status.objects.get(type="TRAITEMENT",
839
                    services__name='CMPP')
840
                try:
841
                    self.set_state(status, modifier, date_selected=act.date)
842
                except:
843
                    pass
844
        # Only for CMPP and open files
845
        if not self.service.name == 'CMPP' or \
846
                self.last_state.status.type == "CLOS":
847
            return
848
        # Nothing to do if no act after the last state date
849
        last_acts = self.act_set.filter(date__gt=self.last_state.date_selected)
850
        if not last_acts:
851
            return
852
        # If the last act is billed, look at the healthcare type
853
        last_act = last_acts.latest('date')
854
        if last_act.is_billed:
855
            if not last_act.healthcare:
856
                # Billed but no healthcare, coming from imported billed acts
857
                return
858
            diag = False
859
            if hasattr(last_act.healthcare, 'cmpphealthcarediagnostic'):
860
                diag = True
861
            return state_switcher(diag, last_act)
862
        # Last act not billed, let's look if it is billable
863
        from calebasse.facturation import list_acts
864
        (acts_not_locked, days_not_locked, acts_not_valide,
865
        acts_not_billable, acts_pause, acts_per_hc, acts_losts) = \
866
            list_acts.list_acts_for_billing_CMPP_per_patient(self,
867
                datetime.today(), self.service)
868
        last_hc = None
869
        last_act_hc = None
870
        for hc, acts in acts_per_hc.iteritems():
871
            if len(acts) and (not last_act_hc or
872
                    acts[-1].date > last_act_hc.date):
873
                last_hc = hc
874
                last_act_hc = acts[-1]
875 3b629e7a Mikaël Ates
        # There is a billable act after the last state so either it is diag
876 3a72b7ac Mikaël Ates
        # or it is treament
877 3b629e7a Mikaël Ates
        if last_act_hc and last_hc and \
878 8b425432 Mikaël Ates
                last_act_hc.date > self.last_state.date_selected.date():
879 3b629e7a Mikaël Ates
            if hasattr(last_hc, 'cmpphealthcarediagnostic'):
880
                state_switcher(True, last_act_hc)
881
            else:
882
                state_switcher(False, last_act_hc)
883 81916a5f Mikaël Ates
884
    def get_healthcare_status(self):
885
        today = date.today()
886
        current_hc_trait = None
887
        try:
888 3c5df84d Jérôme Schneider
            current_hc_trait = CmppHealthCareTreatment.objects.filter(
889
                patient=self, start_date__lte=today, end_date__gte=today
890
            ).latest('start_date')
891 81916a5f Mikaël Ates
        except:
892
            pass
893
        if not current_hc_trait:
894
            current_hc_diag = None
895
            try:
896
                current_hc_diag = CmppHealthCareDiagnostic.objects.filter(patient=self, start_date__lte=today).latest('start_date')
897
            except:
898
                pass
899
            if current_hc_diag and current_hc_diag.get_act_number() > len(current_hc_diag.act_set.all()):
900
901
                #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
902
                # Non parce que je peux ajouter une pc de traitement alors que je veux encore facturer sur la diag precedente.
903
                # Donc si j'ai un acte facturer en traitement alors la diag ne fonctionne plus.
904
                # 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.
905
                lasts_billed = Act.objects.filter(patient=self, is_billed = True, healthcare__isnull=False).order_by('-date')
906
                last_hc_date = None
907
                if lasts_billed:
908
                    last_hc_date = lasts_billed[0].healthcare.start_date
909
                if not last_hc_date or last_hc_date <= current_hc_diag.start_date:
910
                    # Prise en charge disponible
911
                    return (0, len(current_hc_diag.act_set.all()), current_hc_diag.get_act_number())
912
            last_hc_trait = None
913
            try:
914
                last_hc_trait = CmppHealthCareTreatment.objects.filter(patient=self).latest('start_date')
915
            except:
916
                pass
917
            if not last_hc_trait:
918
                if not current_hc_diag:
919
                    # Aucune PC
920
                    return (1, None)
921
                else:
922
                    # PC diag full, demander PC trait
923
                    return (2, current_hc_diag.get_act_number())
924
            if last_hc_trait.end_date < today:
925
                # Expirée
926
                #Test if rediagable
927
                return (3, last_hc_trait.end_date)
928
            if last_hc_trait.start_date > today:
929
                # N'a pas encore pris effet
930
                return (4, last_hc_trait.start_date)
931
            return (-1,)
932
        if current_hc_trait.get_act_number() > len(current_hc_trait.act_set.all()):
933
            # Pris en charge disponible
934
            return (5, len(current_hc_trait.act_set.all()), current_hc_trait.get_act_number())
935
        # Prise en charge au quota
936
        if not current_hc_trait.is_extended():
937
            # Peut être prolongée
938
            return (6, current_hc_trait.get_act_number())
939
        # Prise en charge saturée
940 d635f4ae Mikaël Ates
        return (7, current_hc_trait.get_act_number(), current_hc_trait.end_date)
941 0eec7968 Mikaël Ates
    # END Specific to cmpp healthcare
942
943 5539237c Benjamin Dauvergne
944
    @property
945
    def entry_date(self):
946
        d = self.filestate_set.filter(
947
                Q(status__type='DIAGNOSTIC') |
948 3cd1c742 Mikaël Ates
                Q(status__type='TRAITEMENT') |
949
                Q(status__type='SUIVI')). \
950 5539237c Benjamin Dauvergne
                        aggregate(Min('date_selected'))['date_selected__min']
951
        return d and d.date()
952
953 c1cdf73f Benjamin Dauvergne
954 5539237c Benjamin Dauvergne
    @property
955
    def exit_date(self):
956 c1cdf73f Benjamin Dauvergne
        if self.last_state.status.type != 'CLOS':
957
            return None
958 5539237c Benjamin Dauvergne
        d = self.filestate_set.filter(status__type='CLOS'). \
959
                    aggregate(Max('date_selected'))['date_selected__max']
960
        return d and d.date()
961
962 42811c4c Mikaël Ates
    @property
963
    def care_duration(self):
964
        # Duration between the first act present and the closing date.
965 0d846210 Mikaël Ates
        # If no closing date, end_date is the date of tha last act
966 42811c4c Mikaël Ates
        first_act_date = None
967
        try:
968
            first_act_date = self.act_set.filter(valide=True).order_by('date')[0].date
969
        except:
970
            return 0
971
        exit_date = self.exit_date
972
        if not exit_date:
973 0d846210 Mikaël Ates
            exit_date = self.act_set.filter(valide=True).order_by('-date')[0].date
974 42811c4c Mikaël Ates
        return (exit_date - first_act_date).days
975 5539237c Benjamin Dauvergne
976 573c0b66 Mikaël Ates
    @property
977
    def care_duration_since_last_contact_or_first_act(self):
978
        # Duration between the first act present and the closing date.
979 09970a47 Mikaël Ates
        # If no closing date, end_date is the date of the last act
980 573c0b66 Mikaël Ates
        contacts = FileState.objects.filter(patient=self, status__type='ACCUEIL').order_by('date_selected')
981
        last_contact = None
982 95e9acfe Mikaël Ates
        first_act_after_last_contact = None
983 573c0b66 Mikaël Ates
        if len(contacts) == 1:
984
            last_contact = contacts[0]
985
        elif len(contacts) > 1:
986
            last_contact = contacts[len(contacts)-1]
987
        if last_contact:
988
            # inscription act
989 95e9acfe Mikaël Ates
            first_acts_after_last_contact = Act.objects.filter(patient=self, date__gte=last_contact.date_selected, valide=True).order_by('date')
990 573c0b66 Mikaël Ates
            if first_acts_after_last_contact:
991
                first_act_after_last_contact = first_acts_after_last_contact[0]
992
        if not contacts:
993
            return self.care_duration
994
        if not first_act_after_last_contact:
995
            return 0
996
        exit_date = self.exit_date
997
        if not exit_date or exit_date < first_act_after_last_contact.date:
998
            exit_date = self.act_set.filter(valide=True).order_by('-date')[0].date
999
        return (exit_date - first_act_after_last_contact.date).days
1000
1001 9c69fad0 Mikaël Ates
    def care_duration_before_close_state(self, end_date=None):
1002
        if not end_date:
1003
            end_date = datetime.now()
1004
        last_close = None
1005
        try:
1006
            last_close = FileState.objects.filter(status__type='CLOS',
1007
                    patient=self, date_selected__lt=end_date).order_by('-date_selected')[0]
1008
        except:
1009
            pass
1010
        first_act = None
1011
        if last_close:
1012
            try:
1013
                first_act = Act.objects.filter(patient=self, valide=True, date__lte=end_date, date__gt=last_close.date_selected).order_by('date')[0]
1014
            except:
1015
                return 0
1016
        else:
1017
            try:
1018
                first_act = Act.objects.filter(patient=self, valide=True, date__lte=end_date).order_by('date')[0]
1019
            except:
1020
                return 0
1021
        return (end_date.date() - first_act.date).days + 1
1022
1023 7b9948f9 Mikaël Ates
def create_patient(first_name, last_name, service, creator,
1024
        date_selected=None):
1025
    logger.debug('create_patient: creation for patient %s %s in service %s '
1026
        'by %s' % (first_name, last_name, service, creator))
1027
    if not (first_name and last_name and service and creator):
1028
        raise Exception('Missing parameter to create a patient record.')
1029 586af040 Jérôme Schneider
    status = Status.objects.filter(type="ACCUEIL").filter(services=service)
1030
    if not status:
1031
        raise Exception('%s has no ACCEUIL status' % service.name)
1032 dbf80810 Jérôme Schneider
    patient = PatientRecord.objects.create(first_name=first_name,
1033 586af040 Jérôme Schneider
            last_name=last_name, service=service,
1034
            creator=creator)
1035
    fs = FileState(status=status[0], author=creator, previous_state=None)
1036 7b9948f9 Mikaël Ates
    if not date_selected:
1037
        date_selected = patient.created
1038 586af040 Jérôme Schneider
    fs.patient = patient
1039
    fs.date_selected = date_selected
1040
    fs.save()
1041 dbf80810 Jérôme Schneider
    patient.last_state = fs
1042
    patient.save()
1043 86687095 Mikaël Ates
    patient.policyholder = patient.patientcontact
1044
    patient.save()
1045 7b9948f9 Mikaël Ates
    return patient