]> SALOME platform Git repositories - tools/eficas.git/blob - Ihm/I_JDC.py
Salome HOME
734ffde249dc04fd18f034f903e92c193043f5bf
[tools/eficas.git] / Ihm / I_JDC.py
1 # -*- coding: utf-8 -*-
2 #            CONFIGURATION MANAGEMENT OF EDF VERSION
3 # ======================================================================
4 # COPYRIGHT (C) 1991 - 2002  EDF R&D                  WWW.CODE-ASTER.ORG
5 # THIS PROGRAM IS FREE SOFTWARE; YOU CAN REDISTRIBUTE IT AND/OR MODIFY
6 # IT UNDER THE TERMS OF THE GNU GENERAL PUBLIC LICENSE AS PUBLISHED BY
7 # THE FREE SOFTWARE FOUNDATION; EITHER VERSION 2 OF THE LICENSE, OR
8 # (AT YOUR OPTION) ANY LATER VERSION.
9 #
10 # THIS PROGRAM IS DISTRIBUTED IN THE HOPE THAT IT WILL BE USEFUL, BUT
11 # WITHOUT ANY WARRANTY; WITHOUT EVEN THE IMPLIED WARRANTY OF
12 # MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. SEE THE GNU
13 # GENERAL PUBLIC LICENSE FOR MORE DETAILS.
14 #
15 # YOU SHOULD HAVE RECEIVED A COPY OF THE GNU GENERAL PUBLIC LICENSE
16 # ALONG WITH THIS PROGRAM; IF NOT, WRITE TO EDF R&D CODE_ASTER,
17 #    1 AVENUE DU GENERAL DE GAULLE, 92141 CLAMART CEDEX, FRANCE.
18 #
19 #
20 # ======================================================================
21 """
22 """
23 # Modules Python
24 import types,traceback
25 import string,linecache
26
27 # Modules Eficas
28 import I_OBJECT
29 from Noyau.N_ASSD import ASSD
30 from Noyau.N_ETAPE import ETAPE
31 from Noyau.N_Exception import AsException
32 from Extensions import commentaire,parametre,parametre_eval
33 import CONNECTOR
34
35 class JDC(I_OBJECT.OBJECT):
36    """
37    """
38    def __init__(self):
39       self.editmode=0
40       self.etapes_niveaux=[]
41       self.niveau=self
42       self.params=[]
43       self.fonctions=[]
44       self._etape_context=None
45       self.recorded_units={}
46       self.old_recorded_units={}
47
48    def get_index(self,objet):
49       """
50         Retourne la position d'objet dans la liste self
51       """
52       return self.etapes.index(objet)
53
54    def get_sd_avant_du_bon_type(self,etape,types_permis):
55       """
56           Retourne la liste des concepts avant etape d'un type acceptable
57       """
58       d=self.get_contexte_avant(etape)
59       l=[]
60       for k,v in d.items():
61         if type(v) != types.InstanceType : continue
62         # On considère que seul assd indique un type quelconque pas CO
63         elif self.assd in types_permis :
64            l.append(k)
65         elif self.est_permis(v,types_permis):
66            l.append(k)
67       l.sort()
68       return l
69
70    def est_permis(self,v,types_permis):
71       for type_ok in types_permis:
72           if type_ok in ('R','I','C','TXM') and v in self.params : 
73              return 1
74           elif type_ok == 'R' and v.__class__.__name__ == 'reel' : 
75              return 1
76           elif type_ok == 'I' and v.__class__.__name__ == 'entier' : 
77              return 1
78           elif type_ok == 'C' and v.__class__.__name__ == 'complexe' : 
79              return 1
80           elif type_ok == 'TXM' and v.__class__.__name__ == 'chaine' : 
81              return 1
82           elif type(type_ok) != types.ClassType : 
83              continue
84           elif v.__class__ == type_ok or issubclass(v.__class__,type_ok):
85              return 1
86       return 0
87
88    def addentite(self,name,pos):
89       """
90           Ajoute une entite :
91           Si name est le nom d une commande ou un commentaire ajoute 
92           une etape au JDC
93           Sinon remonte une erreur
94       """
95       self.init_modif()
96       self.editmode=1
97       if name == "COMMENTAIRE" :
98         # ajout d'un commentaire
99         self.set_current_step()
100         ind = 1
101         for child in self.etapes :
102           if isinstance(child,commentaire.COMMENTAIRE):
103             ind = ind+1
104         objet = commentaire.COMMENTAIRE('',parent=self)
105         objet.nom = "_comm_"+`ind`
106         if pos == None : pos = 0
107         self.etapes.insert(pos,objet)
108         self.editmode=0
109         self.active_etapes()
110         CONNECTOR.Emit(self,"add",objet)
111         self.fin_modif()
112         return objet
113       elif name == "PARAMETRE":
114         # ajout d'un parametre
115         self.set_current_step()
116         nom_param = '_param_'+str(len(self.params)+1)
117         objet = parametre.PARAMETRE(nom=nom_param)
118         if pos == None : pos = 0
119         self.etapes.insert(pos,objet)
120         self.editmode=0
121         self.reset_context()
122         self.active_etapes()
123         CONNECTOR.Emit(self,"add",objet)
124         self.fin_modif()
125         return objet
126       elif name == "PARAMETRE_EVAL":
127         # ajout d'un parametre EVAL
128         self.set_current_step()
129         nom_param = '_param_'+str(len(self.params)+1)
130         objet = parametre_eval.PARAMETRE_EVAL(nom=nom_param)
131         if pos == None : pos = 0
132         self.etapes.insert(pos,objet)
133         self.editmode=0
134         self.reset_context()
135         self.active_etapes()
136         CONNECTOR.Emit(self,"add",objet)
137         self.fin_modif()
138         return objet
139       elif type(name)==types.InstanceType:
140         # on est dans le cas où on veut ajouter une commande déjà 
141         # existante (par copie donc)
142         # on est donc nécessairement en mode editeur ...
143         objet = name
144         # Il ne faut pas oublier de reaffecter le parent d'obj (si copie)
145         #if hasattr(objet,'sd'):print "addentite",objet.sd
146         objet.reparent(self)
147         #if hasattr(objet,'sd'):print "addentite",objet.sd
148         self.set_current_step()
149         if isinstance(objet,ETAPE):
150           if objet.nom_niveau_definition == 'JDC':
151             # l'objet dépend directement du JDC
152             objet.niveau = self
153           else:
154             # l'étape dépend d'un niveau et non directement du JDC :
155             # il faut l'enregistrer dans le niveau de parent
156             objet.parent.dict_niveaux[objet.nom_niveau_definition].register(objet)
157             objet.niveau = objet.parent.dict_niveaux[objet.nom_niveau_definition]
158         self.etapes.insert(pos,objet)
159         # il faut vérifier que les concepts utilisés par objet existent bien
160         # à ce niveau d'arborescence
161         #if hasattr(objet,'sd'):print "addentite",objet.sd
162         objet.verif_existence_sd()
163         self.active_etapes()
164         self.editmode=0
165         self.reset_context()
166         #print "addentite",self.etapes
167         CONNECTOR.Emit(self,"add",objet)
168         self.fin_modif()
169         return objet
170       else :
171         # On veut ajouter une nouvelle commande
172         try:
173           self.set_current_step()
174           cmd=self.get_cmd(name)
175           # L'appel a make_objet n'a pas pour effet d'enregistrer l'étape
176           # auprès du step courant car editmode vaut 1
177           # Par contre elle a le bon parent grace a set_current_step
178           e=cmd.make_objet()
179           if pos == None : pos = 0
180           self.etapes.insert(pos,e)
181           self.reset_current_step()
182           self.editmode=0
183           self.reset_context()
184           self.active_etapes()
185           #print "addentite",self.etapes
186           CONNECTOR.Emit(self,"add",e)
187           self.fin_modif()
188           return e
189         except AsException,e:
190           self.reset_current_step()
191           self.editmode=0
192           raise AsException("Impossible d ajouter la commande "+name + '\n' +str(e))
193         except:
194           traceback.print_exc()
195           self.reset_current_step()
196           self.editmode=0
197           raise AsException("Impossible d ajouter la commande "+name)
198
199    def set_current_step(self):
200       CONTEXT.unset_current_step()
201       CONTEXT.set_current_step(self)
202
203    def reset_current_step(self):
204       CONTEXT.unset_current_step()
205
206    def liste_mc_presents(self):
207       return []
208
209    def get_sd_avant_etape(self,nom_sd,etape):
210       return self.get_contexte_avant(etape).get(nom_sd,None)
211
212    def get_sd_apres_etape_avec_detruire(self,nom_sd,sd,etape,avec='non'):
213       """ 
214            Cette méthode retourne la SD sd de nom nom_sd qui est éventuellement
215            définie apres etape en tenant compte des concepts detruits
216            Si avec vaut 'non' exclut etape de la recherche
217       """
218       ietap=self.etapes.index(etape)
219       if avec == 'non':ietap=ietap+1
220       d={nom_sd:sd}
221       for e in self.etapes[ietap:]:
222          if e.isactif():
223             e.update_context(d)
224             autre_sd=d.get(nom_sd,None)
225             if autre_sd is None:
226               # Le concept a ete detruit
227               return None
228             if autre_sd is not sd :
229               # L'etape produit un concept de meme nom
230               if hasattr(e,'reuse') and e.reuse == autre_sd:
231                  # Le concept etant reutilise, on interrompt la recherche. 
232                  # On considere qu'il n'y a pas de nouveau concept defini
233                  # meme si dans les etapes suivantes le concept est detruit
234                  # et un concept de meme nom créé.
235                  return None
236               else:
237                  # Le concept est produit par l'etape
238                  return autre_sd
239       # On n'a rien trouve. Pas de concept de nom nom_sd
240       return None
241
242    def get_sd_apres_etape(self,nom_sd,etape,avec='non'):
243       """ 
244            Cette méthode retourne la SD de nom nom_sd qui est éventuellement
245            définie apres etape 
246            Si avec vaut 'non' exclut etape de la recherche
247       """
248       ietap=self.etapes.index(etape)
249       if avec == 'non':ietap=ietap+1
250       for e in self.etapes[ietap:]:
251         sd=e.get_sdprods(nom_sd)
252         if sd:
253           if hasattr(e,'reuse'):
254             if e.reuse != sd:
255               return sd
256       return None
257
258    def get_sd_autour_etape(self,nom_sd,etape,avec='non'):
259       """
260            Fonction: retourne la SD de nom nom_sd qui est éventuellement
261            définie avant ou apres etape
262            Permet de vérifier si un concept de meme nom existe dans le périmètre 
263            d'une étape
264            Si avec vaut 'non' exclut etape de la recherche
265       """
266       sd=self.get_sd_avant_etape(nom_sd,etape)
267       if sd:return sd
268       return self.get_sd_apres_etape(nom_sd,etape,avec)
269
270    def get_contexte_apres(self,etape):
271       """
272          Retourne le dictionnaire des concepts connus apres etape
273          On tient compte des commandes qui modifient le contexte
274          comme DETRUIRE ou les macros
275          Si etape == None, on retourne le contexte en fin de JDC
276       """
277       if not etape: return self.get_contexte_avant(etape)
278
279       d=self.get_contexte_avant(etape)
280       if etape.isactif():etape.update_context(d)
281       self.index_etape_courante=self.index_etape_courante+1
282       return d
283
284    def active_etapes(self):
285       """
286           Cette méthode a pour fonction de désactiver les étapes qui doivent
287           l'être cad, dans le cas d'ASTER, les étapes qui ne sont pas 
288           comprises entre le premier DEBUT/POURSUITE et le premier FIN 
289           et rendre actives les autres
290       """
291       if self.definition.code == 'ASTER' :
292          # Seulement pour ASTER :
293          # Avant DEBUT actif vaut 0
294          # Apres DEBUT et avant le 1er FIN actif vaut 1
295          # Apres le 1er FIN actif vaut -1
296          actif=0
297       else:
298          actif=1
299       for etape in self.etapes:
300         if actif == 0 and etape.nom in ['DEBUT','POURSUITE']:actif=1
301         if actif == 1:
302            etape.active()
303         else:
304            etape.inactive()
305         if etape.nom == 'FIN':actif=-1
306
307    def suppentite(self,etape) :
308       """  
309           Cette methode a pour fonction de supprimer une étape dans 
310           un jeu de commandes
311           Retourne 1 si la suppression a pu être effectuée,
312           Retourne 0 dans le cas contraire
313       """
314       #print "suppentite",self
315       self.init_modif()
316       # On memorise le contexte avant l'etape a supprimer
317       d=self.get_contexte_avant(etape)
318       index_etape=self.etapes.index(etape)
319       #print "suppentite",index_etape,d
320
321       self.etapes.remove(etape)
322       if etape.niveau is not self:
323         # Dans ce cas l'étape est enregistrée dans un niveau
324         # Il faut la désenregistrer
325         etape.niveau.unregister(etape)
326       etape.supprime_sdprods()
327       self.active_etapes()
328
329       # Apres suppression de l'etape il faut controler que les etapes
330       # suivantes ne produisent pas des concepts DETRUITS dans op_init de etape
331       for e in self.etapes[index_etape:]:
332          e.control_sdprods(d)
333       
334       self.reset_context()
335       CONNECTOR.Emit(self,"supp",etape)
336       self.fin_modif()
337       return 1
338
339    def analyse(self):
340       self.compile()
341       if not self.cr.estvide():return
342       self.exec_compile()
343       self.active_etapes()
344
345    def register_parametre(self,param):
346       """
347           Cette méthode sert à ajouter un paramètre dans la liste des paramètres
348       """
349       self.params.append(param)
350
351    def register_fonction(self,fonction):
352       """
353           Cette méthode sert à ajouter une fonction dans la liste des fonctions
354       """
355       self.fonctions.append(fonction)
356
357    def delete_param(self,param):
358       """
359           Supprime le paramètre param de la liste des paramètres
360           et du contexte gobal
361       """
362       if param in self.params : self.params.remove(param)
363       if self.g_context.has_key(param.nom) : del self.g_context[param.nom]
364
365    def get_parametres_fonctions_avant_etape(self,etape):
366       """
367           Retourne deux éléments :
368           - une liste contenant les noms des paramètres (constantes ou EVAL) 
369             définis avant etape
370           - une liste contenant les formules définies avant etape
371       """
372       l_constantes = []
373       l_fonctions = []
374       # on récupère le contexte avant etape
375       # on ne peut mettre dans les deux listes que des éléments de ce contexte
376       d=self.get_contexte_avant(etape)
377       # construction de l_constantes
378       for param in self.params:
379         nom = param.nom
380         if not nom : continue
381         if d.has_key(nom): l_constantes.append(nom)
382       # construction de l_fonctions
383       for form in self.fonctions:
384         nom = form.nom
385         if not nom : continue
386         if d.has_key(nom): l_fonctions.append(form.get_formule())
387
388       # on ajoute les concepts produits par DEFI_VALEUR
389       # XXX On pourrait peut etre faire plutot le test sur le type
390       # de concept : entier, reel, complexe, etc.
391       for k,v in d.items():
392          if hasattr(v,'etape') and v.etape.nom in ('DEFI_VALEUR',):
393             l_constantes.append(k)
394
395       # on retourne les deux listes
396       return l_constantes,l_fonctions
397
398    def get_nb_etapes_avant(self,niveau):
399       """ 
400           Retourne le nombre d etapes avant le debut de niveau
401       """
402       nb=0
403       for niv in self.etapes_niveaux:
404         if niv == niveau:break
405         nb=nb+len(niv.etapes)
406       return nb
407
408    def send_message(self,message):
409       if self.appli:
410          self.appli.send_message(message)
411
412    def init_modif(self):
413       """
414       Méthode appelée au moment où une modification va être faite afin de 
415       déclencher d'éventuels traitements pré-modification
416       """
417       #print "init_modif",self
418       self.state = 'modified'
419
420    def fin_modif(self):
421       #print "fin_modif",self
422       CONNECTOR.Emit(self,"valid")
423       self.isvalid()
424       pass
425
426    def deep_update_condition_bloc(self):
427       # pour le moment, on ne fait rien
428       raise "Not implemented"
429
430    def update_condition_bloc(self):
431       # pour le moment, on ne fait rien
432       raise "Not implemented"
433
434    def get_liste_mc_inconnus(self):
435      """
436      Retourne une liste contenant les mots-clés inconnus à la relecture du JDC
437      """
438      # cette liste a le format suivant : [etape,(bloc,mcfact,...),nom_mc,valeur_mc]
439      l_mc = []
440      for etape in self.etapes :
441          if etape.isactif() :
442             if not etape.isvalid() :
443                l = etape.get_liste_mc_inconnus()
444                if l : l_mc.extend(l)
445      return l_mc    
446
447    def get_genealogie(self):
448       """
449           Retourne la liste des noms des ascendants de l'objet self
450           jusqu'à la première ETAPE parent.
451       """
452       return []
453
454    def get_liste_cmd(self):
455       """
456           Retourne la liste des commandes du catalogue
457       """
458       return self.niveau.definition.get_liste_cmd()
459
460    def get_groups(self):
461       """
462           Retourne la liste des groupes
463       """
464       return self.niveau.definition.liste_groupes,self.niveau.definition.dict_groupes
465
466    def set_etape_context(self,etape):
467       """
468           Positionne l'etape qui sera utilisee dans NommerSdProd pour
469           decider si le concept passé pourra etre  nommé
470       """
471       self._etape_context=etape
472
473    def reset_context(self):
474       """ 
475           Cette methode reinitialise le contexte glissant pour pouvoir
476           tenir compte des modifications de l'utilisateur : création
477           de commandes, nommage de concepts, etc.
478       """
479       self.current_context={}
480       self.index_etape_courante=0
481
482    def del_sdprod(self,sd):
483       """
484           Supprime la SD sd de la liste des sd et des dictionnaires de contexte
485       """
486       #print "del_sdprod",self,sd
487       #print "del_sdprod",self.sds
488       #print "del_sdprod",self.g_context
489       #print "del_sdprod",self.sds_dict
490       if sd in self.sds : self.sds.remove(sd)
491       if self.g_context.has_key(sd.nom) : del self.g_context[sd.nom]
492       if self.sds_dict.has_key(sd.nom) : del self.sds_dict[sd.nom]
493
494    def del_param(self,param):
495       """
496           Supprime le paramètre param de la liste des paramètres
497           et du contexte gobal
498       """
499       if param in self.params : self.params.remove(param)
500       if self.g_context.has_key(param.nom) : del self.g_context[param.nom]
501
502    def del_fonction(self,fonction):
503       """
504           Supprime la fonction fonction de la liste des fonctions
505           et du contexte gobal
506       """
507       if fonction in self.fonctions : self.fonctions.remove(fonction)
508       if self.g_context.has_key(fonction.nom) : del self.g_context[fonction.nom]
509
510    def append_sdprod(self,sd):
511       """
512           Ajoute la SD sd à la liste des sd en vérifiant au préalable qu'une SD de
513           même nom n'existe pas déjà
514       """
515       if sd == None or sd.nom == None:return
516
517       o=self.sds_dict.get(sd.nom,None)
518       if isinstance(o,ASSD):
519          raise AsException("Nom de concept deja defini : %s" % sd.nom)
520       self.sds_dict[sd.nom]=sd
521       self.g_context[sd.nom] = sd
522       if sd not in self.sds : self.sds.append(sd)
523
524    def append_param(self,param):
525       """
526           Ajoute le paramètre param à la liste des params
527           et au contexte global
528       """
529       # il faudrait vérifier qu'un paramètre de même nom n'existe pas déjà !!!
530       if param not in self.params : self.params.append(param)
531       self.g_context[param.nom]=param
532
533    def append_fonction(self,fonction):
534       """
535           Ajoute la fonction fonction à la liste des fonctions
536           et au contexte global
537       """
538       # il faudrait vérifier qu'une fonction de même nom n'existe pas déjà !!!
539       if fonction not in self.fonctions : self.fonctions.append(fonction)
540       self.g_context[fonction.nom]=fonction
541
542    def delete_concept(self,sd):
543       """
544           Inputs :
545              - sd=concept detruit
546           Fonction :
547           Mettre a jour les etapes du JDC suite à la disparition du
548           concept sd
549           Seuls les mots cles simples MCSIMP font un traitement autre
550           que de transmettre aux fils
551       """
552       #print "delete_concept",self,sd
553       for etape in self.etapes :
554         etape.delete_concept(sd)
555
556    def replace_concept_after_etape(self,etape,old_sd,sd):
557       """
558           Met à jour les étapes du JDC qui sont après etape en fonction
559           du remplacement du concept sd
560       """
561       index = self.etapes.index(etape)+1
562       if index == len(self.etapes) :
563          return # etape est la dernière étape du jdc ...on ne fait rien !
564       for child in self.etapes[index:]:
565         child.replace_concept(old_sd,sd)
566
567    def dump_state(self):
568       print "dump_state"
569       print "JDC.state: ",self.state
570       for etape in self.etapes :
571          print etape.nom+".state: ",etape.state
572       
573    def change_unit(self,unit,etape,old_unit):
574       #print "change_unit",unit,etape,old_unit
575       #print id(self.recorded_units),self.recorded_units
576       #if self.recorded_units.has_key(old_unit):del self.recorded_units[old_unit]
577       self.record_unit(unit,etape)
578
579    def record_unit(self,unit,etape):
580       """Enregistre les unites logiques incluses et les infos relatives a l'etape"""
581       #print "record_unit",unit,etape
582       if unit is None:
583          # Cas de POURSUITE
584          self.recorded_units[None]=(etape.fichier_ini ,etape.fichier_text,etape.recorded_units)
585       else:
586          self.recorded_units[unit]=(etape.fichier_ini ,etape.fichier_text,etape.recorded_units)
587       #print id(self.recorded_units),self.recorded_units
588       #print self.recorded_units.get(None,(None,"",{}))[2]
589       #print self.recorded_units.get(None,(None,"",{}))[2].get(None,(None,"",{}))
590
591 #ATTENTION SURCHARGE : cette methode doit etre gardée en synchronisation avec celle de Noyau
592    def register(self,etape):
593       """
594            Cette méthode ajoute  etape dans la liste
595            des etapes self.etapes et retourne l identificateur d'étape
596            fourni par l appel a g_register
597
598            A quoi sert editmode ?
599               - Si editmode vaut 1, on est en mode edition de JDC. On cherche
600                 à enregistrer une étape que l'on a créée avec eficas (en passant
601                 par addentite) auquel cas on ne veut récupérer que son numéro
602                 d'enregistrement et c'est addentité qui l'enregistre dans
603                 self.etapes à la bonne place...
604               - Si editmode vaut 0, on est en mode relecture d'un fichier de
605                 commandes et on doit enregistrer l'étape à la fin de self.etapes
606                 (dans ce cas l'ordre des étapes est bien l'ordre chronologique
607                 de leur création   )
608       """
609       if not self.editmode:
610          self.etapes.append(etape)
611       else:
612          pass
613       return self.g_register(etape)
614
615 #ATTENTION SURCHARGE : cette methode doit etre gardée en synchronisation avec celle de Noyau
616    def NommerSdprod(self,sd,sdnom,restrict='non'):
617       """
618           Nomme la SD apres avoir verifie que le nommage est possible :
619           nom non utilise
620           Si le nom est deja utilise, leve une exception
621           Met le concept créé dans le concept global g_context
622       """
623       # XXX En mode editeur dans EFICAS, le nommage doit etre géré différemment
624       # Le dictionnaire g_context ne représente pas le contexte
625       # effectif avant une étape.
626       # Il faut utiliser get_contexte_avant avec indication de l'étape
627       # traitée.
628       # Cette etape est indiquee par l'attribut _etape_context qui a ete
629       # positionné préalablement par un appel à set_etape_context
630
631       if CONTEXT.debug : print "JDC.NommerSdprod ",sd,sdnom
632
633       if self._etape_context:
634          o=self.get_contexte_avant(self._etape_context).get(sdnom,None)
635       else:
636          o=self.sds_dict.get(sdnom,None)
637
638       if isinstance(o,ASSD):
639          raise AsException("Nom de concept deja defini : %s" % sdnom)
640
641       # ATTENTION : Il ne faut pas ajouter sd dans sds car il s y trouve deja.
642       # Ajoute a la creation (appel de reg_sd).
643       self.sds_dict[sdnom]=sd
644       sd.nom=sdnom
645
646       # En plus si restrict vaut 'non', on insere le concept dans le contexte du JDC
647       if restrict == 'non':
648          self.g_context[sdnom]=sd
649
650 #ATTENTION SURCHARGE : cette methode doit etre gardée en synchronisation avec celle de Noyau
651    def delete_concept_after_etape(self,etape,sd):
652       """
653           Met à jour les étapes du JDC qui sont après etape en fonction
654           de la disparition du concept sd
655       """
656       index = self.etapes.index(etape)+1
657       if index == len(self.etapes) :
658          return # etape est la dernière étape du jdc ...on ne fait rien !
659       for child in self.etapes[index:]:
660         child.delete_concept(sd)
661
662 #ATTENTION SURCHARGE : les methodes ci-dessous surchargent des methodes de Noyau et Validation : a reintegrer
663