- ZI-main.md
- The main Zotero Integration template
- runImport.md
- Template that enables updating literature notes at the click of a button, and much more.
- Meta bind button template
- Template for in-note button that executes runImport.md
-
-
Save FeralFlora/78f494c1862ce4457cef28d9d9ba5a01 to your computer and use it in GitHub Desktop.
<%* // Get citekey from 'citekey' property, or alias starting with '@' or file name const citekey = tp.frontmatter?.citekey || tp.frontmatter.aliases?.find(alias => alias.startsWith('@')) || tp.file.title; // Get the library ID from the properties. Fall back to "My Library" (1), if the library ID is not accessible const libraryID = tp.frontmatter.libraryID || 1; // Run the import app.plugins.getPlugin('obsidian-zotero-desktop-connector').runImport('IMPORT NAME', citekey, libraryID); ; %>
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}}s:{%- 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.== -#}
{#- 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) %>
- Status:: new
- Connections:: <% tp.file.cursor(4) %>
- Projects::
{% endif %}{% endpersist %}
[!info]- Info 🔗 Zotero{% if DOI %} | 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
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,"")}}{% else %} {{lineRegex.exec(n.note | truncate(30))}} {% 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 %}
{% set colorValueMap = { "#2ea8e5": { "colorCategory": "Blue", "heading": "❓ Problem formulation", "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 and frameworks", "symbol": "~" }, "#e56eee": { "colorCategory": "Magenta", "heading": "🗺️ Context and connections", "symbol": "€" }, "#ff6666": { "colorCategory": "Red", "heading": "🚧 Digging and disclaimers", "symbol": "£" }, "#aaaaaa": { "colorCategory": "Gray", "heading": "📌 Statistics and 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 %}
{% 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 %}
@Shade1698 Thank you for your comment. As @FeralFlora has already mentioned, removing the persistent tags from the code will have significant consequences when refreshing the import. Any solution to this 'problem' (such as it is) should keep the tags in the code in the template, but hide the little tags in the document. I have grown convinced that this is simply something I have to live with, however.
@FeralFlora Thank you for your help, and I wanted to thank you explicitly for the amount of time you've put into this project. That you're still responding to questions after your first post in March 2023 (!) is wonderful. I'll give the "let your markdown breathe" post a read, though I have managed to remove the extra white space on my own already. Might revert back ;)
I'm currently struggling with having new notes and annotations show up under already existent headers, as someone else has indicated is an issue here. You mentioned that you were working on a workaround for this. Have you had any luck with this?
@DaveSamuels1998 The runImport template only comes into play when you need to update your literature note with new Zotero changes. So there's no way for me to tell if it's working based on that screenshot.
But I can tell that you haven't enabled the List callouts plugin, because then the highlights would be colored as in Zotero. My literature note template is made to work with the List callouts plugin.
@IndexLibrorumProhibitorum thanks for the appreciation. I enjoy sharing my work and helping others :)
And, as you mention, I'm not quite done yet as there still improvements left to do. I have some ideas on how to have persistence and still be able to group annotations without duplication of headings. One way would involved more persistent fields, one for each color. The other would involve doing the persistence ourselves using javascript in Templater. However, the complexity of this is quite high. So I'm still mulling it over.
@FeralFlora Gotcha, thanks for giving it some thought though.
To share a tiny tiny micro-contribution of my own: When reviewing papers, I create new notes for concepts that I expect will tie several papers together. An example would be 'Organ transplantation', which naturally takes quotes and info from different papers. Using the Zotero integration plugin it's quite easy to just cite the paper as one normally does, but I noticed that such a citation wouldn't be registered under the "citations" block on the paper's own page. This is because the citation just gets formatted without explicitly using the citekey, which is what that citations block searches for.
Using the citation output format of 'Template', however, lets you set your own format that does allow you to use the citekey, and create a clickable link to that page. The following has worked well for me so far:
([[{{citekey}}|{{ creators[0].lastName }} {% if date %}{{ date | format("YYYY") }}{% endif %}]])
@DaveSamuels1998 The runImport template only comes into play when you need to update your literature note with new Zotero changes. So there's no way for me to tell if it's working based on that screenshot.
But I can tell that you haven't enabled the List callouts plugin, because then the highlights would be colored as in Zotero. My literature note template is made to work with the List callouts plugin.
Hello, for the Meta Bind Button Templates, what would be the beginning of the path? Is it the name of my Vault or the folder that the runImport.md is in?
For example, the name of my Vault is Personal Notes. Would I put command: templater-obsidian:07 - Personal Notes/Templates/runImport.md
or would I put command: templater-obsidian:07 - Templates/runImport.md
?
I tried both and I couldn't get my button to show updates to my document.
@DaveSamuels1998 What you are specifying in the button template is actually a Templater command that happens to specify the path of the template it executes as part of its command name. So you can just look in the command palette to see how the path should be specified.
In my case, 07 -
is actually part of the path. My top level folders are numbered to order them, so it's 07 - Bins
. I doubt your templates reside in a folder with the exact same numbering as I use, so you need to remove that part.
As for your two examples, if "Personal notes" is the vault, you should start with "Templates". You just need the part of the path that's inside the vault. But again, just look in the command palette and you'll see the correct path / command name.
@FeralFlora thank you so much for explaining what those sections are and why it's so important to keep them. I appreciate it! Not much else to say, but I really wanted to thank you for sharing the code and being so responsive to all the questions. You've made a big difference in how I easily I'm able to do my research, thank you!
@Shade1698 The last section you marked ("Import date") is not part of the template. It's added automatically by Zotero Integration.