Acylation / obsidian-chem

Chemistry support for Obsidian. Rendering SMILES strings into chemistry structures.
MIT License
80 stars 4 forks source link

passing variable as SMILES string #43

Closed dpfelse closed 10 months ago

dpfelse commented 11 months ago

What happened?

I have no problem passing a SMILES string to CHEM as suggested in in the online information:

: ```smiles

: CCC=O

: ```

(":" added to allow this to format correctly.)

Is correctly rendered. However, I would like to pass a variable (from a table in the front matter). This would look something like this:

: ```smiles

: = this.compound_properties["ABCD-XXXXXXXX"].SMILES

: ``` Where the table in the front matter includes the following: compound_properties: ABCD-XXXXXXXX: {ABCD#: "ABCD-XXXXXXXX", SMILES: "CCC=)"}

If I use the expression ("=this.compound_properties["ABCD-XXXXXXXX"].SMILES") by itself in Obsidian, the correct SMILES string is returned. However, in the context of the CHEM code, it gives an error.

What did you expect to happen?

I expected the structure to be rendered according to the SMILES string in my front matter table ("CCC=O" in this example)

How can we reproduce it (as minimally and precisely as possible)?

you would need to create a document with a parameter in the front matter.

Anything else we need to know?

Perhaps I have missed something obvious. I am fairly new to Obsidian.

Plugin version

0.2.1

Obsidian version

1.4.16

dpfelse commented 11 months ago

A small update - someone on the Obsidian forum has suggested that this may be an issue with the race conditions - the value of the SMILES string is not solved from the dataview dynamic variant ("=this.compound_properties["ABCD-XXXXXXXX"].SMILES") before it is accessed by Chem. I am not sure if there is a way to address this. For instance, can I call Chem from within dataviewjs (is there an API for Chem)? Otherwise, if Chem only accepts a string, I may be out of luck.

Acylation commented 11 months ago

Hi could you please refer the forum link here? Thanks for posting your use case and I will need more investigation to determine if this syntax can be supported. If this is technically applicable I would certainly like to implement it.

dpfelse commented 11 months ago

Hi, Thanks for your reply. I appreciate your considering this - it would be very useful to me (and I suspect others)!

The link to my original post on the Obsidian forum is here: https://forum.obsidian.md/t/passing-a-string-to-a-plugin/71973?u=dpfels

Best, Dan

dpfelse commented 10 months ago

Hi, Was there any progress on this? I am still interested in this feature. Thanks, Dan

Acylation commented 10 months ago

I've been busy these weeks but will soon be back to keyboard. Thanks for your patience and kind following up!

Alicecomma commented 10 months ago

Unrelated to this plugin, problem is in navigating the yaml with dataview rather than dataviewjs.

With minor changes to the yaml this works for me (list index moved into compound:, SMILES fixed because CCC=) is not valid, while CCC is):

---
compound_properties:
 - {compound: ABCD-XXXXXXXX, ABCD#: "ABCD-XXXXXXXX", SMILES: "CCC"
---

`$= "~~~ smiles\n"+dv.current().compound_properties[0].SMILES+"\n~~~"`

I don't know why you'd want individual list items to be indexed by their name, but that makes your yaml hard to work with in dataviewjs.

Regardless you cannot pass this (= runs dataview) into a codeblock and let it be rendered. You have to pass dv.current() into dataviewjs ($= runs dataviewjs; enable in-line in settings). Then for some reason ~~~smiles defines the codeblock. You use \n to simulate newlines. There may be a way to do .where(x=>x.compound==ABCD-XXXXXXXX) but I can't get this method to recognize .where().

dpfelse commented 10 months ago

Thanks! A couple of comments. First, "CCC=)" was (obviously) a typo. I was attempting to mask the original data from my compound list and mis-typed.

Your method works on my end. However, I was originally doing this to create a look-up table in YAML with critical compound data (including SMILES and other information) so I can pull this into notes on the fly using the "ABCD-XXXXXXX" ID# as an index. I am constantly taking notes that reference the ID#, which is not as useful as the actual structure for reminding me what is being described.

I will see if I can get ".where()" to work. However, given my limited skills, I appreciate any other suggestions you can offer. (Otherwise, I will post here if I come up with a solution of my own.)

Thanks again! Dan

Acylation commented 10 months ago

Thank you so much @Alicecomma !

@dpfelse I've got some references on inline queries from the forum. It seems that in a code block the inline DQL won't be supported and you have to use DataviewJS currently. Same conclusion and solution.

I'm working on incluing Dataview api to support the inline DQLs in smiles block. Hope that won't be too complicated.

Acylation commented 10 months ago

Hi! In the latest 0.3.0 version your proposed syntax is implemented! You can now passing a SMILES string more easily. Thanks for the patience and the request!

```smiles
= this.compound_properties["ABCD-XXXXXXXX"].SMILES
\```
craldaz commented 9 months ago

Hello, i joined this conversation late. Thanks for doing this. I'm reading through the convo now and trying it out. I am getting it to work a little


| Name | MR | SMILES |
| ---- | ---- | ---- |
| Propane | 44 | `$= "~~~ smiles\n"+dv.current().compound_properties[0].SMILES+"\n~~~"` |

Is that the best syntax?

craldaz commented 9 months ago

Cool, it also works with files outside the current note:

```dataviewjs
const files = dv.pages('"compounds"');
dv.table(
  ["File", "Name", "MR", "Structure"],
  files.map(file => {
    const name = file.name;
    const mr = file.MR;
    const smiles = file.SMILES;
    const smilesRendered = smiles ? `~~~smiles\n${smiles}\n~~~` : "N/A";
    return [file.link, name, mr, smilesRendered];
  })
);
Alicecomma commented 5 months ago

Update for the programmatic approach: $="~~~smiles\n"+dv.current().SMILES+"\n~~~"-type syntax only works in reading mode and in table cells for whatever reason. I've found the following to however work:

```dataviewjs
dv.paragraph("`$smiles="+dv.current().SMILES+"`");
\```

(\ before last ``` is to allow rendering in comment)

The in-line version of this sadly doesn't work for me, also not with ~ to replace `

$=dv.paragraph("`$smiles="+dv.current().SMILES+"`")
$=dv.paragraph("~$smiles="+dv.current().SMILES+"~")

Since I've used the in-line method everywhere, I replaced everything instead with a dv.view() .js script that should be able to handle changes to the Chem plugin and allow me to change the program flow across several pages. The snippet:

// Assuming the input is stored in a variable called input
function display(SMILES) {
    dv.paragraph("`$smiles="+SMILES+"`")
}

const current = dv.current();

if (input) {
    let {SMILES = null, chemical = null} = input;
    if (SMILES) {
        display(SMILES);
        return;
    }
    if (chemical && dv.page(chemical)?.SMILES) {
        display(dv.page(chemical).SMILES);
        return;
    }
}

var SMILES_c = current.SMILES;
if (current.Chemical && dv.page(current.Chemical).SMILES) {
    SMILES_c = dv.page(current.Chemical).SMILES;
}

if (SMILES_c) {
    display(SMILES_c);
    return;
}

dv.paragraph("Couldn't find SMILES from input="+str(input)+", dv.current().SMILES="+str(current.SMILES)+", dv.current().Chemical="+str(current.Chemical));

I saved it as js/displayer.js in the vault and it can be called in a dataviewjs codeblock as await dv.view("js/displayer") on the note of interest. There are optional arguments as await dv.view("js/displayer", {chemical="Butane", SMILES="CCCC"}) where the SMILES arg takes precedent over the chemical arg. The in-line version doesn't work for me in live mode, again, as it returns - or <Promise> depending on presence or absence of await.