diff --git a/YKWIM/generateJSON.py b/YKWIM/generateJSON.py
index 57836401c334e9e0d1b147b019b85e467dbd642a..0f174cbbc0a91f94e46678f2785c804c59a18b3d 100644
--- a/YKWIM/generateJSON.py
+++ b/YKWIM/generateJSON.py
@@ -59,6 +59,7 @@ def generateJSON(file, path=app.config["UPLOAD_FOLDER"]):
         element["name"]= enum[0]
         element["definition"]= enum[2]
         element["IRI"]= enum[1]
+        element["source"]=enum[3]
         list.append(element)
     d["enumerations"]=list
     enum_exist = True if (len(list)!=0) else False
diff --git a/YKWIM/generateOntology.py b/YKWIM/generateOntology.py
new file mode 100644
index 0000000000000000000000000000000000000000..95f9a752d498c446c89b4ebff190b5f79f55cea3
--- /dev/null
+++ b/YKWIM/generateOntology.py
@@ -0,0 +1,123 @@
+from YKWIM import helpers as h
+import json
+from YKWIM import app
+from rdflib.namespace import RDF, RDFS, OWL, SKOS
+from rdflib import Graph, Literal, URIRef, Namespace
+
+def generateOntology(path=app.config["UPLOAD_FOLDER"],ontology_namespace = "https://data.grandlyon.com/onto/",vocabulary_namespace ="https://data.grandlyon.com/vocab/"):
+    """generate ontology files from JSON"""
+    f=open(path + "parsing_result.json")
+
+    VS = Namespace("http://www.w3.org/2003/06/sw-vocab-status/ns#")
+    d=json.load(f)
+    g = Graph()
+    g2 = Graph ()
+    
+    #------------------------
+    #Classes
+    #------------------------
+    for cl in filter (lambda value: False if value["IRI"]!='' else True,d["classes"]):
+        classeURI = URIRef(ontology_namespace + h.convertToPascalcase(cl["name"]))
+
+        #update json file 
+        list_of_all_values = [elem["name"] for elem in d["classes"]]
+        index= list_of_all_values.index(cl["name"])
+        d["classes"][index]["IRI"]=str(classeURI)
+        
+        g.add((classeURI,RDF.type, OWL.Class))    
+        g.add((classeURI,RDFS.label, Literal(cl["name"])))
+        g.add((classeURI,RDFS.comment, Literal(cl["definition"])))
+        g.add((classeURI,RDFS.isDefinedBy, URIRef(ontology_namespace)))
+        g.add((classeURI,VS.term_status, Literal("testing")))
+
+    #------------------------
+    #Data Properties
+    #------------------------
+    for cl in d["classes"]:
+        for attr in filter (lambda value: False if value["IRI"]!='' else True,cl["attributes"]):
+            attributeURI = URIRef(ontology_namespace + h.convertToCamelcase(attr["name"]))
+
+            #update json file
+            list_of_all_values1 = [elem["name"] for elem in d["classes"]]
+            index1= list_of_all_values1.index(cl["name"])
+            
+            list_of_all_values2 = [elem["name"] for elem in d["classes"][index1]["attributes"]]
+            index2= list_of_all_values2.index(attr["name"])
+            d["classes"][index1]["attributes"][index2]["IRI"]=str(attributeURI)
+
+            g.add((attributeURI,RDF.type, OWL.DatatypeProperty))    
+            g.add((attributeURI,RDFS.label, Literal(attr["name"])))
+            g.add((attributeURI,RDFS.comment, Literal(attr["definition"])))
+            g.add((attributeURI,RDFS.isDefinedBy, URIRef(ontology_namespace)))
+            g.add((attributeURI,VS.term_status, Literal("testing")))
+
+    #------------------------
+    #Object Properties
+    #------------------------
+    for ass in filter (lambda value: False if value["IRI"]!='' else True,d["associations"]):
+        associationURI = URIRef(ontology_namespace + h.convertToCamelcase(ass["name"]))
+
+        #update json file
+        list_of_all_values = [elem["name"] for elem in d["associations"]]
+        index= list_of_all_values.index(ass["name"])
+        d["associations"][index]["IRI"]=str(associationURI)
+        
+        g.add((associationURI,RDF.type, OWL.ObjectProperty))    
+        g.add((associationURI,RDFS.label, Literal(ass["name"])))
+        g.add((associationURI,RDFS.comment, Literal(ass["definition"])))
+        g.add((associationURI,RDFS.isDefinedBy, URIRef(ontology_namespace)))
+        g.add((associationURI,VS.term_status, Literal("testing")))
+
+    #------------------------
+    #Individuals
+    #------------------------
+    index = None
+    for enum in d["enumerations"]:
+        if enum["IRI"]!="":
+            enumerationURI = URIRef(enum["IRI"])
+        else :
+            enumerationURI = URIRef(vocabulary_namespace + h.convertToPascalcase(enum["name"]))
+
+            #update json file
+            list_of_all_values = [elem["name"] for elem in d["enumerations"]]
+            index= list_of_all_values.index(enum["name"])
+            d["enumerations"][index]["IRI"]=str(enumerationURI)
+
+            g2.add((enumerationURI, RDF.type, SKOS.ConceptScheme))
+            g2.add((enumerationURI,SKOS.prefLabel, Literal(enum["name"])))
+            g2.add((enumerationURI,SKOS.definition, Literal(enum["definition"])))
+            g2.add((enumerationURI,RDFS.isDefinedBy, URIRef(vocabulary_namespace)))
+            g2.add((enumerationURI,VS.term_status, Literal("testing")))
+            
+        for val in filter(lambda value: False if value["IRI"]!="" else True, enum["values"]):     
+            valueURI = URIRef(vocabulary_namespace + h.convertToSnakecase(val["name"]))
+
+            if index!=None : #previous creation of an enumeration URI, the index is already known
+                list_of_all_values2 = [elem["name"] for elem in d["enumerations"][index]["values"]]
+                index2= list_of_all_values2.index(val["name"])
+                d["enumerations"][index]["values"][index2]["IRI"]=str(valueURI)
+            else :
+                list_of_all_values = [elem["name"] for elem in d["enumerations"]]
+                index= list_of_all_values.index(enum["name"])
+                
+                list_of_all_values2 = [elem["name"] for elem in d["enumerations"][index]["values"]]
+                index2= list_of_all_values2.index(val["name"])
+                d["enumerations"][index]["values"][index2]["IRI"]=str(valueURI)
+
+            g2.add((valueURI,RDF.type, SKOS.Concept))    
+            g2.add((valueURI,SKOS.prefLabel, Literal(val["name"])))
+            g2.add((valueURI,SKOS.definition, Literal(val["definition"])))
+            g2.add((valueURI,RDFS.isDefinedBy, URIRef(vocabulary_namespace)))
+            g2.add((valueURI,SKOS.inScheme, enumerationURI))
+            g2.add((valueURI,VS.term_status, Literal("testing")))
+
+    ontology_path = path + "ontology.ttl"
+    vocabulary_path = path + "vocabulary.ttl"
+    json_path = path + "parsing_result_completed.json"
+    with open(ontology_path, 'w') as fo, open(vocabulary_path, 'w') as fv, open(json_path, 'w') as fp :
+        fo.write(g.serialize(format="turtle"))
+        fv.write(g2.serialize(format="turtle"))
+        json.dump(d,fp)
+    
+    return g.serialize(format="turtle") + g2.serialize(format="turtle")
+    
diff --git a/YKWIM/generateSparqlGenerateQuery.py b/YKWIM/generateSparqlGenerateQuery.py
index 08276e87b0ff222d9058cf4946c245aa9ff8e720..beee448f79a7aaebcde48b733b9dc831e2c2d159 100644
--- a/YKWIM/generateSparqlGenerateQuery.py
+++ b/YKWIM/generateSparqlGenerateQuery.py
@@ -1,11 +1,7 @@
-##### to review completly
-import sys
-import subprocess
-import generateJSON as g
-import uuid
-import os
+from YKWIM import app, helpers as h
+import json
 
-def generateSparqlGenerateQuery (d, dataset) :
+def generateSparqlGenerateQuery (dataset,path=app.config["UPLOAD_FOLDER"],vocabulary_namespace ="https://data.grandlyon.com/vocab/", instances_namespace = "https://data.grandlyon.com/id/") :
     """generate SPARQL Generate query"""
 
     s = """PREFIX iter: <http://w3id.org/sparql-generate/iter/>
@@ -14,51 +10,37 @@ def generateSparqlGenerateQuery (d, dataset) :
     PREFIX xsd: <http://www.w3.org/2001/XMLSchema#>
     GENERATE {\n"""
 
+    f=open(path + "parsing_result_completed.json")
+    d=json.load(f)
+    
     #iterate over classes
     for list in d["classes"]:
-        if (list["IRI"]!=""):
-            s+=("?{} a <{}>".format(list["name"],list["IRI"]))+ ";\n"
-        else: s+=("?{} a <http://data.grandlyon.com/ontology/{}".format(list["name"],list["name"]))+ ">;\n"
-        
+        s+=("?{} a <{}>".format(h.convertToPascalcase(list["name"]),list["IRI"]))+ ";\n"
+
         #iterate over attributes
         l=len(list["attributes"])
         for i , attr in enumerate(list["attributes"]):
-            if (attr["IRI"]!=""):
-                s+=("\t<{}> ?{}".format(attr["IRI"],attr["name"]))
-            else: s+=("\t<http://data.grandlyon.com/ontology/{}> ?{}".format(attr["name"],attr["name"]))
+            s+=("\t<{}> ?{}".format(attr["IRI"],h.convertToPascalcase(attr["name"])))
             s+=";\n" if (i<l-1) else ".\n"
     
     #iterate over associations
     for list in d["associations"]:
-        if (list["IRI"]!=""):
-            s += ("?{} <{}> ?{}".format(list["source"],list["IRI"],list["destination"])) + ".\n"
-        else : s += ("?{} <http://data.grandlyon.com/ontology/{}> ?{}".format(list["source"],list["name"],list["destination"])) + ".\n"
-    
-    #iterate over enumerations
-    for list in d["enumerations"]:
-        if (list["IRI"]!=""):
-            s+=("?{} a <{}>".format(list["name"],list["IRI"]))+ ".\n"
-        else: s+=("?{} a <http://data.grandlyon.com/vocabulary/{}".format(list["name"],list["name"]))+ ">.\n"
+        s += ("?{} <{}> ?{}".format(h.convertToPascalcase(list["source"]),list["IRI"],h.convertToPascalcase(list["destination"]))) + ".\n"
     
     s+=("}} \n SOURCE <{}> AS ?source \nITERATOR iter:GeoJSON(?source) AS ?geometricCoordinates ?properties \n WHERE {{\n".format(dataset))
 
     #bindings
     for list in d["classes"]:
         for attr in list["attributes"]:
-            s+=('BIND (fun:JSONPath(?properties,"$.{}") AS ?{})\n'.format(attr["source"], attr["name"])) 
+            s+=('BIND (fun:JSONPath(?properties,"$.{}") AS ?{})\n'.format(attr["source"], h.convertToPascalcase(attr["name"]))) 
             if (attr["id"]=="oui") :
-                s+=('BIND(IRI(CONCAT("http://data.grandlyon.com/id/{}/",fun:JSONPath(?properties,"$.{}"))) AS ?{})\n'.format(list["name"],attr["source"],list["name"]))
-    
+                s+=('BIND(IRI(CONCAT("{}/",fun:JSONPath(?properties,"$.{}"))) AS ?{})\n'.format(instances_namespace+ h.convertToPascalcase(list["name"]),attr["source"],h.convertToPascalcase(list["name"])))
+        
     for enum in d["enumerations"]:
-        s+=('BIND(IRI("http://data.grandlyon.com/vocabulary/Cadres_administratifs") AS ?{})\n'.format(enum["name"]))
+        s+=('BIND(IRI(CONCAT("{}",REPLACE(LCASE(fun:JSONPath(?properties,"$.{}"))," ","_"))) AS ?{})\n'.format(vocabulary_namespace,enum["source"],h.convertToPascalcase(enum["name"])))
     
     s+= "}\n"
-    base_name = "query_"+str(uuid.uuid4())
-    query_path = base_name + ".rq"
+    query_path = path + "query.rq"
     with open(query_path, 'w') as fp:
-        fp.write(s) 
-    return query_path
-
-if __name__ == "__main__":
-    #dataset = "https://download.data.grandlyon.com/wfs/grandlyon?SERVICE=WFS&VERSION=2.0.0&request=GetFeature&typename=adr_voie_lieu.adrcomgl&outputFormat=application/json;%20subtype=geojson&SRSNAME=EPSG:4171"
-    print (generateSparqlGenerateQuery(g.generateJSON(sys.argv[1]), sys.argv[2]))
\ No newline at end of file
+        fp.write(s)
+    return query_path
\ No newline at end of file
diff --git a/YKWIM/helpers.py b/YKWIM/helpers.py
new file mode 100644
index 0000000000000000000000000000000000000000..58a6973edb383ecd50b7ca3b984c06718facd88c
--- /dev/null
+++ b/YKWIM/helpers.py
@@ -0,0 +1,23 @@
+from re import sub
+
+def convertToSnakecase(s):
+    '''
+    >>> ConvertToSnakecase("Hello World Python Programming")
+    hello_world_python_programming 
+    '''
+    return '_'.join(sub('([A-Z][a-z]+)', r' \1',sub('([A-Z]+)', r' \1',s.replace('-', ' '))).split()).lower()
+
+def convertToCamelcase(s):
+    '''
+    >>> ConvertToCamelcase("Hello World Python Programming")
+    helloWorldPythonProgramming 
+    '''
+    s = sub(r"(_|-)+", " ", s).title().replace(" ", "")
+    return ''.join([s[0].lower(), s[1:]])
+
+def convertToPascalcase(s):
+    '''
+    >>> convertToPascalcase("Hello World Python Programming")
+    HelloWorldPythonProgramming
+    '''
+    return "".join(w[0].upper() + w[1:].lower() for w in s.split()) 
\ No newline at end of file
diff --git a/YKWIM/static/jar/lutra.jar b/YKWIM/static/jar/lutra.jar
new file mode 100644
index 0000000000000000000000000000000000000000..67130a9da717a2bbcc99267d6c6e21caca5b1de3
Binary files /dev/null and b/YKWIM/static/jar/lutra.jar differ
diff --git a/YKWIM/templates/index.html b/YKWIM/templates/index.html
index d1f1447fea27b982b0301acb689008b00c8ccdf6..557fe0276bbc16a7613bdac27610260dfcba142b 100644
--- a/YKWIM/templates/index.html
+++ b/YKWIM/templates/index.html
@@ -51,7 +51,7 @@
                     <div class="col-8">
                         <div class="row pt-3">
                             <div class="col-8 offset-7">
-                                <a href="{{url_for('static',filename='/doc/template.ods')}}">
+                                <a href="{{url_for('static',filename='/doc/template.xlsx')}}">
                                     <img src="{{url_for('static',filename='/img/spreadsheet.ico')}}" alt="Lien de téléchargement du Template" class="img-thumbnail img-fluid col-2">
                                 </a>
                             </div>
@@ -92,7 +92,11 @@
                 {% if uml_image %}
                 <div class="container py-3">
                     <div class="row justify-content-center">
-                        <p class="col-6 border">Données RDF</p>
+                        <p class="col-6 border">
+                            {% for line in b_lines %}
+                                {{ line }}
+                            {% endfor %}
+                        </p>
                         <img src="{{url_for('getDocumentLink',document_link=uml_image)}}" class="col-6 border" alt="UML class diagram">
                     </div>
                 </div>
@@ -103,12 +107,26 @@
                 <div class="row justify-content-center">
                     <form class="col-8">
                         <div class="row py-3">
-                            <label for="ontologyURL" class="col-4 form-label col-form-label">URL</label>
+                            <label for="vocabularyURL" class="col-4 form-label col-form-label">URL de base des vocabulaires contrôlés</label>
                             <div class="col-8">
-                                <input type="url" id="ontologyURL" class="form-control" aria-describedby="ontologyURLHelp"> 
+                                <input type="url" id="vocabularyURL" class="form-control" value="{{vocabulary_namespace}}" aria-describedby="vocabularyURLHelp"> 
+                                <span id="vocabularyURLHelp" class="form-text visually-hidden"></span>
+                            </div>
+                        </div>
+                        <div class="row py-3">
+                            <label for="ontologyURL" class="col-4 form-label col-form-label">URL de base des ontologies</label>
+                            <div class="col-8">
+                                <input type="url" id="ontologyURL" class="form-control" value="{{ontology_namespace}}" aria-describedby="ontologyURLHelp"> 
                                 <span id="ontologyURLHelp" class="form-text visually-hidden"></span>
                             </div>
                         </div>
+                        <div class="row py-3">
+                            <label for="intancesURL" class="col-4 form-label col-form-label">URL de base des instances</label>
+                            <div class="col-8">
+                                <input type="url" id="intancesURL" class="form-control" value="{{instances_namespace}}" aria-describedby="instancesURLHelp"> 
+                                <span id="instancesURLHelp" class="form-text visually-hidden"></span>
+                            </div>
+                        </div>
                     </form>
                 </div>
             </div>
diff --git a/YKWIM/validateTemplate.py b/YKWIM/validateTemplate.py
index b91597515e85213463bbcae2d34f43d474efb735..4fd66e2b68f2e94b20f5f20d3e879afc3f9b26c9 100644
--- a/YKWIM/validateTemplate.py
+++ b/YKWIM/validateTemplate.py
@@ -1,4 +1,5 @@
 import pyexcel as p
+from YKWIM import helpers as h
 
 def validateTemplate(file):
     
@@ -19,6 +20,9 @@ def validateTemplate(file):
     #attributes sheet validation
     #------------------------
 
+    attribute_total_number = 0
+    set_of_attributes = set()
+
     #0. Le nom de la classe du 1er attribut est obligatoire
     if (book["Attributs"][1][0]=='') : return "Le nom de la classe du 1er attribut est obligatoire"
 
@@ -37,9 +41,14 @@ def validateTemplate(file):
             classe=attr[0]
             list=[]
         list.append(attr[5])
+        attribute_total_number+=1
+        set_of_attributes.add(h.convertToCamelcase(attr[1]))
 
     # vérifier l'identifiant de la dernière classe
     if ("oui" not in list) : return f"la classe {classe} n'a pas d'identifiant"
+
+    #3. Pas d'attributs avec le même nom dans le modèle UML
+    if len(set_of_attributes)< attribute_total_number : return f"Des attributs avec le même nom existent dans votre modèle UML"
     
     #------------------------
     #associations sheet validation
@@ -52,10 +61,13 @@ def validateTemplate(file):
     #------------------------
     #enumerations sheet validation
     #------------------------
-
+    enum_exit=False
     #0. Chaque énumération doit avoir un lien de référence ou une définition
     for enum in filter(lambda value:True if value[0]!='' else False, book["Énumérations"][1:]): 
-        if (enum[1]=='' and enum[2]==''): return f"L'énumération {enum[0]} doit avoir un lien de référence ou une définition" 
+        if (enum[1]=='' and enum[2]==''): return f"L'énumération {enum[0]} doit avoir un lien de référence ou une définition"
+
+        #1. La source de chaque énumération est obligatoire
+        if (enum[3]==''): return f"L'énumération {enum[0]} n'a pas de source"        
         enum_exit=True #une énumération existe dans le diagramme UML
     
     #------------------------
diff --git a/config.py b/config.py
index e8045d5e218000400fe640640c73caca6785c82b..555e3800b2bffc3d7760091f02d3721561a19828 100644
--- a/config.py
+++ b/config.py
@@ -7,6 +7,9 @@ class Config(object):
     DEBUG = False
     TESTING = False
     UPLOAD_FOLDER = ""
+    ONTOLOGY_NAMESPACE = "https://data.grandlyon.com/onto/"
+    VOCABULARY_NAMESPACE ="https://data.grandlyon.com/vocab/"
+    INSTANCES_NAMESPACE = "https://data.grandlyon.com/id/"
 
 class ProductionConfig(Config):
     UPLOAD_FOLDER = MYDIR + "/tmp/"