From 35fa765423c25c1733e682617ebe1b965297baf4 Mon Sep 17 00:00:00 2001
From: martiivGylden <martin.iversen@gyldendal.no>
Date: Wed, 20 Mar 2024 14:17:50 +0100
Subject: [PATCH] Working on metrics, slow progress

---
 dashBoard.py     | 26 +++++++++++----------
 diagramParser.py |  8 +++----
 dynamics.py      | 12 ++++++----
 matrices.py      | 60 +++++++++++++++++++++++++++++++++++++++---------
 requirements.txt | 21 +++++++++++++++++
 5 files changed, 95 insertions(+), 32 deletions(-)

diff --git a/dashBoard.py b/dashBoard.py
index 7f267dd..91e47c2 100644
--- a/dashBoard.py
+++ b/dashBoard.py
@@ -40,7 +40,7 @@ def openFile():
 def createThreats(diagram: Diagram, top):
     for threat in diagram.threats.values():
         
-        metrics, components = findMetrics(diagram, threat.id)
+        metrics, components = findMetrics(diagram, threat.componentID, "bowtie")
                 
         threatFrame = Frame(top)
         threatFrame.pack()
@@ -64,21 +64,23 @@ def createThreats(diagram: Diagram, top):
         
         
         
-def findMetrics(diagram, id):
-    relevantDynamic = []
+def findMetrics(diagram, id:int, typeOfObject:str):
+    relevantMetrics = []
     relatedComponents = []
     
-    for metric in diagram.metrics.values():
-        if id in metric.erID:
-            relevantDynamic.append(metric.ID)
-            relatedComponents.append(metric.erID)
-            
-        elif id in metric.bowtieID:
-            relevantDynamic.append(metric.ID)
-            relatedComponents.append(metric.bowtieID)
+    for metric in diagram.metrics.values():           
+        if typeOfObject == "bowtie":            
             
+            if id in metric.bowtieID:
+                relevantMetrics.append(metric.ID)
+                relatedComponents.append(metric.erID)
+                
+        elif typeOfObject == "er":    
+            if id in metric.erID:
+                relevantMetrics.append(metric.ID)
+                relatedComponents.append(metric.bowtieID)
     
-    return relevantDynamic, relatedComponents
+    return relevantMetrics, relatedComponents
 
 def createDynamic(diagram: Diagram, top):
     
diff --git a/diagramParser.py b/diagramParser.py
index b8241ea..18d4bf4 100644
--- a/diagramParser.py
+++ b/diagramParser.py
@@ -23,9 +23,9 @@ def parseDiagramFile(csvFile) -> component.Diagram:
     consequences = parseConsequences(df, consequences)    
     
     metrics, dynamics = parseDynamic(df, metrics, dynamics)
-    metricsMatrix, metrics = matrix.parseTable(df, metrics) #Parse the table
-
-    print(metricsMatrix)
+    
+    
+    metricsMatrix, metrics = matrix.parseTable(df, metrics, diagram) #Parse the table
         
     return diagram    
     
@@ -100,8 +100,6 @@ def parseDynamic(df, metricDict, dynamicsDict):
                     df[const.Id][i],                                    # Component ID LucidChart
                     df[const.textArea3][i]                              # Name of dynamic
                 )
-                #attackDynamic.metrics = extractMetrics(df, i, 4)
-                #metricList = joinMetrcs(attackDynamic.metrics, metricList)            # Adds the metrics to the global metric list
                 
                 attackDynamic.associateBowtie(df,attackDynamic.type, metricDict)                     # Associate the dynamic with the correct components
                 dynamicsDict[df.Id[i]] = attackDynamic 
diff --git a/dynamics.py b/dynamics.py
index 5f0d4ea..4c55a4e 100644
--- a/dynamics.py
+++ b/dynamics.py
@@ -104,6 +104,7 @@ class BowtieDynamic(DynamicComponent):
         # ! Edge case, if the second line is in fact not a second line it is an attack meaning that the dynamic component is an attack
         if componentType == const.Attack:  # If the associated component is an attack
             self.associatedAttack = lineTwo          # ! The "line" is an attack, not a line, we add it to the associated attack field and move on
+            self.linkMetric(df, metricsDict)
             return                                   # The attack is the only component associated with the dynamic        
         
         sourceComponent = df.loc[df[const.Id]==lineTwo[const.From].item()]      # Defines the source of the line connecting threats/consequences and attacks
@@ -145,10 +146,11 @@ class BowtieDynamic(DynamicComponent):
             
             if(dynamicComponent["Text Area "+str(i)].isnull().values.any() == True): # If there are no metrics left
                 break                                       # Break the loop
-            
-            
+             
             metricID = dynamicComponent["Text Area "+str(i)].item()  # Find the metric ID in the component      
-            metricName = dynamicComponent["Text Area "+str(i+1)].item()  # Find the metric Name      
+            metricID = int(metricID)
+            
+            metricName = dynamicComponent["Text Area "+str(i+1)].item()  # Find the metric Name
             
             self.metrics.append(metricID) # ? Adds the metric to the metric list associated with the dynamic component specifically
             
@@ -246,7 +248,9 @@ class ERDynamic(DynamicComponent):
             if(dynamicComponent["Text Area "+str(i)].isnull().values.any() == True): # No more metrics
                 break
             
-            metricID = dynamicComponent["Text Area "+str(i)].item()  # Find the metric ID      
+            metricID = dynamicComponent["Text Area "+str(i)].item()  # Find the metric ID   
+            metricID = int(metricID)
+   
             metricName = dynamicComponent["Text Area "+str(i+1)].item()  # Find the metric ID    
                           
             self.metrics.append(metricID) # ? Adds the metric to the metric list associated with the dynamic component specifically
diff --git a/matrices.py b/matrices.py
index 44d76aa..6a96260 100644
--- a/matrices.py
+++ b/matrices.py
@@ -7,7 +7,7 @@ Parses csv table and returns a dataframe
 This is quite finicky as lucidchart has a very weird way of structuring tables as it is based on when different cells were created
 This is quite a big EDGE case and will only work for the specific table shape that I have created
 """
-def parseTable(df, metricsDict):
+def parseTable(df, metricsDict, diagram):
     
     cols = [const.Er_ID, const.Bowtie_ID, const.Metric_ID, const.Metric_Name, const.Value, const.Measure_date, const.Frequency ,const.Measurement_guide]
         
@@ -16,10 +16,11 @@ def parseTable(df, metricsDict):
     matrics = pd.DataFrame(columns=cols)
 
     length = len(table.columns)
+    
     length = length-7 # Setting the length equal to the amount of text fields
+    
     stopLength = length-15
-    print(stopLength)
-
+    
     # ! Parsing the first row of the table which has a fucked structure because lucidchart is garbage : ) 
     erID1 = table[const.textArea4].item()              # Define the ER ID
     BowTieID1 = table[const.textArea5].item()          # Define the Bowtie ID
@@ -30,11 +31,11 @@ def parseTable(df, metricsDict):
     Frequency1 = table[f"Text Area {length-6}"].item()        # Define the Frequency
     Guide1 = table[f"Text Area {length-5}"].item()            # Define the Measurement guide
     
-    
+    MetricID1 = int(MetricID1)
     matrics.loc[0] = erID1, BowTieID1, MetricID1, MetricName1, Value1, MeasureDate1, Frequency1, Guide1 # Fill inn dataframe
     
     # TODO Error handle/Communicate to end user here, "You seem to have a metric in your table which is not used etc"
-    metricsdict = fillMetricinfo(erID1, BowTieID1, MetricID1, MetricName1, Value1, MeasureDate1, Frequency1, Guide1, metricsDict)    
+    fillMetricinfo(erID1, BowTieID1, MetricID1, MetricName1, Value1, MeasureDate1, Frequency1, Guide1, metricsDict, diagram)    
     
     
     # Parsing the second row, this row is also specialized    
@@ -47,9 +48,11 @@ def parseTable(df, metricsDict):
     Frequency2 = table[f"Text Area {length-1}"].item()        # Define the Frequency
     Guide2 = table[f"Text Area {length}"].item()            # Define the Measurement guide    
     
+    MetricID2 = int(MetricID2)
     matrics.loc[1] = erID2, BowTieID2, MetricID2, MetricName2, Value2, MeasureDate2, Frequency2, Guide2
     
     metric = metricsDict[MetricID2]  # Access the appropriate metric in the dictionary 
+    fillMetricinfo(erID2, BowTieID2, MetricID2, MetricName2, Value2, MeasureDate2, Frequency2, Guide2, metricsDict, diagram)
     
     # TODO Error handle/Communicate to end user here, "You seem to have a metric in your table which is not used etc"
     if metric == None: 
@@ -74,10 +77,9 @@ def parseTable(df, metricsDict):
         series = pd.Series([erID, BowTieID, MetricID, MetricName, Value, MeasureDate, Frequency, Guide])
         
         matrics.loc[matricsIndex] = erID, BowTieID, MetricID, MetricName, Value, MeasureDate, Frequency, Guide
+        fillMetricinfo(erID, BowTieID, MetricID, MetricName, Value, MeasureDate, Frequency, Guide,metricsDict, diagram)
         matricsIndex += 1   
-         
-    print(matrics)
-    
+             
     return matrics, metricsDict
 
 def handleMissingMetric():
@@ -93,15 +95,43 @@ def fillMetricinfo(erID1: str,
                    Value1: str, 
                    MeasureDate1: str, 
                    Frequency1: str, 
-                   Guide1: str, metricsDict: dict)-> dict:
+                   Guide1: str, metricsDict: dict, diagram)-> dict:
     """Function will fill in the metric information in the dataframe into the metric object and return the updated dictionary
     """
+    MetricID1 = int(MetricID1)
+        
     metric = metricsDict[MetricID1]  # Access the appropriate metric in the dictionary
     
     if metric == None: 
         print("Metric is not used in the diagram!!!")
         return
     
+    if erID1 == "ID" or erID1 != None:
+        #TODO FML
+        pass
+    else: 
+        erID1 = erID1.split(",")
+        erID1 = [eval(i) for i in erID1] # Parse ERID list to int
+    
+        for i in erID1:
+            print("TODO")
+        
+    if type(BowTieID1) == str:
+        if "," in BowTieID1: # If the amount of associated bowtie IDs is only one or none
+            BowTieID1 = BowTieID1.split(",")
+            BowTieID1 = [eval(i) for i in BowTieID1] # Parse BowtieID to int
+            
+            for i in range(len(BowTieID1)):
+                BowTieID1[i] = findComponentIDBowtie(BowTieID1[i], diagram)
+        else:
+            BowTieID1 = findComponentIDBowtie(BowTieID1, diagram)
+            BowTieID1 = [BowTieID1]
+    else:
+        BowTieID1 = int(BowTieID1)
+        
+        BowTieID1 = findComponentIDBowtie(BowTieID1, diagram)
+        BowTieID1 = [BowTieID1]
+                   
     # Updates the metric with the information found in the dynamics matrix
     metric.erID = erID1                 # Should be string
     metric.bowtieID = BowTieID1         # is list and should be i guess
@@ -112,7 +142,15 @@ def fillMetricinfo(erID1: str,
     metric.guide = Guide1               # Should be string
     
     metricsDict[MetricID1] = metric
-    
+        
     return metricsDict
     
-    
\ No newline at end of file
+def findComponentIDBowtie(id, diagram):
+        
+    for threat in diagram.threats.values():
+        if threat.id == str(id):
+            return threat.componentID
+    
+    for consequence in diagram.consequences.values():
+        if consequence.id == str(id):
+            return consequence.componentID        
\ No newline at end of file
diff --git a/requirements.txt b/requirements.txt
index fae9383..dd57884 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -1,2 +1,23 @@
 pandas==2.2.1
 numpy==1.26.4
+
+
+15: <dynamics.Metric object at 0x11ef92c00>, 
+1: <dynamics.Metric object at 0x11ef93e60>, 
+2: <dynamics.Metric object at 0x11efc0650>, 
+3: <dynamics.Metric object at 0x11efc0860>, 
+10: <dynamics.Metric object at 0x11efc0a70>,
+ 11: <dynamics.Metric object at 0x11efc0c80>, 
+ 12: <dynamics.Metric object at 0x11efc0e90>, 
+ 13: <dynamics.Metric object at 0x11efc10d0>, 
+ 4: <dynamics.Metric object at 0x11ef93b90>, 
+ 5: <dynamics.Metric object at 0x11efc0e00>, 
+ 6: <dynamics.Metric object at 0x11efc0bc0>, 
+ 7: <dynamics.Metric object at 0x11efc0a10>, 
+ 9: <dynamics.Metric object at 0x11efc0dd0>, 
+ 16: <dynamics.Metric object at 0x11efc0440>, 
+ 19: <dynamics.Metric object at 0x11efc12e0>, 
+ 20: <dynamics.Metric object at 0x11efc1e50>, 
+ 8: <dynamics.Metric object at 0x11efecf80>, 
+ 17: <dynamics.Metric object at 0x11efed760>,
+  18: <dynamics.Metric object at 0x11efeda90>
\ No newline at end of file
-- 
GitLab