Zotero-Obsidian Annotation Template: Help Needed with Tweaking

What I’m trying to do

This is my first post here (but I’ve been lurking for a while) so please be gentle with me :slight_smile:

I’ve been using Obsidian for a bit now, just as a kind of notepad of sorts. Recently, however, I decided to focus my flow so that my research (I’m in academia) can be combined in one place, i.e. Obsidian, hence the Zotero highlight importing.

I’m setting up my Obsidian so that I can import my highlights and notes from Zotero to Obsidian directly. I’ve followed this flow by @FeralFlora with some modifications which you can see below:

{# version: 3.6 -#}
---
citekey: {{citekey}}
aliases:
- "{%- if creators -%}
        {{creators[0].lastName or creators[0].name }}
        {%- if creators|length == 2 %} & {{creators[1].lastName or creators[1].name}}{% endif -%}
        {%- if creators|length > 2 %} et al.{% endif -%}
    {%- endif -%}
    {%- if date %} ({{date | format("YYYY")}}){% endif -%} 
    {%- if shortTitle %} {{shortTitle | safe}} {%- else %} {{title | safe}} {%- endif -%}"{% if itemType == "bookSection" %}
book-title: "{{bookTitle | replace('"',"'")}}"{% endif %}
title: "{{title | replace('"',"'")}}"
{%- set camelRegex = r/([a-z])([A-Z])/g %}
{%- for type, creators in creators | groupby("creatorType") %} 
{% if creators.length > 1 %}{{type | replace(camelRegex, "$1 $2") | lower | trim}}:{%- for creator in creators %}{% if creator.name %}
- {{creator.name}}{% else%}
- {{creator.firstName}} {{creator.lastName}} {% endif %}{%- endfor %} {% else -%}
{{type | replace(camelRegex, "$1-$2") | lower | trim}}:{%- for creator in creators %}{% if creator.name %} "{{creator.name}}"{% else%} "{{creator.firstName}} {{creator.lastName}}"{% endif -%}{%- endfor -%}{% endif -%}{% endfor %}
year: {% if date %}{{date | format("YYYY")}}{% endif %}
item-type: {{itemType | replace(camelRegex, "$1 $2") | title | trim}}
publisher: {% if publicationTitle %}"{{publicationTitle}}"{% else %}"{{publisher}}"{% endif %}
{%- if notes.length > 0 -%}
{%- set longShortCutoff = 20 -%}
{%- set shortnotes = [] -%}
{%- set longnotes = [] -%}
{%- for note in notes -%}
{%- if note.note | wordcount <= longShortCutoff -%}
{%- set shortnotes = (shortnotes.push(note.note), shortnotes) -%} 
{%- else -%}
{%- set longnotes = (longnotes.push(note), longnotes) -%}
{%- endif -%}{%- endfor -%}{%- endif -%}
{%- for comment in shortnotes %}
{%- if comment and loop.first %}
comments:
{% endif -%}
- "{{comment|replace('"',"'")| replace("\n"," ")}}"{% endfor %}
tags:{% for t in tags %}
- {{t.tag | replace(r/\s+/g, "-")}}{% endfor %}{% if DOI %}
doi: https://doi.org/{{DOI}}{% endif %}{% if itemType == "book" %}
ISBN: {{ISBN}}{% endif %}
cssclasses: 
- literature-note{% if attachments.length > 0 %}{% for attachment in attachments %}{% if loop.first %}
attachments:{% endif %}
- {{attachment.path}}{% endfor %}{% endif %}
libraryID: {{libraryID}}
---

`BUTTON[update-litnote]`

{% persist "notes" -%}
{%- if isFirstImport %}

{#  ==The following sections (Key takeaways and Processing) are not filled automatically. They are for for you to write into manually.== -#}
## Key takeaways

{#- The following is a cursor placeholder for the Templater plugin. After importing the note, you can jump to each of these with an assigned hotkey like ctrl+J  #}

- <% tp.file.cursor(1) %>

## Processing

- **Status**:: new
- **Connections**:: <% tp.file.cursor(4) %>
- **Projects**::

{% endif %}{% endpersist %}

> [!info]- Info 🔗 [**Zotero**]({{desktopURI}}){% if DOI %} | [**DOI**](https://doi.org/{{DOI}}){% endif %}{% for attachment in attachments | filterby("path", "endswith", ".pdf") %} | [**PDF-{{loop.index}}**](file:///{{attachment.path | replace(" ", "%20")}}){%- endfor %}
>
>{% if bibliography %}**Bibliography**: {{bibliography|replace("\n","" )}}{% endif %}
> 
> **Authors**:: {% for a in creators %} [[03 - Source notes/People/{{a.firstName}} {{a.lastName}}|{{a.firstName}} {{a.lastName}}]]{% if not loop.last %}, {% endif %}{% endfor %}
> 
> {% if tags %}**Tags**: {% for t in tags %}#{{t.tag | replace(r/\s+/g, "-")}}{% if not loop.last %}, {% endif %}{% endfor %}{% endif %}
> 
> **Collections**:: {% for collection in collections %}[[{{collection.name}}]]{% if not loop.last %}, {% endif %}{% endfor -%}
{%- set readingSpeed = 220 %}
{%- set wordsPerPage = 360 %}
{%- if pages %}
    {%- set pageRegex = r/(\d+)\-(\d+)/ %}
    {%- set splitPages = pageRegex.test(pages) %}
    {%- if splitPages %}
        {%- set pageMatch = pageRegex.exec(pages) %}
        {%- set firstPage = pageMatch[1] %}
        {%- set pageCount = pageMatch[2] - pageMatch[1] %}
    {%- else %}
        {%- set pageCount = pages %}
    {%- endif %}
{%- elif numPages %}
    {%- set pageCount = numPages %}
{%- else %}
{%- set pageCount = 0 %}
{% endif -%}
{%- if firstPage %}
>
> **First-page**:: {{firstPage}}
{%- endif -%}
{%- if pageCount > 0 -%}
    {%- set readingTime = ((pageCount* wordsPerPage)/readingSpeed)/60 %}
> 
> **Page-count**:: {{pageCount}}
> 
> **Reading-time**:: {% if readingTime < 1 %}{{(readingTime * 60) | round + " minutes"}}{% else %}{{readingTime | round(3) + " hours"}}{% endif %}{% endif %}

> [!abstract]-
> {% if abstractNote %}
> {{abstractNote|replace("\n","\n>")|striptags(true)|replace("Objectives", "**Objectives**")|replace("Background", "**Background**")|replace("Methodology", "**Methodology**")|replace("Results","**Results**")|replace("Conclusion","**Conclusion**")}}
> {% endif %}

> [!quote]- Citations
> 
> ```query
> content: "@{{citekey}}" -file:@{{citekey}}
> ```

{%- set headingRegex = r/^#+/ -%}
{%- set titleRegex = r/^#+.*/ -%}
{%- set lineRegex = r/^.*$/m %}
{%- if longnotes.length > 0 -%}
{%- for n in longnotes -%}
{%- if n and loop.first %}

> [!note]- Zotero notes ({{longnotes.length}})
> 
> Notes longer than {{longShortCutoff}} words.
{%- endif %}
>> [!example]- Note {{loop.index}} |{%- if headingRegex.test(n.note) == true %}[{{n.note | replace(n.note,titleRegex.exec(n.note))|replace(headingRegex,"")}}]({{n.uri}}){% else %} [{{lineRegex.exec(n.note | truncate(30))}}]({{n.uri}})
>> {% endif %}
>> {{n.note | replace("\n", "\n>> ")| replace(titleRegex, "")}}{% if n.tags.length > 0 %}
>>
>> Tags:{% for t in n.tags %} #{{t.tag}}{% if not loop.last %}, {% endif %}{% endfor %}{% endif -%}{%- if not loop.last %}
>{%- endif -%}
{%- endfor -%}{%- endif %}

___

## Reading notes

{% set colorValueMap = {
    "#2ea8e5": {
        "colorCategory": "Blue",
        "heading": "🧠 Thesis",
        "symbol": "€"
    },
    "#5fb236": {
        "colorCategory": "Green",
        "heading": "🔑 Key Takeaways",
        "symbol": "$"
    },
    "#ffd400": {
        "colorCategory": "Yellow",
        "heading": "🗂️ Evidence and Arguments",
        "symbol": "^"
    },
    "#f19837": {
        "colorCategory": "Orange",
        "heading": "✅ Actionable Takeaways",
        "symbol": "?"
    },
    "#a28ae5": {
        "colorCategory": "Purple",
        "heading": "💡 Concepts & Frameworks",
        "symbol": "~"
    },
    "#e56eee": {
        "colorCategory": "Magenta",
        "heading": "🗺️ Context & Connections",
        "symbol": "&"
    },
"#ff6666": {
        "colorCategory": "Red",
        "heading": "🚧 Digging & Disclaimers",
        "symbol": "!"
    },
    "#aaaaaa": {
        "colorCategory": "Gray",
        "heading": "📌 Statistics & Info",
        "symbol": "%"
    }
} -%}

{%- macro tagFormatter(annotation) -%}
    {% if annotation.tags -%}
        {%- for t in annotation.tags %} #{{ t.tag | replace(r/\s+/g, "-") }}{% if not loop.last %}, {% endif %}{%- endfor %}
    {%- endif %}
{%- endmacro -%}

{% persist "annotations" %}
{% set annotations = annotations | filterby("date", "dateafter", lastImportDate) -%}
{% if annotations.length > 0 %}
*Imported on [[{{importDate | format("YYYY-MM-DD")}}]] at {{importDate | format("HH:mm")}}*

{%- set grouped_annotations = annotations | groupby("color") -%}
{%- for color, colorValue in colorValueMap -%}
{%- if color in grouped_annotations -%} 
{%- set annotations = grouped_annotations[color] -%}
{%- for annotation in annotations -%}
{%- set citationLink = '[(p. ' ~ annotation.pageLabel ~ ')](' ~ annotation.desktopURI ~ ')' %}
{%- set tagString = tagFormatter(annotation) %}

{%- if annotation and loop.first %}

### {{colorValue.heading}} %% fold %%
{% endif -%}

{%- if annotation.imageRelativePath %}

> [!cite]+ Image {{citationLink}}
> ![[{{annotation.imageRelativePath}}]]{% if annotation.tags %}
> {{tagString}}{% endif %}{%- if (annotation.comment or []).indexOf("todo ") !== -1 %}
> - [ ] **{{annotation.comment | replace("todo ", "")}}**{%- elif annotation.comment %}
> **{{annotation.comment}}**{%- endif %}
{% elif (annotation.comment or []).indexOf("todo ") !== -1 %}
- [ ] **{{annotation.comment | replace("todo ", "")}}**:{% if not annotation.annotatedText %} {{citationLink}}{% else %}
- {{colorValue.symbol}}  {{annotation.annotatedText | replace(r/\s+/g, " ")}} {{citationLink}}{{tagString}}{% endif -%}
{% elif annotation.comment %}
- **{{annotation.comment}}**:{% if not annotation.annotatedText %} {{citationLink}}{% else %}
- {{colorValue.symbol}}  {{annotation.annotatedText | replace(r/\s+/g, " ") }} {{citationLink}}{{tagString}}{% endif -%}
{%- elif annotation.annotatedText %}
- {{colorValue.symbol}}  {{annotation.annotatedText | replace(r/\s+/g, " ") }} {{citationLink}}{{tagString}}
{%- endif -%}{%- endfor %}{%- endif -%}
{% endfor -%}
{% endif %}

{% endpersist %}

Everything works smoothly so far, however, I’d like to edit the template itself to include the following modifications:

  1. have the note follow the quote pulled from the Zotero highlight, i.e. quote → note, instead of the current note → quote;
  2. when updating the Update literature note Meta Bind button, I’d like to have the newly imported highlights be appended to the appropriate heading, instead of creating a new one:
  • currently, let’s imagine that I have two highlights in Zotero, one blue and one yellow:
  • when imported for the first time a new file in Obsidian is created that has both the heading called :brain: Thesis (which corresponds to the blue highlight in Zotero) and the other heading called :card_index_dividers: Evidence and Arguments headings (corresponding to the yellow highlight),
  • now, I have made another blue highlight in Zotero (in total two blue, one yellow);
  • I hit Update literature note button in the Obsidian note that I’m working on:
  • the new highlight is pulled to the note after the :brain: Thesis and :card_index_dividers: Evidence and Arguments headings, creating a second :brain: Thesis heading containing the new highlight,
  • what I’d like to happen is for the :brain: Thesis heading to be appended with the second highlight so that after updating the note, it has two highlights from Zotero and :card_index_dividers: Evidence and Arguments has one.

Things I have tried

I have gone and tried tweaking the template as described in [this post](https://forum.obsidian.md/t/zotero-integration-import-templates/36310/79?u=feralflora), however, I can’t seem to get it to work and I don’t have enough coding experience to figure it out on my own.

I’d really really REALLY appreciate your help with editing the template so that both the quote-note order can be changed and that highlights can be appended to the correct heading! Thank you!!!

Hi @headswillroll1789, glad you liked my template.

The changes you’re after are not small, especially number two. If I knew a fix for number two, I would have implemented it. But the current situation is a consequence of how the persistent field feature works, namely that it persists the content in persistent fields, and then appends changes below existing content.

Without grouping the annotations by color under headings, this isn’t too much of an issue, but’s it’s quite annoying with the duplicated headings. I try to minimize updates/re-imports for this reason.

However, I’m going to try to implement a workaround for this soon.

As for the order of the annotations and comments, the comment you linked to was not regarding a current version of the template. There’s another comment here with code that moves the comments after the annotations: Zotero Integration – Import Templates? - #224 by untidytexts

That one is more recent, but still not current, as I’ve made lost of improvements since. But it might give you some idea of what needs to be done. I don’t have time to make these changes for you at the moment.

Hi @Feralflora, it’s like meeting a celebrity because your template has become genuinely so loved by me!

I absolutely get that my tweaks turn out to be more labour intensive so I really appreciate your help anyhow. The template is just so good that I wanted it to be my one-stop for research :slight_smile:

And thanks to your comments, I’ve managed to tweak it enough to do the—first—trick:

---
citekey: {{citekey}}
aliases: ["
    {%- if creators -%}
        {{creators[0].lastName}}
        {%- if creators|length > 1 %} et al.{% endif -%}
    {%- endif -%}
    {%- if date %} ({{date | format("YYYY")}}){% endif -%} 
    {%- if shortTitle %} {{shortTitle | safe}} {%- else %} {{title | safe}} {%- endif -%}
"]
title: "{{title}}"
authors: {{authors}}
tags: [literature-note, {% for t in tags %}{{t.tag | replace(r/\s+/g, "-")}}{% if not loop.last %}, {% endif %}{% endfor %}]
year: {{date | format("YYYY")}}
publisher: "{{publicationTitle}}"
doi: {{DOI}}
---

`BUTTON[update-litnote]`

# [{{title}}]({{desktopURI}})

{% persist "notes" %}
{% if isFirstImport %}
## Key takeaways
- 

## Processing
- **Projects**:: 
- **Connections**:: 
{% endif %}{% endpersist %}

> [!info]- Info - [**Zotero**]({{desktopURI}}) | [**DOI**](https://doi.org/{{DOI}}) | {% for attachment in attachments | filterby("path", "endswith", ".pdf") %}[**PDF**](file:///{{attachment.path | replace(" ", "%20")}}){%- endfor %}
>
> {% if bibliography %}**Bibliography**: {{bibliography|replace("\n"," ")}}{% endif %}
> 
> **Authors**:: {% for a in creators %} [[{{a.firstName}} {{a.lastName}}]]{% if not loop.last %}, {% endif %}{% endfor %}
> 
> {% if hashTags %}**Tags**: {{hashTags}}{% endif %}
> 
> **Collections**:: {% for collection in collections %}[[{{collection.name}}]]{% if not loop.last %}, {% endif %}{% endfor %}
> 
> {# **First-page**: {% for annotation in annotations %}{% if loop.first %}{{annotation.pageLabel}}{% endif %}{% endfor %} -#}
> {%- set readingSpeed = 220 %}
{%- set wordsPerPage = 360 %}
{%- if pages %}
    {%- set pageRegex = r/(\d+)\-(\d+)/ %}
    {%- set splitPages = pageRegex.test(pages) %}
    {%- if splitPages %}
        {%- set pageMatch = pageRegex.exec(pages) %}
        {%- set firstPage = pageMatch[1] %}
        {%- set pageCount = pageMatch[2] - pageMatch[1] %}
    {%- else %}
        {%- set pageCount = pages %}
    {%- endif %}
{%- elif numPages %}
    {%- set pageCount = numPages %}
{%- else %}
	{%- set pageCount = 0 %}
{% endif -%}
{%- if firstPage %}
>
> **First-page**:: {{firstPage}}
{%- endif -%}
{%- if pageCount > 0 -%}
    {%- set readingTime = ((pageCount* wordsPerPage)/readingSpeed)/60 %}
> 
> **Page-count**:: {{pageCount}}
> 
> **Reading-time**:: {% if readingTime < 1 %}{{(readingTime * 60) | round + " minutes"}}{% else %}{{readingTime | round(3) + " hours"}}{% endif %}{% endif %}

> [!abstract]-
> {% if abstractNote %}
> {{abstractNote|replace("\n"," ")|striptags(true)|replace("Objectives", "**Objectives**")|replace("Background", "**Background**")|replace("Methodology", "**Methodology**")|replace("Results","**Results**")|replace("Conclusion","**Conclusion**")}}
> {% endif %}

> [!quote]- Citations
> 
> ```query
> content: "@{{citekey}}" -file:@{{citekey}}
> ```
 
---

## Reading notes
{% macro heading(color) -%}
{%- if color == "#2ea8e5" -%}
🧠 Thesis
{%- endif -%}
{%- if color == "#5fb236" -%}
🔑 Key Takeaways
{%- endif -%}
{%- if color == "#ffd400" -%}
🗂️ Evidence and Arguments
{%- endif -%}
{%- if color == "#e56eee" -%}
🗺️ Context & Connections
{%- endif -%}
{%- if color == "#a28ae5" -%}
💡 Concepts & Frameworks
{%- endif -%}
{%- if color == "#f19837" -%}
✅ Actionable Takeaways
{%- endif -%}
{%- if color == "#ff6666" -%}
🚧 Digging & Disclaimers
{%- endif -%}
{%- if color == "#aaaaaa" -%}
📌 Statistics & Info
{%- endif -%}
{%- endmacro -%}

{% macro calloutCharacter(color) -%}
{%- if color == "#5fb236" -%}
$
{%- endif -%}
{%- if color == "#2ea8e5" -%}
€
{%- endif -%}
{%- if color == "#ffd400" -%}
^
{%- endif -%}
{%- if color == "#a28ae5" -%}
~
{%- endif -%}
{%- if color == "#ff6666" -%}
!
{%- endif -%}
{%- if color == "#e56eee" -%}
&
{%- endif -%}
{%- if color == "#f19837" -%}
?
{%- endif -%}
{%- if color == "#aaaaaa" -%}
%
{%- endif -%}
{%- endmacro -%}

{% persist "annotations" %}
{% set annotations = annotations | filterby("date", "dateafter", lastImportDate) -%}
{% if annotations.length > 0 %}
*Imported on {{importDate | format("YYYY-MM-DD HH:mm")}}*

{% for color, annotations in annotations | groupby("color") -%}

### {{heading(color)}} %% fold %%
{% for annotation in annotations -%}
{%- if annotation.imageRelativePath %}
> [!cite]+ Image [(p. {{annotation.pageLabel}})](zotero://open-pdf/library/items/{{annotation.attachment.itemKey}}?page={{annotation.pageLabel}}&annotation={{annotation.id}})
> ![[{{annotation.imageRelativePath}}]]{% if annotation.hashTags %}
> {{annotation.hashTags}}{% endif %}{%- if (annotation.comment or []).indexOf("todo ") !== -1 %}
> - [ ] **{{annotation.comment | replace("todo ", "")}}**{% else %}
> **{{annotation.comment}}**{%- endif -%}

{% elif (annotation.comment or []).indexOf("todo ") !== -1 %}
{% if not annotation.annotatedText %} [(p. {{annotation.pageLabel}})](zotero://open-pdf/library/items/{{annotation.attachment.itemKey}}?page={{annotation.pageLabel}}&annotation={{annotation.id}}){% else %}
- {{calloutCharacter(annotation.color)}} {{annotation.annotatedText | nl2br}} [(p. {{annotation.pageLabel}})](zotero://open-pdf/library/items/{{annotation.attachment.itemKey}}?page={{annotation.pageLabel}}&annotation={{annotation.id}}) {% if annotation.hashTags %}{{annotation.hashTags}}{% endif %}
	-  [ ] **{{annotation.comment | replace("todo ", "")}}**{% endif %}
{% elif annotation.comment %}
- {{calloutCharacter(annotation.color)}} {{annotation.annotatedText | nl2br}} [(p. {{annotation.pageLabel}})](zotero://open-pdf/library/items/{{annotation.attachment.itemKey}}?page={{annotation.pageLabel}}&annotation={{annotation.id}}) {% if annotation.hashTags %}{{annotation.hashTags}}{% endif %}
	- **{{annotation.comment}}** {% if not annotation.annotatedText %} [(p. {{annotation.pageLabel}})](zotero://open-pdf/library/items/{{annotation.attachment.itemKey}}?page={{annotation.pageLabel}}&annotation={{annotation.id}}){% else %}{% endif %}
{%- elif annotation.annotatedText %}
- {{calloutCharacter(annotation.color)}} {{annotation.annotatedText | nl2br}} [(p. {{annotation.pageLabel}})](zotero://open-pdf/library/items/{{annotation.attachment.itemKey}}?page={{annotation.pageLabel}}&annotation={{annotation.id}}) {% if annotation.hashTags %}{{annotation.hashTags}}{% endif %}
{%- endif -%}{%- endfor %}

{% endfor -%}
{% endif %}
{% endpersist %}