Converting Zotero Tags for Properties (through Zotero Integration)

What I’m trying to do

I am just getting started setting up a workflow in Obsidian, and I have been relying on the great tutorial by Elena Razlogova, Doing History with Zotero and Obsidian, which speaks to my historian-geek tendencies. My setup will look a bit different, especially with the new possibilities for tucking everything away neatly into properties. But she has a lovely bit of code that I’d like to use, which converts Zotero tags to something more useful in Obsidian by converting them into nested tags. The results then make it possible to use Auto Note Mover to conveniently sort imported notes:

{% if tags.length > 0 -%}{% for t in tags -%}#{% if t.tag == "secondary" %}source/secondary{% if not loop.last %}{% endif %}{% elif t.tag == "primary" %}source/primary{% if not loop.last %}{% endif %}{% elif "-project" in t.tag %}project/{{t.tag | lower | replace(" ", "-") | replace("-project", "")}}{% else %}subject/{{t.tag | lower | replace(" ", "-")}}{% endif %}{% if not loop.last %} {% endif %}{%- endfor %}{%- endif %}

Things I have tried

For reasons this noob (who is hopefully using the correct terminology!) does not understand, this code only works inline. It returns nothing if I put it in the properties/yaml portion of my template for Zotero Integration. I did see a few mentions on this forum that nesting doesn’t work in properties, but I’m confused about this. If necessary, I will just tuck the above into a callout, but I thought I’d give the Properties solution, which would be much cleaner, one more try before giving up. Any advice?

I’m still struggling a bit with how to add more complex code blocks in this forum, but I can figure it out and post my current full template if this is helpful. (My geekiness is only relative to the category of historian.)

I don’t use tags in Zotero so I cannot test the template for those.

I don’t understand your comment about –

You need to customize the front part of the template – which is run once as a Templater template as Zotero Integration Templates also are run as designed.

---
cssclasses: research-note
type: "{{itemType}}"{% for type, creators in creators | groupby("creatorType") -%}{% if loop.first %}
{% endif %}{{type | replace("interviewee", "author") | replace("director", "author") | replace("presenter", "author") | replace("podcaster", "author") | replace("programmer", "author") | replace("cartographer", "author") | replace("inventor", "author") | replace("sponsor", "author")  | replace("performer", "author") | replace("artist", "author")}}: "{%- for creator in creators -%}{%- if creator.name %}{{creator.name}}{%- else %}{{creator.lastName}}, {{creator.firstName}}{%- endif %}{% if not loop.last %}; {% endif %}{% endfor %}"{% if not loop.last %}
{% endif %}{%- endfor %}{% if title %}
title: "{{title}}"{% endif %}{% if publicationTitle %}
publication: "{{publicationTitle}}"{% endif %}{% if date %}
date: {{date | format("YYYY-MM-DD")}}{% endif %}{% if archive %}
archive: "{{archive}}"{% endif %}{% if archiveLocation %}
archive-location: "{{archiveLocation}}"{% endif %}
citekey: {{citekey}}
---
  • This is the part that needs to be customized. You need to have the required tags and to probably exchange interviewee and other parts as well that only you know.

As far as I know, Properties doesn’t get involved in this, it is only a live rendering that can also be turned off. The nesting issues I think only relate to DataView queries, but you needn’t worry about that.

I tried the template just now, it works but as I said it doesn’t do anything for me with regards to tags as I don’t use them (I don’t like to set myself extra tasks with those, it’s like having to dance with a different girl every other minute).
What I do is read my imported annotations again and either transclude them or just obstinately cut and paste them in my designated notes.

I meant that if I put the code below the properties/yaml area (so below the —) then it works as intended. If I put precisely the same code after “tags:” in the area between the two sets of —s, it produces nothing. Maybe it will help to provide the description of what this code is supposed to do – from its author:

The template above imports all tags attached to the Zotero item, separated by line breaks. Zotero allows for spaces in tags, but Obsidian does not, so the template replaces spaces between words with dashes. The template converts your tags to nested “subject” tags: #subject/zotero-tag.

This template also helps to categorize notes by type or source and project. It converts tag “secondary” to #source/secondary and tag “primary” to #source/primary. In Zotero, you can add “-project” in the end of any tag that denotes a research project. The template will convert Zotero tag “your-article-project” to Obsidian tag #project/your-article.

I have a huge Zotero database with many tags and a folder structure that has become chaotic but that contains information I want to retain. (I want to know which sources I used for things I’ve already written, for instance.) The nested tags that Elena Razlogova devised seem perfect for this purpose. But they take up a lot of visual space, so tucking them into the properties (which I can collapse) would be ideal.

In other words, this isn’t a question about tags in either Zotero or Obsidian. I just can’t figure out why the code won’t work in the place I want to use it.

I should also mention that I have not yet attempted to use Templater, though I have it installed and could try. (I only started this migration saga and the Obsidian learning curve a week ago.) I suspect that there is a reason why the code that you quoted (which does indeed work perfectly) does not include the code that I was asking about. I was just hoping that the new Properties system Obsidian just implemented would allow me to do something that Razlogova couldn’t.

It would be nice to figure all this out indeed, because it would benefit other people and their workflows.
I did not have time to read the full documentation, though and I have never used Auto Note Mover, which is an integral part of the worklflow.

Generally, I dislike having to align my needs with the logic or code of another person, which makes it difficult when one is lacking of the same ability. What something works for some, may not work for another. It is very rare that someone would take the time to present or showcase a model that fits all or will provide support for more than a couple of days or weeks. And I don’t like to ask or intrude.

Zotero Integration works with Templater. That’s how I ran that template. But I don’t have tags and the setup with Auto Note Mover (I move my own files if I need to with javascript).
So I’ll say give it time. This is a pretty niche case but do try time and again on the forum. Someone may give better input than myself.

I have found some time to look into this. Actually, I remembered a demo vault of the author was downloaded months ago on my iPad and the zip file was left unopened ever since.

So you wanted to take it a step further hoping that Properties would add extra functionality or spruce it up more, but I am still stuck at first base on this.
I took an arbitrary piece of paper, annotated it complete with tags (following some of those used by the author on purpose), added various colors to try the callouts for the fun of it, and then ended up not getting the tags with my annotations.

I thought you also could not start the manual Templater way of sorting – which would lead to Auto Note Mover kicking in and moving the files to designated folders for further processing – because of the same problem – unless I have my tags in Obsidian with each bit of text (annotation), I can’t make that work.

But then I remembered your tags were not annotation tags local/specific to each annotation but globally administered outside of the PDF viewer/editor.

Which means the above cited part dealing with tags (which did not do anything for me as I did not have global tags set for the ad hoc document I started annotating for the sake of testing all this), being outside of the scope of the ‘persist loop’ is not responsible for doing anything I want.

But if you go about the way the the author explains it, it works: select the text, run the extract research note from selection template on the selection, it will cut it into a file the user names and the template will copy the header, meaning that the global tags will be copied over, which when meeting the criteria set by Auto Note Mover, will set off moving the newly created chunk of file.

So what you don’t like is that these tags are inline, rather than nicely nested in the YAML or Properties section, right?
Then delete the section which would produce the inline tags (the part we talked about above) and replace the YAML with this:

---
cssclasses: research-note
type: "{{itemType}}"{% for type, creators in creators | groupby("creatorType") -%}{% if loop.first %}
{% endif %}{{type | replace("interviewee", "author") | replace("director", "author") | replace("presenter", "author") | replace("podcaster", "author") | replace("programmer", "author") | replace("cartographer", "author") | replace("inventor", "author") | replace("sponsor", "author")  | replace("performer", "author") | replace("artist", "author")}}: "{%- for creator in creators -%}{%- if creator.name %}{{creator.name}}{%- else %}{{creator.lastName}}, {{creator.firstName}}{%- endif %}{% if not loop.last %}; {% endif %}{% endfor %}"{% if not loop.last %}
{% endif %}{%- endfor %}{% if title %}
title: "{{title}}"{% endif %}{% if publicationTitle %}
publication: "{{publicationTitle}}"{% endif %}{% if date %}
date: {{date | format("YYYY-MM-DD")}}{% endif %}{% if archive %}
archive: "{{archive}}"{% endif %}{% if archiveLocation %}
archive-location: "{{archiveLocation}}"{% endif %}
citekey: {{citekey}}
tags: {% if tags.length > 0 -%} {%- for t in tags -%} {%- set replaced_tag = t.tag %} {%- if t.tag == "secondary" %} {%- set replaced_tag = "source/secondary" %} {%- elif t.tag == "primary" %} {%- set replaced_tag = "source/primary" %} {%- elif "-project" in t.tag %} {%- set replaced_tag = "project/" ~ t.tag | lower | replace(" ", "-") | replace("-project", "") %} {%- else %} {%- set replaced_tag = "subject/" ~ t.tag | lower | replace(" ", "-") %} {%- endif %} {{ "\n  - " ~ replaced_tag }} {%- endfor %} {%- endif %}
---
  • I tested it with various files, with 2-4 tags and does the conversion the original author sought to conduct.
    The tags line ('triple-click` it in Obsidian to select it) can be shifted into any line; it doesn’t have to be last in YAML.

Now for the version that handles annotation tags as well…
I found some that do here, but they are not as clean as this template. Otherwise a reaaly good thread for inspiration and all kinds of template hexenküch-ing.

The complete template handling annotation tags as well:

---
cssclasses: research-note
type: "{{itemType}}"{% for type, creators in creators | groupby("creatorType") -%}{% if loop.first %}
{% endif %}{{type | replace("interviewee", "author") | replace("director", "author") | replace("presenter", "author") | replace("podcaster", "author") | replace("programmer", "author") | replace("cartographer", "author") | replace("inventor", "author") | replace("sponsor", "author")  | replace("performer", "author") | replace("artist", "author")}}: "{%- for creator in creators -%}{%- if creator.name %}{{creator.name}}{%- else %}{{creator.lastName}}, {{creator.firstName}}{%- endif %}{% if not loop.last %}; {% endif %}{% endfor %}"{% if not loop.last %}
{% endif %}{%- endfor %}{% if title %}
title: "{{title}}"{% endif %}{% if publicationTitle %}
publication: "{{publicationTitle}}"{% endif %}{% if date %}
date: {{date | format("YYYY-MM-DD")}}{% endif %}{% if archive %}
archive: "{{archive}}"{% endif %}{% if archiveLocation %}
archive-location: "{{archiveLocation}}"{% endif %}
citekey: {{citekey}}
tags: {% if tags.length > 0 -%} {%- for t in tags -%} {%- set replaced_tag = t.tag %} {%- if t.tag == "secondary" %} {%- set replaced_tag = "source/secondary" %} {%- elif t.tag == "primary" %} {%- set replaced_tag = "source/primary" %} {%- elif "-project" in t.tag %} {%- set replaced_tag = "project/" ~ t.tag | lower | replace(" ", "-") | replace("-project", "") %} {%- else %} {%- set replaced_tag = "subject/" ~ t.tag | lower | replace(" ", "-") %} {%- endif %} {{ "\n  - " ~ replaced_tag }} {%- endfor %} {%- endif %}
---

{{bibliography}}
[online]({{uri}}) [local]({{desktopURI}}) {%- for attachment in attachments | filterby("path", "endswith", ".pdf") %} [pdf](file://{{attachment.path | replace(" ", "%20")}})
{%- endfor %}

created `= date(this.file.cday)`
modified `= date(this.file.mday)`

### Index

start-date:: {% if date %}{{date | format("YYYY-MM-DD")}}{% endif %}
end-date:: 
page-no:: {% for annotation in annotations %}{% if loop.first %}{{annotation.pageLabel}}{% endif %}{% endfor %}

### Connections

comment:: 

### Note

{% macro calloutHeader(color) -%}
{%- if color == "#ff6666" -%}
Important
{%- endif -%}
{%- if color == "#5fb236" -%}
Reference
{%- endif -%}
{%- if color == "#2ea8e5" -%}
Undefined - Blue
{%- endif -%}
{%- if color == "#a28ae5" -%}
Undefined - Purple
{%- endif -%}
{%- endmacro -%}

{% persist "annotations" %}
{% set annotations = annotations | filterby("date", "dateafter", lastImportDate) -%}
{% if annotations.length > 0 %}

### Imported on {{importDate | format("YYYY-MM-DD h:mm a")}}

{%- for annotation in annotations %}
{% if annotation.color !== "#ffd400" %}
>[!quote{% if annotation.color %}|{{annotation.color}}{% endif %}] {{calloutHeader(annotation.color)}}
>{%- endif -%}{% if annotation.imageRelativePath %}
![[{{annotation.imageRelativePath}}]] {% endif %}{% if annotation.annotatedText %}
> {{annotation.annotatedText}} [(p. {{annotation.pageLabel}})](zotero://open-pdf/library/items/{{annotation.attachment.itemKey}}?page={{annotation.pageLabel}}&annotation={{annotation.id}})
{%- endif %}{%- if annotation.comment%}

%% {{annotation.comment}} %%{%- endif %}
{% if annotation.tags %}{% for t in annotation.tags -%}#{% if t.tag == "secondary" %}source/secondary{% if not loop.last %}{% endif %}{% elif t.tag == "primary" %}source/primary{% if not loop.last %}{% endif %}{% elif "-project" in t.tag %}project/{{t.tag | lower | replace(" ", "-") | replace("-project", "")}}{% else %}subject/{{t.tag | lower | replace(" ", "-")}}{% endif %}{% if not loop.last %}
{% endif %}{%- endfor %}{%- endif %}

---

{%- endfor %}

{% endif %}{% endpersist %}
  • I decided to employ conversion to the nested tags here as well.

No conversion among the annotation tags:

---
cssclasses: research-note
type: "{{itemType}}"{% for type, creators in creators | groupby("creatorType") -%}{% if loop.first %}
{% endif %}{{type | replace("interviewee", "author") | replace("director", "author") | replace("presenter", "author") | replace("podcaster", "author") | replace("programmer", "author") | replace("cartographer", "author") | replace("inventor", "author") | replace("sponsor", "author")  | replace("performer", "author") | replace("artist", "author")}}: "{%- for creator in creators -%}{%- if creator.name %}{{creator.name}}{%- else %}{{creator.lastName}}, {{creator.firstName}}{%- endif %}{% if not loop.last %}; {% endif %}{% endfor %}"{% if not loop.last %}
{% endif %}{%- endfor %}{% if title %}
title: "{{title}}"{% endif %}{% if publicationTitle %}
publication: "{{publicationTitle}}"{% endif %}{% if date %}
date: {{date | format("YYYY-MM-DD")}}{% endif %}{% if archive %}
archive: "{{archive}}"{% endif %}{% if archiveLocation %}
archive-location: "{{archiveLocation}}"{% endif %}
citekey: {{citekey}}
tags: {% if tags.length > 0 -%} {%- for t in tags -%} {%- set replaced_tag = t.tag %} {%- if t.tag == "secondary" %} {%- set replaced_tag = "source/secondary" %} {%- elif t.tag == "primary" %} {%- set replaced_tag = "source/primary" %} {%- elif "-project" in t.tag %} {%- set replaced_tag = "project/" ~ t.tag | lower | replace(" ", "-") | replace("-project", "") %} {%- else %} {%- set replaced_tag = "subject/" ~ t.tag | lower | replace(" ", "-") %} {%- endif %} {{ "\n  - " ~ replaced_tag }} {%- endfor %} {%- endif %}
---

{{bibliography}}
[online]({{uri}}) [local]({{desktopURI}}) {%- for attachment in attachments | filterby("path", "endswith", ".pdf") %} [pdf](file://{{attachment.path | replace(" ", "%20")}})
{%- endfor %}

created `= date(this.file.cday)`
modified `= date(this.file.mday)`

### Index

start-date:: {% if date %}{{date | format("YYYY-MM-DD")}}{% endif %}
end-date:: 
page-no:: {% for annotation in annotations %}{% if loop.first %}{{annotation.pageLabel}}{% endif %}{% endfor %}

### Connections

comment:: 

### Note

{% macro calloutHeader(color) -%}
{%- if color == "#ff6666" -%}
Important
{%- endif -%}
{%- if color == "#5fb236" -%}
Reference
{%- endif -%}
{%- if color == "#2ea8e5" -%}
Undefined - Blue
{%- endif -%}
{%- if color == "#a28ae5" -%}
Undefined - Purple
{%- endif -%}
{%- endmacro -%}

{% persist "annotations" %}
{% set annotations = annotations | filterby("date", "dateafter", lastImportDate) -%}
{% if annotations.length > 0 %}

### Imported on {{importDate | format("YYYY-MM-DD h:mm a")}}

{%- for annotation in annotations %}
{% if annotation.color !== "#ffd400" %}
>[!quote{% if annotation.color %}|{{annotation.color}}{% endif %}] {{calloutHeader(annotation.color)}}
>{%- endif -%}{% if annotation.imageRelativePath %}
![[{{annotation.imageRelativePath}}]] {% endif %}{% if annotation.annotatedText %}
> {{annotation.annotatedText}} [(p. {{annotation.pageLabel}})](zotero://open-pdf/library/items/{{annotation.attachment.itemKey}}?page={{annotation.pageLabel}}&annotation={{annotation.id}})
{%- endif %}{%- if annotation.comment%}

%% {{annotation.comment}} %%{%- endif %}
{% if annotation.tags %}{% for t in annotation.tags %}#{{t.tag}}{% if not loop.last %}, {% endif %}{% endfor %}{% endif -%}

{{ "\n\n---" }}

{%- endfor %}

{% endif %}{% endpersist %}
  • The > parts before {{annotation.annotatedText}} can be deleted from both if blockquotes for normal yellow highlights are unwanted.
    Same for {{ "\n\n---" }} and --- dividers if not found useful.

Wow. This is fantastic! Thank you so much for spending the time to figure this out. It is extremely helpful for my purposes. My life would be easier if I cared a little less about formatting, but I really do find it easier to process information when it is less cluttered. I am very grateful for your time.

I am glad it was working to your liking. My main incentive was to have this working for me too – “on the rebound.” :slight_smile:

I looked on around on the Zotero forums too and found some people were still confused about what they need to do about getting their tags out.

If you have any further interesting ideas to share in the form of requests, please do so in the future.

Cheers




By the way, about the DataView queries … in case you were wondering how you could use those (not a DV guru, either, just dipping my toes), I found out that in the four js files in the meta/dataview folder where you find the dv.pages('"01 notes"') or dv.pages('"01 notes" or "02 analysis" or "04 index"') lines, you can exchange those with dv.pages("") to have the whole of your vault available for queries.

1 Like

Dataview is next on my list to figure out, so this is definitely helpful!

Well, in the author’s vault everything is made easy. No need to code anything there.

To get an idea what DV can do, there’s another immensely helpful resource package to download, again in the form of a demo vault:

Also see:
https://s-blu.github.io/basic-dataview-query-builder/

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.