Révision 552cb32b
Ajouté par Mikaël Ates (de retour le 29 avril) il y a plus de 9 ans
scripts/control_integrity.py | ||
---|---|---|
1 |
# -*- coding: utf-8 -*- |
|
2 |
#!/usr/bin/env python |
|
3 |
|
|
4 |
""" |
|
5 |
Script de contrôle et de reporting |
|
6 |
""" |
|
7 |
|
|
8 |
import os |
|
9 |
import tempfile |
|
10 |
|
|
11 |
from datetime import datetime, timedelta |
|
12 |
from datetime import date as date_setter |
|
13 |
|
|
14 |
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "calebasse.settings") |
|
15 |
|
|
16 |
from calebasse.facturation.models import Invoice |
|
17 |
from calebasse.actes.models import Act, ValidationMessage |
|
18 |
from calebasse.dossiers.models import PatientRecord |
|
19 |
from calebasse.agenda.models import EventWithAct |
|
20 |
|
|
21 |
SEND_MAIL = "mates@entrouvert.com" |
|
22 |
OUTPUT_DIR = "./" |
|
23 |
PREFIX = "analyse" |
|
24 |
|
|
25 |
ANALYSE_SDT = datetime(2013,1,1) |
|
26 |
|
|
27 |
ALWAYS_SEND_MAIL = True |
|
28 |
|
|
29 |
LOOKUP_NEW = True |
|
30 |
END_LOOKUP = datetime.today() |
|
31 |
|
|
32 |
SERVICES = ('CMPP', 'CAMSP', 'SESSAD TED', 'SESSAD DYS') |
|
33 |
#SERVICES = ('CAMSP',) |
|
34 |
#SERVICES = ('CAMSP', 'SESSAD TED', 'SESSAD DYS') |
|
35 |
#SERVICES = ('SESSAD DYS',) |
|
36 |
|
|
37 |
QUIET = False |
|
38 |
|
|
39 |
if __name__ == '__main__' : |
|
40 |
prefix = '%s-%s.' % (PREFIX, datetime.utcnow()) |
|
41 |
logger_fn = os.path.join(OUTPUT_DIR, prefix + 'log') |
|
42 |
assert not os.path.isfile(logger_fn), 'Analyse log file "%s" already exists' % logger_fn |
|
43 |
logger = tempfile.NamedTemporaryFile(suffix='.logtmp', |
|
44 |
prefix=prefix, dir=OUTPUT_DIR, delete=False) |
|
45 |
|
|
46 |
logger.write("Script de contôle de Calebasse : %s\n\n" % datetime.utcnow()) |
|
47 |
|
|
48 |
need_mail = False |
|
49 |
|
|
50 |
if LOOKUP_NEW: |
|
51 |
for service_name in SERVICES: |
|
52 |
for date in [(ANALYSE_SDT + timedelta(days=i)).date() for i in range(0, (END_LOOKUP.date()-ANALYSE_SDT.date()).days)]: |
|
53 |
"Be sure all acts are created in 2014" |
|
54 |
acts = list(Act.objects \ |
|
55 |
.filter(date=date, patient__service__name=service_name) \ |
|
56 |
.select_related() \ |
|
57 |
.prefetch_related('doctors', |
|
58 |
'patient__patientcontact', |
|
59 |
'actvalidationstate_set__previous_state') \ |
|
60 |
.order_by('time')) |
|
61 |
event_ids = [ a.parent_event_id for a in acts if a.parent_event_id ] |
|
62 |
events = EventWithAct.objects.for_today(date) \ |
|
63 |
.filter(patient__service__name=service_name) \ |
|
64 |
.exclude(id__in=event_ids) |
|
65 |
events = [ event.today_occurrence(date) for event in events ] |
|
66 |
acts += [ event.act for event in events if event ] |
|
67 |
for act in acts: |
|
68 |
if not act.id: |
|
69 |
act.save() |
|
70 |
|
|
71 |
logger.write("Traitement des données\n") |
|
72 |
logger.write("----------------------\n\n") |
|
73 |
invoices = {'CMPP' : {}, 'CAMSP' : {}, 'SESSAD TED' : {}, 'SESSAD DYS' : {}} |
|
74 |
for invoice in Invoice.objects.all(): |
|
75 |
try: |
|
76 |
patient = PatientRecord.objects.get(id=invoice.patient_id) |
|
77 |
invoices[patient.service.name].setdefault(invoice.invoicing, []).append(invoice) |
|
78 |
except: |
|
79 |
if invoice.patient_id: |
|
80 |
need_mail = True |
|
81 |
logger.write("$$$ La facture %d (%d, %d) a pour id patient %d qui n'existe pas\n" % (invoice.id, invoice.batch, invoice.number, invoice.patient_id)) |
|
82 |
else: |
|
83 |
logger.write("La facture %d n'a pas de patient\n" % invoice.id) |
|
84 |
if len(invoice.list_dates.split('$')) > invoice.acts.all().count(): |
|
85 |
logger.write("Cette facture présente des actes manquants\n") |
|
86 |
logger.write("Traitement terminé\n\n") |
|
87 |
|
|
88 |
for service_name in SERVICES: |
|
89 |
service_str = "Service : %s" % service_name |
|
90 |
logger.write('='+'='.join(['' for c in service_str])+"\n") |
|
91 |
logger.write(service_str+"\n") |
|
92 |
logger.write('='+'='.join(['' for c in service_str])+"\n\n") |
|
93 |
|
|
94 |
acts = Act.objects.filter(date__gte=ANALYSE_SDT.date(), patient__service__name=service_name) |
|
95 |
|
|
96 |
logger.write("Vérifications Event canceled avec des actes already_billed\n") |
|
97 |
logger.write("---------------------------------------------------------\n\n") |
|
98 |
r_list = [] |
|
99 |
for id in set([a.parent_event.id for a in Act.objects.filter(parent_event__canceled = True, already_billed = True)]): |
|
100 |
e = EventWithAct.objects.get(id=id) |
|
101 |
if e.is_recurring(): |
|
102 |
logger.write("Recurrent : %d\n" % e.id) |
|
103 |
r_list.append(id) |
|
104 |
elif e.exception_to: |
|
105 |
logger.write("Exception %d au recurrent : %d\n" % (e.id, e.exception_to.id)) |
|
106 |
if not e.exception_to.canceled: |
|
107 |
logger.write("\tLe récurrent n'est pas canceled, we can fix easily setting this exception not canceled\n") |
|
108 |
else: |
|
109 |
logger.write("Simple : %d, we can fix easily setting this exception not canceled\n" % e.id) |
|
110 |
|
|
111 |
logger.write("\n") |
|
112 |
|
|
113 |
logger.write("Vérifications des pointages et vérouillages des actes\n") |
|
114 |
logger.write("-----------------------------------------------------\n\n") |
|
115 |
logger.write("*** Les 7 valeurs suivantes doivent rester nulles\n") |
|
116 |
analyse = [[], [], [], [], [], [], [], []] |
|
117 |
for a in acts: |
|
118 |
if not a.last_validation_state: |
|
119 |
analyse[0].append(a) |
|
120 |
need_mail = True |
|
121 |
if a.is_billed and (a.is_new() or a.is_absent()): |
|
122 |
analyse[1].append(a) |
|
123 |
need_mail = True |
|
124 |
if a.validation_locked and a.is_new(): |
|
125 |
analyse[2].append(a) |
|
126 |
need_mail = True |
|
127 |
if a.is_billed and not a.validation_locked: |
|
128 |
analyse[3].append(a) |
|
129 |
need_mail = True |
|
130 |
if a.is_state('ACT_LOST'): |
|
131 |
analyse[4].append(a) |
|
132 |
need_mail = True |
|
133 |
if a.is_state('VALIDE') and not a.valide: |
|
134 |
analyse[5].append(a) |
|
135 |
need_mail = True |
|
136 |
if not a.parent_event: |
|
137 |
analyse[6].append(a) |
|
138 |
need_mail = True |
|
139 |
if a.invoice_set.all() and not a.already_billed: |
|
140 |
analyse[7].append(a) |
|
141 |
need_mail = True |
|
142 |
|
|
143 |
logger.write("$$$ Actes sans valeur 'last validation state' : %d\n" % len(analyse[0])) |
|
144 |
for a in analyse[0]: |
|
145 |
logger.write("\tActe %d : %s\n" % (a.id, a)) |
|
146 |
logger.write("$$$ Actes facturés mais non pointés en présent : %d\n" % len(analyse[1])) |
|
147 |
for a in analyse[1]: |
|
148 |
logger.write("\tActe %d : %s\n" % (a.id, a)) |
|
149 |
logger.write("$$$ Actes vérouillés mais non pointés : %d\n" % len(analyse[2])) |
|
150 |
for a in analyse[2]: |
|
151 |
logger.write("\tActe %d : %s\n" % (a.id, a)) |
|
152 |
logger.write("$$$ Actes facturés mais non vérouillés : %d\n" % len(analyse[3])) |
|
153 |
for a in analyse[3]: |
|
154 |
logger.write("\tActe %d : %s\n" % (a.id, a)) |
|
155 |
logger.write("$$$ Actes pointés perdus (déprécié) : %d\n" % len(analyse[4])) |
|
156 |
for a in analyse[4]: |
|
157 |
logger.write("\tActe %d : %s\n" % (a.id, a)) |
|
158 |
logger.write("$$$ Actes validés mais avec valeur de contrôle non initialisée : %d\n" % len(analyse[5])) |
|
159 |
for a in analyse[5]: |
|
160 |
logger.write("\tActe %d : %s\n" % (a.id, a)) |
|
161 |
logger.write("$$$ Actes sans événement parent : %d\n" % len(analyse[6])) |
|
162 |
for a in analyse[6]: |
|
163 |
logger.write("\tActe %d : %s\n" % (a.id, a)) |
|
164 |
logger.write("$$$ Actes deja facturés mais already_billed est False : %d\n" % len(analyse[6])) |
|
165 |
for a in analyse[7]: |
|
166 |
logger.write("\tActe %d : %s\n" % (a.id, a)) |
|
167 |
logger.write("\n") |
|
168 |
|
|
169 |
|
|
170 |
logger.write("Vérifications des factures\n") |
|
171 |
logger.write("--------------------------\n\n") |
|
172 |
|
|
173 |
for invoicing, invoice_list in invoices[service_name].items(): |
|
174 |
logger.write("Facturation %d\n" % invoicing.seq_id) |
|
175 |
for invoice in invoice_list: |
|
176 |
if len(invoice.list_dates.split('$')) > invoice.acts.all().count(): |
|
177 |
need_mail = True |
|
178 |
logger.write("$$$ Facture %d (%d, %d) patient %s avec des actes manquants\n" % (invoice.id, invoice.batch or 0, invoice.number or 0, PatientRecord.objects.get(id=invoice.patient_id))) |
|
179 |
acts_dates = [datetime.strptime(d, '%d/%m/%Y').date() for d in invoice.list_dates.split('$')] |
|
180 |
if service_name=='CMPP' and len(acts_dates) != len(set(acts_dates)): |
|
181 |
print "$$$ Il y a plus d'un acte facturé le meme jour!" |
|
182 |
logger.write("$$$ Nombre d'actes manquants %d\n" % (len(acts_dates)-invoice.acts.all().count())) |
|
183 |
acts_missing_dates = set(acts_dates) - set([act.date for act in invoice.acts.all()]) |
|
184 |
for date in acts_missing_dates: |
|
185 |
logger.write("$$$ Il manque un acte le %s\n" % date) |
|
186 |
else: |
|
187 |
#Chech that dates of acts and dates in list_dates match |
|
188 |
pass |
|
189 |
|
|
190 |
for act in invoice.acts.all(): |
|
191 |
if service_name=='CMPP': |
|
192 |
if not act.is_billed and not act.is_lost: |
|
193 |
logger.write("Facture %d (%d, %d) patient %s avec acte id %d %s à ce jour en refacturation (normal, autres factures %s)\n" % (invoice.id, invoice.batch or 0, invoice.number or 0, PatientRecord.objects.get(id=invoice.patient_id), act.id, act, str([str(i.number) for i in act.invoice_set.all()]))) |
|
194 |
else: |
|
195 |
if not act.is_billed: |
|
196 |
need_mail = True |
|
197 |
logger.write("$$$ Facture %d (%d, %d) patient %s avec acte id %d %s non is_billed (anormal)\n" % (invoice.id, invoice.batch or 0, invoice.number or 0, PatientRecord.objects.get(id=invoice.patient_id), act.id, act)) |
|
198 |
|
|
199 |
logger.write("\n") |
|
200 |
|
|
201 |
logger.write("Vérifications des actes facturés\n") |
|
202 |
logger.write("--------------------------------\n\n") |
|
203 |
|
|
204 |
|
|
205 |
acts_billed = Act.objects.filter(date__gte=ANALYSE_SDT.date(), patient__service__name=service_name, is_billed=True) |
|
206 |
for act in acts_billed: |
|
207 |
if not act.invoice_set.all(): |
|
208 |
logger.write("$$$ Acte facturé sans facture id %d : %s\n" % (act.id, act)) |
|
209 |
need_mail = True |
|
210 |
|
|
211 |
dic = {} |
|
212 |
if service_name=='CMPP': |
|
213 |
for act in acts_billed: |
|
214 |
dic.setdefault(act.patient, []).append(act) |
|
215 |
for patient, acts in dic.items(): |
|
216 |
dates = [] |
|
217 |
bad_dates = [] |
|
218 |
for act in acts: |
|
219 |
if act.date in dates and not act.date in bad_dates: |
|
220 |
bad_dates.append(act.date) |
|
221 |
else: |
|
222 |
dates.append(act.date) |
|
223 |
for date in bad_dates: |
|
224 |
need_mail = True |
|
225 |
logger.write("$$$ %s a des actes facturés le même jour\n" % patient) |
|
226 |
for act in Act.objects.filter(date=date, patient=patient, is_billed=True): |
|
227 |
if act.invoice_set.all(): |
|
228 |
logger.write("Acte %d (factures %s) : %s\n" % (act.id, str([i.number for i in act.invoice_set.all()]), act)) |
|
229 |
else: |
|
230 |
logger.write("Acte %d (sans factures) : %s\n" % (act.id, act)) |
|
231 |
|
|
232 |
logger.write("\n") |
|
233 |
|
|
234 |
logger.write("Vérifications des jours de validation\n") |
|
235 |
logger.write("-------------------------------------\n\n") |
|
236 |
|
|
237 |
|
|
238 |
for date in [(ANALYSE_SDT + timedelta(days=i)).date() for i in range(0, (datetime.today().date()-ANALYSE_SDT.date()).days)]: |
|
239 |
# Actes non pointés |
|
240 |
acts = Act.objects.filter(last_validation_state__state_name='NON_VALIDE', date=date, patient__service__name=service_name) |
|
241 |
if acts: |
|
242 |
for act in acts: |
|
243 |
if act.validation_locked: |
|
244 |
need_mail = True |
|
245 |
logger.write("$$$ Act %d : %s non validé mais verouillé\n" % (act.id, act)) |
|
246 |
vms = ValidationMessage.objects.filter(validation_date=date, service__name=service_name) |
|
247 |
if not vms: |
|
248 |
logger.write("Acte non pointés et aucune opération de validation pour le %s\n" % date) |
|
249 |
else: |
|
250 |
last = ValidationMessage.objects.filter(validation_date=date, service__name=service_name).latest('when') |
|
251 |
if last.what != 'Validation automatique': |
|
252 |
logger.write("Jour %s dévérouillé le %s par %s\n" % (date, last.when, last.who)) |
|
253 |
else: |
|
254 |
logger.write("$$$ Jour %s vérouillé le %s par %s\n" % (date, last.when, last.who)) |
|
255 |
need_mail = True |
|
256 |
for act in acts: |
|
257 |
logger.write("$$$ Act %d : %s\n" % (act.id, act)) |
|
258 |
logger.write("$$$ Evenement parent %d créé par %s le %s\n" % (act.parent_event.id, act.parent_event.creator, act.parent_event.create_date)) |
|
259 |
if act.parent_event.create_date < last.when: |
|
260 |
logger.write("-------> Création de l'événement antérieur à la validation\n") |
|
261 |
logger.write("\n") |
|
262 |
|
|
263 |
logger.write("\n") |
|
264 |
|
|
265 |
|
|
266 |
logger.write("Vérifications des actes en double\n") |
|
267 |
logger.write("---------------------------------\n\n") |
|
268 |
|
|
269 |
for date in [(ANALYSE_SDT + timedelta(days=i)).date() for i in range(0, (datetime.today().date()-ANALYSE_SDT.date()).days)]: |
|
270 |
acts = Act.objects.filter(date=date, patient__service__name=service_name).order_by('time') |
|
271 |
if QUIET: |
|
272 |
# En double qu'on ne peut traiter. |
|
273 |
# On en exclue qu'un seul pour voir si un troisième apparait... |
|
274 |
acts = acts.exclude(id__in=(575896, 580500)) |
|
275 |
acts_p = {} |
|
276 |
for a in acts: |
|
277 |
acts_p.setdefault(a.patient, []).append(a) |
|
278 |
for p, acts in acts_p.items(): |
|
279 |
if len(acts) > 1: |
|
280 |
acts_t = {} |
|
281 |
for a in acts: |
|
282 |
acts_t.setdefault((a.time, a.act_type), []).append(a) |
|
283 |
for k, aa in acts_t.items(): |
|
284 |
if len(aa) > 1: |
|
285 |
t, ty = k |
|
286 |
logger.write("$$$ Actes en double pour %s le %s à %s\n" % (p, date, t)) |
|
287 |
acts_pe = {} |
|
288 |
for a in aa: |
|
289 |
acts_pe.setdefault(a.parent_event, []).append(a) |
|
290 |
if len(aa) == len(acts_pe.keys()): |
|
291 |
logger.write("$$$ Les événements parents sont distincts\n") |
|
292 |
else: |
|
293 |
logger.write("$$$ Il y a des événements parents communs\n") |
|
294 |
for a in aa: |
|
295 |
logger.write("\t %d : %s\n" % (a.id, a)) |
|
296 |
logger.write("\t\t %s\n" % a.last_validation_state) |
|
297 |
if a.is_billed: |
|
298 |
logger.write("\t\t Acte facturé\n") |
|
299 |
if a.invoice_set.all(): |
|
300 |
logger.write("\t\tHistorique de facturation : %s\n" % str([i.number for i in a.invoice_set.all()])) |
|
301 |
logger.write("\t\t evenement %d : %s, %s\n" % (a.parent_event.id, a.parent_event.creator, a.parent_event.create_date)) |
|
302 |
if a.parent_event.description: |
|
303 |
logger.write("\t\tCommentaire sur event %s\n" % a.parent_event.description.encode('utf-8')) |
|
304 |
if a.parent_event.canceled: |
|
305 |
logger.write("\t\tC'est un événement annulé\n") |
|
306 |
if a.parent_event.is_recurring(): |
|
307 |
logger.write("\t\tC'est un événement récurrent\n") |
|
308 |
if a.parent_event.exception_to: |
|
309 |
logger.write("\t\tC'est une exception à %d\n" % a.parent_event.exception_to.id) |
|
310 |
|
|
311 |
logger.write("\n\n") |
|
312 |
|
|
313 |
|
|
314 |
if need_mail or ALWAYS_SEND_MAIL: |
|
315 |
# send_mail |
|
316 |
pass |
|
317 |
|
|
318 |
logger.write("Fin du script de contôle de Calebasse : %s" % datetime.utcnow()) |
|
319 |
old_fn = logger.name |
|
320 |
logger.close() |
|
321 |
os.rename(old_fn, logger_fn) |
Formats disponibles : Unified diff
scripts: script to control database functional integrity.