]> SALOME platform Git repositories - tools/eficas.git/commitdiff
Salome HOME
pour se mettre d accord pour le changement d API
authorPASCALE NOYRET <pascale.noyret@edf.fr>
Fri, 22 Mar 2024 13:23:00 +0000 (14:23 +0100)
committerPASCALE NOYRET <pascale.noyret@edf.fr>
Fri, 22 Mar 2024 13:23:00 +0000 (14:23 +0100)
Accas/extensions/codeErreur.py
Editeur/editor.py
Editeur/editor_manager.py
Editeur/eficas_appli.py
Web/mdm5.py [new file with mode: 0644]

index 829f74cc60cae37f4292c72579c2f3a2488067aa..11d6ab2428ea94fdc1ae7893a94291e0e58e75c8 100755 (executable)
@@ -1,6 +1,6 @@
 dictErreurs = {
 # Categories
- 1000  :  'Parametre obligatoire : {}',
+ 1000  :  'Parametre obligatoire : {}',
  2000  :  'Erreur d enrolement',     
  3000  :  'Erreur de Catalogue : ', 
  4000  :  'Erreur de fichier Dataset : ', 
@@ -12,5 +12,15 @@ dictErreurs = {
  10 : 'fichier {} non trouve',
  20 : 'erreur ecriture',
  30 : 'erreur a l execution',
+ 40 : 'mauvaise valeur',
+
+# Messages specifiques 
+ 100  : 'Numero de Session deja allouée pour autre chose que de Web',
+}
+
+dictGravite = {
+ 0 : "alert-success",
+ 1 : "alert-info",
+ 2 : "alert-warning",
+ 3 : "alert-danger"
 }
-# 2000  Pas de catalogue connu editor_manager.py
index 938c7086ffad20a5ce80a479c4a65e1fbc683abc..13733f99e3b493703d94cb4b50d50b0875f10b59 100755 (executable)
@@ -113,7 +113,7 @@ class Editor:
     def construitJdC(self,jdc):
     #-------------------------
     # construction du jdc 
-    # Est-il encore necessaire de passer un jdc a l init ?
+    # Est-il encore necessaire de passer en paramtre un jdc a l init ?
     # je ne vois plus quel est le cas d usage
     # je garde . 11 mars 2024
         if jdc : 
@@ -194,9 +194,11 @@ class Editor:
 
         fn = str(fn)
         jdcName = os.path.basename(fn)
-        print ('-------------------------------------------')
-        print (fn)
-        print ('-------------------------------------------')
+        debug = 0
+        if debug : 
+            print ('-------------------------------------------')
+            print (fn)
+            print ('-------------------------------------------')
 
         # Il faut convertir le contenu du dataSetFile en fonction du format
         formatIn = self.appliEficas.formatFichierIn
@@ -721,7 +723,10 @@ class Editor:
         @return tuple of two values (boolean, string) giving a success indicator and
             the name of the saved file
         """
+        #TODO PN : s occuper des suffixes
 
+        if fichier != self.dataSetFile :
+           return (1, 'utiliser saveFileAs pour changer le nom du fichier')
         self.fichierOut = fichier
         if not (self.writeFile(fichier, formatLigne=formatLigne)):
             return (0, None)
@@ -729,13 +734,19 @@ class Editor:
             if self.myWriter != self.XMLWriter:
                 self.XMLWriter.gener(self.jdc)
                 self.XMLWriter.writeDefault(fichier)
-                return (1, self.dataSetFile)
+                return (0, self.dataSetFile)
         if self.jdc.isValid() and hasattr(self.myWriter, "writeDefault"):
             self.myWriter.writeDefault(fichier)
         elif self.code == "TELEMAC" and hasattr(self.myWriter, "writeDefault"):
             self.myWriter.writeDefault(fichier)
         self.modified = 0
-        return (1, self.dataSetFile)
+        return (0, self.dataSetFile)
+
+    # --------------------------------------------------#
+    def saveFileAs(self, fichier, formatLigne="beautifie"):
+    # --------------------------------------------------#
+        self.dataSetFile  = fichier
+        return self.saveFile(fichier, formatLigne)
 
     # -----------------------#
     def sauveLigneFile(self):
@@ -1268,6 +1279,11 @@ class Editor:
         texteStringDataBase = self.readercata.cata.JdC.dumpStringDataBase(nomDataBaseACreer)
         return texteStringDataBase
 
+    # -----------------------------#
+    def getEtapesByName(self, name):
+    # -----------------------------#
+        if not self.jdc : return (1, 'pas de jdc')
+        return self.jdc.getEtapesByName(name)
 
 if __name__ == "__main__":
     print("a faire")
index 4d5db6dc50f1c2ab1599800a9a585822c1f4f51b..6442f35956e908525648b3d0dc7cbb1a936b7e23 100644 (file)
@@ -23,7 +23,7 @@ from uuid import uuid1
 from multiprocessing import Lock
 from Accas.extensions.codeErreur import dictErreurs
 
-debug = 1
+debug = 0
 # --------------------------
 class EditorManager(object):
 # --------------------------
@@ -110,11 +110,12 @@ class EditorManager(object):
         if dataSetFile == None :
             self.appliEficas.afficheMessage(dictErreurs[1000] , 'nom de dataSet obligatoire pour obtenir un editor')
             return (None, 1000 , dictErreurs[1000] + ' : nom de dataset obligatoire pour obtenir un editor', None)
-        if not os.path.isfile(cataFile):
+        if not os.path.isfile(dataSetFile):
             self.appliEficas.afficheMessage(dictErreurs[4000] + dictErreurs[10], 'fichier dataSet {} non trouve'.format(cataFile))
             return (None, 4000 + 10  , dictErreurs[4000] + dictErreurs[10].format(cataFile), None)
+        if debug :   print ('dictEditors', self.dictEditors)
+        messageInfo = ""
         with self.lock :
-            messageInfo = None
             for editor in self.dictEditors.values():
                 if self.samePath(dataSetFile, editor.getDataSetFileName()) and self.samePath(cataFile, editor.getCataFileName()):
                     break
@@ -123,7 +124,10 @@ class EditorManager(object):
                 editor = WebEditor(self.appliEficas, cataFile, dataSetFile)
 
             if not editor.readercata :
-                return (None, 3000 + 30 , 'impossible d allouer l editor : {}'.format(editor.pbLectureCata), None)
+                codeError = 3000 + 30
+                message = dict[3000] + ' '+dict[30] + ' '
+                message += 'impossible d allouer l editor : {}'.format(editor.pbLectureCata)
+                return (None, codeError , message, messageInfo)
 
             if editor.jdc:  # le fichier est bien un jdc
                 self.dictEditors[editor.editorId]=editor
@@ -134,19 +138,13 @@ class EditorManager(object):
                    self.appliEficas.dictEditorIdChannelId[editor.editorId]=[cId,]
             else:
                 # PN : cabler avec le message d info
-                return (None, 4000 + 30, 'impossible de creer le dataset ; {} '.format(editor.pbLectureDataSet), None)
+                codeError = 4000 + 30
+                message = dict[4000] + ' '+dict[30] + ' '
+                message +=  'impossible de creer le dataset ; {} '.format(editor.pbLectureDataSet)
+                return (None, 4000 + 30, 'impossible de creer le dataset ; {} '.format(editor.pbLectureDataSet), messageInfo)
             return (editor, 0, '', messageInfo)
 
 
-    # --------------------------
-    def getEditorById(self, eId):
-    # ---------------------------
-        debug = 1
-        if eId in self.dictEditors:
-           return (self.dictEditors[eId], 0, "")
-        if debug : print ("getEditorById : {} non trouve ".format(eId))
-        return (None, 1, "getEditorById : {} non trouve ".format(eId))
 
     # -------------------------
     def samePath(self, f1, f2):
@@ -155,7 +153,7 @@ class EditorManager(object):
         compare two paths.
         """
         if f1 is None or f2 is None: return 0
-        if os.path.normcase(os.path.normpath(f1)) == os.path.normcase( os.path.normpath(f2)):
+        if os.path.realpath(os.path.normcase(os.path.normpath(f1))) == os.path.realpath(os.path.normcase( os.path.normpath(f2))):
             return 1
         return 0
 
index 421a51c4329e168e285963ab022f97e8f3199911..5cf3ece67d0cebe87c0da9704f3eaa0fb33c8ff8 100755 (executable)
@@ -169,48 +169,68 @@ class EficasAppli:
         return self.editor
 
     #-------------------------------------------------------------------------------------------------------------------
-    def getWebEditor(self, sId, cataFile = None, datasetFile=None, jdc=None, include=0, formatIn='python', formatOut='python'):
+    def getWebEditor(self, cId, cataFile = None, datasetFile=None, jdc=None, include=0, formatIn='python', formatOut='python'):
     #-------------------------------------------------------------------------------------------------------------------
-        # en Web sId est le canal Id
-        debug = 1
-        if sId in self.dictChannelType.keys() and self.dictChannelType[sId] != 'Web' :
-              message = 'Numero de Session deja allouée pour autre chose que de Web' 
-              CR = 1000
-              return (sId, None, CR, message, None) 
-        if not sId in self.dictChannelType.keys() :
-           self.dictChannelType[sId]  =  'Web'
+        debug = 0
+        if debug : 
+           print ('______________________________ Eficas_appli getWebEditor ________________________')
+           print ('self', self)
+           print (cId, cataFile, datasetFile, jdc, include, formatIn, formatOut)
+
         #PN TODO initialiser info
-        info = None
-        (editor, CR, message, info)   = self.editorManager.getWebEditor(sId, cataFile,datasetFile, jdc, include)
-        if not editor :
+        info = ''
+        if cId == None :
+           CR = 1000 + 40
+           message = dictErreurs[1000].format('cId') + dictErreurs[40] 
+           return (None, CR, message, info)
+
+        if formatIn not in ('python', 'xml') : 
+           CR = 1000 + 40
+           message = dictErreurs[1000].format('formatIn') + dictErreurs[40] 
+           return (None, CR, message, info)
+
+        if formatOut not in ('python', 'xml') : 
+           CR = 1000 + 40
+           message = dictErreurs[1000].format('formatIn') + dictErreurs[40] 
            return (None, CR, message, info)
 
+        if cId in self.dictChannelType.keys() and self.dictChannelType[cId] != 'Web' :
+            CR = 100
+            return (cId, None, CR, dictErreurs[100], info) 
+        if not cId in self.dictChannelType.keys() : self.dictChannelType[cId]  =  'Web'
+
+        (editor, CR, message, info)   = self.editorManager.getWebEditor(cId, cataFile,datasetFile, jdc, include)
+        if not editor : return (None, CR, message, info)
+
+        # on pose une lock egalement pour la mise a jour des dico
         with lock :
-             externalEditorId = uuid1().hex
+            externalEditorId = uuid1().hex
+            if editor.editorId not in self.dictEditorIdChannelIdExternEid.keys() :
+                self.dictEditorIdChannelIdExternEid[editor.editorId] = {}
+            if cId not in self.dictEditorIdChannelIdExternEid[editor.editorId] :
+                self.dictEditorIdChannelIdExternEid[editor.editorId][cId] = [externalEditorId,]
+            else : 
+                self.dictEditorIdChannelIdExternEid[editor.editorId][cId].append(externalEditorId)
+
         self.dictExternalEidEditor[externalEditorId] = editor
-        if editor.editorId not in self.dictEditorIdChannelIdExternEid.keys() :
-            self.dictEditorIdChannelIdExternEid[editor.editorId] = {}
 
-        if sId not in self.dictEditorIdChannelIdExternEid[editor.editorId] :
-            self.dictEditorIdChannelIdExternEid[editor.editorId][sId] = [externalEditorId,]
-        else : 
-            self.dictEditorIdChannelIdExternEid[editor.editorId][sId].append(externalEditorId)
         if debug : 
-           if editor : print ('getWebEditor id de sesssion :', sId, ' editor : ', editor.editorId, 'externe ',  externalEditorId)
-           print (externalEditorId, CR, message, info)
+           if editor : print ('getWebEditor id de sesssion :', cId, ' editor : ', editor.editorId, 'externe ',  externalEditorId)
+           print ('dictionnaire ' , self.dictEditorIdChannelIdExternEid)
+           print ('______________________________ fin getWebEditor ________________________')
         return (externalEditorId, CR, message, info) 
 
     #--------------------------------------
-    def getWebEditorById(self, sId, eId):
+    def getWebEditorById(self, cId, eId):
     #--------------------------------------
-        if sId == None : 
-           return ( None, 1000, dict[1000].format ('session Id'))
+        if cId == None : 
+           return ( None, 1000, dict[1000].format ('session Id'), "")
         editor = self.dictExternalEidEditor[eId]
-        if sId not in self.dictEditorIdChannelIdExternEid[editor.editorId] :
-           return ( None, 1000, 'la session ne possede pas cet Editeur')
-        if eId not in self.dictEditorIdChannelIdExternEid[editor.editorId][sId] :
-           return ( None, 1000, 'incoherence entre Editeur et session')
-        return (editor, 0, "")
+        if cId not in self.dictEditorIdChannelIdExternEid[editor.editorId] :
+           return ( None, 1000, 'la session ne possede pas cet Editeur', "")
+        if eId not in self.dictEditorIdChannelIdExternEid[editor.editorId][cId] :
+           return ( None, 1000, 'incoherence entre Editeur et session', "")
+        return (editor, 0, "", "")
         
 
     #--------------------------------------------------------------------------------------------------------------------------------
@@ -410,8 +430,9 @@ class EficasAppli:
     #-------------------------------------------------------------------------------------------------
     def propageChange (self, editorId, emitChannelId , emitEditorId,  toAll , fction, *args, **kwargs):
     #--------------------------------------------------------------------------------------------------
-        if fction == 'appendChildren' : debug = 1
-        else : debug = 0
+        #if fction == 'appendChildren' : debug = 1
+        #else : debug = 0
+        debug = 1
         if debug : 
            print ("------------------------------------------ Eficas")
            print ('propageChange avec les arguments ')
diff --git a/Web/mdm5.py b/Web/mdm5.py
new file mode 100644 (file)
index 0000000..e275176
--- /dev/null
@@ -0,0 +1,689 @@
+# coding: utf-8
+#!/usr/bin/env python3
+import sys, os
+
+_no = 1
+
+code='Essai'
+#code=None
+
+from flask import Flask, request, render_template, url_for, jsonify, make_response, session, g, Response
+
+
+# File management
+from   flask           import redirect, send_from_directory
+from   werkzeug.utils  import secure_filename
+from   lib.upload_file import uploadfile
+import PIL
+from   PIL import Image
+import simplejson
+import traceback
+
+# from flask import ?? json, jsonify ??
+import json
+import os
+from   pprint      import pprint
+#from   forms       import BasicForm  #Essais WtForms
+from   collections import OrderedDict
+from   markupsafe  import escape
+
+# Flask management of Server Side Event
+# Necessite redis
+from flask_sse import sse
+# from flask_uploads import UploadSet, configure_uploads, IMAGES 
+
+app = Flask(__name__)
+
+# CATALOGS_EXT=("py","jpg") #TODO : supprimer jpg pour test
+# catalogs = UploadSet("catalogs",CATALOGS_EXT)
+# app.config["UPLOADED_CATALOGS_DEST"] = "data/catalogs"
+# app.config["SECRET_KEY"]             = os.urandom(24)
+
+# configure_uploads(app, catalogs)
+
+app.config['SECRET_KEY']         = os.urandom(24)
+app.config['UPLOAD_FOLDER']      = 'data/'
+app.config['THUMBNAIL_FOLDER']   = 'data/thumbnail/'
+app.config['MAX_CONTENT_LENGTH'] = 50 * 1024 * 1024
+#PNPN
+#app.config['SESSION_REFRESH_EACH_REQUEST'] = False
+ALLOWED_EXTENSIONS = set(['py','comm','txt', 'gif', 'png', 'jpg', 'jpeg', 'bmp', 'rar', 'zip', '7zip', 'doc', 'docx'])
+IGNORED_FILES = set(['.gitignore'])
+
+
+### Server Side Event config
+app.config["REDIS_URL"] = "redis://localhost"
+#app.config["REDIS_URL"] = "redis://:password@localhost"
+#TODO: personaliser l'url en fonction de la session utilisateur
+app.register_blueprint(sse, url_prefix='/stream')
+
+
+### Eficas Connector
+def createWebAppli(app):
+    import os
+    print('Create Appli');
+    sys.path.append(os.path.join(os.path.abspath(os.path.dirname(__file__)),'..'))
+    from Editeur.eficas_go import getEficas
+    eficasAppli=getEficas(code, langue='ang',appWeb=app)
+    return eficasAppli
+
+
+eficasAppli=createWebAppli(app)
+debug=0
+if debug : print ('eficasAppli = ', eficasAppli)
+
+def fromConnecteur(maFonction, sessionId, externEditorId, *args,**kwargs):
+  if debug : 
+     print ('________________________________________________________________________')
+     print ('fromConnecteur : ', maFonction, sessionId, externEditorId, *args,**kwargs)
+     print ('________________________________________________________________________')
+  fnct=globals()[maFonction]
+  fnct(sessionId, externEditorId, *args,**kwargs)
+
+#TODO : Rattacher à une session
+#         gérer un appel register callback
+app.fromConnecteur=fromConnecteur
+
+## ServerSideEvent from Eficas signals :
+#    - Validite
+#    - Ajouter un noeud   (et ses enfants)
+#    - Supprimer un noeud (et ses enfants),
+#    - ReAffichage d'un noeud (et ses enfants)
+#    - Changement d'un nom de mot-cle reference
+
+def propageValide(cId, eId, id, valid): #TODO: RENAME TO ... propagateValidation
+    if debug : print ('Flask/propageValide: ', id, valid)
+    sse.publish( {'eId' : eId, 'id':id, 'valid':valid, 'message': "Hello from propageValide!"}, type='propageValide', channel = str(cId))
+
+def updateNodeInfo(cId, eId, id, info):
+    debug=1
+    if debug : print ('Flask/updateNodeInfo', cId, eId, id, info)
+    sse.publish( {'eId' : eId, 'id':id, 'info':info, 'message': "Hello from updateNodeInfo!"}, type='updateNodeInfo', channel = str(cId))
+
+def appendChildren(cId, eId, id, fcyTreeJson, pos):
+    debug = 0
+    if debug : print ('Flask/appendChildren: ', id, fcyTreeJson, pos)
+    sse.publish( {'eId' : eId, 'id':id, 'fcyTreeSrc':fcyTreeJson, 'pos':pos, 'message': "Hello from appendChildren!"}, type='appendChildren', channel = str(cId))
+
+def deleteChildren(cId, eId, idList):
+    if debug : print ('Flask/deleteChildren: ', idList)
+    sse.publish( {'eId' : eId, 'idList':idList,'message': "Hello from deleteChildren!"}, type='deleteChildren', channel = str(cId))
+
+#TODO: Câbler la sélection du catalogue avant d'activer les appels suivants
+#      car si la page Web n'est pas rendue et que le connecteur génère une erreur... boom !
+def afficheMessage(sId, txt, couleur):                     #TODO: RENAME TO ... displayWarning
+    if sId != session['eficasSession']  : return
+    print ('Flask/afficheMessage: ', txt, couleur)
+    # sse.publish( {'txt':txt, 'color':couleur, 'messageClass':"alert-warning" ,'message': "Hello from afficheMessage!"},
+    #              type='displayMessage')
+
+def afficheAlerte(sId, titre, message):                  #TODO: RENAME TO ... displayDanger
+    if sId != session['eficasSession']  : return
+    print ('Flask/afficheAlerte: ', titre, message) #TODO: titre & message VS txt ?
+    # sse.publish( {'txt':titre, 'color':couleur, 'messageClass':"alert-danger", 'message': "Hello from afficheAlerte!"},
+    #              type='displayMessage')
+
+#Messages Globaux ?
+def afficheMessage2(cId, eId,  txt, couleur):                     #TODO: RENAME TO ... displayWarning
+    if debug : print ('Flask/afficheMessage: ', txt, couleur)
+    sse.publish( {'txt':txt, 'color':couleur, 'messageClass':"alert-warning" ,'message': txt},
+                 type='message', channel = str(cId))
+
+def afficheAlerte2(cId, eId, titre, message, couleur):          #TODO: RENAME TO ... displayDanger
+    if debug : print ('Flask/afficheAlerte: ', titre, message) #TODO: titre & message VS txt ?
+    sse.publish( {'txt':titre, 'color':couleur, 'messageClass':"alert-danger", 'message': "Hello from afficheAlerte!"},
+                 type='error', channel = str(cId))
+
+
+
+## WebApp -> Eficas  :
+# Pour SIMP : Ajoute, Supprime (MC facultatif), Change la valeur
+# Pour FACT : Ajoute, Supprime
+# Pour PROC : Ajoute, Supprime
+# Pour OPER : Ajoute, Supprime, Nomme, Renomme
+# @app.route('/post/<uuid:post_id>')
+
+@app.route("/updateSimp", methods=['POST'])
+def updateSimp():
+    # Validate the request body contains JSON
+    if request.is_json:
+        # Parse the JSON into a Python dictionary
+        req = request.get_json()
+        # Print the dictionary
+        if debug : print("Flask/updateSimp request : ", req)
+        if debug : print("Flask/updateSimp request['id'] : ",req['id'])
+        eId = req['eId'];id=req['id'];value=req['value']
+        # id, value = req.values()       # Dangereux correspondance implicite
+        value             = str(value)   # L'utilisateur peut écrire la valeur Pi
+
+        (eficasEditor, errorCode, errorMsg, infoMsg) = eficasAppli.getWebEditorById(session['canalId'],eId)
+        if errorCode : 
+            messageLevel = "alert-danger"
+            message = errorMsg + infoMsg
+            return make_response(json.dumps( {'errorCode' : errorCode, 'message': message,'msgLevel':msgLevel} ))
+
+        #(rId, errorCode, errorMsg, infoMsg) = eficasEditor.changeValue(session['canalId'], eId, id,value);
+        #assert(rId==id)
+
+        #changeDone        = True
+        # if debug : print ("Flask/updateSimp changeDone : ",changeDone)
+        # Ne pas recuperer et ne pas renvoyer le noeud dans le cas du SIMP
+        #  (le changeDone et l''ancienne valeur ds la WebApp suffit 
+        #(node, errorCode, errorMsg, errorLevel)  = eficasEditor.getDicoForFancy(eficasEditor.getNodeById(id))
+        if debug : print("Flask/updateSimp node : ",node)
+        # return jsonify([myTreeDico])
+        
+        (node, errorCode, errorMsg, infoMsg) = eficasEditor.changeValue(session['canalId'], eId, id,value);
+        if errorCode : 
+            messageLevel = "alert-danger"
+            message = errorMsg + infoMsg
+            return make_response(json.dumps( {'errorCode' : errorCode, 'message': message,'msgLevel':msgLevel} ))
+        if infoMessage != "" : msgLevel = 'alert-success'
+        else : msgLevel = "alert-info"
+        return make_response(json.dumps( {'source':node, 'errorCode' : errorCode, 'message': infoMessage,'msgLevel':msgLevel} ))
+
+        # Return a string along with an HTTP status code
+        # return "JSON received!", 200
+    else:
+        # The request body wasn't JSON so return a 400 HTTP status code
+        return "Request was not JSON", 400
+        #return make_response(jsonify({"message": "Request body must be JSON"}), 400)
+    
+@app.route("/updateSDName", methods=['POST'])
+def updateSDName():
+    # Validate the request body contains JSON
+    if request.is_json:
+        # Parse the JSON into a Python dictionary
+        req = request.get_json()
+        # Print the dictionary
+        #print(req)
+        #print(req['id'])
+        eId=req['eId'];id=req['id'];sdnom=req['sdnom']
+        sdnom              = str(sdnom)   #On peut écrire Pi
+         # On attendant que l externalEditorId soit dans le tree
+        (eficasEditor, errorCode, errorMsg, infoMsg) = eficasAppli.getWebEditorById(session['canalId'],eId)
+        if errorCode : 
+            messageLevel = "alert-danger"
+            message = errorMsg + infoMsg
+            return make_response(json.dumps( {'changeIsAccepted' : changeDone, 'message': message} ))
+
+        (errorCode, errorMsg, infoMsg) = eficasEditor.updateSDName(session['canalId'],eId,id,sdnom);
+        if errorCode : 
+            messageLevel = "alert-danger"
+            message = errorMsg + infoMsg
+        else :
+            messageLevel = "alert-success"
+        return make_response(json.dumps( {'errorCode' : errorCode, 'message': message,'msgLevel':msgLevel} ))
+    else:
+        # The request body wasn't JSON so return a 400 HTTP status code
+        return "Request was not JSON", 400
+        #return make_response(jsonify({"message": "Request body must be JSON"}), 400)
+    
+    
+@app.route("/removeNode", methods=['POST'])
+def removeNode():
+    # Validate the request body contains JSON
+    if request.is_json:
+        # Parse the JSON into a Python dictionary
+        req = request.get_json()
+        # Print the dictionary
+        if debug : print("Flask/removeNode ",req);print("/removeNode ",req['eId'],req['id']);
+        eId = req['eId'];
+        id  = req['id'];
+        (eficasEditor, errorCode, errorMsg, infoMsg) = eficasAppli.getWebEditorById(session['canalId'],eId)
+        if errorCode : 
+            messageLevel = "alert-danger"
+            message = errorMsg + infoMsg
+            return make_response(json.dumps( {'errorCode' : errorCode, 'message': message,'msgLevel':msgLevel} ))
+
+        (errorCode, errorMsg, infoMsg) = eficasEditor.removeNode(session['canalId'],session['externEditorId'],id);
+        if debug : print ("Flask/removeNode : ret : ",ret," message : ",message)
+        if errorCode : 
+            messageLevel = "alert-danger"
+            message = errorMsg + infoMsg
+        else :
+            messageLevel = "alert-success"
+
+        return make_response(json.dumps( {'errorCode' : errorCode, 'message': message,'msgLevel':msgLevel} ))
+    else:
+        # The request body wasn't JSON so return a 400 HTTP status code
+        return "Request was not JSON", 400
+        #return make_response(jsonify({"message": "Request body must be JSON"}), 400)
+
+@app.route("/appendChild", methods=['POST'])
+def appendChild():
+    # Validate the request body contains JSON
+    # print ('__________________________________________')
+    # print ( 'in appendChild')
+    # print ('__________________________________________')
+    if request.is_json:
+        # Parse the JSON into a Python dictionary
+        req = request.get_json()
+        # Print the dictionary
+        if debug :  print(__file__+"Flask/appendChild : ",req);
+        eId = req['eId'];id=req['id'];name=req['name'];pos=req['pos'];
+        # id, value = req.values() # Dangereux correspondance implicite
+        #rId,message,changeDone  = eficasEditor.appendChild(id,name,pos);
+        (eficasEditor, errorCode, message) = eficasAppli.getWebEditorById(session['canalId'],eId)
+        if errorCode : 
+            messageLevel = "alert-danger"
+            message = errorMsg + infoMsg
+            return make_response(json.dumps( {'errorCode' : errorCode, 'message': message,'msgLevel':msgLevel} ))
+        (newId, errorCode, message) = eficasEditor.appendChild(session['canalId'],eId,id,name,pos);
+        if debug : print (__file__+"Flask/appendChild : newId : ",newId);
+        if errorCode : 
+            messageLevel = "alert-danger"
+            message = errorMsg + infoMsg
+        else :
+            messageLevel = "alert-success"
+        return make_response(json.dumps( {'id':newId, 'errorCode' : errorCode, 'message': message,'msgLevel':msgLevel} )) #TODO: Code Erreur
+        # return make_response(json.dumps( {'source':node, 'changeIsAccepted' : changeDone, 'message': message} ))
+        # Return a string along with an HTTP status code
+        # return "JSON received!", 200
+    else:
+        # The request body wasn't JSON so return a 400 HTTP status code
+        return "Request was not JSON", 400
+        #return make_response(jsonify({"message": "Request body must be JSON"}), 400)
+
+@app.route("/newDataset", methods=['POST'])
+def newDataset():
+    
+    # Validate the request body contains JSON
+    if request.is_json:
+        # Parse the JSON into a Python dictionary
+        req = request.get_json()
+        # Print the dictionary
+        print(__file__+"/newDataset : ",req);
+        #cataFile   =os.path.abspath("../Codes/WebTest/"+req['catalogName']);
+        cataFile   =os.path.abspath("./data/"+req['catalogName']);
+        dataSetFile =os.path.abspath("./data/"+req['datasetName']);
+        #cataFile    = os.path.abspath('../Codes/WebTest/cata_essai.py')
+        #dataSetFile = os.path.abspath('../Codes/WebTest/web_tres_simple_avec_2Fact.comm')
+    else:
+        # The request body wasn't JSON so return a 400 HTTP status code
+        return "Request was not JSON", 400
+        #return make_response(jsonify({"message": "Request body must be JSON"}), 400)
+
+    cId=session['canalId'];
+    (editorId, errorCode, errorMessage, messageInfo) = eficasAppli.getWebEditor(cId, cataFile, dataSetFile)
+    debug = 1
+    if debug :
+        print ('apres getWebEditor : canalId, : ', cId,  ' editorId, : ', editorId,
+               ' code Erreur : ', errorCode,'message : ', errorMessage, 'messageInfo ', messageInfo)
+    if errorCode : 
+        # TODO ERIC
+        afficheMessage2(cId, editorId, errorMessage,'macouleur');
+        return make_response(jsonify({"message": errorMessage, "code": errorCode}), 400)
+        
+    session['externEditorId'] = editorId;
+    
+    if debug :   print ('idEditor = ', session['externEditorId'])
+    (eficasEditor, errorCode, errorMessage,messageInfo)  = eficasAppli.getWebEditorById(session['canalId'],editorId)
+    if errorCode:
+         return make_response(jsonify({"message": errorMessage, "code": errorCode}), 400)
+
+    
+    fancyTreeDict=eficasEditor.getDicoForFancy(eficasEditor.tree.racine) #TODO: changer le nom Dico en Dict
+    #fancyTreeJS=json.dumps([fancyTreeDict],indent=4)                #TODO : remove indent if not DEBUG
+    fancyTreeDict['eId']=editorId;
+    #print("---- myFancyTreeDico ----")
+    pprint(fancyTreeDict)
+    #print("---- myFancyTreeJS ----")
+    #pprint( myFancyTreeJS)
+    commands = eficasEditor.getListeCommandes(); #TODO: Renommer la fonction
+
+    title = os.path.basename(cataFile)+'/'+os.path.basename(dataSetFile)
+    if debug : print('liste des commandes',  eficasEditor.getListeCommandes())
+    return make_response(json.dumps( {'source': [fancyTreeDict], 'commands':commands, 'title':title,} ))
+
+
+@app.route('/')
+def index():
+
+   #  Example :
+   #  tree4Fancy = """ [
+   #     {"title": "Node 1",   "key": "1"},
+   #     {"title": "Folder 2", "key": "2", "folder": true, "children": [
+   #       {"title": "Node 2.1", "key": "3"},
+   #       {"title": "Node 2.2", "key": "4"}
+   #     ]}
+   # ]
+   # """.replace('\n','')
+
+    #print ('_______________________________________________')
+    #cataFile    = os.path.abspath('../Codes/WebTest/cata_essai.py')
+    #dataSetFile = os.path.abspath('../Codes/WebTest/web_tres_simple_avec_2Fact.comm')
+    
+    # En attendant la génération d'un n° de canal unique
+    # notion de plage
+    if not 'canalId' in session :
+        global _no
+        _no = _no + 1
+        canalId = _no
+        session['canalId']  = canalId
+    else :
+        canalId = session['canalId']
+    #if _no == 3:
+    #    dataSetFile = os.path.abspath('../Codes/WebTest/web_tres_simple_incomplet.comm')
+
+    #(canalId, eficasEditor, errorCode, message) = eficasAppli.getWebEditor(cataFile, dataSetFile)
+    #(externEditorId, errorCode, errorMsg, messageInfo) = eficasAppli.getWebEditor(canalId, cataFile, dataSetFile)
+    #debug = 0
+    #if debug : print ('apres getWebEditor : canalId, : ', canalId,  ' externEditorId, : ', externEditorId, ' code Erreur : ', errorCode,'message : ', errorMsg, 'messageInfo ', messageInfo)
+    #if not errorCode :
+    #    if debug : 
+    #        print ('_______________________________________________')
+    #        print ('canalId', canalId)
+    #        print ('externEditorId', externEditorId)
+    #        print ('_______________________________________________')
+    #    session['externEditorId'] = externEditorId
+    #else :
+    #    # Il faudrait gerer les erreurs
+    #    return render_template('commandes_2.html',
+    #        titre= errorMsg,
+    #        listeCommandes = [],
+    #        tree= None
+    #    )
+    
+    #if debug :   print ('idEditor = ', session['externEditorId'])
+    #(eficasEditor, errorCode, message)  = eficasAppli.getWebEditorById(canalId,externEditorId) 
+    #if errorCode:
+    #      return render_template('commandes_2.html',
+    #        titre= errorMsg,
+    #        listeCommandes = [],
+    #        tree= None
+    #    )
+
+    
+    #myFancyTreeDico=eficasEditor.getDicoForFancy(eficasEditor.tree.racine)
+    #if debug : pprint (myFancyTreeDico)
+    #myFancyTreeJS=json.dumps([myFancyTreeDico],indent=4)  #TODO : remove indent if not DEBUG
+    
+    #print("---- myFancyTreeDico ----")
+    #pprint(myFancyTreeDico)
+    #print("---- myFancyTreeJS ----")
+    #pprint( myFancyTreeJS)
+
+    #if debug : print ( 'liste des commandes',  eficasEditor.getListeCommandes())
+    #return render_template('commandes_2.html',
+    #  titre=code,
+    #  efi_update_channel = str(canalId),
+    #  listeCommandes =  eficasEditor.getListeCommandes(),
+    #  tree=myFancyTreeJS,
+      # tree=tree4Fancy,
+    #)
+    myFancyTreeJS=json.dumps([{}],indent=4)  #TODO : remove indent if not DEBUG
+    return render_template('commandes_2.html',
+      titre=code,
+      efi_update_channel = str(canalId),
+      listeCommandes = [],
+      tree=myFancyTreeJS,
+    )
+    # etape  = str(escape(request.args.get("etape", "")))
+
+
+
+# @app.route("/forward/", methods=['POST'])
+# def move_forward():
+#     #Moving forward code
+#     forward_message = "Moving Forward..."
+#     return render_template('mdm.html', message=forward_message)
+
+
+
+# @app.route('/form', methods=['GET', 'POST'])
+# def basicform():
+#    form = BasicForm(request.form)
+#    if request.method == 'POST' and form.validate():
+#       with open('/tmp/test.txt', 'w') as f:
+#           for k in request.form:
+#             f.write('{0}: {1}\n'.format(k, request.form[k]))
+#    return render_template('basic.html', form=form)
+
+# @app.route("/json", methods=["POST"])
+# def json_example():
+
+#     if request.is_json:
+
+#         req = request.get_json()
+
+#         response_body = {
+#             "message": "JSON received!",
+#             "sender": req.get("name")
+#         }
+
+#         res = make_response(jsonify(response_body), 200)
+
+#         return res
+
+#     else:
+
+#         return make_response(jsonify({"message": "Request body must be JSON"}), 400)
+
+#TODO: 
+#from fileManagement import *
+
+def allowed_file(filename):
+    return '.' in filename and \
+        filename.rsplit('.', 1)[1].lower() in ALLOWED_EXTENSIONS
+
+
+def gen_file_name(filename):
+    """
+    If file was exist already, rename it and return a new name
+    """
+
+    i = 1
+    while os.path.exists(os.path.join(app.config['UPLOAD_FOLDER'], filename)):
+        name, extension = os.path.splitext(filename)
+        filename = '%s_%s%s' % (name, str(i), extension)
+        i += 1
+
+    return filename
+
+
+def create_thumbnail(image):
+    try:
+        base_width = 80
+        img = Image.open(os.path.join(app.config['UPLOAD_FOLDER'], image))
+        w_percent = (base_width / float(img.size[0]))
+        h_size = int((float(img.size[1]) * float(w_percent)))
+        img = img.resize((base_width, h_size), PIL.Image.ANTIALIAS)
+        img.save(os.path.join(app.config['THUMBNAIL_FOLDER'], image))
+
+        return True
+
+    except:
+        print(traceback.format_exc())
+        return False
+
+
+@app.route("/upload", methods=['GET', 'POST'])
+def upload():
+    if request.method == 'POST':
+        files = request.files['file']
+
+        if files:
+            filename  = secure_filename(files.filename)
+            filename  = gen_file_name(filename)
+            mime_type = files.content_type
+
+            if not allowed_file(files.filename):
+                result = uploadfile(name=filename, type=mime_type, size=0, not_allowed_msg="File type not allowed")
+
+            else:
+                # save file to disk
+                uploaded_file_path = os.path.join(app.config['UPLOAD_FOLDER'], filename)
+                files.save(uploaded_file_path)
+
+                # create thumbnail after saving
+                if mime_type.startswith('image'):
+                    create_thumbnail(filename)
+                
+                # get file size after saving
+                size = os.path.getsize(uploaded_file_path)
+
+                # return json for js call back
+                result = uploadfile(name=filename, type=mime_type, size=size)
+            
+            return simplejson.dumps({"files": [result.get_file()]})
+
+    if request.method == 'GET':
+        # get all file in ./data directory
+        files = [f for f in os.listdir(app.config['UPLOAD_FOLDER']) if os.path.isfile(os.path.join(app.config['UPLOAD_FOLDER'],f)) and f not in IGNORED_FILES ]
+        
+        file_display = []
+
+        for f in files:
+            size = os.path.getsize(os.path.join(app.config['UPLOAD_FOLDER'], f))
+            file_saved = uploadfile(name=f, size=size)
+            file_display.append(file_saved.get_file())
+
+        return simplejson.dumps({"files": file_display})
+
+    return redirect(url_for('index'))
+
+
+
+@app.route("/delete/<string:filename>", methods=['DELETE'])
+def delete(filename):
+    file_path = os.path.join(app.config['UPLOAD_FOLDER'], filename)
+    file_thumb_path = os.path.join(app.config['THUMBNAIL_FOLDER'], filename)
+
+    if os.path.exists(file_path):
+        try:
+            os.remove(file_path)
+
+            if os.path.exists(file_thumb_path):
+                os.remove(file_thumb_path)
+            
+            return simplejson.dumps({filename: 'True'})
+        except:
+            return simplejson.dumps({filename: 'False'})
+
+
+# serve static files
+@app.route("/thumbnail/<string:filename>", methods=['GET'])
+def get_thumbnail(filename):
+    return send_from_directory(app.config['THUMBNAIL_FOLDER'], filename=filename)
+
+
+@app.route("/data/<string:filename>", methods=['GET'])
+def get_file(filename):
+    return send_from_directory(os.path.join(app.config['UPLOAD_FOLDER']), filename=filename)
+
+
+# @app.route("/_upload", methods=['POST'])
+# def _upload():
+
+#     # Validate the request body contains JSON
+#     if request.is_json:
+#         # Parse the JSON into a Python dictionary
+#         req = request.get_json()
+#         # Print the dictionary
+#         uploadRequest=json.dumps([req],indent=4);  #TODO : remove indent if not DEBUG
+#         pprint(uploadRequest);
+        
+#         return make_response(json.dumps( {'source':node, 'changeIsAccepted' : changeDone, 'message': message} ))
+#         # Return a string along with an HTTP status code
+#         # return "JSON received!", 200
+#     else:
+#         print(request)
+#         files = request.files['files']
+#         if files:
+#             result=catalogs.save(files)
+#         return make_response(json.dumps( {"files": ["coucou"]} ))
+#             # if request.method == 'POST' and 'files' in request.files:
+            
+
+#         # The request body wasn't JSON so return a 400 HTTP status code
+#         return "Request was not JSON", 400
+#         #return make_response(jsonify({"message": "Request body must be JSON"}), 400)
+
+# For example, you may want to override how request parameters are handled to preserve their order:
+# from flask import Flask, Request
+# from werkzeug.datastructures import ImmutableOrderedMultiDict
+# class MyRequest(Request):
+#     """Request subclass to override request parameter storage"""
+#     parameter_storage_class = ImmutableOrderedMultiDict
+# class MyFlask(Flask):
+#     """Flask subclass using the custom request class"""
+#     request_class = MyReq
+
+def create_app(test_config=None):
+    # create and configure the app
+    app = Flask(__name__, instance_relative_config=True)
+    app.config.from_mapping(
+        SECRET_KEY='dev',
+        DATABASE=os.path.join(app.instance_path, 'flaskr.sqlite'),
+    )
+
+    if test_config is None:
+        # load the instance config, if it exists, when not testing
+        app.config.from_pyfile('config.py', silent=True)
+    else:
+        # load the test config if passed in
+        app.config.from_mapping(test_config)
+
+    # ensure the instance folder exists
+    try:
+        os.makedirs(app.instance_path)
+    except OSError:
+        pass
+
+    return app
+
+if __name__ == "__main__":
+    app.run(host="localhost", port=8321, debug=True)
+
+
+
+
+#$("#386bc28a2ff811ec853cac220bca9aa6").html('<label class="form-check-label">Test1<input type="text" class="form-control form-control-sm"></label><essai>Essai</essai>')
+
+# $("#550f63502b6611ecab61ac220bca9aa6").hover(function(){
+#   alert("The text has been changed.");
+# });
+
+# $("#1f3ca24a2cf911ecab61ac220bca9aa6").hover(function(){
+#   $.post("{{ url_for('static', filename='demo_test.txt') }}",
+#   {
+#     id: $(this).attr("id"),
+#   },
+#   function(data, status){
+#     alert("Data: " + data + "\nStatus: " + status);
+#   });
+# });
+
+# $("#e0a1f2862cfa11ecab61ac220bca9aa6").hover(function(){
+#   $.post("http://127.0.0.1:8123/test1",
+#   {
+#     id: $(this).attr("id"),
+#   },
+#   function(data, status){
+#     alert("Data: " + data + "\nStatus: " + status);
+#   });
+# });
+
+# $(".MCSIMPValide").hover(function(){
+#   alert("-5-- :"+ $(this).text() + $("#e0a1f2862cfa11ecab61ac220bca9aa6").text() + $(this).attr("class") + $(this).attr("id"));
+#   $.post("http://127.0.0.1:8123/test1",
+#   {
+#     id: $(this).attr("id"),
+#   },
+#   function(data, status){
+#     alert("Data: " + data + "\nStatus: " + status +"\nId :", $(this).attr("id") );
+#   });
+# });
+
+
+# # # $("#550f63502b6611ecab61ac220bca9aa6")[0].id
+# $("#e0a1f2862cfa11ecab61ac220bca9aa6").hover(function(){
+#   alert("-4-- :"+ $(this).text() + $("#e0a1f2862cfa11ecab61ac220bca9aa6").text() + $(this).attr("class") + $(this).attr("id"));
+# });
+
+# $("#550f63502b6611ecab61ac220bca9aa6")[0].id
+
+#$("#ec7abddd2dca11ecab61ac220bca9aa6").parents()[0]
+# Pour obtenir le nodeid treeview a partir de l'eficasID
+#$("#ec7abddd2dca11ecab61ac220bca9aa6").parent()[0].attributes['data-nodeid'].value