Projet

Général

Profil

Télécharger (44,2 ko) Statistiques
| Branche: | Tag: | Révision:

calebasse / calebasse / dossiers / models.py @ 1164bc89

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