1
|
|
2
|
|
3
|
|
4
|
|
5
|
|
6
|
|
7
|
|
8
|
|
9
|
|
10
|
"""
|
11
|
librairie pour le parsing des fichiers de données Sconet/STS-Web
|
12
|
|
13
|
- parse_sco_eleves : parsing des fichiers XML issus de Sconet
|
14
|
- parse_sts_profs : parsing des fichiers XML issus de STS-Web
|
15
|
|
16
|
"""
|
17
|
|
18
|
from scribe.eoletools import replace_cars, formate_civilite, \
|
19
|
not_empty, is_empty, formate_date, replace_more_cars
|
20
|
from scribe.importation import log
|
21
|
from scribe.importation.config import DEBUG_NUM
|
22
|
from scribe.storage import Eleve, \
|
23
|
Responsable, Adresse, JointureResponsableEleve, \
|
24
|
Classe, Niveau, Enseignant, Administratif, \
|
25
|
EnsClasse, JointureClasseEnseignant, \
|
26
|
Matiere, JointureMatiereEnseignant, \
|
27
|
Groupe, JointureGroupeUser, Service
|
28
|
from scribe.parsing.tools import parse_xml
|
29
|
from scribe.parsing.nomenclature import REGIMES
|
30
|
|
31
|
|
32
|
|
33
|
|
34
|
|
35
|
|
36
|
|
37
|
|
38
|
ERRMEF = "Le niveau portant le code MEF %s n'est pas défini dans la nomenclature"
|
39
|
|
40
|
def parse_sco_divisions(storage, structure_file, nomenclature_file):
|
41
|
"""
|
42
|
Liaison Division (classe) - MEF (niveau)
|
43
|
d'après : Structures.xml et Nomenclatures.xml
|
44
|
"""
|
45
|
num = 0
|
46
|
log.infolog("Lecture des classes et des niveaux...", title=True)
|
47
|
niveaux = {}
|
48
|
context, _ = parse_xml(nomenclature_file, 'MEF')
|
49
|
for _, tnom in context:
|
50
|
mef = tnom.attrib['CODE_MEF']
|
51
|
niveaux[mef] = {}
|
52
|
clean_formation = replace_cars(tnom.find('FORMATION').text)
|
53
|
niveaux[mef]['label'] = unicode(clean_formation)
|
54
|
if tnom.find('MEF_RATTACHEMENT') is not None:
|
55
|
niveaux[mef]['real'] = tnom.find('MEF_RATTACHEMENT').text
|
56
|
divisions = {}
|
57
|
context, _ = parse_xml(structure_file, 'DIVISION')
|
58
|
for _, tstr in context:
|
59
|
div = tstr.attrib['CODE_STRUCTURE']
|
60
|
if div.lower() == 'inactifs':
|
61
|
|
62
|
continue
|
63
|
tmef = tstr.findall('MEFS_APPARTENANCE/MEF_APPARTENANCE/CODE_MEF')
|
64
|
if len(tmef) == 1:
|
65
|
try:
|
66
|
divisions[div] = niveaux[tmef[0].text]['label']
|
67
|
except KeyError:
|
68
|
msg = ERRMEF % tmef[0].text
|
69
|
log.errorlog(msg)
|
70
|
raise Exception(msg)
|
71
|
else:
|
72
|
|
73
|
errmef = set()
|
74
|
for txtmef in tmef:
|
75
|
try:
|
76
|
mef = niveaux[txtmef.text]['real']
|
77
|
except KeyError:
|
78
|
log.errorlog(ERRMEF % tmef[0].text)
|
79
|
errmef.add(tmef[0].text)
|
80
|
continue
|
81
|
try:
|
82
|
divisions[div] = niveaux[mef]['label']
|
83
|
break
|
84
|
except KeyError:
|
85
|
log.errorlog(ERRMEF % mef)
|
86
|
errmef.add(mef)
|
87
|
continue
|
88
|
if not divisions.has_key(div):
|
89
|
if errmef:
|
90
|
raise Exception(ERRMEF % list(errmef))
|
91
|
else:
|
92
|
raise Exception("La division %s n'est associée à aucune MEF" % div)
|
93
|
for classe, niveau in divisions.items():
|
94
|
my_niveau = storage.findOrCreate(Niveau, nom=niveau)
|
95
|
clean_classe = unicode(replace_cars(classe))
|
96
|
storage.findOrCreate(Classe, nom=clean_classe,
|
97
|
niveau=my_niveau)
|
98
|
num += 1
|
99
|
log.infolog("TOTAL : %d classes" % num)
|
100
|
|
101
|
def parse_sco_groupes(store, structure_file):
|
102
|
"""
|
103
|
Recherche des Groupes (Options) et de leurs libellés
|
104
|
"""
|
105
|
num = 0
|
106
|
log.infolog("Lecture des groupes (options)...", title=True)
|
107
|
context, _ = parse_xml(structure_file, 'GROUPE')
|
108
|
for _, tstr in context:
|
109
|
grp = unicode(replace_cars(tstr.attrib['CODE_STRUCTURE']))
|
110
|
desc = unicode(replace_cars(tstr.find('LIBELLE_LONG').text))
|
111
|
Groupe(store=store, nom=grp, description=desc)
|
112
|
num += 1
|
113
|
log.infolog("TOTAL : %d groupes" % num)
|
114
|
|
115
|
def parse_sco_eleves(store, eleve_file):
|
116
|
"""
|
117
|
parsing des élèves depuis Sconet
|
118
|
"""
|
119
|
num = 0
|
120
|
log.infolog("Lecture des élèves...", title=True)
|
121
|
context, eleve_file = parse_xml(eleve_file, 'ELEVE')
|
122
|
|
123
|
mapping = {'nom':'NOM_DE_FAMILLE',
|
124
|
'deprecated_nom':'NOM',
|
125
|
'prenom':'PRENOM',
|
126
|
'date':'DATE_NAISS',
|
127
|
'civilite':'CODE_SEXE',
|
128
|
'numero':'ELENOET',
|
129
|
'ine':'ID_NATIONAL',
|
130
|
'prenom2':'PRENOM2',
|
131
|
'regime':'CODE_REGIME',
|
132
|
}
|
133
|
|
134
|
|
135
|
for _, televe in context:
|
136
|
eleid = televe.attrib['ELEVE_ID']
|
137
|
sortie = televe.find('DATE_SORTIE')
|
138
|
if sortie != None and sortie.text != '':
|
139
|
continue
|
140
|
|
141
|
eleve = {'int_id':unicode(eleid)}
|
142
|
for cle, balise in mapping.items():
|
143
|
try:
|
144
|
clean_text = replace_more_cars(televe.find(balise).text)
|
145
|
eleve[cle] = unicode(clean_text)
|
146
|
except:
|
147
|
pass
|
148
|
if eleve.get('nom') is None and 'deprecated_nom' in eleve:
|
149
|
|
150
|
eleve['nom'] = eleve.get('deprecated_nom')
|
151
|
eleve.pop('deprecated_nom')
|
152
|
if not eleve.get('civilite', ''):
|
153
|
log.infolog("Attribution arbitraire d'une civilité à l'élève : %s %s" % (
|
154
|
str(eleve['prenom']), str(eleve['nom'])))
|
155
|
eleve['civilite'] = u'1'
|
156
|
if eleve.get('regime', ''):
|
157
|
|
158
|
if eleve['regime'].rstrip() in REGIMES:
|
159
|
eleve['regime'] = unicode(REGIMES[eleve['regime'].rstrip()])
|
160
|
try:
|
161
|
Eleve(store=store, **eleve)
|
162
|
num += 1
|
163
|
if num % DEBUG_NUM == 0:
|
164
|
log.debuglog("%d élèves lus..." % num)
|
165
|
except TypeError, msg:
|
166
|
log.infolog("Erreur sur l'élève %s : %s" % (eleid, msg))
|
167
|
continue
|
168
|
log.infolog("TOTAL : %d élèves" % num)
|
169
|
|
170
|
|
171
|
context, _ = parse_xml(eleve_file, 'STRUCTURES_ELEVE', check=False)
|
172
|
num = 0
|
173
|
for _, tstruct in context:
|
174
|
eleid = tstruct.attrib['ELEVE_ID']
|
175
|
eleve = store.findFirst(Eleve, Eleve.int_id==unicode(eleid))
|
176
|
if eleve is None:
|
177
|
continue
|
178
|
for tstr in tstruct.getiterator('STRUCTURE'):
|
179
|
type_struct = str(tstr.find('TYPE_STRUCTURE').text)
|
180
|
if type_struct == 'D':
|
181
|
|
182
|
nom_classe = replace_cars(tstr.find('CODE_STRUCTURE').text)
|
183
|
if nom_classe.lower() == 'inactifs':
|
184
|
|
185
|
continue
|
186
|
classe = store.findFirst(Classe, Classe.nom==unicode(nom_classe))
|
187
|
eleve.classe = classe
|
188
|
eleve.niveau = classe.niveau
|
189
|
num += 1
|
190
|
elif type_struct == 'G':
|
191
|
|
192
|
nom_groupe = replace_cars(tstr.find('CODE_STRUCTURE').text)
|
193
|
groupe = store.findFirst(Groupe, Groupe.nom==unicode(nom_groupe))
|
194
|
if groupe is None:
|
195
|
log.infolog("Groupe inconnu : %s" % nom_groupe)
|
196
|
continue
|
197
|
JointureGroupeUser(store=store, groupe=groupe, user=eleve)
|
198
|
num += 1
|
199
|
else:
|
200
|
log.infolog("Type de structure inconnu :", type_struct)
|
201
|
if num % DEBUG_NUM == 0:
|
202
|
log.debuglog("%d affectations lues..." % num)
|
203
|
log.infolog("TOTAL : %d affectations d'élèves" % num)
|
204
|
|
205
|
|
206
|
def parse_sco_responsables(store, responsable_file):
|
207
|
"""
|
208
|
parsing des responsables depuis Sconet
|
209
|
"""
|
210
|
num = 0
|
211
|
log.infolog("Lecture des responsables...", title=True)
|
212
|
context, responsable_file = parse_xml(responsable_file, 'PERSONNE')
|
213
|
|
214
|
for _, tresp in context:
|
215
|
rid = tresp.attrib['PERSONNE_ID']
|
216
|
responsable = {'int_id':unicode(rid)}
|
217
|
mapping = {'nom':'NOM_DE_FAMILLE',
|
218
|
'deprecated_nom':'NOM',
|
219
|
'prenom':'PRENOM',
|
220
|
'mail':'MEL',
|
221
|
'telephone':'TEL_PERSONNEL',
|
222
|
'civilite':'LC_CIVILITE',
|
223
|
'id_adresse':'ADRESSE_ID',
|
224
|
'tel_portable':'TEL_PORTABLE',
|
225
|
'tel_pro':'TEL_PROFESSIONNEL',
|
226
|
}
|
227
|
for cle, balise in mapping.items():
|
228
|
try:
|
229
|
clean_text = replace_more_cars(tresp.find(balise).text)
|
230
|
responsable[cle] = unicode(clean_text)
|
231
|
except:
|
232
|
pass
|
233
|
if responsable.get('nom') is None and 'deprecated_nom' in responsable:
|
234
|
|
235
|
responsable['nom'] = responsable.get('deprecated_nom')
|
236
|
responsable.pop('deprecated_nom')
|
237
|
if responsable.has_key('civilite'):
|
238
|
responsable['civilite'] = unicode(formate_civilite(str(responsable['civilite'])))
|
239
|
try:
|
240
|
Responsable(store=store, **responsable)
|
241
|
num += 1
|
242
|
if num % DEBUG_NUM == 0:
|
243
|
log.debuglog("%d responsables lus..." % num)
|
244
|
except TypeError, msg:
|
245
|
log.infolog("Erreur sur le responsable %s : %s" % (rid, msg))
|
246
|
log.infolog("TOTAL : %d responsables élèves" % num)
|
247
|
|
248
|
|
249
|
num = 0
|
250
|
context, _ = parse_xml(responsable_file, 'ADRESSE', check=False)
|
251
|
for _, taddr in context:
|
252
|
aid = taddr.attrib['ADRESSE_ID']
|
253
|
adresse = {'int_id':unicode(aid)}
|
254
|
mapping = {'code_postal':'CODE_POSTAL',
|
255
|
'ville':'LIBELLE_POSTAL',
|
256
|
'pays':'LL_PAYS'}
|
257
|
for cle, balise in mapping.items():
|
258
|
try:
|
259
|
clean_text = replace_cars(taddr.find(balise).text)
|
260
|
adresse[cle] = unicode(clean_text)
|
261
|
except:
|
262
|
pass
|
263
|
adr = Adresse(store=store, **adresse)
|
264
|
txt_addr = []
|
265
|
for balise in ['LIGNE1_ADRESSE', 'LIGNE2_ADRESSE',
|
266
|
'LIGNE3_ADRESSE', 'LIGNE4_ADRESSE']:
|
267
|
try:
|
268
|
clean_text = replace_cars(taddr.find(balise).text)
|
269
|
if clean_text != '':
|
270
|
txt_addr.append(unicode(clean_text))
|
271
|
except:
|
272
|
pass
|
273
|
if txt_addr != []:
|
274
|
|
275
|
adr.adresse = unicode("\n".join(txt_addr))
|
276
|
resp = store.findFirst(Responsable,
|
277
|
Responsable.id_adresse==unicode(aid))
|
278
|
if resp is None:
|
279
|
|
280
|
continue
|
281
|
resp.adresse = adr
|
282
|
num += 1
|
283
|
if num % DEBUG_NUM == 0:
|
284
|
log.debuglog("%d adresse lues..." % num)
|
285
|
log.infolog("TOTAL : %d adresses de responsables" % num)
|
286
|
|
287
|
|
288
|
num = 0
|
289
|
context, _ = parse_xml(responsable_file, 'RESPONSABLE_ELEVE', check=False)
|
290
|
for _, tjoint in context:
|
291
|
resp_legal = tjoint.find('NIVEAU_RESPONSABILITE').text
|
292
|
if resp_legal == '0':
|
293
|
|
294
|
continue
|
295
|
eleve_id = tjoint.find('ELEVE_ID').text
|
296
|
responsable_id = tjoint.find('PERSONNE_ID').text
|
297
|
eleve = store.findFirst(Eleve,
|
298
|
Eleve.int_id==unicode(eleve_id))
|
299
|
if eleve is None:
|
300
|
continue
|
301
|
responsable = store.findFirst(Responsable,
|
302
|
Responsable.int_id==unicode(responsable_id))
|
303
|
if responsable is None:
|
304
|
log.infolog("responsable n°%s non trouvé" % responsable_id)
|
305
|
continue
|
306
|
jointure = dict(eleve=eleve, responsable=responsable)
|
307
|
mapping = {'resp_legal':'NIVEAU_RESPONSABILITE',
|
308
|
'resp_financier':'RESP_FINANCIER',
|
309
|
'code_parente':'CODE_PARENTE',}
|
310
|
for cle, balise in mapping.items():
|
311
|
try:
|
312
|
clean_text = replace_cars(tjoint.find(balise).text)
|
313
|
jointure[cle] = unicode(clean_text)
|
314
|
except:
|
315
|
pass
|
316
|
JointureResponsableEleve(store=store, **jointure)
|
317
|
num += 1
|
318
|
if num % DEBUG_NUM == 0:
|
319
|
log.debuglog("%d liens responsable/élève lus..." % num)
|
320
|
log.infolog("TOTAL : %d liens responsable/élève" % num)
|
321
|
|
322
|
|
323
|
|
324
|
|
325
|
|
326
|
|
327
|
|
328
|
def _parse_service(tree):
|
329
|
"""
|
330
|
lecture des services d'une classe ou d'une option
|
331
|
"""
|
332
|
tservs = tree.find('SERVICES')
|
333
|
if tservs is None:
|
334
|
return []
|
335
|
machin = []
|
336
|
for tserv in tservs.getiterator('SERVICE'):
|
337
|
type_cours = tserv.attrib['CODE_MOD_COURS']
|
338
|
|
339
|
matiere = tserv.attrib['CODE_MATIERE']
|
340
|
tens = tserv.find('ENSEIGNANTS')
|
341
|
for ten in tens.getiterator('ENSEIGNANT'):
|
342
|
|
343
|
|
344
|
machin.append([ten.attrib['ID'], matiere, type_cours])
|
345
|
return machin
|
346
|
|
347
|
def _parse_division_appartenance(tree):
|
348
|
"""
|
349
|
lecture des divisions affectées à une option
|
350
|
"""
|
351
|
tdivs = tree.find('DIVISIONS_APPARTENANCE')
|
352
|
if tdivs is None:
|
353
|
return []
|
354
|
div = []
|
355
|
for tdiv in tdivs.getiterator('DIVISION_APPARTENANCE'):
|
356
|
div.append(tdiv.attrib['CODE'])
|
357
|
return div
|
358
|
|
359
|
def parse_sts_profs(storage, sts_file):
|
360
|
"""
|
361
|
parsing des professeurs depuis sts
|
362
|
"""
|
363
|
num = 0
|
364
|
log.infolog("Lecture des personnels", title=True)
|
365
|
context, sts_file = parse_xml(sts_file, 'MATIERE')
|
366
|
code_matieres = {}
|
367
|
|
368
|
for _, tcmat in context:
|
369
|
code = tcmat.attrib['CODE']
|
370
|
mat = tcmat.find('CODE_GESTION').text
|
371
|
lib = tcmat.find('LIBELLE_EDITION').text
|
372
|
code_matieres[code] = (mat, lib)
|
373
|
|
374
|
|
375
|
classes = {}
|
376
|
matieres = {}
|
377
|
options = {}
|
378
|
|
379
|
|
380
|
context, _ = parse_xml(sts_file, 'DIVISION', check=False)
|
381
|
for _, tmat in context:
|
382
|
code_classe = tmat.attrib['CODE']
|
383
|
nom = unicode(replace_cars(code_classe))
|
384
|
my_classe = storage.findOrCreate(EnsClasse, nom=nom)
|
385
|
for prof, mat, _ in _parse_service(tmat):
|
386
|
classes.setdefault(prof, []).append(my_classe)
|
387
|
if code_matieres.has_key(mat):
|
388
|
matiere = code_matieres[mat]
|
389
|
nom = unicode(replace_cars(matiere[0]))
|
390
|
desc = unicode(replace_cars(matiere[1]))
|
391
|
my_mat = storage.findOrCreate(Matiere, nom=nom, description=desc)
|
392
|
matieres.setdefault(prof, []).append(my_mat)
|
393
|
else:
|
394
|
log.infolog("matière %s inconnue" % mat)
|
395
|
|
396
|
|
397
|
context, _ = parse_xml(sts_file, 'GROUPE', check=False)
|
398
|
for _, tgrp in context:
|
399
|
code_groupe = tgrp.attrib['CODE']
|
400
|
nom = unicode(replace_cars(code_groupe))
|
401
|
desc = unicode(replace_cars(tgrp.find('LIBELLE_LONG').text))
|
402
|
my_groupe = storage.findOrCreate(Groupe, nom=nom, description=desc)
|
403
|
my_classes = []
|
404
|
for div in _parse_division_appartenance(tgrp):
|
405
|
nom = unicode(replace_cars(div))
|
406
|
my_classes.append(storage.findOrCreate(EnsClasse, nom=nom))
|
407
|
for prof, mat, _ in _parse_service(tgrp):
|
408
|
options.setdefault(prof, []).append(my_groupe)
|
409
|
for my_classe in my_classes:
|
410
|
|
411
|
classes.setdefault(prof, []).append(my_classe)
|
412
|
if code_matieres.has_key(mat):
|
413
|
matiere = code_matieres[mat]
|
414
|
nom = unicode(replace_cars(matiere[0]))
|
415
|
desc = unicode(replace_cars(matiere[1]))
|
416
|
my_mat = storage.findOrCreate(Matiere, nom=nom, description=desc)
|
417
|
matieres.setdefault(prof, []).append(my_mat)
|
418
|
else:
|
419
|
log.infolog("matière %s inconnue" % mat)
|
420
|
|
421
|
mapping = {'NOM_USAGE':'nom',
|
422
|
'PRENOM':'prenom',
|
423
|
'DATE_NAISSANCE':'date',
|
424
|
'CIVILITE':'civilite',
|
425
|
'NOM_PATRONYMIQUE':'nom_patronymique',
|
426
|
}
|
427
|
context, _ = parse_xml(sts_file, 'INDIVIDU', check=False)
|
428
|
for _, ind in context:
|
429
|
|
430
|
profid = ind.attrib['ID']
|
431
|
professeur = {'int_id':unicode(profid)}
|
432
|
fonction = ind.find('FONCTION')
|
433
|
if fonction is None:
|
434
|
log.infolog("(pas de fonction pour le personnel %s)" % profid)
|
435
|
continue
|
436
|
for balise, cle in mapping.items():
|
437
|
value = ind.find(balise)
|
438
|
if value is None:
|
439
|
professeur[cle] = u''
|
440
|
else:
|
441
|
clean_text = replace_more_cars(value.text)
|
442
|
professeur[cle] = unicode(clean_text)
|
443
|
if not_empty(professeur, 'date'):
|
444
|
my_date = formate_date(str(professeur['date']).replace('-', ''))
|
445
|
professeur['date'] = unicode(my_date)
|
446
|
else:
|
447
|
|
448
|
professeur['date'] = u'01/01/0001'
|
449
|
if is_empty(professeur, 'civilite'):
|
450
|
|
451
|
log.infolog("Attribution arbitraire d'une civilité au personnel : %s %s" % (
|
452
|
str(professeur['prenom']), str(professeur['nom'])))
|
453
|
professeur['civilite'] = u'1'
|
454
|
if fonction.text == 'ENS':
|
455
|
|
456
|
|
457
|
principal = []
|
458
|
tprincs = ind.findall('PROFS_PRINC/PROF_PRINC')
|
459
|
for tprinc in tprincs:
|
460
|
nom = unicode(replace_cars(tprinc.find('CODE_STRUCTURE').text))
|
461
|
my_classe = storage.findOrCreate(EnsClasse, nom=nom)
|
462
|
principal.append(my_classe)
|
463
|
try:
|
464
|
prof = Enseignant(store=storage, **professeur)
|
465
|
num += 1
|
466
|
except Exception, msg:
|
467
|
log.infolog("Erreur sur l'enseignant %s : %s" % (profid, msg))
|
468
|
|
469
|
if classes.has_key(profid):
|
470
|
for classe in classes[profid]:
|
471
|
if classe in principal:
|
472
|
is_principal = True
|
473
|
else:
|
474
|
is_principal = False
|
475
|
JointureClasseEnseignant(store=storage, classe=classe,
|
476
|
enseignant=prof, profprincipal=is_principal)
|
477
|
|
478
|
|
479
|
if options.has_key(profid):
|
480
|
for groupe in options[profid]:
|
481
|
JointureGroupeUser(store=storage, groupe=groupe,
|
482
|
user=prof)
|
483
|
|
484
|
|
485
|
if matieres.has_key(profid):
|
486
|
for mat in matieres[profid]:
|
487
|
storage.findOrCreate(JointureMatiereEnseignant,
|
488
|
matiere=mat, enseignant=prof)
|
489
|
else:
|
490
|
|
491
|
try:
|
492
|
admin = Administratif(store=storage, **professeur)
|
493
|
nom = unicode(replace_cars(fonction.text))
|
494
|
groupe = storage.findOrCreate(Service, nom=nom)
|
495
|
admin.groupe = groupe
|
496
|
num += 1
|
497
|
except Exception, msg:
|
498
|
log.infolog("Erreur sur le personnel %s : %s" % (profid, msg))
|
499
|
if num % DEBUG_NUM == 0:
|
500
|
log.debuglog("%d personnels lus..." % num)
|
501
|
log.infolog("TOTAL : %d personnels" % num)
|
502
|
|