From 4f9a2a5816134f417f15a6bde01c32a432e978d1 Mon Sep 17 00:00:00 2001
From: George Marchment <georgemarchment@yahoo.fr>
Date: Wed, 18 Jun 2025 15:28:14 +0200
Subject: [PATCH] Added Ro-crate generation

---
 src/code_.py                    |   3 +
 src/include.py                  |   3 +
 src/main.py                     |  55 +++++-
 src/nextflow_building_blocks.py |  14 +-
 src/nextflow_file.py            |  19 ++
 src/process.py                  |  56 ++++++
 src/ro_crate.py                 | 297 +++++++++++++++++++++++++++-----
 src/subworkflow.py              |  62 ++++++-
 src/workflow.py                 |  15 ++
 9 files changed, 475 insertions(+), 49 deletions(-)

diff --git a/src/code_.py b/src/code_.py
index 3b2ead5..5a9ee7e 100644
--- a/src/code_.py
+++ b/src/code_.py
@@ -476,4 +476,7 @@ class Code:
     
     def get_file_address(self):
         return self.origin.get_file_address()
+    
+    def get_nextflow_file(self):
+        return self.origin.get_nextflow_file()
     
\ No newline at end of file
diff --git a/src/include.py b/src/include.py
index a86fdd9..ce217e8 100644
--- a/src/include.py
+++ b/src/include.py
@@ -38,6 +38,9 @@ class Include(Nextflow_Building_Blocks):
     def get_duplicate_status(self):
         return self.nextflow_file_origin.get_duplicate_status()
 
+    def get_nextflow_file(self):
+        return self.nextflow_file
+
 
     #def get_list_name_includes(self):
     #    if(self.get_duplicate_status()):
diff --git a/src/main.py b/src/main.py
index b6514af..e12a175 100644
--- a/src/main.py
+++ b/src/main.py
@@ -98,6 +98,9 @@ class Main(Nextflow_Building_Blocks):
     
     def get_file_address(self):
         return self.nextflow_file.get_file_address()
+    
+    def get_nextflow_file(self):
+        return self.nextflow_file
 
 
     def get_output_dir(self):
@@ -254,4 +257,54 @@ class Main(Nextflow_Building_Blocks):
         
     
     def check_that_a_channel_is_not_defined_used_and_redefined_used_in_another_block(self):
-        return self.root.check_that_a_channel_is_not_defined_used_and_redefined_used_in_another_block()
\ No newline at end of file
+        return self.root.check_that_a_channel_is_not_defined_used_and_redefined_used_in_another_block()
+    
+    
+
+    #=========================================================
+    #-----------------------RO-CRATE--------------------------
+    #=========================================================
+
+
+    def add_2_rocrate(self, dico):
+        #By definition we add this info
+        dico["@graph"].append({ "@id": "#nextflow", "@type": "ComputerLanguage", "name": "Nextflow", "identifier": {   "@id": "https://www.nextflow.io/" }, "url": {   "@id": "https://www.nextflow.io/" }})
+        parent_key = self.nextflow_file.get_file_rocrate_key(dico)
+        self.nextflow_file.add_computational_workflow_to_types(dico)
+        main_key = f"{parent_key}#MAIN"
+        dico_main = get_dico_from_tab_from_id(dico, main_key)
+        if(dico_main==None):
+            dico_main = {}
+            dico_main["@id"] = main_key
+            dico_main["name"] = "Main Workflow"
+            dico_main["@type"] = ["SoftwareSourceCode", "ComputationalWorkflow"]
+            #TODO -> check if this remains true
+            #dico_main["conformsTo"] = {"@id": "https://bioschemas.org/profiles/ComputationalWorkflow/0.5-DRAFT-2020_07_21"}
+            #dico_main["dct:conformsTo"]= "https://bioschemas.org/profiles/ComputationalWorkflow/1.0-RELEASE/"
+            dico_main["input"] = []
+            dico_main["output"] = []
+            dico_main["isPartOf"] = [{"@id": parent_key}]
+            dico_main["hasPart"] = []
+            called = []
+            for call in self.root.get_all_calls_from_root():
+                called.append(call.get_first_element_called())
+            for c in called:
+                c.add_2_rocrate(dico, main_key)
+                dico_main["hasPart"].append({"@id":c.get_rocrate_key(dico)})
+                
+            dico["@graph"].append(dico_main)
+        self.nextflow_file.add_to_has_part(dico, main_key)
+
+        #Remove duplicates from dico["@graph"]
+        duplicates = []
+        seen = []
+        for ele in dico["@graph"]:
+            if(ele in seen):
+                duplicates.append(ele)
+            else:
+                seen.append(ele)
+        for ele in duplicates:
+            dico["@graph"].remove(ele)
+
+        
+    
\ No newline at end of file
diff --git a/src/nextflow_building_blocks.py b/src/nextflow_building_blocks.py
index aba604f..c27c2d0 100644
--- a/src/nextflow_building_blocks.py
+++ b/src/nextflow_building_blocks.py
@@ -51,6 +51,13 @@ class Nextflow_Building_Blocks:
     def get_file_address(self):
         return self.origin.get_file_address()
     
+    def get_nextflow_file(self):
+        try:
+            return self.origin.get_file_address()
+        except:
+            return self.nextflow_file
+        
+    
     def get_display_info(self):
         return self.origin.get_display_info()
     
@@ -91,10 +98,13 @@ class Nextflow_Building_Blocks:
     
     
     def get_rocrate_key(self, dico):
-        return f"{self.get_file_address()[len(dico['temp_directory'])+1:]}#{self.get_name()}"
+        return f"{str(self.get_file_address())[len(dico['temp_directory']):]}#{self.get_name()}"
 
     def get_file_address(self):
-        return self.origin.get_file_address()
+        try:
+            return self.origin.get_file_address()
+        except:
+            return self.nextflow_file.get_file_address()
     
     def get_workflow_address(self):
         return self.origin.get_workflow_address()
diff --git a/src/nextflow_file.py b/src/nextflow_file.py
index 14c6fa2..f4aef0e 100644
--- a/src/nextflow_file.py
+++ b/src/nextflow_file.py
@@ -29,6 +29,7 @@ class Nextflow_File(Nextflow_Building_Blocks):
         self.subworkflows = []
         self.functions = []
         self.initialised = False
+        self.added_2_rocrate = False
         contents = check_file_exists(self.get_file_address(), self)
         Nextflow_Building_Blocks.__init__(self, contents, initialise_code=True)
         self.check_file_correctness()
@@ -59,6 +60,9 @@ class Nextflow_File(Nextflow_Building_Blocks):
     def get_file_address(self):
         return Path(os.path.normpath(self.address))
     
+    def get_nextflow_file(self):
+        return self
+    
     def get_DSL(self):
         return self.workflow.get_DSL()
     
@@ -395,3 +399,18 @@ class Nextflow_File(Nextflow_Building_Blocks):
                 
             else:
                 raise Exception("This shouldn't happen")
+        
+    def add_to_has_part(self, dico, to_add_key):
+        file_name = str(self.get_file_address())[len(dico["temp_directory"]):]
+        file_dico = get_dico_from_tab_from_id(dico, file_name)
+        file_dico["hasPart"].append({"@id":to_add_key})
+
+    
+    def add_computational_workflow_to_types(self, dico):
+        file_name = str(self.get_file_address())[len(dico["temp_directory"]):]
+        file_dico = get_dico_from_tab_from_id(dico, file_name)
+        file_dico["@type"].append("ComputationalWorkflow")
+
+    def get_file_rocrate_key(self, dico):
+        file_name = str(self.get_file_address())[len(dico["temp_directory"]):]
+        return file_name
diff --git a/src/process.py b/src/process.py
index e887430..3ffb410 100644
--- a/src/process.py
+++ b/src/process.py
@@ -593,5 +593,61 @@ class Process(Nextflow_Building_Blocks):
             return self.origin.get_all_conditions(conditions)
 
 
+    def add_2_rocrate(self, dico, parent_key):
+        process_key = self.get_rocrate_key(dico)
+        dico_process = get_dico_from_tab_from_id(dico, process_key)
+        if(dico_process==None):
+            dico_process = {}
+            dico_process["@id"] = process_key
+            dico_process["name"] = f"Process#{self.get_alias()}"
+            dico_process["@type"] = ["SoftwareSourceCode"]
+            #ADD INPUTS
+            dico_process["input"] = []
+            for input in self.get_inputs():
+                if(type(input)==str):
+                    name_input = input
+                else:
+                    name_input = input.get_code()
+                dico_input = get_dico_from_tab_from_id(dico, name_input)
+                if(dico_input==None):
+                    dico_input = {"@id":f"#{name_input}", "name": name_input, "@type": "FormalParameter"}
+                    dico["@graph"].append(dico_input)
+                dico_process["input"].append({"@id":dico_input["@id"]})
+            #ADD OUTPUTS
+            dico_process["output"] = []
+            for output in self.get_outputs():
+                if(type(output)==str):
+                    name_output = output
+                else:
+                    name_output = output.get_code()
+                dico_output = get_dico_from_tab_from_id(dico, name_output)
+                if(dico_output==None):
+                    dico_output = {"@id":f"#{name_output}", "name": name_output, "@type": "FormalParameter"}
+                    dico["@graph"].append(dico_output)
+                dico_process["output"].append({"@id":dico_output["@id"]})
+            #ADD isPartOf
+            dico_process["isPartOf"] = []
+            dico_process["isPartOf"].append({"@id":parent_key})
+            #ADD hasPart
+            dico_process["hasPart"] = []
+            for tool in self.get_tools():
+                dico_tool = get_dico_from_tab_from_id(dico, tool)
+                if(dico_tool==None):
+                    dico_tool = {"@id":tool, 
+                                   "name": tool,
+                                   "@type": "Tool"
+                                   #TODO in later versions
+                                   , "url": f"https://bio.tools/t?page=1&q={tool}&sort=score"
+                                   #, "identifier": "tool_identifier"
+                                   }
+                    dico["@graph"].append(dico_tool)
+                dico_process["hasPart"].append({"@id":dico_tool["@id"]})
+
+            dico["@graph"].append(dico_process)
+        else:
+            if(not check_if_element_in_tab_rocrate(dico_process["isPartOf"], parent_key)):
+                dico_process["isPartOf"].append({"@id":parent_key})
+        self.get_nextflow_file().add_to_has_part(dico, process_key)
+
 
 
diff --git a/src/ro_crate.py b/src/ro_crate.py
index 186c53f..935f541 100644
--- a/src/ro_crate.py
+++ b/src/ro_crate.py
@@ -19,19 +19,228 @@ from . import constant
 
 
 class RO_Crate:
-    def __init__(self, workflow):
+    def __init__(self, workflow, personnal_acces_token = None,
+                 display_info=False,
+                 datePublished=None, description=None,
+                  license=None, authors = None,
+                   publisher = None, keywords = None,
+                   producer = None):
         self.workflow = workflow
-        self.directory = '/'.join(workflow.get_file_address().split('/')[:-1])
+        self.directory = workflow.get_root_directory()
+        self.personnal_acces_token = personnal_acces_token
+        self.display_info = display_info
         self.files = []
         self.dico = {}
+        self.info_dico_workflow = {}
+        self.log = ""
         self.dico["temp_directory"] = self.directory
+        self.datePublished = datePublished
+        self.description = description
+        self.license = license
+        self.authors = authors
+        self.publisher = publisher
+        self.keywords = keywords
+        self.producer = producer
+
+        self.fill_log()
+        self.workflow_git_name = self.set_address()
+        self.fill_info_dico_workflow()
+
+    def set_address(self):
+        address = ""
+        current_directory = os.getcwd()
+        os.chdir(self.directory)
+        try:
+            os.system(f"git ls-remote --get-url origin > temp_address_{id(self)}.txt")
+            with open(f'temp_address_{id(self)}.txt') as f:
+                address = f.read()
+            os.system(f"rm temp_address_{id(self)}.txt")
+        except:
+            None
+        os.chdir(current_directory)
+        for match in re.finditer(r"https:\/\/github\.com\/([^\.]+)\.git", address):
+            address = match.group(1)
+            return address
+        return ""
+
+    def fill_info_dico_workflow(self):
+        current_directory = os.getcwd()
+        os.chdir(self.directory)
+        try:
+            if(self.personnal_acces_token!=None):
+                command = f'curl --silent --request GET --url "https://api.github.com/repos/{self.workflow_git_name}" --header "Authorization: Bearer {self.personnal_acces_token}" --header "X-GitHub-Api-Version: 2022-11-28" > temp_dico_{id(self)}.json'
+            else:
+                command = f'curl --silent --request GET --url "https://api.github.com/repos/{self.workflow_git_name}" > temp_dico_{id(self)}.json'
+            _ = os.system(command)
+            with open(f'temp_dico_{id(self)}.json') as json_file:
+                self.info_dico_workflow = json.load(json_file)
+            os.system(f"rm temp_dico_{id(self)}.json")
+            
+        except:
+            _ = os.system(f"rm temp_dico_{id(self)}.json")
+        if(self.display_info):
+            if(self.info_dico_workflow=={}):
+                print("Unable to retrieve information regarding the commits")
+            else:
+                print("Successfully retrieved information regarding the commits")
+        os.chdir(current_directory)
+
+
+    def fill_log(self):
+        """Method that reads the git log and saves it
+
+        Keyword arguments:
+        
+        """
+        current_directory = os.getcwd()
+        os.chdir(self.directory)
+        try:
+
+            os.system(f"git log --reverse > temp_{id(self)}.txt")
+            with open(f'temp_{id(self)}.txt') as f:
+                self.log = f.read()
+            os.system(f"rm temp_{id(self)}.txt")
+        except:
+            None
+        if(self.display_info):
+            if(self.log==""):
+                print("Unable to retrieve the git log")
+            else:
+                print("Successfully retrieved the git log")
+        os.chdir(current_directory)
 
     def get_files(self):
         self.files = glob.glob(f'{self.directory}/**/*.*', recursive=True)
         tab_files = []
         for file in self.files:
-            tab_files.append({"@id":file[len(self.directory)+1:]})
+            tab_files.append({"@id":file[len(self.directory):]})
         return tab_files
+    
+    #Format yyyy-mm-dd
+    #Here i return the first commit date
+    def get_datePublished(self):
+        """Method that returns the date of publication
+
+        Keyword arguments:
+        
+        """
+        if(self.datePublished==None):
+            for match in re.finditer(r"Date: +\w+ +(\w+) +(\d+) +\d+:\d+:\d+ +(\d+)",self.log):
+                month = constant.month_mapping[match.group(1)]
+                day = match.group(2)
+                year = match.group(3)
+                if(int(month)<10 and len(month)==1):
+                    month = f"0{month}"
+                if(int(day)<10):
+                    day = f"0{day}"
+                return f"{year}-{month}-{day}"
+        else:
+            return self.datePublished
+    
+    def get_description(self):
+        """Method that returns the description
+
+        Keyword arguments:
+        
+        """
+        if(self.description==None):
+            try:
+                res = self.info_dico_workflow["description"]
+            except:
+                res = None
+            return res
+        else:
+            return self.description
+        
+    def get_license(self):
+        """Method that returns the license
+
+        Keyword arguments:
+        
+        """
+        if(self.license==None):
+            try:
+                res = self.info_dico_workflow["license"]["key"]
+            except:
+                res = None
+            return res
+        else:
+            return self.license
+        
+    def get_authors(self):
+        """Method that returns a list of the authors
+
+        Keyword arguments:
+        
+        """
+        if(self.authors==None):
+            authors = {}
+            for match in re.finditer(r"Author: ([^>]+)<([^>]+)>",self.log):
+                authors[match.group(2)] = match.group(1).strip()
+            tab = []
+            for author in authors:
+                #tab.append({"@id":author, "name":authors[author]})
+                tab.append({"@id":authors[author], "email":author})
+            return tab
+        else:
+            authors = self.authors.split(',')
+            tab = []
+            for a in authors:
+                tab.append({"@id":a.strip()})
+            return tab
+        
+    def get_publisher(self):
+        """Method that returns the publisher
+
+        Keyword arguments:
+        
+        """
+        if(self.publisher==None):
+            if(self.info_dico_workflow!={}):
+                return "https://github.com/"
+            else:
+                return None
+        else:
+            self.publisher
+
+    #TODO
+    def get_creativeWorkStatus(self):
+        return "TODO"
+    
+    #TODO
+    def get_version(self):
+        return "TODO"
+
+    #Need to follow this format : "rna-seq, nextflow, bioinformatics, reproducibility, workflow, reproducible-research, bioinformatics-pipeline"
+    def get_keywords(self):
+        """Method that returns the keywords
+
+        Keyword arguments:
+        
+        """
+        if(self.keywords==None):
+            try:
+                res = ", ".join(self.info_dico_workflow["topics"])
+            except:
+                res = None
+            return res
+        else:
+            return self.keywords
+        
+    def get_producer(self):
+        """Method that returns the producer
+
+        Keyword arguments:
+        
+        """
+        if(self.producer==None):
+            try:
+                res = {"@id": str(self.dico["owner"]["login"])}
+            except:
+                res = None
+            return res
+        else:
+            return self.producer
 
     def initialise_dico(self):
         self.dico["@context"] = "https://w3id.org/ro/crate/1.1/context"
@@ -51,88 +260,89 @@ class RO_Crate:
         root["@id"] = "./"
         root["@type"] = "Dataset"
         root["name"] = self.workflow.get_name()
-        root["datePublished"] = self.workflow.get_datePublished()
-        root["description"] = self.workflow.get_description()
-        root["mainEntity"] = {"@id": self.workflow.get_main_file()}
+        root["datePublished"] = self.get_datePublished()
+        root["description"] = str(self.get_description())
+        root["mainEntity"] = {"@id": str(self.workflow.get_first_file().get_file_address()).split("/")[-1]}
                               #, "@type":["File", "SoftwareSourceCode"]} #We do not consider a File as a "ComputationalWorkflow" since multiple (sub)workflows can be defined in a same file
-        root["license"] = {"@id":self.workflow.get_license()}
-        authors = self.workflow.get_authors()
-        tab_authors = []
+        root["license"] = {"@id":str(self.get_license())}
+        authors = self.get_authors()
+        tab_authors, tab_authors_ids= [], []
         for author in authors:
+            id_author = f'#{"_".join(author["@id"].split())}'
+            tab_authors_ids.append({"@id":id_author})
             try:
                 #tab_authors.append({"@id":author["@id"], "email":author["email"]})
-                tab_authors.append({"@id":f'#{"_".join(author["@id"].split())}', "@name":author["@id"],"email":author["email"]})
+                tab_authors.append({"@id":id_author, "@type": ["Person"], "name":author["@id"],"email":author["email"]})
             except:
                 #tab_authors.append({"@id":author["@id"]})
-                tab_authors.append({"@id":f'#{"_".join(author["@id"].split())}', "@name":author["@id"]})
-        root["author"] = tab_authors
-        root["maintainer"] = tab_authors #Right now i'm assuming that all the authors are maintainers
+                tab_authors.append({"@id":id_author, "@type": ["Person"], "name":author["@id"]})
+        self.dico["@graph"]+=tab_authors
+        root["author"] = tab_authors_ids
+        root["maintainer"] = tab_authors_ids #Right now i'm assuming that all the authors are maintainers
         files = self.get_files()
         tab_files = []
         for file in files:
             tab_files.append({"@id":file["@id"]})
         root["hasPart"] = tab_files
-        root["publisher"] = {"@id":self.workflow.get_publisher()}
+        publisher = str(self.get_publisher())
+        root["publisher"] = {"@id":publisher}
+        self.dico["@graph"].append({"@id":publisher, "@type":["Organization"]})
         #subjectOf TODO
         root["subjectOf"] = None
-        root["creativeWorkStatus"] = self.workflow.get_creativeWorkStatus()
-        root["@version"] = self.workflow.get_version()
-        root["keywords"] = self.workflow.get_keywords()
-        root["producer"] = self.workflow.get_producer()
+        root["creativeWorkStatus"] = self.get_creativeWorkStatus()
+        root["version"] = self.get_version()
+        root["keywords"] = self.get_keywords()
+        root["producer"] = self.get_producer()
         self.dico["@graph"].append(root)
 
     #TODO 
     def get_programming_language(self, file):
         if(file[-3:]==".nf"):
-            return "https://w3id.org/workflowhub/workflow-ro-crate#nextflow"
+            #return "https://w3id.org/workflowhub/workflow-ro-crate#nextflow"
+            return "#nextflow"
         return None
     
     def get_contentSize(self, file):
         file_stats = os.stat(file)
         return file_stats.st_size/1e3
     
-    def fill_log_file(self, file, reverse = True):
-        info = ""
-        current_directory = os.getcwd()
-        os.chdir("/".join(self.workflow.nextflow_file.get_file_address().split("/")[:-1]))
-        try:           
-            os.system(f"git log {'--reverse'*reverse} \"{file}\" > temp_{id(self)}.txt")
-            with open(f'temp_{id(self)}.txt') as f:
-                info = f.read()
-            os.system(f"rm temp_{id(self)}.txt")
-        except:
-            None
-        os.chdir(current_directory)
-        return info
+
 
     def get_dateCreated(self, file):
-        info = self.fill_log_file(file, reverse = True)
+        info = self.log
         for match in re.finditer(r"Date: +\w+ +(\w+) +(\d+) +\d+:\d+:\d+ +(\d+)", info):
             month = constant.month_mapping[match.group(1)]
             day = match.group(2)
             year = match.group(3)
+            if(int(month)<10 and len(month)==1):
+                month = f"0{month}"
+            if(int(day)<10):
+                day = f"0{day}"
             return f"{year}-{month}-{day}"
         return None
     
  
     def get_dateModified(self, file):
-        info = self.fill_log_file(file, reverse = False)
+        info = self.log
         for match in re.finditer(r"Date: +\w+ +(\w+) +(\d+) +\d+:\d+:\d+ +(\d+)", info):
             month = constant.month_mapping[match.group(1)]
             day = match.group(2)
             year = match.group(3)
-            return f"{year}-{month}-{day}"
-        return None
+            if(int(month)<10 and len(month)==1):
+                month = f"0{month}"
+            if(int(day)<10):
+                day = f"0{day}"
+        return f"{year}-{month}-{day}"
+        
     
-    #TODO -> update this -> it's incomplet
     def get_url(self, file):
-        if(self.workflow.dico!={}):
-            return f"https://github.com/{self.workflow.get_file_address()}/blob/main/{file}"
+        if(self.workflow_git_name!=""):
+            return f"https://github.com/{self.workflow_git_name}/blob/main/{file}"
         return None
     
 
     def get_creators(self, file):
-        info = self.fill_log_file(file, reverse = True)
+        info = self.log
         for match in re.finditer(r"Author: ([^>]+)<([^>]+)>",info):
             return [{"@id": match.group(1).strip()}]
         return []
@@ -146,12 +356,12 @@ class RO_Crate:
         
 
     def initialise_file(self, file):
-        key = file[len(self.directory)+1:]
+        key = file[len(self.directory):]
         dico = {}
         dico["@id"] = key
         dico["name"] = key
         dico["@type"] = self.get_types(file)
-        dico["programmingLanguage"] = {"@id":self.get_programming_language(file)}
+        dico["programmingLanguage"] = {"@id":str(self.get_programming_language(file))}
         dico["contentSize"] = self.get_contentSize(file)
         dico["dateCreated"] = self.get_dateCreated(key)
         dico["dateModified"] = self.get_dateModified(key)
@@ -174,9 +384,6 @@ class RO_Crate:
         self.fill_from_workflow()
         self.dico.pop("temp_directory")
 
-        name = self.workflow.get_name()
-        name = name.replace('github.com/', '')
-        name = re.sub(r"^[ .]|[/<>:\"\\|?*]+|[ .]$", "-", name)
 
         #with open(f"{self.workflow.get_output_dir()}/ro-crate-metadata-{name}.json", 'w') as output_file :
         with open(f"{self.workflow.get_output_dir()}/ro-crate-metadata.json", 'w') as output_file :
diff --git a/src/subworkflow.py b/src/subworkflow.py
index 4749628..fc16dd2 100644
--- a/src/subworkflow.py
+++ b/src/subworkflow.py
@@ -464,4 +464,64 @@ class Subworkflow(Main):
             #ope.set_operation_type("Branch")
             ope.get_structure(dico)
 
-  
\ No newline at end of file
+  
+    def add_2_rocrate(self, dico, parent_key):
+        sub_key = self.get_rocrate_key(dico)
+        dico_sub = get_dico_from_tab_from_id(dico, sub_key)
+        if(dico_sub==None):
+            dico_sub = {}
+            dico_sub["@id"] = sub_key
+            dico_sub["name"] = f"Subworkflow#{self.get_alias()}"
+            dico_sub["@type"] = ["SoftwareSourceCode", "ComputationalWorkflow"]
+            #TODO -> check if this remains true
+            #dico_main["conformsTo"] = {"@id": "https://bioschemas.org/profiles/ComputationalWorkflow/0.5-DRAFT-2020_07_21"}
+            #dico_main["dct:conformsTo"]= "https://bioschemas.org/profiles/ComputationalWorkflow/1.0-RELEASE/"
+            
+            
+            #ADD INPUTS
+            dico_sub["input"] = []
+            for input in self.get_takes():
+                if(type(input)==str):
+                    name_input = input
+                else:
+                    name_input = input.get_code(get_OG = True)
+                dico_input = get_dico_from_tab_from_id(dico, name_input)
+                if(dico_input==None):
+                    dico_input = {"@id":f"#{name_input}", "name": name_input, "@type": "FormalParameter"}
+                    dico["@graph"].append(dico_input)
+                dico_sub["input"].append({"@id":dico_input["@id"]})
+            #ADD OUTPUTS
+            dico_sub["output"] = []
+            for output in self.get_emit():
+                if(type(output)==str):
+                    name_output = output
+                else:
+                    name_output = output.get_code(get_OG = True)
+                dico_output = get_dico_from_tab_from_id(dico, name_output)
+                if(dico_output==None):
+                    dico_output = {"@id":f"#{name_output}", "name": name_output, "@type": "FormalParameter"}
+                    dico["@graph"].append(dico_output)
+                dico_sub["output"].append({"@id":dico_output["@id"]})
+
+
+            dico_sub["isPartOf"] = [{"@id": parent_key}]
+            dico_sub["hasPart"] = []
+
+
+            called = []
+            for call in self.root.get_all_calls_from_root():
+                called.append(call.get_first_element_called())
+
+            for c in called:
+                if(c==self):
+                    raise Exception("This shoudn't happen!")
+                c.add_2_rocrate(dico, sub_key)
+                dico_sub["hasPart"].append({"@id":c.get_rocrate_key(dico)})
+
+            dico["@graph"].append(dico_sub)
+        else:
+            if(not check_if_element_in_tab_rocrate(dico_sub["isPartOf"], parent_key)):
+                dico_sub["isPartOf"].append({"@id":parent_key})
+
+        self.get_nextflow_file().add_to_has_part(dico, sub_key)
+        
\ No newline at end of file
diff --git a/src/workflow.py b/src/workflow.py
index e21542b..7f66578 100644
--- a/src/workflow.py
+++ b/src/workflow.py
@@ -91,6 +91,12 @@ class Workflow:
         self.alias_2_tools = {}
         self.scripts_2_tools = {}
         
+    def get_name(self):
+        if(self.name!=None):
+            return self.name
+        else:
+            return self.get_root_directory().split('/')[-2]
+
 
     def create_empty_results(self):
         os.makedirs(self.output_dir, exist_ok=True)
@@ -129,6 +135,9 @@ class Workflow:
     def set_DSL(self, DSL):
         self.DSL = DSL
 
+    def add_2_rocrate(self, dico):
+        self.get_workflow_main().add_2_rocrate(dico)
+
     def get_first_file(self):
         for file in self.nextflow_files:
             if(file.first_file):
@@ -291,6 +300,12 @@ George Marchment, Bryan Brancotte, Marie Schmit, Frédéric Lemoine, Sarah Cohen
         self.generate_executors_per_subworkflows()
 
     
+    #The generation of the Ro-Crate has been valid from 
+    # - https://ro-crate.ldaca.edu.au/explorer, and
+    # - https://github.com/crs4/rocrate-validator (with the workflow-ro-crate-1.0 profile) 
+    def get_rocrate(self, display_info=False):
+        self.rocrate = RO_Crate(self, display_info=display_info)
+        self.rocrate.initialise()
 
 
     #Returns a dico of number of processes called per each condition 
-- 
GitLab