<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Python on Nicole van der Hoeven</title><link>https://nicolevanderhoeven.com/tags/python/</link><description>Recent content in Python on Nicole van der Hoeven</description><generator>Hugo -- gohugo.io</generator><language>en-us</language><copyright>Nicole van der Hoeven</copyright><lastBuildDate>Wed, 02 Jun 2021 00:27:59 +0200</lastBuildDate><atom:link href="https://nicolevanderhoeven.com/tags/python/index.xml" rel="self" type="application/rss+xml"/><item><title>Downloading files uploaded to Roam Research for use with Obsidian</title><link>https://nicolevanderhoeven.com/blog/20210602-downloading-files-from-roam/</link><pubDate>Wed, 02 Jun 2021 00:27:59 +0200</pubDate><guid>https://nicolevanderhoeven.com/blog/20210602-downloading-files-from-roam/</guid><description>&lt;p&gt;If you&amp;rsquo;ve recently transitioned from &lt;a href="https://roamresearch.com"&gt;Roam Research&lt;/a&gt; to &lt;a href="https://obsidian.md"&gt;Obsidian&lt;/a&gt;, you might run into a few issues, even after you&amp;rsquo;ve run the &amp;ldquo;Markdown format importer&amp;rdquo; core plugin. One of the most annoying ones that I ran into was that files uploaded to Roam (images, pdfs, etc.) are automatically uploaded to &lt;a href="https://firebase.google.com/"&gt;Google Firebase&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Roam assets are linked to in the Markdown files like this:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-markdown" data-lang="markdown"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;![](https://firebasestorage.googleapis.com/v0/b/firescript-471a1.appspot.com/o/imgs%2Fapp%2FMyNotes%2FxRFXx7l-ZK.png?alt=media&amp;amp;token=1e03b680-5472-33dc-9b06-a127301af37f)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Now, since Roam relies on security by obscurity on this front, the links to your assets will still be accessible in your Obsidian vault, and they&amp;rsquo;ll still render correctly. However, I still think it&amp;rsquo;s worth downloading the files for three reasons:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;If you don&amp;rsquo;t intend to use Roam anymore and cancel your subscription, they will likely delete those assets at some point, leaving you with a lot of broken links in Obsidian.&lt;/li&gt;
&lt;li&gt;Part of Obsidian&amp;rsquo;s appeal is that you own your assets. Not so if they&amp;rsquo;re on someone else&amp;rsquo;s server.&lt;/li&gt;
&lt;li&gt;You can&amp;rsquo;t access Firebase files without an internet connection.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;So, instead, you want to download the contents of that link and replace it in your notes with something like this:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-markdown" data-lang="markdown"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;![](assets/newfile.png)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;That way, you&amp;rsquo;ll have full control over all your assets, and you won&amp;rsquo;t be dependent on those assets being stored on Firebase by Roam.&lt;/p&gt;
&lt;p&gt;To fix this, I wrote a Python script that will:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Go through every file and every folder within a specified root vault directory&lt;/li&gt;
&lt;li&gt;Look for links to Google Firebase&lt;/li&gt;
&lt;li&gt;Download all assets from Firebase to a local folder (&lt;code&gt;/assets&lt;/code&gt; by default), using the timestamp and index number as a filename&lt;/li&gt;
&lt;li&gt;Replace the Firebase links in your vault with links to the newly downloaded files, accounting for PDFs, whose links are formatted differently in Roam as &lt;code&gt;{{ pdf: ... }}&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Before you run this script, I highly recommend backing up your vault or trying this out on a dummy vault.&lt;/p&gt;
&lt;script src="https://gist.github.com/nicolevanderhoeven/b25c97a8a68ea97e7bdf5ee674fdaec4.js"&gt;&lt;/script&gt;
&lt;p&gt;After downloading and saving the script, run &lt;code&gt;python3 downloadfirebase.py&lt;/code&gt; on your terminal.&lt;/p&gt;
&lt;p&gt;This worked really well for me, and I can breathe a little more easily now that my vault is self-contained.&lt;/p&gt;</description></item><item><title>Readwise to Obsidian</title><link>https://nicolevanderhoeven.com/blog/20210206-readwise-to-obsidian/</link><pubDate>Sat, 06 Feb 2021 09:55:36 +0100</pubDate><guid>https://nicolevanderhoeven.com/blog/20210206-readwise-to-obsidian/</guid><description>
&lt;meta property=“og:image” content="/assets/20210206-06-readwise-to-obsidian.png" /&gt;
&lt;meta property=”og:image:width” content=”1200″ /&gt;
&lt;meta property=”og:image:height” content=”628″ /&gt;
&lt;p&gt;&lt;img src="https://nicolevanderhoeven.com/assets/20210206-06-readwise-to-obsidian.png" alt="Readwise to Obsidian workflow" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;p&gt;&lt;em&gt;August 10th, 2021: Readwise just released their own Obsidian plugin, &lt;a href="https://help.readwise.io/article/125-how-does-the-readwise-to-obsidian-export-integration-work"&gt;Readwise Official&lt;/a&gt;, which is available now! I now recommend &lt;a href="https://nicolevanderhoeven.com/blog/20210810-readwise-to-obsidian-redux/"&gt;this method&lt;/a&gt; over the one outlined in this article.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;m about 9 months into heavily using &lt;a href="https://roamresearch.com"&gt;Roam Research&lt;/a&gt; as my primary note-taking, task management, capture-everything system, and for the most part, that&amp;rsquo;s gone very well. &lt;a href="https://obsidian.md"&gt;Obsidian&lt;/a&gt; is another excellent personal knowledge management (PKM) system, though, and I&amp;rsquo;ve been curious about it for almost as long as I&amp;rsquo;ve been using Roam. I&amp;rsquo;ve had it installed for a while, but I never really &lt;em&gt;seriously&lt;/em&gt; attempted to make the switch. Until now. I&amp;rsquo;ve decided to use Obsidian as my primary PKM and zettelkasten for the next month, to see if it&amp;rsquo;s something I can switch to.&lt;/p&gt;
&lt;p&gt;Why I&amp;rsquo;m considering switching from Roam to Obsidian is another post, probably one I&amp;rsquo;ll write at the end of this experiment, when my thoughts and opinions are more solidified. For now, I want to address one of the things I struggled with when switching over: Readwise integration.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://readwise.io/nicole"&gt;Readwise&lt;/a&gt; is an app that&amp;rsquo;s all about importing notes and highlights you&amp;rsquo;ve made from all over the internet and exporting them all into a single format. Readwise supports far more import sources than I use, but here are the ones I&amp;rsquo;ve got hooked up to it:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Instapaper (for highlights and notes on online articles)&lt;/li&gt;
&lt;li&gt;Amazon Kindle (for highlights on ebooks)&lt;/li&gt;
&lt;li&gt;Twitter (for interesting tweets)&lt;/li&gt;
&lt;li&gt;(Readwise) Web highlighter (for straight highlights off any webpage)&lt;/li&gt;
&lt;li&gt;Airr (for quotes and notes from audio podcasts)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Readwise collates my activity in all of these input sources and automatically sends them to my Roam Research account. Here&amp;rsquo;s what that looks like when it gets pulled into my Daily Note:&lt;/p&gt;
&lt;p&gt;&lt;img src="https://nicolevanderhoeven.com/assets/20210206-01-readwiseimportsinroam.png" alt="Tweet, podcast, and book notes pulled into Roam Research via Readwise" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;p&gt;Readwise also pulls metadata into Roam, depending on the source. Here&amp;rsquo;s what the page for the book &amp;ldquo;Ready Player Two&amp;rdquo; looks like in Roam:&lt;/p&gt;
&lt;p&gt;&lt;img src="https://nicolevanderhoeven.com/assets/20210206-02-readyplayertwo.png" alt="Ready Player Two book notes in Roam, imported via Readwise" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;p&gt;Readwise integration is an &lt;em&gt;essential&lt;/em&gt; feature for me in any PKM, because I can take notes wherever I&amp;rsquo;m consuming my content and just have it automagically appear in my notes, tagged and ready for processing. So it&amp;rsquo;s absolutely something I need to have in my Obsidian workflow.&lt;/p&gt;
&lt;p&gt;That&amp;rsquo;s where things get tricky, because Readwise doesn&amp;rsquo;t &lt;em&gt;have&lt;/em&gt; an Obsidian integration.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://nicolevanderhoeven.com/assets/20210206-03-readwise-export-options.png" alt="Readwise doesn’t automatically export to Obsidian" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;p&gt;Source: &lt;a href="https://readwise.io/export"&gt;Readwise Exports&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;The closest thing is the Markdown export, but that&amp;rsquo;s not automatic. You can manually trigger an export, and then copy those exports over into your Obsidian vault, and thne make sure they&amp;rsquo;re in the right format with the right tags. I knew I was never going to do that, so I found a way to automate it.&lt;/p&gt;
&lt;h2 id="readwise2directory"&gt;readwise2directory&lt;/h2&gt;
&lt;p&gt;I discovered that Readwise has an API, and briefly thought about building my own integration. Happily, I found &lt;a href="https://github.com/nicrivard/readwise2directory"&gt;this Python project on GitHub&lt;/a&gt; by &lt;a href="https://github.com/nicrivard"&gt;nicrivard&lt;/a&gt;, who had already done that! Why rebuild the wheel? His solution already did what I needed:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;It connects to Readwise via your Readwise access token&lt;/li&gt;
&lt;li&gt;Checks for new highlights or updated ones&lt;/li&gt;
&lt;li&gt;Exports the new/updated highlights to a local folder of your choosing (presumably your Obsidian vault)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;It even pulls in highlight tags from Readwise, if you&amp;rsquo;ve created any (which I haven&amp;rsquo;t).&lt;/p&gt;
&lt;p&gt;I &lt;a href="https://github.com/nicolevanderhoeven/readwise2directory"&gt;forked the project here&lt;/a&gt; to add just a few minor touches:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Changed date format of imported notes&lt;/li&gt;
&lt;li&gt;Added a section to output the import date to a comment block (an &lt;a href="https://forum.obsidian.md/t/obsidian-release-v0-10-12-insider-build/12295"&gt;Obsidian Insider build feature&lt;/a&gt;) rather than YAML frontmatter, so that the date would actually be linked and backlinked.&lt;/li&gt;
&lt;li&gt;Customized tags and filename for my use&lt;/li&gt;
&lt;li&gt;Sort imports into folders within my Obsidian vault depending on source (&lt;code&gt;Article/&lt;/code&gt;, &lt;code&gt;Tweet/&lt;/code&gt;, and so on).&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This works out pretty well. Here&amp;rsquo;s an example of an import from the podcast app &lt;a href="https://airr.io"&gt;Airr&lt;/a&gt; into Obsidian, in Edit mode.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://nicolevanderhoeven.com/assets/20210206-04-airrtoobsidian.png" alt="readwise2directory import from Airr into Obsidian" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;p&gt;It&amp;rsquo;s placed within my &lt;code&gt;Podcast&lt;/code&gt; folder inside my value, is tagged appropriately, and contains links to the source. Switching to Preview mode shows an imported cover photo and even a playable recorder for the part of the podcast that I snipped on Airr:&lt;/p&gt;
&lt;p&gt;&lt;img src="https://nicolevanderhoeven.com/assets/20210206-05-airrinobsidian_preview.png" alt="Preview mode of imported Airr quote in Obsidian" loading="lazy" decoding="async"&gt;
&lt;/p&gt;
&lt;p&gt;So that works out well, except for one thing: firing off the script that does all this is still manual.&lt;/p&gt;
&lt;h2 id="scheduling-the-script"&gt;Scheduling the script&lt;/h2&gt;
&lt;p&gt;Luckily, it&amp;rsquo;s pretty easy to schedule a Python script to run at certain intervals. I&amp;rsquo;m on macOS, so I used a built-in utility called &lt;code&gt;cron&lt;/code&gt;.&lt;/p&gt;
&lt;h3 id="give-cron-full-system-access"&gt;Give cron full system access&lt;/h3&gt;
&lt;p&gt;Cron needs to have the right permissions to run scripts on my behalf, so I had to go to System Preferences &amp;gt; Security &amp;amp; Privacy &amp;gt; Privacy &amp;gt; Full Disk Access on my laptop and add &lt;code&gt;/usr/bin/cron&lt;/code&gt; to the &amp;ldquo;allowed&amp;rdquo; list.&lt;/p&gt;
&lt;h3 id="open-crontab"&gt;Open crontab&lt;/h3&gt;
&lt;p&gt;Then, from my terminal, I ran the command &lt;code&gt;env EDITOR=nano crontab -e&lt;/code&gt; to open up the list of cronjobs I had currently.&lt;/p&gt;
&lt;h3 id="set-a-schedule"&gt;Set a schedule&lt;/h3&gt;
&lt;p&gt;I added a line to crontab for the schedule I wanted to create. This line has to be in the format&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-shell" data-lang="shell"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;* * * * * command
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;where the five asterisks correspond to:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;minute&lt;/li&gt;
&lt;li&gt;hour&lt;/li&gt;
&lt;li&gt;day of the month&lt;/li&gt;
&lt;li&gt;month&lt;/li&gt;
&lt;li&gt;day of the week&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;In my case, I wanted it to run every hour, so here&amp;rsquo;s what my schedule looks like:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-shell" data-lang="shell"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#ae81ff"&gt;0&lt;/span&gt; * * * * cd /Users/nic/git/readwise2directory/ &lt;span style="color:#f92672"&gt;&amp;amp;&amp;amp;&lt;/span&gt; /Library/Frameworks/Python.framework/Versions/3.9/bin/python3 /Users/nic/git/readwise2directory/readwise-GET.py &amp;gt;&amp;gt; /Users/nic/git/readwise2directory/readwiseGET.log
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h4 id="python-issues"&gt;Python issues&lt;/h4&gt;
&lt;p&gt;I had some trouble getting cron to work until I realized it was because I had multiple versions of Python installed. I ran a &lt;code&gt;which python3&lt;/code&gt; to find the path to the version I wanted to use and added that path to the command. I also had to &lt;code&gt;cd&lt;/code&gt; to the project directory, for some reason.&lt;/p&gt;
&lt;p&gt;While I was troubleshooting, I found it really useful to output to a log so I could see what had gone wrong.&lt;/p&gt;
&lt;p&gt;Once I added the schedule as in the example above, though, it worked a treat!&lt;/p&gt;
&lt;h2 id="conclusion"&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;This solution did require some setup at the beginning, but doesn&amp;rsquo;t need any maintenance once it&amp;rsquo;s going. I&amp;rsquo;m pretty happy with it as it means I can keep taking notes on various places from anywhere, and it&amp;rsquo;ll all be waiting for me in my Obsidian vault whenever I&amp;rsquo;m ready to sit down and process them into my system. Thanks to nicrivard for the repo that made it possible!&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Note: This post contains an affiliate link for &lt;a href="https://readwise.io/nicole"&gt;Readwise&lt;/a&gt;. Clicking on that link will give you a two-month trial of Readwise, double the usual amount, and they give me a small percentage if you sign up for a plan. Check out &lt;a href="https://nicolevanderhoeven.com/ethics/"&gt;My Ethics Statement&lt;/a&gt; to see my standards for promotions.&lt;/em&gt;&lt;/p&gt;</description></item><item><title>Python for Everybody Specialization</title><link>https://nicolevanderhoeven.com/blog/20210126-python-for-everybody/</link><pubDate>Tue, 26 Jan 2021 23:01:18 +0100</pubDate><guid>https://nicolevanderhoeven.com/blog/20210126-python-for-everybody/</guid><description>
&lt;iframe src="https://open.spotify.com/embed-podcast/episode/4KQ7OfVztCvpnOBfWcB1p1" width="100%" height="232" frameborder="0" allowtransparency="true" allow="encrypted-media"&gt;&lt;/iframe&gt;
&lt;p&gt;I&amp;rsquo;m starting off the new year by learning something new. I am a big fan of non-traditional education, and &lt;a href="https://coursera.org"&gt;Coursera&lt;/a&gt; is a platform I&amp;rsquo;ve used a few times before. You may not get courses that qualify as university credit, but I&amp;rsquo;ve found it to be a useful resource for learning. I might be biased (my university degree is in Economics, which I&amp;rsquo;ve never used), but in some ways, I actually prefer a MOOC (that&amp;rsquo;s a Massively Open Online Course) to a typical formal university course for a few reasons:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;MOOCs are entirely online, which means it&amp;rsquo;s more accessible - I don&amp;rsquo;t need to physically be in the US to take a course at Harvard&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;MOOCs are usually self-paced - perfect for someone working full-time like me&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;MOOCs are free or cheap - no huge student loans here; MOOCs are democratizing education and making it available to more people&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Good online education tends to be more practical. There is none of the intellectual posturing that I&amp;rsquo;ve seen in physical classrooms; there are no random classes you need to take to get a fancy piece of paper. I love being able to choose a topic and learn ONLY that&amp;ndash; and get right down to business in the first lesson.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Formal education is often centered around getting students jobs, not helping workers do their jobs well. I already have a job, and I want to learn real, usable skills, not &amp;ldquo;marketable&amp;rdquo; ones.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;My main complaint with MOOCs was that they tend to be short (in comparison to a formal course), but Coursera has addressed that somewhat with their specializations. A specialization is a series of courses, but it&amp;rsquo;s more than just disparate courses that are strung together; it&amp;rsquo;s a thoughtfully designed curriculum that builds up on knowledge from previous courses. Last year, I began learning Python.&lt;/p&gt;
&lt;p&gt;Why Python? Others might have different reasons, but here were mine:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Python is one of the best languages to learn for data analysis (the other being R). I&amp;rsquo;ve always been interested in data visualization, and as a load tester, I&amp;rsquo;m frequently exposed to large quantities of data that I somehow need to make sense of.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Python is pretty good at retrieving data from websites, and I wanted to explore how good it is to use in load testing. I&amp;rsquo;m always eager to learn different ways to test, and I&amp;rsquo;ve wanted to try &lt;a href="https://locust.io/"&gt;Locust&lt;/a&gt; for a while.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Python is increasing in popularity, and I wanted to see why.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;So I decided to take the &lt;a href="https://www.coursera.org/learn/python"&gt;Python for Everybody Specialization&lt;/a&gt; from the University of Michigan (via Coursera) to get an introduction to this cool language. The specialization consists of five modules, each of which I had to take separately:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Programming for Everybody (Getting Started with Python)&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Python Data Structure&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Using Python to Access Web Data&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Using Databases with Python&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Capstone: Retrieving, Processing, and Visualizing Data with Python&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The specialization was really fun, and the teacher, Charles Severance, is fantastic. I&amp;rsquo;d taken courses from him before, so I knew what to expect. He didn&amp;rsquo;t disappoint. He has a way of explaining daunting technical concepts in a way that&amp;rsquo;s easy to digest for beginners.&lt;/p&gt;
&lt;p&gt;You can &lt;a href="https://coursera.org/share/11534b2c1b88aa2ab1f0098ebbe32e18"&gt;check out my certificate here&lt;/a&gt;. I&amp;rsquo;m pretty happy with my progress. I&amp;rsquo;m by no means a Python expert now, but I&amp;rsquo;m definitely eager to keep learning how to do more things with this new knowledge.&lt;/p&gt;</description></item></channel></rss>