From 98d01333ce245c2a8540f7ba201b57ff15771fe0 Mon Sep 17 00:00:00 2001
From: Olivier Friard <olivier.friard@unito.it>
Date: Mon, 19 Aug 2024 13:16:11 +0200
Subject: [PATCH] fixed issue  #781

---
 boris/project.py               | 13 ++++--------
 boris/project_import_export.py | 38 ++++++++++++++++++++--------------
 boris/version.py               |  4 ++--
 3 files changed, 28 insertions(+), 27 deletions(-)

diff --git a/boris/project.py b/boris/project.py
index 7ead436a..5c8190d6 100644
--- a/boris/project.py
+++ b/boris/project.py
@@ -578,7 +578,7 @@ def convert_behaviors_keys_to_lower_case(self):
             # convert modifier shortcuts
             if self.twBehaviors.item(row, cfg.behavioursFields[cfg.MODIFIERS]).text():
                 modifiers_dict = (
-                    eval(self.twBehaviors.item(row, cfg.behavioursFields[cfg.MODIFIERS]).text())
+                    json.loads(self.twBehaviors.item(row, cfg.behavioursFields[cfg.MODIFIERS]).text())
                     if self.twBehaviors.item(row, cfg.behavioursFields[cfg.MODIFIERS]).text()
                     else {}
                 )
@@ -593,7 +593,7 @@ def convert_behaviors_keys_to_lower_case(self):
                     except Exception:
                         logging.warning("error during conversion of modifier short cut to lower case")
 
-                self.twBehaviors.item(row, cfg.behavioursFields[cfg.MODIFIERS]).setText(str(modifiers_dict))
+                self.twBehaviors.item(row, cfg.behavioursFields[cfg.MODIFIERS]).setText(json.dumps(modifiers_dict))
 
     def convert_subjects_keys_to_lower_case(self):
         """
@@ -796,7 +796,7 @@ def twBehaviors_cellDoubleClicked(self, row: int, column: int) -> None:
             else:
                 QMessageBox.information(self, cfg.programName, "Change the behavior type on first column to select a coding map")
 
-        # check if double click on category
+        # check if double click on behavior type
         if column == cfg.behavioursFields["type"]:
             self.behavior_type_doubleclicked(row)
 
@@ -808,6 +808,7 @@ def twBehaviors_cellDoubleClicked(self, row: int, column: int) -> None:
         if column == cfg.behavioursFields[cfg.BEHAVIOR_CATEGORY]:
             self.category_doubleclicked(row)
 
+        # check if double click on modifiers
         if column == cfg.behavioursFields[cfg.MODIFIERS]:
             # check if behavior has coding map
             if (
@@ -1735,12 +1736,6 @@ def check_ethogram(self) -> dict:
                                 QMessageBox.critical(self, cfg.programName, "Error removing leading/trailing spaces in modifiers")
 
                         else:
-                            """
-                            if row["modifiers"]:
-                                row["modifiers"] = eval(row["modifiers"])
-                            else:
-                                row["modifiers"] = {}
-                            """
                             row["modifiers"] = json.loads(row["modifiers"]) if row["modifiers"] else {}
                 else:
                     row[field] = ""
diff --git a/boris/project_import_export.py b/boris/project_import_export.py
index 03eed6a4..7a77ce10 100644
--- a/boris/project_import_export.py
+++ b/boris/project_import_export.py
@@ -133,7 +133,7 @@ def export_ethogram(self) -> None:
             # modifiers
             if self.twBehaviors.item(r, cfg.behavioursFields[cfg.MODIFIERS]).text():
                 # modifiers a string
-                modifiers_dict = eval(self.twBehaviors.item(r, cfg.behavioursFields[cfg.MODIFIERS]).text())
+                modifiers_dict = json.loads(self.twBehaviors.item(r, cfg.behavioursFields[cfg.MODIFIERS]).text())
                 modifiers_list = []
                 for key in modifiers_dict:
                     values = ",".join(modifiers_dict[key]["values"])
@@ -350,19 +350,22 @@ def import_ethogram_from_dict(self, project: dict):
                 item.setBackground(QColor(230, 230, 230))
 
             else:
-                if field == cfg.MODIFIERS and isinstance(project[cfg.ETHOGRAM][i][field], str):
-                    modif_set_dict = {}
-                    if project[cfg.ETHOGRAM][i][field]:
-                        modif_set_list = project[cfg.ETHOGRAM][i][field].split("|")
-                        for modif_set in modif_set_list:
-                            modif_set_dict[str(len(modif_set_dict))] = {
-                                "name": "",
-                                "type": cfg.SINGLE_SELECTION,
-                                "values": modif_set.split(","),
-                            }
-                    project[cfg.ETHOGRAM][i][field] = dict(modif_set_dict)
-
-                item.setText(str(project[cfg.ETHOGRAM][i][field]))
+                if field == cfg.MODIFIERS:
+                    if isinstance(project[cfg.ETHOGRAM][i][field], str):
+                        modif_set_dict = {}
+                        if project[cfg.ETHOGRAM][i][field]:
+                            modif_set_list = project[cfg.ETHOGRAM][i][field].split("|")
+                            for modif_set in modif_set_list:
+                                modif_set_dict[str(len(modif_set_dict))] = {
+                                    "name": "",
+                                    "type": cfg.SINGLE_SELECTION,
+                                    "values": modif_set.split(","),
+                                }
+                        project[cfg.ETHOGRAM][i][field] = dict(modif_set_dict)
+                    else:
+                        item.setText(json.dumps(project[cfg.ETHOGRAM][i][field]))
+                else:
+                    item.setText(project[cfg.ETHOGRAM][i][field])
 
                 if field not in cfg.ETHOGRAM_EDITABLE_FIELDS:
                     item.setFlags(Qt.ItemIsEnabled)
@@ -460,8 +463,11 @@ def load_dataframe_into_behaviors_tablewidget(self, df: pd.DataFrame) -> int:
 
 
 def import_behaviors_from_project(self):
+    """
+    import ethogram from a BORIS project file
+    """
     fn = QFileDialog().getOpenFileName(
-        self, "Import behaviors from project file", "", ("Project files (*.boris *.boris.gz);;" "All files (*)")
+        self, "Import behaviors from BORIS project file", "", ("Project files (*.boris *.boris.gz);;" "All files (*)")
     )
     file_name = fn[0] if type(fn) is tuple else fn
 
@@ -474,7 +480,7 @@ def import_behaviors_from_project(self):
 
 def import_behaviors_from_text_file(self):
     """
-    Import behaviors from text file (CSV or TSV)
+    Import ethogram from text file (CSV or TSV)
     """
 
     if self.twBehaviors.rowCount():
diff --git a/boris/version.py b/boris/version.py
index 65f78073..f9392e86 100644
--- a/boris/version.py
+++ b/boris/version.py
@@ -20,5 +20,5 @@
 
 """
 
-__version__ = "8.27.2"
-__version_date__ = "2024-07-03"
+__version__ = "8.27.3"
+__version_date__ = "2024-08-19"