Project

General

Profile

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

calebasse / calebasse / statistics / statistics.py @ 37ef36c7

1
# -*- coding: utf-8 -*-
2
import os
3
import tempfile
4
import csv
5

    
6
from collections import OrderedDict
7
from datetime import datetime, timedelta
8
from dateutil.relativedelta import relativedelta
9

    
10
from django.db import models
11
from django.db.models import Count
12
from django.utils import formats
13
from django.conf import settings
14

    
15
from calebasse.dossiers.models import PatientRecord, FileState
16
from calebasse.personnes.models import Worker
17
from calebasse.actes.models import Act
18
from calebasse.agenda.models import Event
19

    
20

    
21
STATISTICS = {
22
    'patients_per_worker_for_period' :
23
        {
24
        'display_name': 'Enfants suivis par intervenant sur une période',
25
        'category': 'Patients',
26
        'comment': """Liste et décompte des patients par intervenant pour les
27
            patients ayant eu au moins un acte proposé avec cet intervenant
28
            sur la plage de dates spécifiée. La date de début de la plage par
29
            défaut est le 1er janvier de l'année en cours. La date de fin de
30
            la plage par défaut est aujourd'hui. Si aucun patient ou
31
            intervenant n'est spécifié, tous les patients et les intervenants
32
            sont pris en compte. A l'inverse, si des patients ou des
33
            intervenants sont indiqués, seuls ceux-ci sont pris en compte.
34
            """
35
    },
36
    'active_patients_by_state_only' :
37
        {
38
        'display_name': "Dossiers actifs selon l'état du dossier à une date donnée",
39
        'category': 'Patients',
40
        'comment': """Listes des patients dont le dossier était actif à une date donnée.
41
            Rappel des états actifs des dossiers : CMPP : diagnostic
42
            ou traitement, CAMSP : suivi, SESSAD: Traitement.
43
            La date par défaut est aujourd'hui.
44
            """
45
    },
46
    'active_patients_with_act' :
47
        {
48
        'display_name': 'Dossiers actifs et inactifs avec un acte validé ou non sur une période',
49
        'category': 'Patients',
50
        'comment': """Listes des patients ayant eu au moins un acte proposé
51
            durant la période indiquée. Les patients sont scindés en quatre
52
            tableaux.
53
            Les patients dont au moins un acte a été
54
            validé ET dont le dossier est dans un état "actif".
55
            Les patients dont au moins un acte a été
56
            validé ET dont le dossier N'est PAS dans un état "actif".
57
            Les patients sans aucun acte validé ET dont le dossier est dans
58
            un état "actif".
59
            Les patients sans aucun acte validé ET dont le dossier N'est PAS
60
            dans un état "actif".
61
            Rappel des états actifs des dossiers : CMPP : diagnostic
62
            ou traitement, CAMSP : suivi, SESSAD: Traitement.
63
            La date de début de la plage par
64
            défaut est le 1er janvier de l'année en cours. La date de fin de
65
            la plage par défaut est aujourd'hui.
66
            """
67
    },
68
    'closed_files' :
69
        {
70
        'display_name': 'Dossier clos sur une période et durée de la prise '
71
            'en charge',
72
        'category': 'Patients',
73
        'comment': """Liste des dossiers clos avec leur durée de la prise en
74
            charge sur la plage de dates spécifiée. Le nombre de dossier et la
75
            durée moyenne de la prise en charge est également donnée. La date
76
            de début de la plage par défaut est le 1er janvier de l'année en
77
            cours. La date de fin de la plage par défaut est aujourd'hui.
78
            """
79
    },
80
    'annual_activity' :
81
        {
82
        'display_name': "Activité annuelle",
83
        'category': 'Intervenants',
84
        'comment': """Tableaux de synthèse annuelle. La date saisie
85
            indique l'année à traiter. Si aucune date n'est spécifiée ce sera
86
            l'année en cours. Si aucun intervenant n'est spécifié, les
87
            tableaux de synthèse globaux sont retournés. Si des intervenants
88
            sont indiqués, les tableaux de synthèse pour chaque intervenant
89
            sont retournés.
90
            """
91
    },
92
    'patients_details' :
93
        {
94
        'display_name': "Synthèse par patient",
95
        'category': 'Patients',
96
        'comment': """Tableaux de synthèse par patient. Si aucun patient n'est
97
            indiqué, une synthèse pour chaque patient est retournée. La plage
98
            de date permet de selectionner les patients pour lesquels au
99
            moins au acte a été proposé durant cette période. Si un dossier a
100
            été clos durant cette période mais qu'aucun acte n'a été proposé
101
            durant cette période, il ne sera pas pris en compte. La date de
102
            début de la plage par défaut est le 1er janvier de l'année en
103
            cours. La date de fin de la plage par défaut est aujourd'hui. """
104
    },
105
    'patients_synthesis' :
106
        {
107
        'display_name': 'Synthèse sur les patients avec un acte valide sur '
108
            'la plage de date',
109
        'category': 'Patients',
110
        'comment': """Patients ayant eu au moins un acte validé
111
            sur la plage de dates spécifiée. La date de début de la plage par
112
            défaut est le 1er janvier de l'année en cours. La date de fin de
113
            la plage par défaut est aujourd'hui.
114
            """
115
    },
116
    'acts_synthesis' :
117
        {
118
        'display_name': 'Synthèse sur les actes',
119
        'category': 'Actes',
120
        'comment': """Synthèse sur les actes
121
            sur la plage de dates spécifiée. La date de début de la plage par
122
            défaut est le 1er janvier de l'année en cours. La date de fin de
123
            la plage par défaut est aujourd'hui.
124
            """
125
    },
126
    'acts_synthesis_cmpp' :
127
        {
128
        'display_name': 'Synthèse sur les dossiers facturés au CMPP',
129
        'category': 'Patients',
130
        'services': ['CMPP', ],
131
        'comment': """Synthèse sur les dossiers facturés au CMPP selon que ce
132
            soit en diagnostic, en traitement ou les deux,
133
            sur la plage de dates spécifiée. La date de début de la plage par
134
            défaut est le 1er janvier de l'année en cours. La date de fin de
135
            la plage par défaut est aujourd'hui.
136
            """
137
    },
138
    'mises' :
139
        {
140
        'display_name': 'Synthèse sur les pathologies MISES',
141
        'category': 'Patients',
142
        'comment': """Synthèse sur les pathologies
143
            sur la plage de dates spécifiée. La date de début de la plage par
144
            défaut est le 1er janvier de l'année en cours. La date de fin de
145
            la plage par défaut est aujourd'hui.
146
            """
147
    },
148
    'deficiencies' :
149
        {
150
        'display_name': 'Synthèse sur les déficiences',
151
        'category': 'Patients',
152
        'comment': """Synthèse sur les déficiences
153
            sur la plage de dates spécifiée. La date de début de la plage par
154
            défaut est le 1er janvier de l'année en cours. La date de fin de
155
            la plage par défaut est aujourd'hui.
156
            """
157
    },
158
    'patients_protection' :
159
        {
160
        'display_name': 'Synthèse sur les mesures de protection des patients '
161
            'à une date donnée',
162
        'category': 'Patients',
163
        'comment': """La date par défaut est aujourd'hui.
164
            """
165
    },
166
}
167

    
168
ANNUAL_ACTIVITY_ROWS = ['total', 'pointe', 'non_pointe', 'absent', 'percent_abs', 'reporte', 'acts_present', 'abs_non_exc', 'abs_exc', 'abs_inter', 'annul_nous', 'annul_famille', 'abs_ess_pps', 'enf_hosp', 'non_facturables', 'facturables', 'perdus', 'doubles', 'really_facturables', 'factures', 'diag', 'trait', 'restants_a_fac', 'refac', 'nf', 'percent_nf', 'patients', 'intervenants', 'days', 'fact_per_day', 'moving_time', 'moving_time_per_intervene', 'moving_time_per_act']
169

    
170
ANNUAL_ACTIVITY_COLUMN_LABELS = ['Janv', 'Fév', 'Mar', 'T1', 'Avr', 'Mai', 'Jui', 'T2', 'Jui', 'Aoû', 'Sep', 'T3', 'Oct', 'Nov', 'Déc', 'T4', 'Total']
171

    
172
class AnnualActivityProcessingColumn():
173
    total = 0
174
    pointe = 0
175
    non_pointe = 0
176
    absent = 0
177
    percent_abs = 0
178
    reporte = 0
179
    acts_present = 0
180
    abs_non_exc = 0
181
    abs_exc = 0
182
    abs_inter = 0
183
    annul_nous = 0
184
    annul_famille = 0
185
    abs_ess_pps = 0
186
    enf_hosp = 0
187
    non_facturables = 0
188
    facturables = 0
189
    perdus = 0
190
    doubles = 0
191
    really_facturables = 0
192
    factures = 0
193
    diag = 0
194
    trait = 0
195
    restants_a_fac = 0
196
    refac = 0
197
    nf = 0
198
    percent_nf = 0
199
    patients = 0
200
    intervenants = 0
201
    days = 0
202
    fact_per_day = 0
203
    moving_time = timedelta()
204
    moving_time_per_intervene = timedelta()
205
    moving_time_per_act = timedelta()
206

    
207
def annual_activity_month_analysis(statistic, start_day, analyses, key, i, trim_cnt, participant=None):
208
    rd = relativedelta(months=1)
209
    sd = start_day + i * rd
210
    ed = sd + rd
211
    acts = None
212
    moving_events = None
213
    if participant:
214
        acts = Act.objects.filter(date__gte=sd.date(),
215
            date__lt=ed.date(), patient__service=statistic.in_service,
216
            doctors__in=[participant])
217
        moving_events = Event.objects.filter(event_type__label='Temps de trajet',
218
            start_datetime__gte=sd, end_datetime__lt=ed,
219
            services__in=[statistic.in_service],
220
            participants__in=[participant])
221
    else:
222
        acts = Act.objects.filter(date__gte=sd.date(),
223
            date__lt=ed.date(), patient__service=statistic.in_service)
224
        moving_events = Event.objects.filter(event_type__label='Temps de trajet',
225
            start_datetime__gte=sd, end_datetime__lt=ed,
226
            services__in=[statistic.in_service])
227
    analyses[key].append(AnnualActivityProcessingColumn())
228
    analyses[key][i+trim_cnt].patients = acts.aggregate(Count('patient', distinct=True))['patient__count']
229
    analyses[key][i+trim_cnt].intervenants = acts.aggregate(Count('doctors', distinct=True))['doctors__count']
230
    analyses[key][i+trim_cnt].days = acts.aggregate(Count('date', distinct=True))['date__count']
231
    for me in moving_events:
232
        analyses[key][i+trim_cnt].moving_time += me.timedelta()
233
    for a in acts:
234
        if a.is_new():
235
            analyses[key][i+trim_cnt].non_pointe += 1
236
        elif a.is_absent():
237
            state = a.get_state()
238
            if state.state_name == 'REPORTE':
239
                analyses[key][i+trim_cnt].reporte += 1
240
            else:
241
                analyses[key][i+trim_cnt].absent += 1
242
                if state.state_name == 'ABS_NON_EXC':
243
                    analyses[key][i+trim_cnt].abs_non_exc += 1
244
                elif state.state_name == 'ABS_EXC':
245
                    analyses[key][i+trim_cnt].abs_exc += 1
246
                elif state.state_name == 'ABS_INTER':
247
                    analyses[key][i+trim_cnt].abs_inter += 1
248
                elif state.state_name == 'ANNUL_NOUS':
249
                    analyses[key][i+trim_cnt].annul_nous += 1
250
                elif state.state_name == 'ANNUL_FAMILLE':
251
                    analyses[key][i+trim_cnt].annul_famille += 1
252
                elif state.state_name == 'ABS_ESS_PPS':
253
                    analyses[key][i+trim_cnt].abs_ess_pps += 1
254
                elif state.state_name == 'ENF_HOSP':
255
                    analyses[key][i+trim_cnt].enf_hosp += 1
256
        else:
257
            analyses[key][i+trim_cnt].acts_present += 1
258
            if statistic.in_service.name == 'CMPP':
259
                if not a.is_billable():
260
                    analyses[key][i+trim_cnt].non_facturables += 1
261
                elif a.is_lost:
262
                    analyses[key][i+trim_cnt].perdus += 1
263
                elif a.get_state().state_name == 'ACT_DOUBLE':
264
                    analyses[key][i+trim_cnt].doubles += 1
265
                elif a.is_billed:
266
                    analyses[key][i+trim_cnt].factures += 1
267
                    if a.invoice_set.latest('created').first_tag[0] == 'D':
268
                        analyses[key][i+trim_cnt].diag += 1
269
                    else:
270
                        analyses[key][i+trim_cnt].trait += 1
271
                else:
272
                    analyses[key][i+trim_cnt].restants_a_fac += 1
273
                    if a.invoice_set.all():
274
                        analyses[key][i+trim_cnt].refac += 1
275

    
276
    analyses[key][i+trim_cnt].total = analyses[key][i+trim_cnt].non_pointe + analyses[key][i+trim_cnt].reporte + analyses[key][i+trim_cnt].absent + analyses[key][i+trim_cnt].acts_present
277
    analyses[key][i+trim_cnt].pointe = analyses[key][i+trim_cnt].total - analyses[key][i+trim_cnt].non_pointe
278
    percent_abs = 100
279
    if not analyses[key][i+trim_cnt].pointe or not analyses[key][i+trim_cnt].absent:
280
        percent_abs = 0
281
    elif analyses[key][i+trim_cnt].absent:
282
        percent_abs = (analyses[key][i+trim_cnt].absent/float(analyses[key][i+trim_cnt].pointe))*100
283
    analyses[key][i+trim_cnt].percent_abs = "%.2f" % percent_abs
284

    
285
    if statistic.in_service.name == 'CMPP':
286
        analyses[key][i+trim_cnt].facturables = analyses[key][i+trim_cnt].acts_present - analyses[key][i+trim_cnt].non_facturables
287
        analyses[key][i+trim_cnt].really_facturables = analyses[key][i+trim_cnt].facturables - analyses[key][i+trim_cnt].perdus - analyses[key][i+trim_cnt].doubles
288
        analyses[key][i+trim_cnt].nf = analyses[key][i+trim_cnt].perdus + analyses[key][i+trim_cnt].doubles + analyses[key][i+trim_cnt].non_facturables # + analyses[key][i+trim_cnt].reporte
289
        percent_nf = 100
290
        if not analyses[key][i+trim_cnt].pointe or not analyses[key][i+trim_cnt].nf:
291
            percent_nf = 0
292
        elif analyses[key][i+trim_cnt].nf:
293
            percent_nf = (analyses[key][i+trim_cnt].nf/float(analyses[key][i+trim_cnt].pointe))*100
294
        analyses[key][i+trim_cnt].percent_nf = "%.2f" % percent_nf
295
        if analyses[key][i+trim_cnt].days:
296
            analyses[key][i+trim_cnt].fact_per_day = "%.2f" % (analyses[key][i+trim_cnt].really_facturables / float(analyses[key][i+trim_cnt].days))
297

    
298
    if analyses[key][i+trim_cnt].moving_time and analyses[key][i+trim_cnt].intervenants:
299
        analyses[key][i+trim_cnt].moving_time_per_intervene = analyses[key][i+trim_cnt].moving_time / analyses[key][i+trim_cnt].intervenants
300
    if analyses[key][i+trim_cnt].moving_time and analyses[key][i+trim_cnt].acts_present:
301
        analyses[key][i+trim_cnt].moving_time_per_act = analyses[key][i+trim_cnt].moving_time / analyses[key][i+trim_cnt].acts_present
302

    
303

    
304
def annual_activity_trimester_analysis(statistic, start_day, analyses, key, i, trim_cnt, participant=None):
305
    analyses[key].append(AnnualActivityProcessingColumn())
306
    rd = relativedelta(months=1)
307
    sd = start_day + i * rd
308
    start = start_day + (i-2) * rd
309
    end = sd + rd
310
    acts = None
311
    if participant:
312
        acts = Act.objects.filter(date__gte=start.date(),
313
            date__lt=end.date(), patient__service=statistic.in_service,
314
            doctors__in=[participant])
315
    else:
316
        acts = Act.objects.filter(date__gte=start.date(),
317
            date__lt=end.date(), patient__service=statistic.in_service)
318
    for row in ANNUAL_ACTIVITY_ROWS:
319
        if row == 'percent_abs':
320
            pointe = analyses[key][i+trim_cnt-1].pointe + analyses[key][i-2+trim_cnt].pointe + analyses[key][i-3+trim_cnt].pointe
321
            tot_abs = analyses[key][i+trim_cnt-1].absent + analyses[key][i-2+trim_cnt].absent + analyses[key][i-3+trim_cnt].absent
322
            percent_abs = 100
323
            if not pointe or not tot_abs:
324
                percent_abs = 0
325
            elif tot_abs:
326
                percent_abs = (tot_abs/float(pointe))*100
327
            analyses[key][i+trim_cnt].percent_abs = "%.2f" % percent_abs
328
        elif row == 'percent_nf':
329
            pointe = analyses[key][i+trim_cnt-1].pointe + analyses[key][i-2+trim_cnt].pointe + analyses[key][i-3+trim_cnt].pointe
330
            tot_nf = analyses[key][i+trim_cnt-1].nf + analyses[key][i-2+trim_cnt].nf + analyses[key][i-3+trim_cnt].nf
331
            percent_nf = 100
332
            if not pointe or not tot_nf:
333
                percent_nf = 0
334
            elif tot_nf:
335
                percent_nf = (tot_nf/float(pointe))*100
336
            analyses[key][i+trim_cnt].percent_nf = "%.2f" % percent_nf
337
        elif row == 'patients':
338
            analyses[key][i+trim_cnt].patients = acts.aggregate(Count('patient', distinct=True))['patient__count']
339
        elif row == 'intervenants':
340
            analyses[key][i+trim_cnt].intervenants = acts.aggregate(Count('doctors', distinct=True))['doctors__count']
341
        elif row == 'fact_per_day':
342
            if analyses[key][i+trim_cnt].days:
343
                analyses[key][i+trim_cnt].fact_per_day = "%.2f" % (analyses[key][i+trim_cnt].really_facturables / float(analyses[key][i+trim_cnt].days))
344
        elif row == 'moving_time_per_intervene':
345
            if analyses[key][i+trim_cnt].moving_time and analyses[key][i+trim_cnt].intervenants:
346
                analyses[key][i+trim_cnt].moving_time_per_intervene = analyses[key][i+trim_cnt].moving_time / analyses[key][i+trim_cnt].intervenants
347
        elif row == 'moving_time_per_act':
348
            if analyses[key][i+trim_cnt].moving_time and analyses[key][i+trim_cnt].acts_present:
349
                analyses[key][i+trim_cnt].moving_time_per_act = analyses[key][i+trim_cnt].moving_time / analyses[key][i+trim_cnt].acts_present
350
        else:
351
            setattr(analyses[key][i+trim_cnt], row, getattr(analyses[key][i+trim_cnt-1], row) + getattr(analyses[key][i-2+trim_cnt], row) + getattr(analyses[key][i-3+trim_cnt], row))
352

    
353
def annual_activity_synthesis_analysis(statistic, start_day, end_day, analyses, key, participant=None):
354
    analyses[key].append(AnnualActivityProcessingColumn())
355
    acts = None
356
    if participant:
357
        acts = Act.objects.filter(date__gte=start_day.date(),
358
            date__lt=end_day.date(), patient__service=statistic.in_service,
359
            doctors__in=[participant])
360
    else:
361
        acts = Act.objects.filter(date__gte=start_day.date(),
362
            date__lt=end_day.date(), patient__service=statistic.in_service)
363
    for row in ANNUAL_ACTIVITY_ROWS:
364
        if row == 'percent_abs':
365
            tot_abs = 0
366
            pointe = 0
367
            for i in (3, 7, 11, 15):
368
                pointe += analyses[key][i].pointe
369
                tot_abs += analyses[key][i].absent
370
            percent_abs = 100
371
            if not pointe or not tot_abs:
372
                percent_abs = 0
373
            elif tot_abs:
374
                percent_abs = (tot_abs/float(pointe))*100
375
            analyses[key][16].percent_abs = "%.2f" % percent_abs
376
        elif row == 'percent_nf':
377
            tot_nf= 0
378
            pointe = 0
379
            for i in (3, 7, 11, 15):
380
                pointe += analyses[key][i].pointe
381
                tot_nf += analyses[key][i].nf
382
            percent_nf = 100
383
            if not pointe or not tot_nf:
384
                percent_nf = 0
385
            elif tot_nf:
386
                percent_nf = (tot_nf/float(pointe))*100
387
            analyses[key][16].percent_nf = "%.2f" % percent_nf
388
        elif row == 'patients':
389
            analyses[key][16].patients = acts.aggregate(Count('patient', distinct=True))['patient__count']
390
        elif row == 'intervenants':
391
            analyses[key][16].intervenants = acts.aggregate(Count('doctors', distinct=True))['doctors__count']
392
        elif row == 'fact_per_day':
393
            if analyses[key][16].days:
394
                analyses[key][16].fact_per_day = "%.2f" % (analyses[key][16].really_facturables / float(analyses[key][16].days))
395
        elif row == 'moving_time_per_intervene':
396
            if analyses[key][16].moving_time and analyses[key][16].intervenants:
397
                analyses[key][16].moving_time_per_intervene = analyses[key][16].moving_time / analyses[key][16].intervenants
398
        elif row == 'moving_time_per_act':
399
            if analyses[key][16].moving_time and analyses[key][16].acts_present:
400
                analyses[key][16].moving_time_per_act = analyses[key][16].moving_time / analyses[key][16].acts_present
401
        else:
402
            val = 0
403
            if row == 'moving_time':
404
                val = timedelta()
405
            for i in (3, 7, 11, 15):
406
                val += getattr(analyses[key][i], row)
407
            setattr(analyses[key][16], row, val)
408

    
409
def strfdelta(tdelta, fmt):
410
    if not tdelta:
411
        return '0'
412
    d = {"days": tdelta.days}
413
    d["hours"], rem = divmod(tdelta.seconds, 3600)
414
    d["minutes"], d["seconds"] = divmod(rem, 60)
415
    return fmt.format(**d)
416

    
417
def annual_activity_build_tables(statistic, analyses, key, label, data_tables):
418
    table_1 = []
419
    table_1_label = label + ' - général'
420
    table_1.append([table_1_label] + ANNUAL_ACTIVITY_COLUMN_LABELS)
421
    rows = []
422
    row = ['Proposés']
423
    for column in analyses[key]:
424
        row.append(column.total)
425
    rows.append(row)
426
    row = ['Non pointés']
427
    for column in analyses[key]:
428
        row.append(column.non_pointe)
429
    rows.append(row)
430
    row = ['Absences']
431
    for column in analyses[key]:
432
        row.append(column.absent)
433
    rows.append(row)
434
    row = ['Reportés']
435
    for column in analyses[key]:
436
        row.append(column.reporte)
437
    rows.append(row)
438
    row = ['Présents']
439
    for column in analyses[key]:
440
        row.append(column.acts_present)
441
    rows.append(row)
442
    if statistic.in_service.name == 'CMPP':
443
        row = ['Facturables']
444
        for column in analyses[key]:
445
            row.append(column.really_facturables)
446
        rows.append(row)
447
        row = ['Facturés']
448
        for column in analyses[key]:
449
            row.append(column.factures)
450
        rows.append(row)
451
        row = ['Diagnostics']
452
        for column in analyses[key]:
453
            row.append(column.diag)
454
        rows.append(row)
455
        row = ['Traitements']
456
        for column in analyses[key]:
457
            row.append(column.trait)
458
        rows.append(row)
459
        row = ['Restants à facturer']
460
        for column in analyses[key]:
461
            row.append(column.restants_a_fac)
462
        rows.append(row)
463
        row = ['Dont en refact.']
464
        for column in analyses[key]:
465
            row.append(column.refac)
466
        rows.append(row)
467
    row = ['Patients']
468
    for column in analyses[key]:
469
        row.append(column.patients)
470
    rows.append(row)
471
    row = ['Intervenants']
472
    for column in analyses[key]:
473
        row.append(column.intervenants)
474
    rows.append(row)
475
    row = ['Jours']
476
    for column in analyses[key]:
477
        row.append(column.days)
478
    rows.append(row)
479
    if statistic.in_service.name == 'CMPP':
480
        row = ['Facturables / jour']
481
        for column in analyses[key]:
482
            row.append(column.fact_per_day)
483
        rows.append(row)
484
    row = ['Temps de déplacement']
485
    for column in analyses[key]:
486
        row.append(strfdelta(column.moving_time, "{hours}h {minutes}m"))
487
        if column.moving_time.days:
488
            row.append(strfdelta(column.moving_time, "{days}j {hours}h {minutes}m"))
489
    rows.append(row)
490
    row = ['Temps de déplacement par intervenant']
491
    for column in analyses[key]:
492
        row.append(strfdelta(column.moving_time_per_intervene, "{hours}h {minutes}m"))
493
        if column.moving_time_per_intervene.days:
494
            row.append(strfdelta(column.moving_time_per_intervene, "{days}j {hours}h {minutes}m"))
495
    rows.append(row)
496
    row = ['Temps de déplacement par acte']
497
    for column in analyses[key]:
498
        row.append(strfdelta(column.moving_time_per_act, "{hours}h {minutes}m"))
499
        if column.moving_time_per_act.days:
500
            row.append(strfdelta(column.moving_time_per_act, "{days}j {hours}h {minutes}m"))
501
    rows.append(row)
502
    table_1.append(rows)
503
    data_tables.append(table_1)
504

    
505
    table_2 = []
506
    table_2_label = label + ' - absences'
507
    table_2.append([table_2_label] + ANNUAL_ACTIVITY_COLUMN_LABELS)
508
    rows = []
509
    row = ['Pointés']
510
    for column in analyses[key]:
511
        row.append(column.pointe)
512
    rows.append(row)
513
    row = ['Absences']
514
    for column in analyses[key]:
515
        row.append(column.absent)
516
    rows.append(row)
517
    row = ['% absences / pointés']
518
    for column in analyses[key]:
519
        row.append(column.percent_abs)
520
    rows.append(row)
521
    row = ['Excusées']
522
    for column in analyses[key]:
523
        row.append(column.abs_exc)
524
    rows.append(row)
525
    row = ['Non excusées']
526
    for column in analyses[key]:
527
        row.append(column.abs_non_exc)
528
    rows.append(row)
529
    row = ["De l'intervenant"]
530
    for column in analyses[key]:
531
        row.append(column.abs_inter)
532
    rows.append(row)
533
    row = ["Annulés par nous"]
534
    for column in analyses[key]:
535
        row.append(column.annul_nous)
536
    rows.append(row)
537
    row = ['Annulés par la famille']
538
    for column in analyses[key]:
539
        row.append(column.annul_famille)
540
    rows.append(row)
541
    row = ['ESS PPS']
542
    for column in analyses[key]:
543
        row.append(column.abs_ess_pps)
544
    rows.append(row)
545
    row = ['Hospitalisations']
546
    for column in analyses[key]:
547
        row.append(column.enf_hosp)
548
    rows.append(row)
549
    table_2.append(rows)
550
    data_tables.append(table_2)
551

    
552
    if statistic.in_service.name == 'CMPP':
553
        table_3 = []
554
        table_3_label = label + ' - non fact.'
555
        table_3.append([table_3_label] + ANNUAL_ACTIVITY_COLUMN_LABELS)
556
        rows = []
557
        row = ['Pointés']
558
        for column in analyses[key]:
559
            row.append(column.pointe)
560
        rows.append(row)
561
        row = ['Présents']
562
        for column in analyses[key]:
563
            row.append(column.acts_present)
564
        rows.append(row)
565
        row = ['De type non fact.']
566
        for column in analyses[key]:
567
            row.append(column.non_facturables)
568
        rows.append(row)
569
        row = ['De type fact.']
570
        for column in analyses[key]:
571
            row.append(column.facturables)
572
        rows.append(row)
573
        row = ['Perdus']
574
        for column in analyses[key]:
575
            row.append(column.perdus)
576
        rows.append(row)
577
        row = ['En doubles']
578
        for column in analyses[key]:
579
            row.append(column.doubles)
580
        rows.append(row)
581
        row = ['Non facturables']
582
        for column in analyses[key]:
583
            row.append(column.nf)
584
        rows.append(row)
585
        row = ['% NF / pointés']
586
        for column in analyses[key]:
587
            row.append(column.percent_nf)
588
        rows.append(row)
589
        table_3.append(rows)
590
        data_tables.append(table_3)
591

    
592
def run_annual_activity(statistic, start_day, analyses, key, label, data_tables, participant=None):
593
    analyses[key] = list()
594
    trim_cnt = 0
595
    for i in range(0, 12):
596
        annual_activity_month_analysis(statistic, start_day, analyses, key, i, trim_cnt, participant)
597
        if not (i + 1) % 3:
598
            trim_cnt += 1
599
            annual_activity_trimester_analysis(statistic, start_day, analyses, key, i, trim_cnt, participant)
600
    end_day = datetime(start_day.year+1, 1, 1)
601
    annual_activity_synthesis_analysis(statistic, start_day, end_day, analyses, key, participant)
602
    annual_activity_build_tables(statistic, analyses, key, label, data_tables)
603

    
604
def annual_activity(statistic):
605
    if not statistic.in_service:
606
        return None
607
    start_day = datetime(datetime.today().year, 1, 1)
608
    if statistic.in_year:
609
        start_day = datetime(statistic.in_year, 1, 1)
610
    data_tables = list()
611
    analyses = dict()
612
    if not statistic.in_participants:
613
        run_annual_activity(statistic, start_day, analyses, 'global', 'Tous', data_tables, participant=None)
614
    else:
615
        for participant in statistic.in_participants:
616
            run_annual_activity(statistic, start_day, analyses, participant.id, str(participant), data_tables, participant=participant)
617
    return [data_tables]
618

    
619
def patients_per_worker_for_period(statistic):
620
    if not statistic.in_service:
621
        return None
622
    data_tables = []
623
    data = []
624
    data.append(['Intervenants', 'Nombre', 'Patients'])
625
    values = []
626
    if not statistic.in_end_date:
627
        statistic.in_end_date = datetime.today()
628
    if not statistic.in_start_date:
629
        statistic.in_start_date = datetime(statistic.in_end_date.year, 1, 1)
630
    acts = None
631
    if statistic.in_patients:
632
        acts = Act.objects.filter(date__gte=statistic.in_start_date,
633
            date__lte=statistic.in_end_date,
634
            patient__service=statistic.in_service,
635
            patient__in=statistic.in_patients)
636
    else:
637
        acts = Act.objects.filter(date__gte=statistic.in_start_date,
638
            date__lte=statistic.in_end_date,
639
            patient__service=statistic.in_service)
640
    analyse = dict()
641
    for act in acts:
642
        for intervene in act.doctors.all():
643
            if statistic.in_participants:
644
                if intervene in statistic.in_participants:
645
                    analyse.setdefault(intervene, []).append(str(act.patient))
646
            else:
647
                analyse.setdefault(intervene, []).append(str(act.patient))
648
    o_analyse = OrderedDict(sorted(analyse.items(), key=lambda t: t[0]))
649
    for intervene, patients in o_analyse.iteritems():
650
        lst = list(set(patients))
651
        values.append([str(intervene), len(lst), lst])
652
    data.append(values)
653
    data_tables.append(data)
654
    return [data_tables]
655

    
656
def active_patients_by_state_only(statistic):
657
    if not statistic.in_service:
658
        return None
659
    if not statistic.in_start_date:
660
        statistic.in_start_date = datetime.today()
661
    active_states = None
662
    if statistic.in_service.name == 'CMPP':
663
        active_states = ('TRAITEMENT', 'DIAGNOSTIC')
664
    elif statistic.in_service.name == 'CAMSP':
665
        active_states = ('SUIVI', )
666
    else:
667
        active_states = ('TRAITEMENT', )
668
    patients = [(p.last_name, p.first_name, p.paper_id) \
669
        for p in PatientRecord.objects.filter(service=statistic.in_service) \
670
            if p.get_state_at_day(
671
                statistic.in_start_date).status.type in active_states]
672
    data_tables_set=[[[['En date du :', formats.date_format(statistic.in_start_date, "SHORT_DATE_FORMAT"), len(patients)]]]]
673
    data = []
674
    data.append(['Nom', 'Prénom', 'N° Dossier'])
675
    p_list = []
676
    for ln, fn, pid in patients:
677
        ln = ln or ''
678
        if len(ln) > 1:
679
            ln = ln[0].upper() + ln[1:].lower()
680
        fn = fn or ''
681
        if len(fn) > 1:
682
            fn = fn[0].upper() + fn[1:].lower()
683
        p_list.append((ln, fn, str(pid or '')))
684
    data.append(sorted(p_list,
685
        key=lambda k: k[0]+k[1]))
686
    data_tables_set[0].append(data)
687
    return data_tables_set
688

    
689
def patients_protection(statistic):
690
    if not statistic.in_service:
691
        return None
692
    if not statistic.in_start_date:
693
        statistic.in_start_date = datetime.today()
694
    patients = PatientRecord.objects.filter(protectionstate__isnull=False).distinct()
695
    print patients
696
    protection_states = [p.get_protection_state_at_date(
697
            statistic.in_start_date) for p in patients
698
            if p.get_protection_state_at_date(statistic.in_start_date)]
699
    analyse = {}
700
    for state in protection_states:
701
        print state.patient
702
        analyse.setdefault(state.status.name, 0)
703
        analyse[state.status.name] += 1
704
    data_tables_set=[[[['En date du :', formats.date_format(statistic.in_start_date, "SHORT_DATE_FORMAT"), len(protection_states)]]]]
705
    data = []
706
    data.append(['Mesure de protection', 'Nombre de dossiers'])
707
    data.append(analyse.items())
708
    data_tables_set[0].append(data)
709
    return data_tables_set
710

    
711
def active_patients_with_act(statistic):
712
    def process(patients_list, title):
713
        data_tables = []
714
        data = []
715
        data.append([title, len(patients_list), '', ''])
716
        data_tables.append(data)
717
        data = []
718
        data.append(['Nom', 'Prénom', 'N° Dossier'])
719
        patients_values = patients_list.\
720
                values_list('last_name', 'first_name', 'paper_id')
721
        p_list = []
722
        for ln, fn, pid in patients_values:
723
            ln = ln or ''
724
            if len(ln) > 1:
725
                ln = ln[0].upper() + ln[1:].lower()
726
            fn = fn or ''
727
            if len(fn) > 1:
728
                fn = fn[0].upper() + fn[1:].lower()
729
            p_list.append((ln, fn, str(pid or '')))
730
        data.append(sorted(p_list,
731
            key=lambda k: k[0]+k[1]))
732
        data_tables.append(data)
733
        return data_tables
734
    if not statistic.in_service:
735
        return None
736
    if not statistic.in_end_date:
737
        statistic.in_end_date = datetime.today()
738
    if not statistic.in_start_date:
739
        statistic.in_start_date = datetime(statistic.in_end_date.year, 1, 1)
740
    active_states = None
741
    if statistic.in_service.name == 'CMPP':
742
        active_states = ('TRAITEMENT', 'DIAGNOSTIC')
743
    elif statistic.in_service.name == 'CAMSP':
744
        active_states = ('SUIVI', )
745
    else:
746
        active_states = ('TRAITEMENT', )
747

    
748
    data_tables_set = []
749
    data_tables = []
750
    data = []
751
    data.append(['Période', 'Jours'])
752
    data.append([("%s - %s"
753
        % (formats.date_format(statistic.in_start_date, "SHORT_DATE_FORMAT"),
754
        formats.date_format(statistic.in_end_date, "SHORT_DATE_FORMAT")),
755
        (statistic.in_end_date-statistic.in_start_date).days+1)])
756
    data_tables.append(data)
757
    data_tables_set.append(data_tables)
758

    
759
    acts_base = Act.objects.filter(
760
        date__gte=statistic.in_start_date,
761
        date__lte=statistic.in_end_date,
762
        patient__service=statistic.in_service)
763
    acts_valide = acts_base.filter(valide=True)
764
    acts_valide_patients_ids = acts_valide.order_by('patient').\
765
        distinct('patient').values_list('patient')
766
    acts_valide_patients = PatientRecord.objects.filter(
767
        id__in=[patient[0] for patient in acts_valide_patients_ids])
768
    all_patients_ids = acts_base.order_by('patient').distinct('patient').\
769
        values_list('patient')
770
    acts_not_valide_patients = PatientRecord.objects.filter(
771
        id__in=[patient[0] for patient in all_patients_ids
772
            if not patient in acts_valide_patients_ids])
773

    
774

    
775
    patients_1 = acts_valide_patients.filter(
776
        last_state__status__type__in=active_states)
777
    patients_2 = acts_valide_patients.exclude(
778
        last_state__status__type__in=active_states)
779
    patients_3 = acts_not_valide_patients.filter(
780
        last_state__status__type__in=active_states)
781
    patients_4 = acts_not_valide_patients.exclude(
782
        last_state__status__type__in=active_states)
783

    
784
    data_tables_set.append(process(patients_1, 'Patients avec un acte validé et dans un état actif'))
785
    data_tables_set.append(process(patients_2, 'Patients avec un acte validé et dans un état non actif'))
786
    data_tables_set.append(process(patients_3, 'Patients sans acte validé et dans un état actif'))
787
    data_tables_set.append(process(patients_4, 'Patients sans acte validé et dans un état non actif'))
788

    
789
    return data_tables_set
790

    
791
def closed_files(statistic):
792
    if not statistic.in_service:
793
        return None
794
    data_tables = []
795
    data1 = []
796
    data1.append(['Période', 'Jours',
797
        'Nombre de dossiers clos durant la période', 'PEC totale', 'PEC moyenne', "Dossiers qui ne sont plus clos"])
798
    data2 = []
799
    data2.append(['Nom', 'Prénom', 'N° Dossier', 'Date de clôture', 'Durée de la PEC', "N'est plus clos"])
800
    if not statistic.in_end_date:
801
        statistic.in_end_date = datetime.today()
802
    if not statistic.in_start_date:
803
        statistic.in_start_date = datetime(statistic.in_end_date.year, 1, 1)
804
    closed_records = FileState.objects.filter(status__type='CLOS',
805
        date_selected__gte=statistic.in_start_date,
806
        date_selected__lte=statistic.in_end_date). \
807
        order_by('patient').distinct('patient').\
808
        values_list('patient')
809
    closed_records = PatientRecord.objects.filter(service=statistic.in_service, id__in=[patient[0]
810
        for patient in closed_records])
811
    total_pec = 0
812
    p_list = []
813
    not_closed_now = 0
814
    for record in closed_records:
815
        ln = record.last_name or ''
816
        if len(ln) > 1:
817
            ln = ln[0].upper() + ln[1:].lower()
818
        fn = record.first_name or ''
819
        if len(fn) > 1:
820
            fn = fn[0].upper() + fn[1:].lower()
821
        current_state = ''
822
        close_date = record.last_state.date_selected.date()
823
        if record.get_current_state().status.type != 'CLOS':
824
            not_closed_now += 1
825
            current_state = record.get_current_state().status.name + \
826
                ' le ' + formats.date_format(record.get_current_state(). \
827
                date_selected, "SHORT_DATE_FORMAT")
828
            close_date = FileState.objects.filter(status__type='CLOS',
829
                patient=record).order_by('-date_selected')[0].date_selected.date()
830
        p_list.append((ln, fn, str(record.paper_id or ''),
831
            close_date,
832
            record.care_duration_since_last_contact_or_first_act,
833
            current_state))
834
        total_pec += record.care_duration_since_last_contact_or_first_act
835
    data2.append(sorted(p_list,
836
        key=lambda k: k[0]+k[1]))
837
    avg_pec = 0
838
    if closed_records.count() and total_pec:
839
        avg_pec = total_pec/closed_records.count()
840
    data1.append([("%s - %s"
841
        % (formats.date_format(statistic.in_start_date, "SHORT_DATE_FORMAT"),
842
        formats.date_format(statistic.in_end_date, "SHORT_DATE_FORMAT")),
843
        (statistic.in_end_date-statistic.in_start_date).days+1,
844
        closed_records.count(), total_pec, avg_pec, not_closed_now)])
845
    data_tables.append(data1)
846
    data_tables.append(data2)
847
    return [data_tables]
848

    
849
def patients_details(statistic):
850
    if not statistic.in_service:
851
        return None
852
    data_tables_set = []
853
    if not statistic.in_end_date:
854
        statistic.in_end_date = datetime.today()
855
    if not statistic.in_start_date:
856
        statistic.in_start_date = datetime(statistic.in_end_date.year, 1, 1)
857
    acts = None
858
    if statistic.in_patients:
859
        acts = Act.objects.filter(date__gte=statistic.in_start_date,
860
            date__lte=statistic.in_end_date,
861
            patient__service=statistic.in_service,
862
            patient__in=statistic.in_patients)
863
    else:
864
        acts = Act.objects.filter(date__gte=statistic.in_start_date,
865
            date__lte=statistic.in_end_date,
866
            patient__service=statistic.in_service)
867
    analyse = dict()
868
    for act in acts:
869
        analyse.setdefault(act.patient, []).append(act)
870
    o_analyse = OrderedDict(sorted(analyse.items(),
871
        key=lambda t: t[0].last_name))
872
    data = []
873
    data.append(['Période', 'Jours',
874
        'Nombre de dossiers'])
875
    data.append([("%s - %s"
876
        % (formats.date_format(statistic.in_start_date, "SHORT_DATE_FORMAT"),
877
        formats.date_format(statistic.in_end_date, "SHORT_DATE_FORMAT")),
878
        (statistic.in_end_date-statistic.in_start_date).days+1,
879
        len(o_analyse))])
880
    data_tables_set.append([data])
881
    for patient, acts in o_analyse.iteritems():
882
        data_tables = list()
883
        data_tables.append([["%s %s" % (str(patient), str(patient.paper_id))]])
884
        data = []
885
        data.append(["Statut de l'acte", 'Nombre'])
886
        values = []
887
        values.append(('Proposés', len(acts)))
888
        np, absent = 0, 0
889
        act_types = dict()
890
        for a in acts:
891
            if a.is_new():
892
                np += 1
893
            elif a.is_absent():
894
                absent += 1
895
            act_types.setdefault(a.act_type, []).append(a)
896
        values.append(('Non pointés', np))
897
        values.append(('Présents', len(acts) - np - absent))
898
        values.append(('Absents', absent))
899
        data.append(values)
900
        data_tables.append(data)
901
        data = []
902
        data.append(["Types d'acte", "Nombre d'actes proposés"])
903
        values = []
904
        for act_type, acts in act_types.iteritems():
905
            values.append((act_type, len(acts)))
906
        data.append(values)
907
        data_tables.append(data)
908

    
909
        data = []
910
        data.append(["Historique", "Nombre de jours par état"])
911
        values = []
912
        for state, duration in patient.get_states_history_with_duration():
913
            values.append(("%s (%s)" % (state.status.name,
914
                formats.date_format(state.date_selected,
915
                "SHORT_DATE_FORMAT")), duration.days))
916
        data.append(values)
917
        data_tables.append(data)
918

    
919
        contacts = FileState.objects.filter(patient=patient, status__type='ACCUEIL').order_by('date_selected')
920
        recontact = 'Non'
921
        last_contact = None
922
        first_acts_after_contact = None
923
        if len(contacts) == 1:
924
            last_contact = contacts[0]
925
        elif len(contacts) > 1:
926
            recontact = 'Oui'
927
            last_contact = contacts[len(contacts)-1]
928
        if last_contact:
929
            # inscription act
930
            first_acts_after_contact = Act.objects.filter(patient=patient, date__gte=last_contact.date_selected).order_by('date')
931
            if first_acts_after_contact:
932
                first_act_after_contact = first_acts_after_contact[0]
933
                if first_act_after_contact.date <= statistic.in_end_date.date() and first_act_after_contact.date >= statistic.in_start_date.date():
934
                    # inscription during the selected date range.
935
                    waiting_duration = first_act_after_contact.date - last_contact.date_selected.date()
936
                    data = []
937
                    data.append(["Date inscription", "Date accueil", 'Attente', 'Réinscription'])
938
                    values = []
939
                    values.append((first_act_after_contact.date, last_contact.date_selected.date(), waiting_duration.days, recontact))
940
                    data.append(values)
941
                    data_tables.append(data)
942

    
943
        closed_during_range_date = None
944
        try:
945
            closed_during_range_date = FileState.objects.filter(patient=patient, status__type='CLOS',
946
                date_selected__gte=statistic.in_start_date,
947
                date_selected__lte=statistic.in_end_date).latest('date_selected')
948
        except:
949
            pass
950
        care_duration = patient.care_duration_since_last_contact_or_first_act
951
        closure_date = ''
952
        if closed_during_range_date:
953
            closure_date = closed_during_range_date.date_selected.date
954
        reopen = ''
955
        if closed_during_range_date and not patient.exit_date:
956
            reopen = 'Oui'
957
        data = []
958
        data.append(["Durée de la prise en charge", "Clos pendant la période", "Actes suivants la clôture"])
959
        values = []
960
        values.append((patient.care_duration_since_last_contact_or_first_act, closure_date, reopen))
961
        data.append(values)
962
        data_tables.append(data)
963

    
964
        if patient.mdph_requests.exists():
965
            data = []
966
            data.append(["Demande(s) MDPH pendant la période", "Date de la demande", "Demande antérieure à la date de début saisie"])
967
            values = []
968
            for request in patient.mdph_requests.order_by('start_date'):
969
                before = 'Non'
970
                if request.start_date < statistic.in_start_date.date():
971
                    before = 'Oui'
972
                values.append(('MDPH : ' + request.mdph.department, request.start_date, before))
973
            data.append(values)
974
            data_tables.append(data)
975
        if patient.mdph_responses.exists():
976
            data = []
977
            data.append(["Réponse(s) MDPH pendant la période", "Date de début", "Date de fin"])
978
            values = []
979
            for response in patient.mdph_responses.order_by('start_date'):
980
                values.append(('MDPH : ' + response.mdph.department, response.start_date, response.end_date))
981
            data.append(values)
982
            data_tables.append(data)
983
        data_tables_set.append(data_tables)
984

    
985
    return data_tables_set
986

    
987
def patients_synthesis(statistic):
988
    if not statistic.in_service:
989
        return None
990
    data_tables = []
991
    data = []
992
    data.append(['Période', 'Jours',
993
        'Nombre de dossiers avec un acte validé sur la période',
994
        "Nombre d'actes validés sur la période"])
995
    if not statistic.in_end_date:
996
        statistic.in_end_date = datetime.today()
997
    if not statistic.in_start_date:
998
        statistic.in_start_date = datetime(statistic.in_end_date.year, 1, 1)
999
    acts = Act.objects.filter(valide=True,
1000
        date__gte=statistic.in_start_date,
1001
        date__lte=statistic.in_end_date,
1002
        patient__service=statistic.in_service)
1003
    patients = acts.order_by('patient').distinct('patient').\
1004
        values_list('patient')
1005
    patients = PatientRecord.objects.filter(id__in=[patient[0]
1006
        for patient in patients])
1007
    nb_patients = patients.count()
1008
    data.append([("%s - %s"
1009
        % (formats.date_format(statistic.in_start_date, "SHORT_DATE_FORMAT"),
1010
        formats.date_format(statistic.in_end_date, "SHORT_DATE_FORMAT")),
1011
        (statistic.in_end_date-statistic.in_start_date).days+1,
1012
        nb_patients, acts.count())])
1013
    data_tables.append(data)
1014
    data = []
1015
    data.append(['Féminin', 'Masculin'])
1016
    data.append([(patients.filter(gender='2').count(),
1017
        patients.filter(gender='1').count())])
1018
    data_tables.append(data)
1019
    data = []
1020
    data.append(['Durée totale de la PEC',
1021
        'Durée moyenne de la PEC par patient'])
1022
    pec_total = sum([p.care_duration_since_last_contact_or_first_act for p in patients])
1023
    data.append([(pec_total, pec_total/nb_patients)])
1024
    data_tables.append(data)
1025
    data = []
1026
    data.append(["Etat du dossier à ce jour", 'Nombre de patients'])
1027
    states = dict()
1028
    for patient in patients:
1029
        states.setdefault(patient.get_current_state().status, []).append(patient)
1030
    values = []
1031
    closed_patients_tmp = None
1032
    for state, ps in states.iteritems():
1033
        values.append((state.name, len(ps)))
1034
        if state.id == 5:
1035
            closed_patients_tmp = ps
1036
    data.append(values)
1037
    data_tables.append(data)
1038

    
1039
    # Pour les patients inscrits pendant la périodes
1040
    inscriptions = 0
1041
    recontact_cnt = 0
1042
    waiting_duration = timedelta()
1043

    
1044
    for patient in patients:
1045
        recontact = False
1046
        contacts = FileState.objects.filter(patient=patient, status__type='ACCUEIL').order_by('date_selected')
1047
        last_contact = None
1048
        first_acts_after_contact = None
1049
        if len(contacts) == 1:
1050
            last_contact = contacts[0]
1051
        elif len(contacts) > 1:
1052
            recontact = True
1053
            last_contact = contacts[len(contacts)-1]
1054
        if last_contact:
1055
            # inscription act
1056
            first_acts_after_contact = Act.objects.filter(patient=patient, date__gte=last_contact.date_selected).order_by('date')
1057
            if first_acts_after_contact:
1058
                first_act_after_contact = first_acts_after_contact[0]
1059
                if first_act_after_contact.date <= statistic.in_end_date.date() and first_act_after_contact.date >= statistic.in_start_date.date():
1060
                    # inscription during the selected date range.
1061
                    waiting_duration += first_act_after_contact.date - last_contact.date_selected.date()
1062
                    inscriptions += 1
1063
                    if recontact:
1064
                        recontact_cnt += 1
1065
    if inscriptions:
1066
        data = []
1067
        data.append(['Inscriptions (premier acte suivant le dernier contact dans la période)', 'Dont réinscription', "Durée moyenne de l'attente"])
1068
        data.append([(inscriptions, recontact_cnt, (waiting_duration/inscriptions).days)])
1069
        data_tables.append(data)
1070

    
1071
    closed_records = FileState.objects.filter(status__type='CLOS',
1072
        date_selected__gte=statistic.in_start_date,
1073
        date_selected__lte=statistic.in_end_date, patient__in=patients). \
1074
        order_by('patient').distinct('patient').\
1075
        values_list('patient')
1076
    closed_records = PatientRecord.objects.filter(service=statistic.in_service, id__in=[patient[0]
1077
        for patient in closed_records])
1078
    total_pec = 0
1079
    not_closed_now = 0
1080
    for record in closed_records:
1081
        if record.get_current_state().status.type != 'CLOS':
1082
            not_closed_now += 1
1083
        total_pec += record.care_duration_since_last_contact_or_first_act
1084
    avg_pec = 0
1085
    if closed_records.count() and total_pec:
1086
        avg_pec = total_pec/closed_records.count()
1087
    if closed_records.count():
1088
        data = []
1089
        data.append(['Clos dans la période', 'Durée totale de la PEC', 'Durée moyenne de la PEC', "Qui ne sont plus clos à ce jour"])
1090
        data.append([(closed_records.count(), total_pec, avg_pec, not_closed_now)])
1091
        data_tables.append(data)
1092

    
1093
    mdph_requests = 0
1094
    mdph_requests_before = 0
1095
    for patient in patients:
1096
        if patient.mdph_requests.exists():
1097
            mdph_requests += 1
1098
            # We only look to the last one
1099
            if patient.mdph_requests.order_by('-start_date')[0].start_date < statistic.in_start_date.date():
1100
                mdph_requests_before +=1
1101
    data = []
1102
    data.append(['Dossier avec une demande MDPH', "Dont la dernière demande a été faite avant la période"])
1103
    data.append([(mdph_requests, mdph_requests_before)])
1104
    data_tables.append(data)
1105

    
1106
    birth_years = dict()
1107
    patients_without_birthyear = []
1108
    for patient in patients:
1109
        try:
1110
            birth_years.setdefault(patient.birthdate.year, []).append(patient)
1111
        except:
1112
            patients_without_birthyear.append(patient)
1113
    data = []
1114
    data.append(['Année de naissance', "Nombre de dossiers"])
1115
    values = []
1116
    for birth_year, pts in birth_years.iteritems():
1117
        values.append((birth_year, len(pts)))
1118
    if patients_without_birthyear:
1119
        values.append(('%d patient(s) sans date de naissance' % len(patients_without_birthyear), patients_without_birthyear))
1120
    data.append(values)
1121
    data_tables.append(data)
1122

    
1123
    lower_bounds = [0, 3, 5, 7, 11, 16, 20, 25, 30, 35, 40, 45, 50, 55, 60, 75, 85, 96]
1124
    anap_code = 198
1125
    data = []
1126
    data.append(["Code ANAP", "Tranche d'âge (au %s)" \
1127
        % formats.date_format(statistic.in_end_date, "SHORT_DATE_FORMAT"),
1128
        "Nombre de dossiers"])
1129
    values = []
1130
    for i in range(len(lower_bounds)):
1131
        lower_bound = lower_bounds[i]
1132
        if i == len(lower_bounds) - 1:
1133
            values.append([anap_code, "De %d ans et plus" % lower_bound, 0])
1134
        else:
1135
            values.append([anap_code, "De %d à %d ans" % (lower_bound, lower_bounds[i + 1] - 1), 0])
1136
        anap_code += 1
1137
    for patient in patients:
1138
        try:
1139
            age = statistic.in_end_date.date() - patient.birthdate
1140
            age = age.days / 365
1141
            i = 0
1142
            while age >= lower_bounds[i+1]:
1143
                i += 1
1144
                if i == len(lower_bounds) - 1:
1145
                    break
1146
            values[i][2] += 1
1147
        except:
1148
            pass
1149
    data.append(values)
1150
    data_tables.append(data)
1151

    
1152
    jobs = dict()
1153
    for patient in patients:
1154
        if patient.job_mother:
1155
            jobs.setdefault(patient.job_mother, []).append(patient)
1156
        if patient.job_father:
1157
            jobs.setdefault(patient.job_father, []).append(patient)
1158
    data = []
1159
    data.append(["Profession d'un parent", "Nombre de dossiers"])
1160
    values = []
1161
    for job, pts in jobs.iteritems():
1162
        values.append((job, len(pts)))
1163
    data.append(values)
1164
    data_tables.append(data)
1165

    
1166
    provenances = dict()
1167
    unknown = 0
1168
    for patient in patients:
1169
        if patient.provenance:
1170
            provenances.setdefault(patient.provenance, []).append(patient)
1171
        else:
1172
            unknown += 1
1173
    data = []
1174
    data.append(["Provenances", "Nombre de dossiers"])
1175
    values = []
1176
    for provenance, pts in provenances.iteritems():
1177
        values.append((provenance, len(pts)))
1178
    values.append(('Non renseignée', unknown))
1179
    data.append(values)
1180
    data_tables.append(data)
1181

    
1182
    outmotives = dict()
1183
    for patient in patients:
1184
        if patient.outmotive:
1185
            outmotives.setdefault(patient.outmotive, []).append(patient)
1186
    data = []
1187
    data.append(["Motifs de sortie", "Nombre de dossiers"])
1188
    values = []
1189
    for outmotive, pts in outmotives.iteritems():
1190
        values.append((outmotive, len(pts)))
1191
    data.append(values)
1192
    data_tables.append(data)
1193

    
1194
    outtos = dict()
1195
    for patient in patients:
1196
        if patient.outto:
1197
            outtos.setdefault(patient.outto, []).append(patient)
1198
    data = []
1199
    data.append(["Orientations", "Nombre de dossiers"])
1200
    values = []
1201
    for outto, pts in outtos.iteritems():
1202
        values.append((outto, len(pts)))
1203
    data.append(values)
1204
    data_tables.append(data)
1205

    
1206
    provenance_places = dict()
1207
    for patient in patients:
1208
        if patient.provenanceplace:
1209
            provenance_places.setdefault(patient.provenanceplace, []).append(patient)
1210
    data = []
1211
    data.append(["Lieux de provenance", "Nombre de dossiers"])
1212
    values = []
1213
    for provenance_place, pts in provenance_places.iteritems():
1214
        values.append((provenance_place, len(pts)))
1215
    data.append(values)
1216
    data_tables.append(data)
1217

    
1218
    return [data_tables]
1219

    
1220
def acts_synthesis(statistic):
1221
    if not statistic.in_service:
1222
        return None
1223
    if not statistic.in_end_date:
1224
        statistic.in_end_date = datetime.today()
1225
    if not statistic.in_start_date:
1226
        statistic.in_start_date = datetime(statistic.in_end_date.year, 1, 1)
1227
    data_tables = []
1228
    data = []
1229
    data.append(['Période', 'Jours',
1230
        "Nombre d'actes proposés sur la période",
1231
        "Nombre d'actes validés sur la période"])
1232
    acts = Act.objects.filter(date__gte=statistic.in_start_date,
1233
        date__lte=statistic.in_end_date,
1234
        patient__service=statistic.in_service)
1235
    acts_valide = acts.filter(valide=True)
1236
    data.append([("%s - %s"
1237
        % (formats.date_format(statistic.in_start_date, "SHORT_DATE_FORMAT"),
1238
        formats.date_format(statistic.in_end_date, "SHORT_DATE_FORMAT")),
1239
        (statistic.in_end_date-statistic.in_start_date).days+1,
1240
        acts.count(), acts_valide.count())])
1241
    data_tables.append(data)
1242

    
1243
    acts_types = dict()
1244
    for act in acts:
1245
        acts_types.setdefault(act.act_type, []).append(act)
1246
    data = []
1247
    data.append(["Types des actes", "Nombre d'actes"])
1248
    values = []
1249
    for act_type, acts in acts_types.iteritems():
1250
        values.append((act_type, len(acts)))
1251
    data.append(values)
1252
    data_tables.append(data)
1253

    
1254
    for act_type, acts in acts_types.iteritems():
1255
        analysis = {'Non pointés': 0,
1256
            'Reportés': 0, 'Absents': 0, 'Présents': 0}
1257
        for a in acts:
1258
            if a.is_new():
1259
                analysis['Non pointés'] += 1
1260
            elif a.is_absent():
1261
                state = a.get_state()
1262
                if state.state_name == 'REPORTE':
1263
                    analysis['Reportés'] += 1
1264
                else:
1265
                    analysis['Absents'] += 1
1266
            else:
1267
                analysis['Présents'] += 1
1268
        data = []
1269
        data.append(["Type d'acte", act_type])
1270
        values = []
1271
        for status, number in analysis.iteritems():
1272
            values.append((status, number))
1273
        data.append(values)
1274
        data_tables.append(data)
1275

    
1276
    acts_count_participants = dict()
1277
    for act in acts_valide:
1278
        acts_count_participants.setdefault(act.doctors.count(), []).append(act)
1279
    data = []
1280
    data.append(["Nombre d'intervenants des actes réalisés", "Nombre d'actes", "Nombre de dossiers concernés"])
1281
    values = []
1282
    for number, acts_counted in acts_count_participants.iteritems():
1283
        values.append((number, len(acts_counted), len(set([a.patient.id for a in acts_counted]))))
1284
    data.append(values)
1285
    data_tables.append(data)
1286

    
1287
    return [data_tables]
1288

    
1289
def acts_synthesis_cmpp(statistic):
1290
    data_tables_set = []
1291
    if not statistic.in_service:
1292
        return None
1293
    if not statistic.in_end_date:
1294
        statistic.in_end_date = datetime.today()
1295
    if not statistic.in_start_date:
1296
        statistic.in_start_date = datetime(statistic.in_end_date.year, 1, 1)
1297
    acts = Act.objects.filter(date__gte=statistic.in_start_date,
1298
        date__lte=statistic.in_end_date,
1299
        patient__service=statistic.in_service)
1300
    acts_billed = acts.filter(is_billed=True)
1301
    patients_billed = dict()
1302
    for act in acts_billed:
1303
        patients_billed.setdefault(act.patient, [False, False])
1304
        if act.get_hc_tag() and act.get_hc_tag()[0] == 'D':
1305
            patients_billed[act.patient][0] = True
1306
        elif act.get_hc_tag() and act.get_hc_tag()[0] == 'T':
1307
            patients_billed[act.patient][1] = True
1308
    values1, values2, values3 = [], [], []
1309
    for patient, vals in patients_billed.iteritems():
1310
        pfields = [patient.last_name, patient.first_name, patient.paper_id]
1311
        if vals == [True, False]:
1312
            values1.append(pfields)
1313
        elif vals == [False, True]:
1314
            values2.append(pfields)
1315
        elif vals == [True, True]:
1316
            values3.append(pfields)
1317
    cols = ['Nom', 'Prénom', 'Numéro de dossier']
1318
    data_tables_set.append([[['Seulement facturé en diagnostic'], [[len(values1)]]], [cols, sorted(values1, key=lambda t: t[0])]])
1319
    data_tables_set.append([[['Seulement facturé en traitement'], [[len(values2)]]], [cols, sorted(values2, key=lambda t: t[0])]])
1320
    data_tables_set.append([[['Facturé en diagnostic et en traitement'], [[len(values3)]]], [cols, sorted(values3, key=lambda t: t[0])]])
1321
    return data_tables_set
1322

    
1323
def mises(statistic):
1324
    if not statistic.in_service:
1325
        return None
1326
    if not statistic.in_end_date:
1327
        statistic.in_end_date = datetime.today()
1328
    if not statistic.in_start_date:
1329
        statistic.in_start_date = datetime(statistic.in_end_date.year, 1, 1)
1330
    acts = Act.objects.filter(valide='True',
1331
        date__gte=statistic.in_start_date,
1332
        date__lte=statistic.in_end_date,
1333
        patient__service=statistic.in_service)
1334
    patients = acts.order_by('patient').distinct('patient').\
1335
        values_list('patient')
1336
    patients = PatientRecord.objects.filter(id__in=[patient[0]
1337
        for patient in patients])
1338
    pathologies = dict()
1339
    for patient in patients:
1340
        for pathology in patient.mises_1.all():
1341
            pathologies.setdefault(pathology, 0)
1342
            pathologies[pathology] += 1
1343
        for pathology in patient.mises_2.all():
1344
            pathologies.setdefault(pathology, 0)
1345
            pathologies[pathology] += 1
1346
        for pathology in patient.mises_3.all():
1347
            pathologies.setdefault(pathology, 0)
1348
            pathologies[pathology] += 1
1349
    data = [['Pathologies MISES', 'Nombre de patients concernés']]
1350
    data.append(OrderedDict(sorted(pathologies.items(), key=lambda t: t[0].ordering_code)).items())
1351
    return [[data]]
1352

    
1353
def deficiencies(statistic):
1354
    if not statistic.in_service:
1355
        return None
1356
    if not statistic.in_end_date:
1357
        statistic.in_end_date = datetime.today()
1358
    if not statistic.in_start_date:
1359
        statistic.in_start_date = datetime(statistic.in_end_date.year, 1, 1)
1360
    acts = Act.objects.filter(valide='True',
1361
        date__gte=statistic.in_start_date,
1362
        date__lte=statistic.in_end_date,
1363
        patient__service=statistic.in_service)
1364
    patients = acts.order_by('patient').distinct('patient').\
1365
        values_list('patient')
1366
    patients = PatientRecord.objects.filter(id__in=[patient[0]
1367
        for patient in patients])
1368
    deficiencies_three = ('deficiency_intellectual',
1369
            'deficiency_autism_and_other_ted',
1370
            'deficiency_mental_disorder', 'deficiency_learning_disorder',
1371
            'deficiency_auditory', 'deficiency_visual', 'deficiency_motor',
1372
            'deficiency_metabolic_disorder', 'deficiency_brain_damage',
1373
            'deficiency_behavioral_disorder', 'deficiency_other_disorder')
1374
    data = [['Déficiences', 'Nombre de patients concernés'], []]
1375
    for deficiency in deficiencies_three:
1376
        name = PatientRecord._meta.get_field_by_name(deficiency)[0].verbose_name
1377
        filter_dict = {deficiency: 1}
1378
        data[1].append((name + ' à titre principal', patients.filter(**filter_dict).count()))
1379
        filter_dict = {deficiency: 2}
1380
        data[1].append((name + ' à titre associé', patients.filter(**filter_dict).count()))
1381
    name = PatientRecord._meta.get_field_by_name('deficiency_polyhandicap')[0].verbose_name
1382
    data[1].append((name, patients.filter(deficiency_polyhandicap=True).count()))
1383
    name = PatientRecord._meta.get_field_by_name('deficiency_in_diagnostic')[0].verbose_name
1384
    data[1].append((name, patients.filter(deficiency_in_diagnostic=True).count()))
1385
    return [[data]]
1386

    
1387
class Statistic(models.Model):
1388
    patients = models.ManyToManyField('dossiers.PatientRecord',
1389
            null=True, blank=True, default=None)
1390
    participants = models.ManyToManyField('personnes.People',
1391
            null=True, blank=True, default=None)
1392
    in_start_date = None
1393
    in_end_date = None
1394
    in_service = None
1395
    in_participants = None
1396
    in_patients = None
1397
    in_year = None
1398

    
1399
    def __init__(self, name=None, inputs=dict()):
1400
        self.name = name
1401
        params = STATISTICS.get(name, {})
1402
        self.display_name = params['display_name']
1403
        self.category = params['category']
1404
        self.inputs = inputs
1405
        self.in_participants = list()
1406
        participants = inputs.get('participants')
1407
        if participants:
1408
            p_str_ids = [p for p in participants.split('|') if p]
1409
            for str_id in p_str_ids:
1410
                try:
1411
                    self.in_participants.append(Worker.objects.get(pk=int(str_id)))
1412
                except:
1413
                    pass
1414
        self.in_patients = list()
1415
        patients = inputs.get('patients')
1416
        if patients:
1417
            p_str_ids = [p for p in patients.split('|') if p]
1418
            for str_id in p_str_ids:
1419
                try:
1420
                    self.in_patients.append(PatientRecord.objects.get(pk=int(str_id)))
1421
                except:
1422
                    pass
1423
        self.in_service = inputs.get('service')
1424
        self.in_start_date = None
1425
        try:
1426
            self.in_start_date = datetime.strptime(inputs.get('start_date'),
1427
                "%d/%m/%Y")
1428
            self.in_year = self.in_start_date.year
1429
        except:
1430
            pass
1431
        self.in_end_date = None
1432
        try:
1433
            self.in_end_date = datetime.strptime(inputs.get('end_date'),
1434
                "%d/%m/%Y")
1435
        except:
1436
            pass
1437

    
1438
    def get_data(self):
1439
        func = globals()[self.name]
1440
        self.data = func(self)
1441
        return self.data
1442

    
1443
    def render_to_csv(self):
1444
        _delimiter = ';'
1445
        _quotechar = '|'
1446
        _doublequote = True
1447
        _skipinitialspace = False
1448
        _lineterminator = '\r\n'
1449
        _quoting = csv.QUOTE_MINIMAL
1450
        if getattr(settings, 'CSVPROFILE', None):
1451
            csv_profile = settings.CSVPROFILE
1452
            _delimiter = csv_profile.get('delimiter', ';')
1453
            _quotechar = csv_profile.get('quotechar', '|')
1454
            _doublequote = csv_profile.get('doublequote', True)
1455
            _skipinitialspace = csv_profile.get('skipinitialspace', False)
1456
            _lineterminator = csv_profile.get('lineterminator', '\r\n')
1457
            _quoting = csv_profile.get('quoting', csv.QUOTE_MINIMAL)
1458
        class CSVProfile(csv.Dialect):
1459
            delimiter = _delimiter
1460
            quotechar = _quotechar
1461
            doublequote = _doublequote
1462
            skipinitialspace = _skipinitialspace
1463
            lineterminator = _lineterminator
1464
            quoting = _quoting
1465
        csv.register_dialect('csv_profile', CSVProfile())
1466
        encoding = getattr(settings, 'CSV_ENCODING', 'utf-8')
1467
#        Python 3: , encoding=encoding
1468
#        with tempfile.NamedTemporaryFile(delete=False) as temp_out_csv:
1469
#            try:
1470
#                writer = csv.writer(temp_out_csv, dialect='csv_profile')
1471
#                for data_set in self.data:
1472
#                    for data in data_set:
1473
#                        writer.writerow(data[0])
1474
#                        if len(data) > 1:
1475
#                            for d in data[1]:
1476
#                                writer.writerow(d)
1477
#                        writer.writerow([])
1478
#                    writer.writerow([])
1479
#                return temp_out_csv.name
1480
#            except Exception, e:
1481
#                print e
1482
#                try:
1483
#                    os.unlink(temp_out_pdf.name)
1484
#                except:
1485
#                    pass
1486

    
1487
        import codecs
1488
        filename = None
1489
        with tempfile.NamedTemporaryFile(delete=False) as temp_out_csv:
1490
            filename = temp_out_csv.name
1491
            temp_out_csv.close()
1492
        with codecs.open(filename, 'w+b', encoding=encoding) as encoded_f:
1493
            try:
1494
                writer = csv.writer(encoded_f, dialect='csv_profile')
1495
                for data_set in self.data:
1496
                    for data in data_set:
1497
                        writer.writerow(data[0])
1498
                        if len(data) > 1:
1499
                            for d in data[1]:
1500
                                writer.writerow(d)
1501
                        writer.writerow([])
1502
                    writer.writerow([])
1503
                return filename
1504
            except:
1505
                try:
1506
                    os.unlink(temp_out_pdf.name)
1507
                except:
1508
                    pass
1509

    
1510
    def get_file(self):
1511
        self.get_data()
1512
        return self.render_to_csv()
(5-5/8)