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