From 32357914280cb69d9fc53bc2f5bc8c47116bc850 Mon Sep 17 00:00:00 2001
From: George Marchment <georgemarchment@yahoo.fr>
Date: Mon, 21 Apr 2025 14:58:16 +0200
Subject: [PATCH] Added warning when channels are defined/used multiple times
 in different blocks -> sometimes breaks the rewrite

---
 src/bioflowinsightwarning.py |  5 +++++
 src/main.py                  |  6 +++++-
 src/nextflow_file.py         |  3 ---
 src/operation.py             | 17 ++++++++++++++---
 src/root.py                  | 34 ++++++++++++++++++++++++++++++++++
 src/subworkflow.py           |  6 ++++++
 src/workflow.py              | 29 +++++++++++++++++++++++++++--
 7 files changed, 91 insertions(+), 9 deletions(-)
 create mode 100644 src/bioflowinsightwarning.py

diff --git a/src/bioflowinsightwarning.py b/src/bioflowinsightwarning.py
new file mode 100644
index 0000000..e6d7ea9
--- /dev/null
+++ b/src/bioflowinsightwarning.py
@@ -0,0 +1,5 @@
+# creating a custom warning
+class BioFlowInsightWarning(UserWarning):
+    pass
+
+
diff --git a/src/main.py b/src/main.py
index 4718360..0a72807 100644
--- a/src/main.py
+++ b/src/main.py
@@ -246,4 +246,8 @@ class Main(Nextflow_Building_Blocks):
                             most_influential_conditions[condition]+=1
             return most_influential_conditions
         else:
-            raise Exception("This shouldn't happen")
\ No newline at end of file
+            raise Exception("This shouldn't happen")
+        
+    
+    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
diff --git a/src/nextflow_file.py b/src/nextflow_file.py
index cbf1edf..4a16634 100644
--- a/src/nextflow_file.py
+++ b/src/nextflow_file.py
@@ -5,13 +5,10 @@ import json
 import glob
 from datetime import date
 
-#TODO -> check this or either change the warnings to nothing 
-import warnings
 from pathlib import Path
 
 from . import constant
 
-warnings.filterwarnings("ignore")
 from .nextflow_building_blocks import Nextflow_Building_Blocks
 from .outils import *
 from .bioflowinsighterror import BioFlowInsightError
diff --git a/src/operation.py b/src/operation.py
index bd5a006..ffb8816 100644
--- a/src/operation.py
+++ b/src/operation.py
@@ -128,6 +128,7 @@ class Operation(Executor):
             else:
                 origin = self.origin.root
             
+            #V1
             #First check that the channel is not defined at the same level
             channels = origin.get_channels_from_name_same_level(name)
             #Then check that the channel is defined in the below level
@@ -138,6 +139,11 @@ class Operation(Executor):
                 channels = origin.get_channels_from_name_above_level(name)
             if(channels==[]):
                 channels = origin.get_channels_from_name_other_blocks_on_same_level(name)
+
+            #V2
+            channels = origin.get_channels_from_name_same_level(name)+origin.get_channels_from_name_inside_level(name)+origin.get_channels_from_name_above_level(name)+origin.get_channels_from_name_other_blocks_on_same_level(name)
+            channels = list(set(channels))
+            
             #If it still doesn't exist -> we create it 
             if(channels==[]):
                 channel = Channel(name=name, origin=origin)
@@ -243,7 +249,8 @@ class Operation(Executor):
                 origin = self.origin.root
             else:
                 origin = self.origin
-
+            
+            #V1
             channels = origin.get_channels_from_name_same_level(name)
             if(channels==[]):
                 channels = origin.get_channels_from_name_inside_level(name)
@@ -251,6 +258,10 @@ class Operation(Executor):
                 channels = origin.get_channels_from_name_above_level(name)
             #if(channels==[]):
             #    channels = origin.get_channels_from_name_other_blocks_on_same_level(name)
+            
+            #V2
+            #channels = origin.get_channels_from_name_same_level(name)+origin.get_channels_from_name_inside_level(name)+origin.get_channels_from_name_above_level(name)
+
             if(channels==[]):
                 channel = Channel(name=name, origin=self.origin)
                 origin.add_channel(channel)
@@ -590,7 +601,7 @@ class Operation(Executor):
                                 if(to_add):
                                     self.add_origin(c.get_name())
                         #TODO update this -> it's an operation itselfs
-                        warnings.warn(f"I don't know what i'm looking at '{name}' in '{self.get_code()}'\n")
+                        #warnings.warn(f"I don't know what i'm looking at '{name}' in '{self.get_code()}'\n")
 
 
 
@@ -876,7 +887,7 @@ class Operation(Executor):
             self.gives = list(set(self.gives))
             #TODO -> this was originally uncommented, check it doesn't add any other bugs
             #self.origins = []
-            warnings.warn(f"TO CHECK !! From this : '{self.get_code()}'. I extracted to give (for a call) '{self.gives}' (in file '{self.get_file_address()}')\n")
+            #warnings.warn(f"TO CHECK !! From this : '{self.get_code()}'. I extracted to give (for a call) '{self.gives}' (in file '{self.get_file_address()}')\n")
             #TODO
             #We check that the operation is an actuel operation and not just a string for example
             #if(len(self.get_origins())==0 and len(self.get_gives())==0):
diff --git a/src/root.py b/src/root.py
index 8d9909d..004e0d6 100644
--- a/src/root.py
+++ b/src/root.py
@@ -345,6 +345,40 @@ class Root(Nextflow_Building_Blocks):
             element = sorted_position_2_thing_2_analyse[key]
             element.initialise()
 
+    #Example with 132
+    def check_that_a_channel_is_not_defined_used_and_redefined_used_in_another_block(self):
+        channels_defined_at_root_level = []
+        for exe in self.get_executors_same_level():
+            if(exe.get_type()=="Operation"):
+                channels_defined_at_root_level+=exe.get_gives()
+        channels_defined_used_in_calls_at_root = []
+        for call in self.get_calls_same_level():
+            params = call.get_parameters()
+            for p in params:
+                if(p.get_type()=="Operation"):
+                    channels_defined_used_in_calls_at_root += p.origins
+
+        if(channels_defined_used_in_calls_at_root!=[]):
+            for block in self.blocks:
+                temp_return = block.check_that_a_channel_is_not_defined_used_and_redefined_used_in_another_block()
+                if(temp_return!=None):
+                    return temp_return
+                channels_defined_inside_block = []
+                for exe in block.get_executors_same_level()+block.get_inside_executors():
+                    if(exe.get_type()=="Operation"):
+                        channels_defined_inside_block+=exe.get_gives()
+                calls_inside_block = block.get_calls_same_level()+block.get_calls_inside_level()
+                for call in calls_inside_block:
+                    params = call.get_parameters()
+                    for p in params:
+                        if(p.get_type()=="Operation"):
+                            for o in p.origins:
+                                if(o in channels_defined_inside_block):
+                                    for ch in channels_defined_used_in_calls_at_root:
+                                        if(o.get_code()==ch.get_code()):
+                                            return o.get_code()
+        return None
+
 
     def get_process_from_name(self, name):
         for m in self.modules_defined:
diff --git a/src/subworkflow.py b/src/subworkflow.py
index 7bff264..17a39e3 100644
--- a/src/subworkflow.py
+++ b/src/subworkflow.py
@@ -349,11 +349,17 @@ class Subworkflow(Main):
             for i in range(len(code)):
                 code[i] = code[i].strip()
                 if(code[i]!=""):
+                    #V1
                     channels = self.root.get_channels_from_name_same_level(code[i])
                     if(channels==[]):
                         channels = self.root.get_channels_from_name_inside_level(code[i])
                     if(channels==[]):
                         channels = self.root.get_channels_from_name_other_blocks_on_same_level(code[i])
+
+                    #V2
+                    #channels = self.root.get_channels_from_name_same_level(code[i])+self.root.get_channels_from_name_inside_level(code[i])+self.root.get_channels_from_name_other_blocks_on_same_level(code[i])
+                    
+                    
                     if(channels!=[]):
                         ope = Operation(code=f"e: {code[i]}", origin=self)
                         ope.set_as_artificial()
diff --git a/src/workflow.py b/src/workflow.py
index 724d5a1..722b9ec 100644
--- a/src/workflow.py
+++ b/src/workflow.py
@@ -8,6 +8,8 @@ from .outils_graph import get_flatten_dico, initia_link_dico_rec, get_number_cyc
 from .outils_annotate import get_tools_commands_from_user_for_process
 from .bioflowinsighterror import BioFlowInsightError
 from .graph import Graph
+import warnings
+from .bioflowinsightwarning import BioFlowInsightWarning
 
 #Outside packages
 import os
@@ -597,6 +599,7 @@ George Marchment, Bryan Brancotte, Marie Schmit, Frédéric Lemoine, Sarah Cohen
                 min_condition_score = score
         return min_relevant_processes
 
+    #TODO -> add excpetion Channel exists in multiple forms -> check with 132
     def get_relevant_following_best_general_score(self, 
                                                   reduction_alpha = 0.2, 
                                                   reduction_beta = 0.8, 
@@ -841,7 +844,10 @@ George Marchment, Bryan Brancotte, Marie Schmit, Frédéric Lemoine, Sarah Cohen
                 generate_graph(self.get_output_dir()/ "debug" /"spec_graph", self.graph.full_dico, render_graphs = True)
                 #generate_graph(self.get_output_dir()/ "debug" /"process_dependency_graph_OG", temp_process_dependency_graph, render_graphs = True)
                 generate_graph(self.get_output_dir()/ "debug" /"process_dependency_graph", self.graph.get_process_dependency_graph() , render_graphs = True)
-            raise Exception("Something went wrong: The flat dependency graph is not the same!")
+            if(self.channel_that_is_defined_used_and_redefined_used_in_another_block!=""):
+                raise BioFlowInsightError(f"Given that the channel '{self.channel_that_is_defined_used_and_redefined_used_in_another_block}' is defined and used in multiple conditional blocks. The rewrite could not be done with the proprosed relavant processes. Either correct the defintion of the workflow or give another set of relevant processes.", type="Channel exists in multiple forms")
+            else:
+                raise Exception("Something went wrong: The flat dependency graph is not the same!")
 
 
     def check_relevant_processes_in_workflow(self, relevant_processes):
@@ -1036,12 +1042,31 @@ George Marchment, Bryan Brancotte, Marie Schmit, Frédéric Lemoine, Sarah Cohen
                 seen.append(sub.get_code())
   
 
+
+    #This methods raises a warning when there is a channel is defined in a bloc
+    #And used as a parameter in a call in the same bloc
+    #And that the channel is defined somewhere else in the code
+    def check_that_a_channel_is_not_defined_used_and_redefined_used_in_another_block(self):
+        self.channel_that_is_defined_used_and_redefined_used_in_another_block = False
+        for main in [self.get_workflow_main()]+self.get_subworkflows_called():
+            problematic_channel = main.check_that_a_channel_is_not_defined_used_and_redefined_used_in_another_block()
+            if(problematic_channel!=None):
+                self.channel_that_is_defined_used_and_redefined_used_in_another_block = problematic_channel
+                warnings.warn(f'The channel "{problematic_channel}" is defined and used in multiple seperate conditional blocks, depending on the relevant processes given, the rewrite may not be done.', BioFlowInsightWarning)
+
+
+    #This function checks that the rewrite can be done correctly -> checks for the elements "illegal" in the code
+    def check_something_illegal_for_rewrite(self):
+        self.check_that_a_channel_is_not_defined_used_and_redefined_used_in_another_block()
+        
+        
+
     #Method which rewrites the workflow follwong the user view
     #Conert workflow to user_view only makes sense when the option duplicate is activated -> otherwise is doesn't make sense + it makes the analysis way more complicated
     def convert_workflow_2_user_view(self, relevant_processes = [], render_graphs = True):
         self.iniatilise_tab_processes_2_remove()
         self.graph.initialise(processes_2_remove = self.processes_2_remove)
-
+        self.check_something_illegal_for_rewrite()
         def get_object(address):
             address = int(re.findall(r"\dx\w+", address)[0], base=16)
             return ctypes.cast(address, ctypes.py_object).value
-- 
GitLab