Repository: cbouy/mols2grid (language: python)

Issue:
Save CSV not working as expected
Hello,
I used the following code from the documentation:

```python
import mols2grid
from pathlib import Path
from rdkit import RDConfig
SDF_FILE = (f"{RDConfig.RDDocsDir}/Book/data/solubility.test.sdf"
            if Path(RDConfig.RDDocsDir).is_dir() else "solubility.test.sdf")

mols2grid.display(
    SDF_FILE,
    # rename fields for the output document
    rename={"SOL": "Solubility",
            "SOL_classification": "Class",
            "NAME": "Name"},
    # set what's displayed on the grid
    subset=["ID", "img", "Solubility"],
    # set what's displayed on the hover tooltip
    tooltip=["Name", "SMILES", "Class", "Solubility"],
    # style for the grid labels and tooltips
    style={
      "Solubility": lambda x: "color: red; font-weight: bold;" if x < -3 else "",
      "__all__": lambda x: "background-color: azure;" if x["Solubility"] > -1 else ""
    },
    # change the precision and format (or other transformations)
    transform={"Solubility": lambda x: round(x, 2)},
    # sort the grid in a different order by default
    sort_by="Name",
    # molecule drawing parameters
    fixedBondLength=25, clearBackground=False
)
```
Then I checked the molecule with ID = 150, Solubility = -2.37, SMILES = CCCCBr. However, when I open the CSV files, the saved structure was:

index | ID | Solubility | Name | SMILES | Class |   |   |  
-- | -- | -- | -- | -- | -- | -- | -- | --
29 | 246 | -6.8 | 2 | 2ﾴ | 5 | 6ﾴ-PCB | c1c(Cl)ccc(Cl)c1c2c(Cl)cccc2Cl | (A) low






Discussion / hints:
Hi @ale94mleon,

Thanks for reporting this!
I could indeed reproduce the issue, the problem seems to come from sorting the grid, I'll look into it soon.

Cheers,
Cédric


Fix patch:
diff --git a/mols2grid/templates/js/selection_actions.js b/mols2grid/templates/js/selection_actions.js
index 34067a3..7c1bb8c 100644
--- a/mols2grid/templates/js/selection_actions.js
+++ b/mols2grid/templates/js/selection_actions.js
@@ -85,20 +85,24 @@ $("#btn-chkbox-dlcsv").click(function(e) {
     var sep = "\t"
     // same order as subset + tooltip
     var columns = Array.from(listObj.items[0].elm.querySelectorAll("div.data"))
-                       .map(elm => elm.classList[1]);
-    // remove 'data-'
+                       .map(elm => elm.classList[1])
+                       .filter(name => name !== "data-img");
+    // remove 'data-' and img
     var header = columns.map(name => name.slice(5));
     // csv content
     header = ["index"].concat(header).join(sep);
     var content = header + "\n";
-    for (let [index, smiles] of SELECTION.entries()) {
-        var data = listObj.items[index].values();
-        content += index;
-        columns.forEach((key) => {
-            content += sep + data[key];
-        })
-        content += "\n";
-    }
+    listObj.items.forEach(function (item) {
+        let data = item.values();
+        let index = data["mols2grid-id"];
+        if (SELECTION.has(index)) {
+            content += index;
+            columns.forEach((key) => {
+                content += sep + data[key];
+            })
+            content += "\n";
+        }
+    });
     var a = document.createElement("a");
     var file = new Blob([content], {type: "text/csv"});
     a.href = URL.createObjectURL(file);


Test patch:
diff --git a/tests/test_interface.py b/tests/test_interface.py
index ccc2744..28f3edc 100644
--- a/tests/test_interface.py
+++ b/tests/test_interface.py
@@ -4,6 +4,8 @@
 from flaky import flaky
 from ast import literal_eval
 from base64 import b64encode, b64decode
+from datetime import datetime, timezone
+from pathlib import Path
 from types import SimpleNamespace
 from selenium import webdriver
 from selenium.webdriver.support.ui import WebDriverWait
@@ -239,6 +241,27 @@ def test_selection_click(driver: FirefoxDriver, html_doc):
     assert sel == {0: "CCC(C)CC"}
     register._clear()
 
+def test_export_csv(driver: FirefoxDriver, html_doc):
+    driver.get(html_doc)
+    driver.wait_for_img_load()
+    driver.find_clickable(By.CSS_SELECTOR, "input[type='checkbox']").click()
+    driver.wait_for_selection(is_empty=False)
+    driver.find_clickable(By.ID, "sortDropdown").click()
+    driver.find_clickable(By.CSS_SELECTOR,
+                          'button.sort-btn[data-name="data-_Name"]').click()
+    driver.wait_for_img_load()
+    driver.find_clickable(By.ID, "chkboxDropdown").click()
+    now = datetime.now(tz=timezone.utc)
+    driver.find_clickable(By.ID, "btn-chkbox-dlcsv").click()
+    csv_files = sorted((Path.home() / "Downloads").glob("selection*.csv"), key=lambda x: x.stat().st_mtime)
+    csv_file = csv_files[-1]
+    file_mtime = datetime.fromtimestamp(csv_file.stat().st_mtime, tz=timezone.utc)
+    assert (file_mtime - now).seconds < 1, "Could not find recent selection file"
+    content = csv_file.read_text()
+    assert content == "index\t_Name\tSMILES\n0\t3-methylpentane\tCCC(C)CC\n"
+    csv_file.unlink()
+    register._clear()
+
 def test_selection_with_cache_check_and_uncheck(driver: FirefoxDriver, df):
     register._init_grid("cached_sel")
     event = SimpleNamespace(new='{0: "CCC(C)CC"}')