Project

General

Profile

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

calebasse / calebasse / statistics / statistics.py @ f8d1732b

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
}
149

    
150
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']
151

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

    
154
class AnnualActivityProcessingColumn():
155
    total = 0
156
    pointe = 0
157
    non_pointe = 0
158
    absent = 0
159
    percent_abs = 0
160
    reporte = 0
161
    acts_present = 0
162
    abs_non_exc = 0
163
    abs_exc = 0
164
    abs_inter = 0
165
    annul_nous = 0
166
    annul_famille = 0
167
    abs_ess_pps = 0
168
    enf_hosp = 0
169
    non_facturables = 0
170
    facturables = 0
171
    perdus = 0
172
    doubles = 0
173
    really_facturables = 0
174
    factures = 0
175
    diag = 0
176
    trait = 0
177
    restants_a_fac = 0
178
    refac = 0
179
    nf = 0
180
    percent_nf = 0
181
    patients = 0
182
    intervenants = 0
183
    days = 0
184
    fact_per_day = 0
185
    moving_time = timedelta()
186
    moving_time_per_intervene = timedelta()
187
    moving_time_per_act = timedelta()
188

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

    
258
    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
259
    analyses[key][i+trim_cnt].pointe = analyses[key][i+trim_cnt].total - analyses[key][i+trim_cnt].non_pointe
260
    percent_abs = 100
261
    if not analyses[key][i+trim_cnt].pointe or not analyses[key][i+trim_cnt].absent:
262
        percent_abs = 0
263
    elif analyses[key][i+trim_cnt].absent:
264
        percent_abs = (analyses[key][i+trim_cnt].absent/float(analyses[key][i+trim_cnt].pointe))*100
265
    analyses[key][i+trim_cnt].percent_abs = "%.2f" % percent_abs
266

    
267
    if statistic.in_service.name == 'CMPP':
268
        analyses[key][i+trim_cnt].facturables = analyses[key][i+trim_cnt].acts_present - analyses[key][i+trim_cnt].non_facturables
269
        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
270
        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
271
        percent_nf = 100
272
        if not analyses[key][i+trim_cnt].pointe or not analyses[key][i+trim_cnt].nf:
273
            percent_nf = 0
274
        elif analyses[key][i+trim_cnt].nf:
275
            percent_nf = (analyses[key][i+trim_cnt].nf/float(analyses[key][i+trim_cnt].pointe))*100
276
        analyses[key][i+trim_cnt].percent_nf = "%.2f" % percent_nf
277
        if analyses[key][i+trim_cnt].days:
278
            analyses[key][i+trim_cnt].fact_per_day = "%.2f" % (analyses[key][i+trim_cnt].really_facturables / float(analyses[key][i+trim_cnt].days))
279

    
280
    if analyses[key][i+trim_cnt].moving_time and analyses[key][i+trim_cnt].intervenants:
281
        analyses[key][i+trim_cnt].moving_time_per_intervene = analyses[key][i+trim_cnt].moving_time / analyses[key][i+trim_cnt].intervenants
282
    if analyses[key][i+trim_cnt].moving_time and analyses[key][i+trim_cnt].acts_present:
283
        analyses[key][i+trim_cnt].moving_time_per_act = analyses[key][i+trim_cnt].moving_time / analyses[key][i+trim_cnt].acts_present
284

    
285

    
286
def annual_activity_trimester_analysis(statistic, start_day, analyses, key, i, trim_cnt, participant=None):
287
    analyses[key].append(AnnualActivityProcessingColumn())
288
    rd = relativedelta(months=1)
289
    sd = start_day + i * rd
290
    start = start_day + (i-2) * rd
291
    end = sd + rd
292
    acts = None
293
    if participant:
294
        acts = Act.objects.filter(date__gte=start.date(),
295
            date__lt=end.date(), patient__service=statistic.in_service,
296
            doctors__in=[participant])
297
    else:
298
        acts = Act.objects.filter(date__gte=start.date(),
299
            date__lt=end.date(), patient__service=statistic.in_service)
300
    for row in ANNUAL_ACTIVITY_ROWS:
301
        if row == 'percent_abs':
302
            pointe = analyses[key][i+trim_cnt-1].pointe + analyses[key][i-2+trim_cnt].pointe + analyses[key][i-3+trim_cnt].pointe
303
            tot_abs = analyses[key][i+trim_cnt-1].absent + analyses[key][i-2+trim_cnt].absent + analyses[key][i-3+trim_cnt].absent
304
            percent_abs = 100
305
            if not pointe or not tot_abs:
306
                percent_abs = 0
307
            elif tot_abs:
308
                percent_abs = (tot_abs/float(pointe))*100
309
            analyses[key][i+trim_cnt].percent_abs = "%.2f" % percent_abs
310
        elif row == 'percent_nf':
311
            pointe = analyses[key][i+trim_cnt-1].pointe + analyses[key][i-2+trim_cnt].pointe + analyses[key][i-3+trim_cnt].pointe
312
            tot_nf = analyses[key][i+trim_cnt-1].nf + analyses[key][i-2+trim_cnt].nf + analyses[key][i-3+trim_cnt].nf
313
            percent_nf = 100
314
            if not pointe or not tot_nf:
315
                percent_nf = 0
316
            elif tot_nf:
317
                percent_nf = (tot_nf/float(pointe))*100
318
            analyses[key][i+trim_cnt].percent_nf = "%.2f" % percent_nf
319
        elif row == 'patients':
320
            analyses[key][i+trim_cnt].patients = acts.aggregate(Count('patient', distinct=True))['patient__count']
321
        elif row == 'intervenants':
322
            analyses[key][i+trim_cnt].intervenants = acts.aggregate(Count('doctors', distinct=True))['doctors__count']
323
        elif row == 'fact_per_day':
324
            if analyses[key][i+trim_cnt].days:
325
                analyses[key][i+trim_cnt].fact_per_day = "%.2f" % (analyses[key][i+trim_cnt].really_facturables / float(analyses[key][i+trim_cnt].days))
326
        elif row == 'moving_time_per_intervene':
327
            if analyses[key][i+trim_cnt].moving_time and analyses[key][i+trim_cnt].intervenants:
328
                analyses[key][i+trim_cnt].moving_time_per_intervene = analyses[key][i+trim_cnt].moving_time / analyses[key][i+trim_cnt].intervenants
329
        elif row == 'moving_time_per_act':
330
            if analyses[key][i+trim_cnt].moving_time and analyses[key][i+trim_cnt].acts_present:
331
                analyses[key][i+trim_cnt].moving_time_per_act = analyses[key][i+trim_cnt].moving_time / analyses[key][i+trim_cnt].acts_present
332
        else:
333
            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))
334

    
335
def annual_activity_synthesis_analysis(statistic, start_day, end_day, analyses, key, participant=None):
336
    analyses[key].append(AnnualActivityProcessingColumn())
337
    acts = None
338
    if participant:
339
        acts = Act.objects.filter(date__gte=start_day.date(),
340
            date__lt=end_day.date(), patient__service=statistic.in_service,
341
            doctors__in=[participant])
342
    else:
343
        acts = Act.objects.filter(date__gte=start_day.date(),
344
            date__lt=end_day.date(), patient__service=statistic.in_service)
345
    for row in ANNUAL_ACTIVITY_ROWS:
346
        if row == 'percent_abs':
347
            tot_abs = 0
348
            pointe = 0
349
            for i in (3, 7, 11, 15):
350
                pointe += analyses[key][i].pointe
351
                tot_abs += analyses[key][i].absent
352
            percent_abs = 100
353
            if not pointe or not tot_abs:
354
                percent_abs = 0
355
            elif tot_abs:
356
                percent_abs = (tot_abs/float(pointe))*100
357
            analyses[key][16].percent_abs = "%.2f" % percent_abs
358
        elif row == 'percent_nf':
359
            tot_nf= 0
360
            pointe = 0
361
            for i in (3, 7, 11, 15):
362
                pointe += analyses[key][i].pointe
363
                tot_nf += analyses[key][i].nf
364
            percent_nf = 100
365
            if not pointe or not tot_nf:
366
                percent_nf = 0
367
            elif tot_nf:
368
                percent_nf = (tot_nf/float(pointe))*100
369
            analyses[key][16].percent_nf = "%.2f" % percent_nf
370
        elif row == 'patients':
371
            analyses[key][16].patients = acts.aggregate(Count('patient', distinct=True))['patient__count']
372
        elif row == 'intervenants':
373
            analyses[key][16].intervenants = acts.aggregate(Count('doctors', distinct=True))['doctors__count']
374
        elif row == 'fact_per_day':
375
            if analyses[key][16].days:
376
                analyses[key][16].fact_per_day = "%.2f" % (analyses[key][16].really_facturables / float(analyses[key][16].days))
377
        elif row == 'moving_time_per_intervene':
378
            if analyses[key][16].moving_time and analyses[key][16].intervenants:
379
                analyses[key][16].moving_time_per_intervene = analyses[key][16].moving_time / analyses[key][16].intervenants
380
        elif row == 'moving_time_per_act':
381
            if analyses[key][16].moving_time and analyses[key][16].acts_present:
382
                analyses[key][16].moving_time_per_act = analyses[key][16].moving_time / analyses[key][16].acts_present
383
        else:
384
            val = 0
385
            if row == 'moving_time':
386
                val = timedelta()
387
            for i in (3, 7, 11, 15):
388
                val += getattr(analyses[key][i], row)
389
            setattr(analyses[key][16], row, val)
390

    
391
def strfdelta(tdelta, fmt):
392
    if not tdelta:
393
        return '0'
394
    d = {"days": tdelta.days}
395
    d["hours"], rem = divmod(tdelta.seconds, 3600)
396
    d["minutes"], d["seconds"] = divmod(rem, 60)
397
    return fmt.format(**d)
398

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

    
487
    table_2 = []
488
    table_2_label = label + ' - absences'
489
    table_2.append([table_2_label] + ANNUAL_ACTIVITY_COLUMN_LABELS)
490
    rows = []
491
    row = ['Pointés']
492
    for column in analyses[key]:
493
        row.append(column.pointe)
494
    rows.append(row)
495
    row = ['Absences']
496
    for column in analyses[key]:
497
        row.append(column.absent)
498
    rows.append(row)
499
    row = ['% absences / pointés']
500
    for column in analyses[key]:
501
        row.append(column.percent_abs)
502
    rows.append(row)
503
    row = ['Excusées']
504
    for column in analyses[key]:
505
        row.append(column.abs_exc)
506
    rows.append(row)
507
    row = ['Non excusées']
508
    for column in analyses[key]:
509
        row.append(column.abs_non_exc)
510
    rows.append(row)
511
    row = ["De l'intervenant"]
512
    for column in analyses[key]:
513
        row.append(column.abs_inter)
514
    rows.append(row)
515
    row = ["Annulés par nous"]
516
    for column in analyses[key]:
517
        row.append(column.annul_nous)
518
    rows.append(row)
519
    row = ['Annulés par la famille']
520
    for column in analyses[key]:
521
        row.append(column.annul_famille)
522
    rows.append(row)
523
    row = ['ESS PPS']
524
    for column in analyses[key]:
525
        row.append(column.abs_ess_pps)
526
    rows.append(row)
527
    row = ['Hospitalisations']
528
    for column in analyses[key]:
529
        row.append(column.enf_hosp)
530
    rows.append(row)
531
    table_2.append(rows)
532
    data_tables.append(table_2)
533

    
534
    if statistic.in_service.name == 'CMPP':
535
        table_3 = []
536
        table_3_label = label + ' - non fact.'
537
        table_3.append([table_3_label] + ANNUAL_ACTIVITY_COLUMN_LABELS)
538
        rows = []
539
        row = ['Pointés']
540
        for column in analyses[key]:
541
            row.append(column.pointe)
542
        rows.append(row)
543
        row = ['Présents']
544
        for column in analyses[key]:
545
            row.append(column.acts_present)
546
        rows.append(row)
547
        row = ['De type non fact.']
548
        for column in analyses[key]:
549
            row.append(column.non_facturables)
550
        rows.append(row)
551
        row = ['De type fact.']
552
        for column in analyses[key]:
553
            row.append(column.facturables)
554
        rows.append(row)
555
        row = ['Perdus']
556
        for column in analyses[key]:
557
            row.append(column.perdus)
558
        rows.append(row)
559
        row = ['En doubles']
560
        for column in analyses[key]:
561
            row.append(column.doubles)
562
        rows.append(row)
563
        row = ['Non facturables']
564
        for column in analyses[key]:
565
            row.append(column.nf)
566
        rows.append(row)
567
        row = ['% NF / pointés']
568
        for column in analyses[key]:
569
            row.append(column.percent_nf)
570
        rows.append(row)
571
        table_3.append(rows)
572
        data_tables.append(table_3)
573

    
574
def run_annual_activity(statistic, start_day, analyses, key, label, data_tables, participant=None):
575
    analyses[key] = list()
576
    trim_cnt = 0
577
    for i in range(0, 12):
578
        annual_activity_month_analysis(statistic, start_day, analyses, key, i, trim_cnt, participant)
579
        if not (i + 1) % 3:
580
            trim_cnt += 1
581
            annual_activity_trimester_analysis(statistic, start_day, analyses, key, i, trim_cnt, participant)
582
    end_day = datetime(start_day.year+1, 1, 1)
583
    annual_activity_synthesis_analysis(statistic, start_day, end_day, analyses, key, participant)
584
    annual_activity_build_tables(statistic, analyses, key, label, data_tables)
585

    
586
def annual_activity(statistic):
587
    if not statistic.in_service:
588
        return None
589
    start_day = datetime(datetime.today().year, 1, 1)
590
    if statistic.in_year:
591
        start_day = datetime(statistic.in_year, 1, 1)
592
    data_tables = list()
593
    analyses = dict()
594
    if not statistic.in_participants:
595
        run_annual_activity(statistic, start_day, analyses, 'global', 'Tous', data_tables, participant=None)
596
    else:
597
        for participant in statistic.in_participants:
598
            run_annual_activity(statistic, start_day, analyses, participant.id, str(participant), data_tables, participant=participant)
599
    return [data_tables]
600

    
601
def patients_per_worker_for_period(statistic):
602
    if not statistic.in_service:
603
        return None
604
    data_tables = []
605
    data = []
606
    data.append(['Intervenants', 'Nombre', 'Patients'])
607
    values = []
608
    if not statistic.in_end_date:
609
        statistic.in_end_date = datetime.today()
610
    if not statistic.in_start_date:
611
        statistic.in_start_date = datetime(statistic.in_end_date.year, 1, 1)
612
    acts = None
613
    if statistic.in_patients:
614
        acts = Act.objects.filter(date__gte=statistic.in_start_date,
615
            date__lte=statistic.in_end_date,
616
            patient__service=statistic.in_service,
617
            patient__in=statistic.in_patients)
618
    else:
619
        acts = Act.objects.filter(date__gte=statistic.in_start_date,
620
            date__lte=statistic.in_end_date,
621
            patient__service=statistic.in_service)
622
    analyse = dict()
623
    for act in acts:
624
        for intervene in act.doctors.all():
625
            if statistic.in_participants:
626
                if intervene in statistic.in_participants:
627
                    analyse.setdefault(intervene, []).append(str(act.patient))
628
            else:
629
                analyse.setdefault(intervene, []).append(str(act.patient))
630
    o_analyse = OrderedDict(sorted(analyse.items(), key=lambda t: t[0]))
631
    for intervene, patients in o_analyse.iteritems():
632
        lst = list(set(patients))
633
        values.append([str(intervene), len(lst), lst])
634
    data.append(values)
635
    data_tables.append(data)
636
    return [data_tables]
637

    
638
def active_patients_by_state_only(statistic):
639
    if not statistic.in_service:
640
        return None
641
    if not statistic.in_start_date:
642
        statistic.in_start_date = datetime.today()
643
    active_states = None
644
    if statistic.in_service.name == 'CMPP':
645
        active_states = ('TRAITEMENT', 'DIAGNOSTIC')
646
    elif statistic.in_service.name == 'CAMSP':
647
        active_states = ('SUIVI', )
648
    else:
649
        active_states = ('TRAITEMENT', )
650
    patients = [(p.last_name, p.first_name, p.paper_id) \
651
        for p in PatientRecord.objects.filter(service=statistic.in_service) \
652
            if p.get_state_at_day(
653
                statistic.in_start_date).status.type in active_states]
654
    data_tables_set=[[[['En date du :', formats.date_format(statistic.in_start_date, "SHORT_DATE_FORMAT"), len(patients)]]]]
655
    data = []
656
    data.append(['Nom', 'Prénom', 'N° Dossier'])
657
    p_list = []
658
    for ln, fn, pid in patients:
659
        ln = ln or ''
660
        if len(ln) > 1:
661
            ln = ln[0].upper() + ln[1:].lower()
662
        fn = fn or ''
663
        if len(fn) > 1:
664
            fn = fn[0].upper() + fn[1:].lower()
665
        p_list.append((ln, fn, str(pid or '')))
666
    data.append(sorted(p_list,
667
        key=lambda k: k[0]+k[1]))
668
    data_tables_set[0].append(data)
669
    return data_tables_set
670

    
671
def active_patients_with_act(statistic):
672
    def process(patients_list, title):
673
        data_tables = []
674
        data = []
675
        data.append([title, len(patients_list), '', ''])
676
        data_tables.append(data)
677
        data = []
678
        data.append(['Nom', 'Prénom', 'N° Dossier'])
679
        patients_values = patients_list.\
680
                values_list('last_name', 'first_name', 'paper_id')
681
        p_list = []
682
        for ln, fn, pid in patients_values:
683
            ln = ln or ''
684
            if len(ln) > 1:
685
                ln = ln[0].upper() + ln[1:].lower()
686
            fn = fn or ''
687
            if len(fn) > 1:
688
                fn = fn[0].upper() + fn[1:].lower()
689
            p_list.append((ln, fn, str(pid or '')))
690
        data.append(sorted(p_list,
691
            key=lambda k: k[0]+k[1]))
692
        data_tables.append(data)
693
        return data_tables
694
    if not statistic.in_service:
695
        return None
696
    if not statistic.in_end_date:
697
        statistic.in_end_date = datetime.today()
698
    if not statistic.in_start_date:
699
        statistic.in_start_date = datetime(statistic.in_end_date.year, 1, 1)
700
    active_states = None
701
    if statistic.in_service.name == 'CMPP':
702
        active_states = ('TRAITEMENT', 'DIAGNOSTIC')
703
    elif statistic.in_service.name == 'CAMSP':
704
        active_states = ('SUIVI', )
705
    else:
706
        active_states = ('TRAITEMENT', )
707

    
708
    data_tables_set = []
709
    data_tables = []
710
    data = []
711
    data.append(['Période', 'Jours'])
712
    data.append([("%s - %s"
713
        % (formats.date_format(statistic.in_start_date, "SHORT_DATE_FORMAT"),
714
        formats.date_format(statistic.in_end_date, "SHORT_DATE_FORMAT")),
715
        (statistic.in_end_date-statistic.in_start_date).days+1)])
716
    data_tables.append(data)
717
    data_tables_set.append(data_tables)
718

    
719
    acts_base = Act.objects.filter(
720
        date__gte=statistic.in_start_date,
721
        date__lte=statistic.in_end_date,
722
        patient__service=statistic.in_service)
723
    acts_valide = acts_base.filter(valide=True)
724
    acts_valide_patients_ids = acts_valide.order_by('patient').\
725
        distinct('patient').values_list('patient')
726
    acts_valide_patients = PatientRecord.objects.filter(
727
        id__in=[patient[0] for patient in acts_valide_patients_ids])
728
    all_patients_ids = acts_base.order_by('patient').distinct('patient').\
729
        values_list('patient')
730
    acts_not_valide_patients = PatientRecord.objects.filter(
731
        id__in=[patient[0] for patient in all_patients_ids
732
            if not patient in acts_valide_patients_ids])
733

    
734

    
735
    patients_1 = acts_valide_patients.filter(
736
        last_state__status__type__in=active_states)
737
    patients_2 = acts_valide_patients.exclude(
738
        last_state__status__type__in=active_states)
739
    patients_3 = acts_not_valide_patients.filter(
740
        last_state__status__type__in=active_states)
741
    patients_4 = acts_not_valide_patients.exclude(
742
        last_state__status__type__in=active_states)
743

    
744
    data_tables_set.append(process(patients_1, 'Patients avec un acte validé et dans un état actif'))
745
    data_tables_set.append(process(patients_2, 'Patients avec un acte validé et dans un état non actif'))
746
    data_tables_set.append(process(patients_3, 'Patients sans acte validé et dans un état actif'))
747
    data_tables_set.append(process(patients_4, 'Patients sans acte validé et dans un état non actif'))
748

    
749
    return data_tables_set
750

    
751
def closed_files(statistic):
752
    if not statistic.in_service:
753
        return None
754
    data_tables = []
755
    data1 = []
756
    data1.append(['Période', 'Jours',
757
        'Nombre de dossiers clos durant la période', 'PEC totale', 'PEC moyenne', "Dossiers qui ne sont plus clos"])
758
    data2 = []
759
    data2.append(['Nom', 'Prénom', 'N° Dossier', 'Date de clôture', 'Durée de la PEC', "N'est plus clos"])
760
    if not statistic.in_end_date:
761
        statistic.in_end_date = datetime.today()
762
    if not statistic.in_start_date:
763
        statistic.in_start_date = datetime(statistic.in_end_date.year, 1, 1)
764
    closed_records = FileState.objects.filter(status__type='CLOS',
765
        date_selected__gte=statistic.in_start_date,
766
        date_selected__lte=statistic.in_end_date). \
767
        order_by('patient').distinct('patient').\
768
        values_list('patient')
769
    closed_records = PatientRecord.objects.filter(service=statistic.in_service, id__in=[patient[0]
770
        for patient in closed_records])
771
    total_pec = 0
772
    p_list = []
773
    not_closed_now = 0
774
    for record in closed_records:
775
        ln = record.last_name or ''
776
        if len(ln) > 1:
777
            ln = ln[0].upper() + ln[1:].lower()
778
        fn = record.first_name or ''
779
        if len(fn) > 1:
780
            fn = fn[0].upper() + fn[1:].lower()
781
        current_state = ''
782
        close_date = record.last_state.date_selected.date()
783
        if record.get_current_state().status.type != 'CLOS':
784
            not_closed_now += 1
785
            current_state = record.get_current_state().status.name + \
786
                ' le ' + formats.date_format(record.get_current_state(). \
787
                date_selected, "SHORT_DATE_FORMAT")
788
            close_date = FileState.objects.filter(status__type='CLOS',
789
                patient=record).order_by('-date_selected')[0].date_selected.date()
790
        p_list.append((ln, fn, str(record.paper_id or ''),
791
            close_date,
792
            record.care_duration_since_last_contact_or_first_act,
793
            current_state))
794
        total_pec += record.care_duration_since_last_contact_or_first_act
795
    data2.append(sorted(p_list,
796
        key=lambda k: k[0]+k[1]))
797
    avg_pec = 0
798
    if closed_records.count() and total_pec:
799
        avg_pec = total_pec/closed_records.count()
800
    data1.append([("%s - %s"
801
        % (formats.date_format(statistic.in_start_date, "SHORT_DATE_FORMAT"),
802
        formats.date_format(statistic.in_end_date, "SHORT_DATE_FORMAT")),
803
        (statistic.in_end_date-statistic.in_start_date).days+1,
804
        closed_records.count(), total_pec, avg_pec, not_closed_now)])
805
    data_tables.append(data1)
806
    data_tables.append(data2)
807
    return [data_tables]
808

    
809
def patients_details(statistic):
810
    if not statistic.in_service:
811
        return None
812
    data_tables_set = []
813
    if not statistic.in_end_date:
814
        statistic.in_end_date = datetime.today()
815
    if not statistic.in_start_date:
816
        statistic.in_start_date = datetime(statistic.in_end_date.year, 1, 1)
817
    acts = None
818
    if statistic.in_patients:
819
        acts = Act.objects.filter(date__gte=statistic.in_start_date,
820
            date__lte=statistic.in_end_date,
821
            patient__service=statistic.in_service,
822
            patient__in=statistic.in_patients)
823
    else:
824
        acts = Act.objects.filter(date__gte=statistic.in_start_date,
825
            date__lte=statistic.in_end_date,
826
            patient__service=statistic.in_service)
827
    analyse = dict()
828
    for act in acts:
829
        analyse.setdefault(act.patient, []).append(act)
830
    o_analyse = OrderedDict(sorted(analyse.items(),
831
        key=lambda t: t[0].last_name))
832
    data = []
833
    data.append(['Période', 'Jours',
834
        'Nombre de dossiers'])
835
    data.append([("%s - %s"
836
        % (formats.date_format(statistic.in_start_date, "SHORT_DATE_FORMAT"),
837
        formats.date_format(statistic.in_end_date, "SHORT_DATE_FORMAT")),
838
        (statistic.in_end_date-statistic.in_start_date).days+1,
839
        len(o_analyse))])
840
    data_tables_set.append([data])
841
    for patient, acts in o_analyse.iteritems():
842
        data_tables = list()
843
        data_tables.append([["%s %s" % (str(patient), str(patient.paper_id))]])
844
        data = []
845
        data.append(["Statut de l'acte", 'Nombre'])
846
        values = []
847
        values.append(('Proposés', len(acts)))
848
        np, absent = 0, 0
849
        act_types = dict()
850
        for a in acts:
851
            if a.is_new():
852
                np += 1
853
            elif a.is_absent():
854
                absent += 1
855
            act_types.setdefault(a.act_type, []).append(a)
856
        values.append(('Non pointés', np))
857
        values.append(('Présents', len(acts) - np - absent))
858
        values.append(('Absents', absent))
859
        data.append(values)
860
        data_tables.append(data)
861
        data = []
862
        data.append(["Types d'acte", "Nombre d'actes proposés"])
863
        values = []
864
        for act_type, acts in act_types.iteritems():
865
            values.append((act_type, len(acts)))
866
        data.append(values)
867
        data_tables.append(data)
868

    
869
        data = []
870
        data.append(["Historique", "Nombre de jours par état"])
871
        values = []
872
        for state, duration in patient.get_states_history_with_duration():
873
            values.append(("%s (%s)" % (state.status.name,
874
                formats.date_format(state.date_selected,
875
                "SHORT_DATE_FORMAT")), duration.days))
876
        data.append(values)
877
        data_tables.append(data)
878

    
879
        contacts = FileState.objects.filter(patient=patient, status__type='ACCUEIL').order_by('date_selected')
880
        recontact = 'Non'
881
        last_contact = None
882
        first_acts_after_contact = None
883
        if len(contacts) == 1:
884
            last_contact = contacts[0]
885
        elif len(contacts) > 1:
886
            recontact = 'Oui'
887
            last_contact = contacts[len(contacts)-1]
888
        if last_contact:
889
            # inscription act
890
            first_acts_after_contact = Act.objects.filter(patient=patient, date__gte=last_contact.date_selected).order_by('date')
891
            if first_acts_after_contact:
892
                first_act_after_contact = first_acts_after_contact[0]
893
                if first_act_after_contact.date <= statistic.in_end_date.date() and first_act_after_contact.date >= statistic.in_start_date.date():
894
                    # inscription during the selected date range.
895
                    waiting_duration = first_act_after_contact.date - last_contact.date_selected.date()
896
                    data = []
897
                    data.append(["Date inscription", "Date accueil", 'Attente', 'Réinscription'])
898
                    values = []
899
                    values.append((first_act_after_contact.date, last_contact.date_selected.date(), waiting_duration.days, recontact))
900
                    data.append(values)
901
                    data_tables.append(data)
902

    
903
        closed_during_range_date = None
904
        try:
905
            closed_during_range_date = FileState.objects.filter(patient=patient, status__type='CLOS',
906
                date_selected__gte=statistic.in_start_date,
907
                date_selected__lte=statistic.in_end_date).latest('date_selected')
908
        except:
909
            pass
910
        care_duration = patient.care_duration_since_last_contact_or_first_act
911
        closure_date = ''
912
        if closed_during_range_date:
913
            closure_date = closed_during_range_date.date_selected.date
914
        reopen = ''
915
        if closed_during_range_date and not patient.exit_date:
916
            reopen = 'Oui'
917
        data = []
918
        data.append(["Durée de la prise en charge", "Clos pendant la période", "Actes suivants la clôture"])
919
        values = []
920
        values.append((patient.care_duration_since_last_contact_or_first_act, closure_date, reopen))
921
        data.append(values)
922
        data_tables.append(data)
923

    
924
        if patient.mdph_requests.exists():
925
            data = []
926
            data.append(["Demande(s) MDPH pendant la période", "Date de la demande", "Demande antérieure à la date de début saisie"])
927
            values = []
928
            for request in patient.mdph_requests.order_by('start_date'):
929
                before = 'Non'
930
                if request.start_date < statistic.in_start_date.date():
931
                    before = 'Oui'
932
                values.append(('MDPH : ' + request.mdph.department, request.start_date, before))
933
            data.append(values)
934
            data_tables.append(data)
935
        if patient.mdph_responses.exists():
936
            data = []
937
            data.append(["Réponse(s) MDPH pendant la période", "Date de début", "Date de fin"])
938
            values = []
939
            for response in patient.mdph_responses.order_by('start_date'):
940
                values.append(('MDPH : ' + response.mdph.department, response.start_date, response.end_date))
941
            data.append(values)
942
            data_tables.append(data)
943
        data_tables_set.append(data_tables)
944

    
945
    return data_tables_set
946

    
947
def patients_synthesis(statistic):
948
    if not statistic.in_service:
949
        return None
950
    data_tables = []
951
    data = []
952
    data.append(['Période', 'Jours',
953
        'Nombre de dossiers avec un acte validé sur la période',
954
        "Nombre d'actes validés sur la période"])
955
    if not statistic.in_end_date:
956
        statistic.in_end_date = datetime.today()
957
    if not statistic.in_start_date:
958
        statistic.in_start_date = datetime(statistic.in_end_date.year, 1, 1)
959
    acts = Act.objects.filter(valide=True,
960
        date__gte=statistic.in_start_date,
961
        date__lte=statistic.in_end_date,
962
        patient__service=statistic.in_service)
963
    patients = acts.order_by('patient').distinct('patient').\
964
        values_list('patient')
965
    patients = PatientRecord.objects.filter(id__in=[patient[0]
966
        for patient in patients])
967
    nb_patients = patients.count()
968
    data.append([("%s - %s"
969
        % (formats.date_format(statistic.in_start_date, "SHORT_DATE_FORMAT"),
970
        formats.date_format(statistic.in_end_date, "SHORT_DATE_FORMAT")),
971
        (statistic.in_end_date-statistic.in_start_date).days+1,
972
        nb_patients, acts.count())])
973
    data_tables.append(data)
974
    data = []
975
    data.append(['Féminin', 'Masculin'])
976
    data.append([(patients.filter(gender='2').count(),
977
        patients.filter(gender='1').count())])
978
    data_tables.append(data)
979
    data = []
980
    data.append(['Durée totale de la PEC',
981
        'Durée moyenne de la PEC par patient'])
982
    pec_total = sum([p.care_duration_since_last_contact_or_first_act for p in patients])
983
    data.append([(pec_total, pec_total/nb_patients)])
984
    data_tables.append(data)
985
    data = []
986
    data.append(["Etat du dossier à ce jour", 'Nombre de patients'])
987
    states = dict()
988
    for patient in patients:
989
        states.setdefault(patient.get_current_state().status, []).append(patient)
990
    values = []
991
    closed_patients_tmp = None
992
    for state, ps in states.iteritems():
993
        values.append((state.name, len(ps)))
994
        if state.id == 5:
995
            closed_patients_tmp = ps
996
    data.append(values)
997
    data_tables.append(data)
998

    
999
    # Pour les patients inscrits pendant la périodes
1000
    inscriptions = 0
1001
    recontact_cnt = 0
1002
    waiting_duration = timedelta()
1003

    
1004
    for patient in patients:
1005
        recontact = False
1006
        contacts = FileState.objects.filter(patient=patient, status__type='ACCUEIL').order_by('date_selected')
1007
        last_contact = None
1008
        first_acts_after_contact = None
1009
        if len(contacts) == 1:
1010
            last_contact = contacts[0]
1011
        elif len(contacts) > 1:
1012
            recontact = True
1013
            last_contact = contacts[len(contacts)-1]
1014
        if last_contact:
1015
            # inscription act
1016
            first_acts_after_contact = Act.objects.filter(patient=patient, date__gte=last_contact.date_selected).order_by('date')
1017
            if first_acts_after_contact:
1018
                first_act_after_contact = first_acts_after_contact[0]
1019
                if first_act_after_contact.date <= statistic.in_end_date.date() and first_act_after_contact.date >= statistic.in_start_date.date():
1020
                    # inscription during the selected date range.
1021
                    waiting_duration += first_act_after_contact.date - last_contact.date_selected.date()
1022
                    inscriptions += 1
1023
                    if recontact:
1024
                        recontact_cnt += 1
1025
    if inscriptions:
1026
        data = []
1027
        data.append(['Inscriptions (premier acte suivant le dernier contact dans la période)', 'Dont réinscription', "Durée moyenne de l'attente"])
1028
        data.append([(inscriptions, recontact_cnt, (waiting_duration/inscriptions).days)])
1029
        data_tables.append(data)
1030

    
1031
    closed_records = FileState.objects.filter(status__type='CLOS',
1032
        date_selected__gte=statistic.in_start_date,
1033
        date_selected__lte=statistic.in_end_date, patient__in=patients). \
1034
        order_by('patient').distinct('patient').\
1035
        values_list('patient')
1036
    closed_records = PatientRecord.objects.filter(service=statistic.in_service, id__in=[patient[0]
1037
        for patient in closed_records])
1038
    total_pec = 0
1039
    not_closed_now = 0
1040
    for record in closed_records:
1041
        if record.get_current_state().status.type != 'CLOS':
1042
            not_closed_now += 1
1043
        total_pec += record.care_duration_since_last_contact_or_first_act
1044
    avg_pec = 0
1045
    if closed_records.count() and total_pec:
1046
        avg_pec = total_pec/closed_records.count()
1047
    if closed_records.count():
1048
        data = []
1049
        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"])
1050
        data.append([(closed_records.count(), total_pec, avg_pec, not_closed_now)])
1051
        data_tables.append(data)
1052

    
1053
    mdph_requests = 0
1054
    mdph_requests_before = 0
1055
    for patient in patients:
1056
        if patient.mdph_requests.exists():
1057
            mdph_requests += 1
1058
            # We only look to the last one
1059
            if patient.mdph_requests.order_by('-start_date')[0].start_date < statistic.in_start_date.date():
1060
                mdph_requests_before +=1
1061
    data = []
1062
    data.append(['Dossier avec une demande MDPH', "Dont la dernière demande a été faite avant la période"])
1063
    data.append([(mdph_requests, mdph_requests_before)])
1064
    data_tables.append(data)
1065

    
1066
    birth_years = dict()
1067
    patients_without_birthyear = []
1068
    for patient in patients:
1069
        try:
1070
            birth_years.setdefault(patient.birthdate.year, []).append(patient)
1071
        except:
1072
            patients_without_birthyear.append(patient)
1073
    data = []
1074
    data.append(['Année de naissance', "Nombre de dossiers"])
1075
    values = []
1076
    for birth_year, pts in birth_years.iteritems():
1077
        values.append((birth_year, len(pts)))
1078
    if patients_without_birthyear:
1079
        values.append(('%d patient(s) sans date de naissance' % len(patients_without_birthyear), patients_without_birthyear))
1080
    data.append(values)
1081
    data_tables.append(data)
1082

    
1083
    lower_bounds = [0, 3, 5, 7, 11, 16, 20, 25, 30, 35, 40, 45, 50, 55, 60, 75, 85, 96]
1084
    anap_code = 198
1085
    data = []
1086
    data.append(["Code ANAP", "Tranche d'âge (au %s)" \
1087
        % formats.date_format(statistic.in_end_date, "SHORT_DATE_FORMAT"),
1088
        "Nombre de dossiers"])
1089
    values = []
1090
    for i in range(len(lower_bounds)):
1091
        lower_bound = lower_bounds[i]
1092
        if i == len(lower_bounds) - 1:
1093
            values.append([anap_code, "De %d ans et plus" % lower_bound, 0])
1094
        else:
1095
            values.append([anap_code, "De %d à %d ans" % (lower_bound, lower_bounds[i + 1] - 1), 0])
1096
        anap_code += 1
1097
    for patient in patients:
1098
        try:
1099
            age = statistic.in_end_date.date() - patient.birthdate
1100
            age = age.days / 365
1101
            i = 0
1102
            while age >= lower_bounds[i+1]:
1103
                i += 1
1104
                if i == len(lower_bounds) - 1:
1105
                    break
1106
            values[i][2] += 1
1107
        except:
1108
            pass
1109
    data.append(values)
1110
    data_tables.append(data)
1111

    
1112
    jobs = dict()
1113
    for patient in patients:
1114
        if patient.job_mother:
1115
            jobs.setdefault(patient.job_mother, []).append(patient)
1116
        if patient.job_father:
1117
            jobs.setdefault(patient.job_father, []).append(patient)
1118
    data = []
1119
    data.append(["Profession d'un parent", "Nombre de dossiers"])
1120
    values = []
1121
    for job, pts in jobs.iteritems():
1122
        values.append((job, len(pts)))
1123
    data.append(values)
1124
    data_tables.append(data)
1125

    
1126
    provenances = dict()
1127
    unknown = 0
1128
    for patient in patients:
1129
        if patient.provenance:
1130
            provenances.setdefault(patient.provenance, []).append(patient)
1131
        else:
1132
            unknown += 1
1133
    data = []
1134
    data.append(["Provenances", "Nombre de dossiers"])
1135
    values = []
1136
    for provenance, pts in provenances.iteritems():
1137
        values.append((provenance, len(pts)))
1138
    values.append(('Non renseignée', unknown))
1139
    data.append(values)
1140
    data_tables.append(data)
1141

    
1142
    outmotives = dict()
1143
    for patient in patients:
1144
        if patient.outmotive:
1145
            outmotives.setdefault(patient.outmotive, []).append(patient)
1146
    data = []
1147
    data.append(["Motifs de sortie", "Nombre de dossiers"])
1148
    values = []
1149
    for outmotive, pts in outmotives.iteritems():
1150
        values.append((outmotive, len(pts)))
1151
    data.append(values)
1152
    data_tables.append(data)
1153

    
1154
    outtos = dict()
1155
    for patient in patients:
1156
        if patient.outto:
1157
            outtos.setdefault(patient.outto, []).append(patient)
1158
    data = []
1159
    data.append(["Orientations", "Nombre de dossiers"])
1160
    values = []
1161
    for outto, pts in outtos.iteritems():
1162
        values.append((outto, len(pts)))
1163
    data.append(values)
1164
    data_tables.append(data)
1165

    
1166
    return [data_tables]
1167

    
1168
def acts_synthesis(statistic):
1169
    if not statistic.in_service:
1170
        return None
1171
    if not statistic.in_end_date:
1172
        statistic.in_end_date = datetime.today()
1173
    if not statistic.in_start_date:
1174
        statistic.in_start_date = datetime(statistic.in_end_date.year, 1, 1)
1175
    data_tables = []
1176
    data = []
1177
    data.append(['Période', 'Jours',
1178
        "Nombre d'actes proposés sur la période",
1179
        "Nombre d'actes validés sur la période"])
1180
    acts = Act.objects.filter(date__gte=statistic.in_start_date,
1181
        date__lte=statistic.in_end_date,
1182
        patient__service=statistic.in_service)
1183
    acts_valide = acts.filter(valide=True)
1184
    data.append([("%s - %s"
1185
        % (formats.date_format(statistic.in_start_date, "SHORT_DATE_FORMAT"),
1186
        formats.date_format(statistic.in_end_date, "SHORT_DATE_FORMAT")),
1187
        (statistic.in_end_date-statistic.in_start_date).days+1,
1188
        acts.count(), acts_valide.count())])
1189
    data_tables.append(data)
1190

    
1191
    acts_types = dict()
1192
    for act in acts:
1193
        acts_types.setdefault(act.act_type, []).append(act)
1194
    data = []
1195
    data.append(["Types des actes", "Nombre d'actes"])
1196
    values = []
1197
    for act_type, acts in acts_types.iteritems():
1198
        values.append((act_type, len(acts)))
1199
    data.append(values)
1200
    data_tables.append(data)
1201

    
1202
    for act_type, acts in acts_types.iteritems():
1203
        analysis = {'Non pointés': 0,
1204
            'Reportés': 0, 'Absents': 0, 'Présents': 0}
1205
        for a in acts:
1206
            if a.is_new():
1207
                analysis['Non pointés'] += 1
1208
            elif a.is_absent():
1209
                state = a.get_state()
1210
                if state.state_name == 'REPORTE':
1211
                    analysis['Reportés'] += 1
1212
                else:
1213
                    analysis['Absents'] += 1
1214
            else:
1215
                analysis['Présents'] += 1
1216
        data = []
1217
        data.append(["Type d'acte", act_type])
1218
        values = []
1219
        for status, number in analysis.iteritems():
1220
            values.append((status, number))
1221
        data.append(values)
1222
        data_tables.append(data)
1223

    
1224
    acts_count_participants = dict()
1225
    for act in acts_valide:
1226
        acts_count_participants.setdefault(act.doctors.count(), []).append(act)
1227
    data = []
1228
    data.append(["Nombre d'intervenants des actes réalisés", "Nombre d'actes", "Nombre de dossiers concernés"])
1229
    values = []
1230
    for number, acts_counted in acts_count_participants.iteritems():
1231
        values.append((number, len(acts_counted), len(set([a.patient.id for a in acts_counted]))))
1232
    data.append(values)
1233
    data_tables.append(data)
1234

    
1235
    return [data_tables]
1236

    
1237
def acts_synthesis_cmpp(statistic):
1238
    data_tables_set = []
1239
    if not statistic.in_service:
1240
        return None
1241
    if not statistic.in_end_date:
1242
        statistic.in_end_date = datetime.today()
1243
    if not statistic.in_start_date:
1244
        statistic.in_start_date = datetime(statistic.in_end_date.year, 1, 1)
1245
    acts = Act.objects.filter(date__gte=statistic.in_start_date,
1246
        date__lte=statistic.in_end_date,
1247
        patient__service=statistic.in_service)
1248
    acts_billed = acts.filter(is_billed=True)
1249
    patients_billed = dict()
1250
    for act in acts_billed:
1251
        patients_billed.setdefault(act.patient, [False, False])
1252
        if act.get_hc_tag() and act.get_hc_tag()[0] == 'D':
1253
            patients_billed[act.patient][0] = True
1254
        elif act.get_hc_tag() and act.get_hc_tag()[0] == 'T':
1255
            patients_billed[act.patient][1] = True
1256
    values1, values2, values3 = [], [], []
1257
    for patient, vals in patients_billed.iteritems():
1258
        pfields = [patient.last_name, patient.first_name, patient.paper_id]
1259
        if vals == [True, False]:
1260
            values1.append(pfields)
1261
        elif vals == [False, True]:
1262
            values2.append(pfields)
1263
        elif vals == [True, True]:
1264
            values3.append(pfields)
1265
    cols = ['Nom', 'Prénom', 'Numéro de dossier']
1266
    data_tables_set.append([[['Seulement facturé en diagnostic'], [[len(values1)]]], [cols, sorted(values1, key=lambda t: t[0])]])
1267
    data_tables_set.append([[['Seulement facturé en traitement'], [[len(values2)]]], [cols, sorted(values2, key=lambda t: t[0])]])
1268
    data_tables_set.append([[['Facturé en diagnostic et en traitement'], [[len(values3)]]], [cols, sorted(values3, key=lambda t: t[0])]])
1269
    return data_tables_set
1270

    
1271
def mises(statistic):
1272
    if not statistic.in_service:
1273
        return None
1274
    if not statistic.in_end_date:
1275
        statistic.in_end_date = datetime.today()
1276
    if not statistic.in_start_date:
1277
        statistic.in_start_date = datetime(statistic.in_end_date.year, 1, 1)
1278
    acts = Act.objects.filter(valide='True',
1279
        date__gte=statistic.in_start_date,
1280
        date__lte=statistic.in_end_date,
1281
        patient__service=statistic.in_service)
1282
    patients = acts.order_by('patient').distinct('patient').\
1283
        values_list('patient')
1284
    patients = PatientRecord.objects.filter(id__in=[patient[0]
1285
        for patient in patients])
1286
    pathologies = dict()
1287
    for patient in patients:
1288
        for pathology in patient.mises_1.all():
1289
            pathologies.setdefault(pathology, 0)
1290
            pathologies[pathology] += 1
1291
        for pathology in patient.mises_2.all():
1292
            pathologies.setdefault(pathology, 0)
1293
            pathologies[pathology] += 1
1294
        for pathology in patient.mises_3.all():
1295
            pathologies.setdefault(pathology, 0)
1296
            pathologies[pathology] += 1
1297
    data = [['Pathologies MISES', 'Nombre de patients concernés']]
1298
    data.append(OrderedDict(sorted(pathologies.items(), key=lambda t: t[0].ordering_code)).items())
1299
    return [[data]]
1300

    
1301

    
1302
class Statistic(models.Model):
1303
    patients = models.ManyToManyField('dossiers.PatientRecord',
1304
            null=True, blank=True, default=None)
1305
    participants = models.ManyToManyField('personnes.People',
1306
            null=True, blank=True, default=None)
1307
    in_start_date = None
1308
    in_end_date = None
1309
    in_service = None
1310
    in_participants = None
1311
    in_patients = None
1312
    in_year = None
1313

    
1314
    def __init__(self, name=None, inputs=dict()):
1315
        self.name = name
1316
        params = STATISTICS.get(name, {})
1317
        self.display_name = params['display_name']
1318
        self.category = params['category']
1319
        self.inputs = inputs
1320
        self.in_participants = list()
1321
        participants = inputs.get('participants')
1322
        if participants:
1323
            p_str_ids = [p for p in participants.split('|') if p]
1324
            for str_id in p_str_ids:
1325
                try:
1326
                    self.in_participants.append(Worker.objects.get(pk=int(str_id)))
1327
                except:
1328
                    pass
1329
        self.in_patients = list()
1330
        patients = inputs.get('patients')
1331
        if patients:
1332
            p_str_ids = [p for p in patients.split('|') if p]
1333
            for str_id in p_str_ids:
1334
                try:
1335
                    self.in_patients.append(PatientRecord.objects.get(pk=int(str_id)))
1336
                except:
1337
                    pass
1338
        self.in_service = inputs.get('service')
1339
        self.in_start_date = None
1340
        try:
1341
            self.in_start_date = datetime.strptime(inputs.get('start_date'),
1342
                "%d/%m/%Y")
1343
        except:
1344
            pass
1345
        self.in_end_date = None
1346
        try:
1347
            self.in_end_date = datetime.strptime(inputs.get('end_date'),
1348
                "%d/%m/%Y")
1349
        except:
1350
            pass
1351

    
1352
    def get_data(self):
1353
        func = globals()[self.name]
1354
        self.data = func(self)
1355
        return self.data
1356

    
1357
    def render_to_csv(self):
1358
        _delimiter = ';'
1359
        _quotechar = '|'
1360
        _doublequote = True
1361
        _skipinitialspace = False
1362
        _lineterminator = '\r\n'
1363
        _quoting = csv.QUOTE_MINIMAL
1364
        if getattr(settings, 'CSVPROFILE', None):
1365
            csv_profile = settings.CSVPROFILE
1366
            _delimiter = csv_profile.get('delimiter', ';')
1367
            _quotechar = csv_profile.get('quotechar', '|')
1368
            _doublequote = csv_profile.get('doublequote', True)
1369
            _skipinitialspace = csv_profile.get('skipinitialspace', False)
1370
            _lineterminator = csv_profile.get('lineterminator', '\r\n')
1371
            _quoting = csv_profile.get('quoting', csv.QUOTE_MINIMAL)
1372
        class CSVProfile(csv.Dialect):
1373
            delimiter = _delimiter
1374
            quotechar = _quotechar
1375
            doublequote = _doublequote
1376
            skipinitialspace = _skipinitialspace
1377
            lineterminator = _lineterminator
1378
            quoting = _quoting
1379
        csv.register_dialect('csv_profile', CSVProfile())
1380
        encoding = getattr(settings, 'CSV_ENCODING', 'utf-8')
1381
#        Python 3: , encoding=encoding
1382
#        with tempfile.NamedTemporaryFile(delete=False) as temp_out_csv:
1383
#            try:
1384
#                writer = csv.writer(temp_out_csv, dialect='csv_profile')
1385
#                for data_set in self.data:
1386
#                    for data in data_set:
1387
#                        writer.writerow(data[0])
1388
#                        if len(data) > 1:
1389
#                            for d in data[1]:
1390
#                                writer.writerow(d)
1391
#                        writer.writerow([])
1392
#                    writer.writerow([])
1393
#                return temp_out_csv.name
1394
#            except Exception, e:
1395
#                print e
1396
#                try:
1397
#                    os.unlink(temp_out_pdf.name)
1398
#                except:
1399
#                    pass
1400

    
1401
        import codecs
1402
        filename = None
1403
        with tempfile.NamedTemporaryFile(delete=False) as temp_out_csv:
1404
            filename = temp_out_csv.name
1405
            temp_out_csv.close()
1406
        with codecs.open(filename, 'w+b', encoding=encoding) as encoded_f:
1407
            try:
1408
                writer = csv.writer(encoded_f, dialect='csv_profile')
1409
                for data_set in self.data:
1410
                    for data in data_set:
1411
                        writer.writerow(data[0])
1412
                        if len(data) > 1:
1413
                            for d in data[1]:
1414
                                writer.writerow(d)
1415
                        writer.writerow([])
1416
                    writer.writerow([])
1417
                return filename
1418
            except:
1419
                try:
1420
                    os.unlink(temp_out_pdf.name)
1421
                except:
1422
                    pass
1423

    
1424
    def get_file(self):
1425
        self.get_data()
1426
        return self.render_to_csv()
(5-5/8)