<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:media="http://search.yahoo.com/mrss/"><channel><title><![CDATA[Amit Jotwani]]></title><description><![CDATA[Thoughts, stories and ideas - by Amit Jotwani]]></description><link>https://ajot.me/</link><image><url>https://ajot.me/favicon.png</url><title>Amit Jotwani</title><link>https://ajot.me/</link></image><generator>Ghost 5.59</generator><lastBuildDate>Tue, 26 Sep 2023 12:53:33 GMT</lastBuildDate><atom:link href="https://ajot.me/rss/" rel="self" type="application/rss+xml"/><ttl>60</ttl><item><title><![CDATA[Automating Weekly Review Date Headers with AppleScript and TextExpander]]></title><description><![CDATA[<p>While reviewing my week in <a href="https://dayoneapp.com/?ref=ajot.me">Day One</a>, I like to kick things off with a heading that reads something like &quot;Week of Date 1 to Date 2.&quot; Sounds simple, right? But almost always, there&apos;s a bit of friction to this. I have to pause my reflection,</p>]]></description><link>https://ajot.me/finding-the-current-weeks-start-and-end-dates-in-applescript/</link><guid isPermaLink="false">64e9e48b39493b04929df3bf</guid><dc:creator><![CDATA[Amit Jotwani]]></dc:creator><pubDate>Sat, 26 Aug 2023 12:38:54 GMT</pubDate><content:encoded><![CDATA[<p>While reviewing my week in <a href="https://dayoneapp.com/?ref=ajot.me">Day One</a>, I like to kick things off with a heading that reads something like &quot;Week of Date 1 to Date 2.&quot; Sounds simple, right? But almost always, there&apos;s a bit of friction to this. I have to pause my reflection, switch gears, and open up my calendar app to find the start and end dates of the week. Then, I manually type them out. All these steps disrupt my flow.</p><p>So, I automated this task using AppleScript, and now a simple <a href="https://textexpander.com/?ref=ajot.me">TextExpander</a> shortcut does it all for me. I just type <code>wk;</code> and voila, the script fills in the week&apos;s date range. </p><figure class="kg-card kg-image-card"><img src="https://ajot.me/content/images/2023/08/get-current-week.gif" class="kg-image" alt loading="lazy" width="1424" height="686" srcset="https://ajot.me/content/images/size/w600/2023/08/get-current-week.gif 600w, https://ajot.me/content/images/size/w1000/2023/08/get-current-week.gif 1000w, https://ajot.me/content/images/2023/08/get-current-week.gif 1424w" sizes="(min-width: 720px) 720px"></figure><p></p><p>Here&apos;s how you can do it too.</p><h2 id="the-complete-applescript">The Complete AppleScript</h2><p>Although you can paste this script directly into TextExpander (choosing &quot;AppleScript&quot; as the content type), I find saving and using the Script Editor for initial testing helpful because it provides code formatting and allows you to debug easily.</p><!--kg-card-begin: html--><script src="https://gist.github.com/ajot/556cfdb6abd3a227a6e5e06b23a14963.js"></script><!--kg-card-end: html--><pre><code class="language-AppleScript">-- Get the current date
set currentDate to current date

-- Get the current day of the week as a number (Sunday is 1, Monday is 2, etc.)
set currentDay to weekday of currentDate as integer

-- Calculate the date of the beginning of the week (Monday)
-- We use &quot;currentDay - 2&quot; because in AppleScript, Monday is represented by 2.
-- So, we&apos;re asking, &quot;How many days back do we have to go to reach Monday?&quot;
set beginningOfWeek to currentDate - (currentDay - 2) * days

-- Calculate the date of the end of the week (Sunday)
-- We use &quot;8 - currentDay&quot; to find out how many days we need to move forward to get to Sunday.
set endOfWeek to currentDate + (8 - currentDay) * days

-- AppleScript&apos;s current date includes the time. We reset this to focus only on the date by setting start to 00:00:00 and end of day to 23:59:59.
set time of beginningOfWeek to 0
set time of endOfWeek to (23 * hours + 59 * minutes + 59)

-- Function to convert date to string in the desired format
on formatDate(theDate)
    set theWeekday to weekday of theDate as string
    set theMonth to month of theDate as string
    set theDay to day of theDate as integer
    set theYear to year of theDate as integer
    return theWeekday &amp; &quot;, &quot; &amp; theMonth &amp; &quot; &quot; &amp; theDay &amp; &quot;, &quot; &amp; theYear
end formatDate

-- Format the dates
set formattedBeginningOfWeek to formatDate(beginningOfWeek)
set formattedEndOfWeek to formatDate(endOfWeek)

return formattedBeginningOfWeek &amp; &quot; to &quot; &amp; formattedEndOfWeek
</code></pre><h2 id="testing-the-script-in-script-editor">Testing the script in Script Editor</h2><ol><li>On your Mac, open the Script Editor application. </li><li>Copy the entire AppleScript code above and paste it into a new Script Editor window. </li><li>Save the file, say <code>GetCurrentWeek.applescript</code></li><li>Then click &quot;Run&quot; to execute it. If everything is set up correctly, you should see the week&apos;s date range.</li></ol><p>Optional: If you want the date format to look different, you can tweak the formatDate function in the script. Make your changes, run the script again, and see if you like the output.</p><figure class="kg-card kg-image-card"><img src="https://ajot.me/content/images/2023/08/CleanShot-2023-08-26-08-22-25@2x.png" class="kg-image" alt loading="lazy" width="1884" height="2038" srcset="https://ajot.me/content/images/size/w600/2023/08/CleanShot-2023-08-26-08-22-25@2x.png 600w, https://ajot.me/content/images/size/w1000/2023/08/CleanShot-2023-08-26-08-22-25@2x.png 1000w, https://ajot.me/content/images/size/w1600/2023/08/CleanShot-2023-08-26-08-22-25@2x.png 1600w, https://ajot.me/content/images/2023/08/CleanShot-2023-08-26-08-22-25@2x.png 1884w" sizes="(min-width: 720px) 720px"></figure><h2 id="using-the-script-with-textexpander">Using the Script with TextExpander</h2><p>Now that we have the script tested the script, the next step is integrating it with TextExpander for seamless use. This way, you can fire off this script anytime, anywhere, just by typing a quick &quot;abbreviation shortcut&quot;</p><figure class="kg-card kg-image-card"><img src="https://ajot.me/content/images/2023/08/CleanShot-2023-08-26-08-22-47@2x.png" class="kg-image" alt loading="lazy" width="2000" height="1245" srcset="https://ajot.me/content/images/size/w600/2023/08/CleanShot-2023-08-26-08-22-47@2x.png 600w, https://ajot.me/content/images/size/w1000/2023/08/CleanShot-2023-08-26-08-22-47@2x.png 1000w, https://ajot.me/content/images/size/w1600/2023/08/CleanShot-2023-08-26-08-22-47@2x.png 1600w, https://ajot.me/content/images/size/w2400/2023/08/CleanShot-2023-08-26-08-22-47@2x.png 2400w" sizes="(min-width: 720px) 720px"></figure><ol><li>If you haven&apos;t already, download and install TextExpander from their <a href="https://textexpander.com/?ref=ajot.me">official website</a>.</li><li>Open TextExpander and <strong>create a new snippet</strong>. Choose &quot;AppleScript&quot; from the content type dropdown.</li><li><strong>Copy the AppleScript code</strong> you&apos;ve prepared and <strong>paste</strong> it into the snippet content area.</li><li>This is the shortcut you&apos;ll use to trigger the snippet. For instance, you could set it to <code>wk;</code></li><li>Save your snippet. Open a text editor, type your abbreviation - <code>wk;</code> and see if the week&apos;s date range gets filled in as expected.</li></ol><figure class="kg-card kg-image-card"><img src="https://ajot.me/content/images/2023/08/get-current-week-1.gif" class="kg-image" alt loading="lazy" width="1424" height="686" srcset="https://ajot.me/content/images/size/w600/2023/08/get-current-week-1.gif 600w, https://ajot.me/content/images/size/w1000/2023/08/get-current-week-1.gif 1000w, https://ajot.me/content/images/2023/08/get-current-week-1.gif 1424w" sizes="(min-width: 720px) 720px"></figure><p></p><h2 id="what-do-you-think-share-your-hacks">What do you think? Share Your Hacks!</h2><p>So, that&apos;s my little hack for making weekly reviews less of a chore. What about you? Got any neat tricks for planning your week? Or maybe there&apos;s something you keep doing manually and wish you could automate?</p><p>Hit me up on Twitter <a href="https://twitter.com/amit?ref=ajot.me">@amit</a>. I&apos;d genuinely love to hear your thoughts or swap some productivity tips.</p>]]></content:encoded></item><item><title><![CDATA[Quickly Cycling Through and Fixing Spelling Errors in VS Code Markdown Files]]></title><description><![CDATA[<p>I wanted to cycle through and fix all spelling errors in a VS Code markdown file. I already had the spell-checking extension installed but couldn&apos;t figure out how to see all the errors together and fix them quickly.</p><p>Here&apos;s how -</p><ol><li>Open the markdown file in</li></ol>]]></description><link>https://ajot.me/quickly-cycling-through-and-fixing-spelling-errors-in-vs-code-markdown-files/</link><guid isPermaLink="false">64e4abf439493b04929df38d</guid><dc:creator><![CDATA[Amit Jotwani]]></dc:creator><pubDate>Tue, 22 Aug 2023 12:46:17 GMT</pubDate><content:encoded><![CDATA[<p>I wanted to cycle through and fix all spelling errors in a VS Code markdown file. I already had the spell-checking extension installed but couldn&apos;t figure out how to see all the errors together and fix them quickly.</p><p>Here&apos;s how -</p><ol><li>Open the markdown file in VS Code where you want to correct the errors.</li><li>Install the <a href="https://marketplace.visualstudio.com/items?itemName=streetsidesoftware.code-spell-checker&amp;ref=ajot.me">Code Spell Checker Extension</a> if you haven&apos;t already.</li><li>Press <code>CMD + SHIFT + M</code> to see all spelling errors together in the &apos;Problems&apos; pane.</li><li>Click on an error or highlight the word and press <code>CMD + .</code> to see quick fix suggestions.</li></ol><figure class="kg-card kg-image-card"><img src="https://ajot.me/content/images/2023/08/CleanShot-2023-08-22-08-42-19@2x-5.png" class="kg-image" alt loading="lazy" width="1462" height="858" srcset="https://ajot.me/content/images/size/w600/2023/08/CleanShot-2023-08-22-08-42-19@2x-5.png 600w, https://ajot.me/content/images/size/w1000/2023/08/CleanShot-2023-08-22-08-42-19@2x-5.png 1000w, https://ajot.me/content/images/2023/08/CleanShot-2023-08-22-08-42-19@2x-5.png 1462w" sizes="(min-width: 720px) 720px"></figure>]]></content:encoded></item><item><title><![CDATA[Changing the Number of Posts Displayed Per Page in Ghost CMS]]></title><description><![CDATA[<p>So, I wanted to customize the number of posts displayed per page on my Ghost CMS website using the Dawn theme. The default setting was showing only 5 posts, and I wanted to bump that up to 25. </p><p>Here&apos;s how I did it, broken down into simple steps,</p>]]></description><link>https://ajot.me/changing-the-number-of-posts-displayed-per-page-in-ghost-cms/</link><guid isPermaLink="false">64de95c939493b04929df29e</guid><dc:creator><![CDATA[Amit Jotwani]]></dc:creator><pubDate>Thu, 17 Aug 2023 22:05:41 GMT</pubDate><media:content url="https://ajot.me/content/images/2023/08/ghost-tips-tricks-banner.png" medium="image"/><content:encoded><![CDATA[<img src="https://ajot.me/content/images/2023/08/ghost-tips-tricks-banner.png" alt="Changing the Number of Posts Displayed Per Page in Ghost CMS"><p>So, I wanted to customize the number of posts displayed per page on my Ghost CMS website using the Dawn theme. The default setting was showing only 5 posts, and I wanted to bump that up to 25. </p><p>Here&apos;s how I did it, broken down into simple steps, so it&apos;s easy to remember and follow along.</p><h3 id="download-the-theme-you-are-currently-using">Download the theme you are currently using</h3><p>First, we need to download the current theme to access the file containing the posts&apos; settings.</p><p>Go to **Settings -&gt; Design** in your Ghost CMS admin panel.</p><figure class="kg-card kg-image-card"><img src="https://ajot.me/content/images/2023/08/CleanShot-2023-08-17-18-02-52@2x.png" class="kg-image" alt="Changing the Number of Posts Displayed Per Page in Ghost CMS" loading="lazy" width="2000" height="1125" srcset="https://ajot.me/content/images/size/w600/2023/08/CleanShot-2023-08-17-18-02-52@2x.png 600w, https://ajot.me/content/images/size/w1000/2023/08/CleanShot-2023-08-17-18-02-52@2x.png 1000w, https://ajot.me/content/images/size/w1600/2023/08/CleanShot-2023-08-17-18-02-52@2x.png 1600w, https://ajot.me/content/images/size/w2400/2023/08/CleanShot-2023-08-17-18-02-52@2x.png 2400w" sizes="(min-width: 720px) 720px"></figure><h3 id="edit-packagejson-file">Edit package.json File</h3><ol><li>Open the downloaded theme directory and locate the `package.json` file.</li></ol><p>2. Find the key `posts_per_page` and change its value to your desired number (For Dawn theme, by default, this was set to 5. I &#xA0;changed mine to 25).</p><p>3. Save the changes to the file.</p><figure class="kg-card kg-image-card"><img src="https://ajot.me/content/images/2023/08/image.png" class="kg-image" alt="Changing the Number of Posts Displayed Per Page in Ghost CMS" loading="lazy" width="2000" height="1327" srcset="https://ajot.me/content/images/size/w600/2023/08/image.png 600w, https://ajot.me/content/images/size/w1000/2023/08/image.png 1000w, https://ajot.me/content/images/size/w1600/2023/08/image.png 1600w, https://ajot.me/content/images/2023/08/image.png 2000w" sizes="(min-width: 720px) 720px"></figure><h3 id="zip-up-the-directory">Zip Up the Directory</h3><p>We need to prepare the modified theme for uploading back to Ghost CMS.</p><p>Zip up the entire theme directory, ensuring all necessary files are included (on a Mac, you can right click on the directory, and click &quot;Compress&quot;). </p><h3 id="upload-the-modified-theme-to-ghost-cms">Upload the Modified Theme to Ghost CMS</h3><p>Finally, we need to upload the customized theme back to Ghost CMS.</p><ol><li>Head back to the **Design** section in your Ghost CMS admin panel.</li><li>Upload the zipped directory containing the modified theme.</li></ol><figure class="kg-card kg-image-card"><img src="https://ajot.me/content/images/2023/08/CleanShot-2023-08-17-18-02-52@2x-1.png" class="kg-image" alt="Changing the Number of Posts Displayed Per Page in Ghost CMS" loading="lazy" width="2000" height="1125" srcset="https://ajot.me/content/images/size/w600/2023/08/CleanShot-2023-08-17-18-02-52@2x-1.png 600w, https://ajot.me/content/images/size/w1000/2023/08/CleanShot-2023-08-17-18-02-52@2x-1.png 1000w, https://ajot.me/content/images/size/w1600/2023/08/CleanShot-2023-08-17-18-02-52@2x-1.png 1600w, https://ajot.me/content/images/size/w2400/2023/08/CleanShot-2023-08-17-18-02-52@2x-1.png 2400w" sizes="(min-width: 720px) 720px"></figure><p>Fin!</p>]]></content:encoded></item><item><title><![CDATA[How to Globally Ignore .DS_Store Files in Git Repositories on a Mac]]></title><description><![CDATA[<p>So, I got tired of adding `.DS_Store` to every git repo on my Mac. I was pretty sure there would be a better way to handle this on a global way on my Mac, so I don&apos;t have to do this for every repo. </p><p>Here&apos;s</p>]]></description><link>https://ajot.me/how-to-globally-ignore-ds_store-files-in-git-repositories-on-a-mac/</link><guid isPermaLink="false">64de8a0239493b04929df245</guid><dc:creator><![CDATA[Amit Jotwani]]></dc:creator><pubDate>Thu, 17 Aug 2023 21:06:09 GMT</pubDate><media:content url="https://images.unsplash.com/photo-1618401471353-b98afee0b2eb?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=M3wxMTc3M3wwfDF8c2VhcmNofDF8fGdpdHxlbnwwfHx8fDE2OTIzMDU5NDN8MA&amp;ixlib=rb-4.0.3&amp;q=80&amp;w=2000" medium="image"/><content:encoded><![CDATA[<img src="https://images.unsplash.com/photo-1618401471353-b98afee0b2eb?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=M3wxMTc3M3wwfDF8c2VhcmNofDF8fGdpdHxlbnwwfHx8fDE2OTIzMDU5NDN8MA&amp;ixlib=rb-4.0.3&amp;q=80&amp;w=2000" alt="How to Globally Ignore .DS_Store Files in Git Repositories on a Mac"><p>So, I got tired of adding `.DS_Store` to every git repo on my Mac. I was pretty sure there would be a better way to handle this on a global way on my Mac, so I don&apos;t have to do this for every repo. </p><p>Here&apos;s what I found: You can create a global `.gitignore` file, add <code>.DS_Store</code> to it, and have git ignore them in all my repositories at once. </p><h3 id="1-create-a-global-git-ignore-file">1. <strong>Create a Global Git Ignore File</strong></h3><p>Open up your favorite Terminal app, and type the following - </p><p><br>```bash<br>git config --global core.excludesfile ~/.gitignore_global<br>```</p><p>This command tells Git where the global `.gitignore` file is located or creates it if it doesn&apos;t exist.</p><h3 id="2-edit-the-global-git-ignore-file">2. <strong>Edit the Global Git Ignore File</strong></h3><p>Next, I had to edit this file to include the `.DS_Store` pattern.</p><p>Open this global git ignore file in your favorite Text Editor (e.g., nano or vim)<br>```bash<br>nano ~/.gitignore_global<br>```</p><p>Add the file/folder patterns you would like to ignore for all your repos to `.gitignore_global`<br>```<br>.DS_Store<br>```</p><p>By adding this line, I told Git to ignore `.DS_Store` files globally.</p><h3 id="3-apply-the-changes">3. <strong>Apply the Changes</strong></h3><p>Finally, to apply the changesr run the same configuration command again:</p><p><br>```bash<br>git config --global core.excludesfile ~/.gitignore_global<br>```</p><p>And that&apos;s it! No more manually adding `.DS_Store` to each individual repository&apos;s `.gitignore` file. Now, my Mac ignores these files in all Git repositories.</p>]]></content:encoded></item><item><title><![CDATA[Connecting ChatGPT to Google Drive]]></title><description><![CDATA[<p>I recently built my very first app using ChatGPT&apos;s API.</p><figure class="kg-card kg-embed-card"><blockquote class="twitter-tweet"><p lang="en" dir="ltr">Just followed <a href="https://twitter.com/greggyb?ref_src=twsrc%5Etfw&amp;ref=ajot.me">@greggyb</a>&apos;s super helpful guide to write my first ChatGPT app that queries a PDF sitting in a Google Drive. &#x1F929; <br><br>Here&apos;s me querying <a href="https://twitter.com/culturedcode?ref_src=twsrc%5Etfw&amp;ref=ajot.me">@culturedcode</a>&apos;s AppleScript Guide (PDF) &#x1F4DA; <br><br>Very helpful</p></blockquote></figure>]]></description><link>https://ajot.me/my-first-chatgpt-app-google-drive-langchain-python/</link><guid isPermaLink="false">64def65939493b04929df354</guid><dc:creator><![CDATA[Amit Jotwani]]></dc:creator><pubDate>Thu, 20 Jul 2023 04:51:00 GMT</pubDate><media:content url="https://ajot.me/content/images/2023/08/chat_gpt_google_drive.png" medium="image"/><content:encoded><![CDATA[<img src="https://ajot.me/content/images/2023/08/chat_gpt_google_drive.png" alt="Connecting ChatGPT to Google Drive"><p>I recently built my very first app using ChatGPT&apos;s API.</p><figure class="kg-card kg-embed-card"><blockquote class="twitter-tweet"><p lang="en" dir="ltr">Just followed <a href="https://twitter.com/greggyb?ref_src=twsrc%5Etfw&amp;ref=ajot.me">@greggyb</a>&apos;s super helpful guide to write my first ChatGPT app that queries a PDF sitting in a Google Drive. &#x1F929; <br><br>Here&apos;s me querying <a href="https://twitter.com/culturedcode?ref_src=twsrc%5Etfw&amp;ref=ajot.me">@culturedcode</a>&apos;s AppleScript Guide (PDF) &#x1F4DA; <br><br>Very helpful in finding quick help when automating my todo list in the Things app&#x1F642; <a href="https://t.co/d6Cjl9NTFU?ref=ajot.me">pic.twitter.com/d6Cjl9NTFU</a></p>&#x2014; Amit Jotwani (@amit) <a href="https://twitter.com/amit/status/1682086918995976192?ref_src=twsrc%5Etfw&amp;ref=ajot.me">July 20, 2023</a></blockquote>
<script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>
</figure><p>The thing about ChatGPT is that it&apos;s trained on information from the internet, but what if you want it to answer questions using your custom data? Like, let&apos;s say you have some data stored on Google Drive, and you want ChatGPT to answer questions based on that.</p><p>I ran into this <a href="https://www.haihai.ai/gpt-gdrive/?ref=ajot.me" rel="noopener noreferrer nofollow">super helpful post</a> by <a href="https://twitter.com/greggyb?lang=en&amp;ref=ajot.me" rel="noopener noreferrer nofollow">Greg Bagues</a>, explaining how to do just that using Google Drive API and Langchain, and Python. I won&apos;t reproduce all the steps here, because he does a phenomenal job at explaining the concepts, and the steps.</p><h3 id="i-did-however-run-into-some-errors-which-i-want-to-document-here">I did, however run into some errors, which I want to document here</h3><p>Greg&apos;s instructions worked for the most part, except I ran into some credentials errors from the Google Drive API. Langchain expects the <code>credentials.json</code> file you download from Google&apos;s developer console to be located inside <code>~/.credentials/credentials.json</code>. However, in my case, I had to manually set an environment variable using the Export command (found <a href="https://github.com/hwchase17/langchain/issues/6997?ref=ajot.me#issuecomment-1622227921" rel="noopener noreferrer nofollow">this fix</a> in the GitHub repo issues for Langchain)</p><p>Here&apos;s the command I used to set the environment variable:</p><pre><code class="language-bash">export GOOGLE_APPLICATION_CREDENTIALS=&quot;~/.credentials/credentials.json&quot;</code></pre><p>You also need to set up the OPEN AI API key as an environment variable <code>export OPENAI_API_KEY=&lt;YOUR OPEN AI API KEY&gt;</code></p><p>Outside of that, Greg&apos;s instructions worked like a champ.</p><h3 id="but-it-seems-to-work-only-for-docs-what-if-i-wanted-to-query-csv">But, it seems to work only for docs. What if I wanted to query CSV?</h3><p>This setup seems to work fine for documents, but when it comes to CSV files inside Google Drive, things get a little wonky. It looks like it only loads part of the CSV file, and the answers are not accurate. I will look into that in the next Mint!</p><p>This was a pretty gratifying and exciting start. Definitely going to play around more with this.</p>]]></content:encoded></item><item><title><![CDATA[Automating Python Project Setup: A Shell Script to Create New Projects Quickly]]></title><description><![CDATA[<p>As someone who loves experimenting with Python libraries and APIs, starting new projects quickly is important. Setting up these projects can sometimes be time-consuming, and a bit annoying to be honest.</p><p>Wrote this shell script to automate the process and start coding quickly. Here&apos;s what it does -</p>]]></description><link>https://ajot.me/start-new-python-projects-quickly-using-a-shell-script/</link><guid isPermaLink="false">64def48a39493b04929df34a</guid><dc:creator><![CDATA[Amit Jotwani]]></dc:creator><pubDate>Thu, 13 Jul 2023 04:40:00 GMT</pubDate><media:content url="https://ajot.me/content/images/2023/08/python_terminal.png" medium="image"/><content:encoded><![CDATA[<img src="https://ajot.me/content/images/2023/08/python_terminal.png" alt="Automating Python Project Setup: A Shell Script to Create New Projects Quickly"><p>As someone who loves experimenting with Python libraries and APIs, starting new projects quickly is important. Setting up these projects can sometimes be time-consuming, and a bit annoying to be honest.</p><p>Wrote this shell script to automate the process and start coding quickly. Here&apos;s what it does -</p><ul><li>Prompts the user to <strong>provide a project name</strong> as a command-line argument, and throws an error message and usage instructions if the project name argument is missing.</li><li>Creates a <strong>new project directory</strong> with the specified name.</li><li>Sets up a <strong>virtual environment</strong> within the project directory.</li><li>Installs required <strong>Python dependencies</strong> using pip or a preferred package manager.</li><li>Creates sub-directories for source code, tests, and documentation.</li><li>Generates essential files like <strong>main.py</strong>, <strong>requirements.txt</strong>, and <a href="http://readme.md/?ref=ajot.me" rel="noopener noreferrer nofollow">README.md</a>.</li><li>Initializes a <strong>Git repository</strong> for version control.</li><li>Opens the <a href="http://main.py/?ref=ajot.me" rel="noopener noreferrer nofollow">main.py</a> file in <strong>Visual Studio Code</strong> in a <strong>new window</strong>.</li></ul><h2 id="setting-up-the-script">Setting up the script</h2><pre><code class="language-bash">#!/bin/bash

# Check if project name argument is provided
if [ $# -eq 0 ]; then
  echo &quot;Error: Please provide a project name.&quot;
  echo &quot;Usage: ./start_python_project.sh &lt;project_name&gt;&quot;
  exit 1
fi

# Get project name as a command-line argument
project_name=$1

# Create project directory
mkdir $project_name
cd $project_name

# Initialize virtual environment
virtualenv -p python3 venv
source venv/bin/activate

# Install dependencies (e.g., requests and numpy)
# pip install numpy

# Create project structure
mkdir src tests docs

# Create main script
touch src/main.py

# Create requirements.txt file
touch requirements.txt

# Create README.md file
touch README.md

# Initialize version control (Git)
git init

# Open main.py in Visual Studio Code in a new window
code --new-window src/main.py

echo &quot;Project setup complete. Happy coding!&quot;</code></pre><p><strong>Make the Script Executable</strong></p><pre><code class="language-bash">chmod +x start_python_project.sh</code></pre><p><strong>Run the Script</strong></p><pre><code class="language-bash">./start_python_project.sh my_project</code></pre><p><strong>[Optional] Create an alias</strong></p><p>Open your shell&apos;s configuration file (e.g., <code>.bashrc</code>, <code>.bash_profile</code>) using a text editor,</p><pre><code class="language-bash">nano ~/.bashrc

#or if you&apos;re using zsh
nano ~/.zshrc</code></pre><p>Add the following line at the end of the file, replacing <code>/path/to/start_python_project.sh</code> with the actual path to your shell script:</p><pre><code class="language-bash">alias newproject=&apos;/path/to/start_python_project.sh&apos;</code></pre><h2 id="using-the-script-to-create-a-new-project">Using the script to create a new project</h2><p>In the terminal, you can now start a new Python project by running the following command, replacing <code>my_project</code> with your desired project name:</p><pre><code class="language-bash">newproject &lt;project name&gt;</code></pre><p>Fin!</p>]]></content:encoded></item><item><title><![CDATA[Export todos from Things app using AppleScript]]></title><description><![CDATA[<p>I had a bunch of todos in the <a href="https://culturedcode.com/things/?ref=ajot.me" rel="noopener noreferrer nofollow">Things</a> app that I wanted to move to my notes. Manually copying them from the app didn&apos;t grab the notes in the todo, which was quite a hassle. So, I ended up writing this quick AppleScript to export all todos</p>]]></description><link>https://ajot.me/export-todos-from-things-app-using-applescript/</link><guid isPermaLink="false">64dee0a839493b04929df30b</guid><dc:creator><![CDATA[Amit Jotwani]]></dc:creator><pubDate>Thu, 13 Jul 2023 03:18:00 GMT</pubDate><media:content url="https://ajot.me/content/images/2023/08/banner-2.png" medium="image"/><content:encoded><![CDATA[<img src="https://ajot.me/content/images/2023/08/banner-2.png" alt="Export todos from Things app using AppleScript"><p>I had a bunch of todos in the <a href="https://culturedcode.com/things/?ref=ajot.me" rel="noopener noreferrer nofollow">Things</a> app that I wanted to move to my notes. Manually copying them from the app didn&apos;t grab the notes in the todo, which was quite a hassle. So, I ended up writing this quick AppleScript to export all todos from a specific list in the Things app, including the notes.</p><pre><code class="language-AppleScript">tell application &quot;Things3&quot;
	set inboxToDos to to dos of list &quot;Inbox&quot;
	repeat with toDo in inboxToDos
		set todoName to name of toDo
		set taskNotes to notes of toDo
		log (todoName &amp; &quot; -&gt; &quot; &amp; taskNotes)
	end repeat
end tell</code></pre><p><strong>Update:</strong> Turns out Things app provides a way to &quot;Share&quot; each list by right clicking the list -&gt; Share &gt; Copy as text.</p><figure class="kg-card kg-image-card"><img src="https://ajot.me/content/images/2023/08/image-1.png" class="kg-image" alt="Export todos from Things app using AppleScript" loading="lazy" width="2000" height="1800" srcset="https://ajot.me/content/images/size/w600/2023/08/image-1.png 600w, https://ajot.me/content/images/size/w1000/2023/08/image-1.png 1000w, https://ajot.me/content/images/size/w1600/2023/08/image-1.png 1600w, https://ajot.me/content/images/size/w2400/2023/08/image-1.png 2400w" sizes="(min-width: 720px) 720px"></figure>]]></content:encoded></item><item><title><![CDATA[Resolving a 504 Timeout Error on Ghost with Digital Ocean]]></title><description><![CDATA[<p>Woke up to a 504 error this morning. It&apos;s hosted on Digital Ocean, so I reached out to support, which wasn&apos;t very helpful. They basically said this, and then dumped a bunch of not-very-helpful-generic-504-articles.</p><blockquote>As a self-managed infrastructure as a service (IaaS) provider, we do not</blockquote>]]></description><link>https://ajot.me/resolving-a-504-timeout-error-on-ghost-with-digital-ocean/</link><guid isPermaLink="false">64d926d6f48ed035ee97f1c9</guid><dc:creator><![CDATA[Amit Jotwani]]></dc:creator><pubDate>Tue, 26 Apr 2022 10:48:38 GMT</pubDate><media:content url="https://ajot.me/content/images/2023/08/ghost-tips-tricks-banner-1.png" medium="image"/><content:encoded><![CDATA[<img src="https://ajot.me/content/images/2023/08/ghost-tips-tricks-banner-1.png" alt="Resolving a 504 Timeout Error on Ghost with Digital Ocean"><p>Woke up to a 504 error this morning. It&apos;s hosted on Digital Ocean, so I reached out to support, which wasn&apos;t very helpful. They basically said this, and then dumped a bunch of not-very-helpful-generic-504-articles.</p><blockquote>As a self-managed infrastructure as a service (IaaS) provider, we do not have access to your servers so I cannot see your environment. The answer will depend on your webserver and/or database configuration. The system and application error and access logs may include additional details that could provide more insight on this. </blockquote><p>I hadn&apos;t changed anything on my website, so I knew it probably had nothing to do with webserver/database configurations. Nothing had changed as far as I knew. A lot of the articles pointed me to the console. I couldn&apos;t even access the console through digital ocean dashboard though. It threw an error of - &quot;<em><strong>Timed out while waiting for handshake&quot;.</strong></em></p><p>I then ran into <a href="https://gloathost.com/blog/how-to-restart-your-ghost-site-on-digital-ocean?ref=ajot.me">this post</a> <em>(How to restart your Ghost site on Digital Ocean)</em><strong> </strong>from <a href="https://twitter.com/dr?ref=ajot.me">Dan Rowden</a>. That gave me a hint that I should look into forcefully restarting my Ghost site. Another quick google search, and I ran into this post - <a href="https://nuweb.blog/reboot-a-droplet-on-digitalocean/?ref=ajot.me">2 Ways To Reboot a Droplet on DigitalOcean</a>.</p><h2 id="restarting-a-droplet-on-digital-ocean">Restarting a Droplet on Digital Ocean</h2><p>To summarize - There are two ways to reboot a Droplet on DigitalOcean, these are:</p><ol><li><strong>Gracefully<strong>: </strong></strong>Similar to that of restarting your machine through the Restart or Shut Down commands.</li><li><strong>Forcefully<strong>: </strong></strong>Similar to holding down the power button on your machine and then powering it back on again.</li></ol><p>The graceful way wasn&apos;t working out in this case, since I couldn&apos;t even access the console. So, I went the forceful way. </p><ol><li>Visit the <strong><strong>Power </strong></strong>tab in the Droplet.</li><li>Click the <strong><strong>Power Cycle</strong></strong> button in the <strong><strong>Power Cycle</strong></strong> section</li><li>Wait for confirmation message and wait a few minutes for the Droplet to reboot.</li></ol><p><strong>That fixed it.</strong> I could access the console, and the website came right back up.</p><figure class="kg-card kg-image-card"><img src="https://ajot.me/content/images/2022/04/CleanShot-2022-04-26-at-06.33.07.png" class="kg-image" alt="Resolving a 504 Timeout Error on Ghost with Digital Ocean" loading="lazy" width="1241" height="849" srcset="https://ajot.me/content/images/size/w600/2022/04/CleanShot-2022-04-26-at-06.33.07.png 600w, https://ajot.me/content/images/size/w1000/2022/04/CleanShot-2022-04-26-at-06.33.07.png 1000w, https://ajot.me/content/images/2022/04/CleanShot-2022-04-26-at-06.33.07.png 1241w" sizes="(min-width: 720px) 720px"></figure><p></p><h2 id="so-why-did-this-happen">So why did this happen?</h2><p>During my troubleshooting, I came across <a href="https://www.digitalocean.com/community/questions/504-gateway-timeout-memory-error?ref=ajot.me">this post</a> on Digital Ocean community, pointing to memory issues being one of the causes for a 504 errors.</p><p>This reminded me that I was trying to upload a large size image (&gt;6MB) to my Ghost site yesterday. That was taking forever to load, and timed out each time I tried it. I now wonder if that process consumed most of the RAM, and the server essentially ran out of memory. Rebooting the server must have reset the memory. That makes sense to me. I will go with that argument, and vow never to upload a 6MB PNG to my Ghost site!</p>]]></content:encoded></item><item><title><![CDATA[Creating Custom Workflows in Warp.dev]]></title><description><![CDATA[<p>I&apos;ve been playing around with <a href="https://www.warp.dev/?ref=ajot.me">warp.dev</a>, which is a new terminal app for Mac (Windows coming soon it looks like) with a focus on improving developer productivity. Looks really good, and has already improved quite a few workflows for me. </p><p>Speaking of which, it actually has a</p>]]></description><link>https://ajot.me/creating-custom-workflows-in-warp-dev/</link><guid isPermaLink="false">64d926d6f48ed035ee97f1c7</guid><category><![CDATA[terminal]]></category><category><![CDATA[tutorials]]></category><dc:creator><![CDATA[Amit Jotwani]]></dc:creator><pubDate>Sun, 24 Apr 2022 11:44:25 GMT</pubDate><media:content url="https://ajot.me/content/images/2023/08/warp.png" medium="image"/><content:encoded><![CDATA[<img src="https://ajot.me/content/images/2023/08/warp.png" alt="Creating Custom Workflows in Warp.dev"><p>I&apos;ve been playing around with <a href="https://www.warp.dev/?ref=ajot.me">warp.dev</a>, which is a new terminal app for Mac (Windows coming soon it looks like) with a focus on improving developer productivity. Looks really good, and has already improved quite a few workflows for me. </p><p>Speaking of which, it actually has a feature called <a href="https://docs.warp.dev/features/workflows?ref=ajot.me">Workflows</a>, which is a more flexible and powerful version of alias&apos;. For one they&apos;re searchable from within warp, which makes it easier to explore and use them. They&apos;re also sharable, so you can create a workflow, and share with your team members.</p><p>Here&apos;s how I <a href="https://github.com/warpdotdev/workflows?ref=ajot.me">created my first workflow</a>. I wanted to see if I could create a workflow that launches a Flask app with some arguments. It&apos;s a pretty simple one, which could easily be an alias too, but I guess it&apos;s a good hello world of sorts. </p><h3 id="creating-the-workflow">Creating the workflow</h3><p><strong>Step 1:</strong> Create a subdirectory within the <code>~/.warp</code> folder called <code>worlflows</code> (the <code>.warp</code> doesn&apos;t exist by default, so we will have to create this too with the -p parent flag)</p><p><code>mkdir -p ~/.warp/workflows</code></p><p><strong>Step 2:</strong> Create a yaml file for your workflow inside this workflows directory. Here&apos;s a template</p><!--kg-card-begin: markdown--><pre><code class="language-yaml">---
# The name of the workflow.
name: Uninstall a Homebrew package and all of its dependencies
# The corresponding command for the workflow. Any arguments should be surrounded with two curly braces. E.g `command {{arg}}`.
command: |-
    brew tap beeftornado/rmtree
    brew rmtree {{package_name}}
# Any tags that the workflow should be categorized with.
tags:
  - homebrew
# A description of the workflow.
description: Uses the external command rmtree to remove a Homebrew package and all of its dependencies
# List of arguments within the command.
arguments:
    # Name of the argument within the command. This must exactly match the name of the argument
    # within the command (without the curly braces).
  - name: package_name
    # The description of the argument.
    description: The name of the package that should be removed
    # The default value for the argument.
    default_value: ~
# The source URL for where the workflow was generated from, if any.
source_url: &quot;https://stackoverflow.com/questions/7323261/uninstall-remove-a-homebrew-package-including-all-its-dependencies&quot;
# The author of the workflow.
author: Ory Band
# The URL of original author of the Workflow. For example, if this workflow was generated from StackOverflow, the `author_url` would be the StackOverflow author&apos;s profile page.
author_url: &quot;https://stackoverflow.com/users/207894&quot;
# The valid shells where this workflow should be active. If valid for all shells, this can be left empty. 
# See FORMAT.md for the full list of accepted values.
shells: []
</code></pre>
<!--kg-card-end: markdown--><p>I modifed the command in this file to a python3 file, that essentially launches a flask app with some arguments. </p><p>The new workflow immediately shows up in Warp (CTRT+SHIFT+R). Selecting it from the UI pastes the command in the terminal window. </p><figure class="kg-card kg-image-card"><img src="https://ajot.me/content/images/2022/04/CleanShot-2022-04-24-at-07.32.06.png" class="kg-image" alt="Creating Custom Workflows in Warp.dev" loading="lazy" width="1237" height="981" srcset="https://ajot.me/content/images/size/w600/2022/04/CleanShot-2022-04-24-at-07.32.06.png 600w, https://ajot.me/content/images/size/w1000/2022/04/CleanShot-2022-04-24-at-07.32.06.png 1000w, https://ajot.me/content/images/2022/04/CleanShot-2022-04-24-at-07.32.06.png 1237w" sizes="(min-width: 720px) 720px"></figure><p></p><p></p>]]></content:encoded></item><item><title><![CDATA[Speeding Up Terminal Load Time: How I Fixed a 90-Second Delay by Cleaning Up .zshrc File]]></title><description><![CDATA[<p><a href="https://www.reddit.com/r/zsh/comments/nfp20t/zsh_loading_slow_on_macos/gymx1g2/?ref=ajot.me">This answer</a> on Reddit helped troubleshoot. I just commented everything, and it made the load lightening fast. Then, on a closer look, I found this mess. No idea how it got to this, but commenting out all but one of them did the trick. </p><figure class="kg-card kg-embed-card"><blockquote class="twitter-tweet"><p lang="en" dir="ltr">I was wondering why my terminal</p></blockquote></figure>]]></description><link>https://ajot.me/speeding-up-terminal-load-time-how-i-fixed-a-90-second-delay-by-cleaning-up-zshrc-file/</link><guid isPermaLink="false">64d926d6f48ed035ee97f1c6</guid><dc:creator><![CDATA[Amit Jotwani]]></dc:creator><pubDate>Sun, 24 Apr 2022 11:05:46 GMT</pubDate><media:content url="https://images.unsplash.com/photo-1493020258366-be3ead1b3027?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=MnwxMTc3M3wwfDF8c2VhcmNofDF8fHRlcm1pbmFsJTIwbWFjfGVufDB8fHx8MTY1MDc5ODMwOQ&amp;ixlib=rb-1.2.1&amp;q=80&amp;w=2000" medium="image"/><content:encoded><![CDATA[<img src="https://images.unsplash.com/photo-1493020258366-be3ead1b3027?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=MnwxMTc3M3wwfDF8c2VhcmNofDF8fHRlcm1pbmFsJTIwbWFjfGVufDB8fHx8MTY1MDc5ODMwOQ&amp;ixlib=rb-1.2.1&amp;q=80&amp;w=2000" alt="Speeding Up Terminal Load Time: How I Fixed a 90-Second Delay by Cleaning Up .zshrc File"><p><a href="https://www.reddit.com/r/zsh/comments/nfp20t/zsh_loading_slow_on_macos/gymx1g2/?ref=ajot.me">This answer</a> on Reddit helped troubleshoot. I just commented everything, and it made the load lightening fast. Then, on a closer look, I found this mess. No idea how it got to this, but commenting out all but one of them did the trick. </p><figure class="kg-card kg-embed-card"><blockquote class="twitter-tweet"><p lang="en" dir="ltr">I was wondering why my terminal session was taking forever to load (~90 seconds each time I opened a tab). <br><br>Then I found this in my .zshrc file. <br><br>Over 300 lines of the same if condition.<br><br>I have no idea how it got here, but removing all but one reduced the load time to 1 second <a href="https://t.co/M2PTWXawWg?ref=ajot.me">pic.twitter.com/M2PTWXawWg</a></p>&#x2014; Amit Jotwani (@amit) <a href="https://twitter.com/amit/status/1518182685264470017?ref_src=twsrc%5Etfw&amp;ref=ajot.me">April 24, 2022</a></blockquote>
<script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>
</figure>]]></content:encoded></item><item><title><![CDATA[My Sublime Text 3 Setup: Aliases, Themes, and Terminal Fixes]]></title><description><![CDATA[<p></p><h2 id="adding-an-alias-to-zshrc-file">Adding an alias to zshrc file</h2><p>Wanted to add an alias to quickly open Sublime Text editor from the command line. Here&apos;s how - </p><ol><li>Open the file ~/.zshrc in a text editor</li><li>Add the following line - <code>alias sub=&quot;/Applications/Sublime\ Text.app/Contents/SharedSupport/bin/subl&</code></li></ol>]]></description><link>https://ajot.me/my-sublime-text-3-setup-aliases-themes-and-terminal-fixes/</link><guid isPermaLink="false">64d926d6f48ed035ee97f1c5</guid><dc:creator><![CDATA[Amit Jotwani]]></dc:creator><pubDate>Sat, 23 Apr 2022 10:49:09 GMT</pubDate><media:content url="https://images.unsplash.com/photo-1508921108053-9f757ead871c?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=MnwxMTc3M3wwfDF8c2VhcmNofDJ8fHRleHQlMjBlZGl0b3J8ZW58MHx8fHwxNjUwNzEyNTI3&amp;ixlib=rb-1.2.1&amp;q=80&amp;w=2000" medium="image"/><content:encoded><![CDATA[<img src="https://images.unsplash.com/photo-1508921108053-9f757ead871c?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=MnwxMTc3M3wwfDF8c2VhcmNofDJ8fHRleHQlMjBlZGl0b3J8ZW58MHx8fHwxNjUwNzEyNTI3&amp;ixlib=rb-1.2.1&amp;q=80&amp;w=2000" alt="My Sublime Text 3 Setup: Aliases, Themes, and Terminal Fixes"><p></p><h2 id="adding-an-alias-to-zshrc-file">Adding an alias to zshrc file</h2><p>Wanted to add an alias to quickly open Sublime Text editor from the command line. Here&apos;s how - </p><ol><li>Open the file ~/.zshrc in a text editor</li><li>Add the following line - <code>alias sub=&quot;/Applications/Sublime\ Text.app/Contents/SharedSupport/bin/subl&quot;</code></li><li>Close and reopen the command line, or reload by typing <code>source ~/.zshrc</code></li></ol><figure class="kg-card kg-image-card"><img src="https://ajot.me/content/images/2022/04/CleanShot-2022-04-23-at-06.48.39.png" class="kg-image" alt="My Sublime Text 3 Setup: Aliases, Themes, and Terminal Fixes" loading="lazy" width="552" height="69"></figure><p></p><hr><h2 id="managing-themes">Managing Themes</h2><p>Make sure you have <a href="https://packagecontrol.io/installation?ref=ajot.me#st3">Package Control</a> already set up for Sublime Text 3. </p><p>You can then install and use the <a href="https://packagecontrol.io/packages/Skins?ref=ajot.me">Skins package</a>, which provides an easy way to manage your themes for Sublime Text 3.</p><p>Install via package manager with <code>CMD+SHIFT+P</code> -&gt; Install Package -&gt; Skins</p><p><strong>Using Skins package to select a theme</strong></p><p><code>CMD+SHIFT+P</code> -&gt; Skin</p><figure class="kg-card kg-image-card"><img src="https://ajot.me/content/images/2022/04/CleanShot-2022-04-23-at-07.13.07.png" class="kg-image" alt="My Sublime Text 3 Setup: Aliases, Themes, and Terminal Fixes" loading="lazy" width="619" height="217" srcset="https://ajot.me/content/images/size/w600/2022/04/CleanShot-2022-04-23-at-07.13.07.png 600w, https://ajot.me/content/images/2022/04/CleanShot-2022-04-23-at-07.13.07.png 619w"></figure><figure class="kg-card kg-image-card"><img src="https://ajot.me/content/images/2022/04/CleanShot-2022-04-23-at-07.12.54.png" class="kg-image" alt="My Sublime Text 3 Setup: Aliases, Themes, and Terminal Fixes" loading="lazy" width="604" height="445" srcset="https://ajot.me/content/images/size/w600/2022/04/CleanShot-2022-04-23-at-07.12.54.png 600w, https://ajot.me/content/images/2022/04/CleanShot-2022-04-23-at-07.12.54.png 604w"></figure><h2 id="fixing-ohmyzsh-theme-symbols-in-sublime-texts-terminalterminus-package">Fixing ohmyzsh theme symbols in Sublime Text&apos;s terminal - Terminus package</h2><figure class="kg-card kg-bookmark-card"><a class="kg-bookmark-container" href="https://stackoverflow.com/a/71980861/1195247?ref=ajot.me"><div class="kg-bookmark-content"><div class="kg-bookmark-title">Sublime Text&#x2019;s Terminus is not displaying oh-my-zsh icons</div><div class="kg-bookmark-description">I get a generic ? where the icons should be. A similar issue but for Visual Studio Code is documented here:https://dev.to/avantar/how-to-fix-zsh-icons-in-visual-studio-code-terminal-38bbNeedless ...</div><div class="kg-bookmark-metadata"><img class="kg-bookmark-icon" src="https://cdn.sstatic.net/Sites/stackoverflow/Img/apple-touch-icon.png?v=c78bd457575a" alt="My Sublime Text 3 Setup: Aliases, Themes, and Terminal Fixes"><span class="kg-bookmark-author">Stack Overflow</span><span class="kg-bookmark-publisher">ctrebbau</span></div></div><div class="kg-bookmark-thumbnail"><img src="https://cdn.sstatic.net/Sites/stackoverflow/Img/apple-touch-icon@2.png?v=73d79a89bded" alt="My Sublime Text 3 Setup: Aliases, Themes, and Terminal Fixes"></div></a></figure><figure class="kg-card kg-image-card"><img src="https://ajot.me/content/images/2022/04/CleanShot-2022-04-23-at-10.58.12.png" class="kg-image" alt="My Sublime Text 3 Setup: Aliases, Themes, and Terminal Fixes" loading="lazy" width="534" height="173"></figure>]]></content:encoded></item><item><title><![CDATA[Playing with APIs in Postman: Getting Code Samples and Curl Commands Quickly]]></title><description><![CDATA[<p>Now that I am playing around a bit more frequently with APIs, especially Twitter&#x2019;s v2 API, and Airtable&#x2019;s API, I have found myself playing around a lot more with Postman. It&#x2019;s my new favorite app/service.</p><p>I have obviously been living under a huge</p>]]></description><link>https://ajot.me/playing-with-apis-in-postman-getting-code-samples-and-curl-commands-quickly/</link><guid isPermaLink="false">64d926d6f48ed035ee97f1c4</guid><dc:creator><![CDATA[Amit Jotwani]]></dc:creator><pubDate>Thu, 21 Apr 2022 16:13:11 GMT</pubDate><media:content url="https://images.unsplash.com/photo-1531564701487-f238224b7ce3?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=M3wxMTc3M3wwfDF8c2VhcmNofDN8fHBvc3RtYW58ZW58MHx8fHwxNjkxOTUzNzQ2fDA&amp;ixlib=rb-4.0.3&amp;q=80&amp;w=2000" medium="image"/><content:encoded><![CDATA[<img src="https://images.unsplash.com/photo-1531564701487-f238224b7ce3?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=M3wxMTc3M3wwfDF8c2VhcmNofDN8fHBvc3RtYW58ZW58MHx8fHwxNjkxOTUzNzQ2fDA&amp;ixlib=rb-4.0.3&amp;q=80&amp;w=2000" alt="Playing with APIs in Postman: Getting Code Samples and Curl Commands Quickly"><p>Now that I am playing around a bit more frequently with APIs, especially Twitter&#x2019;s v2 API, and Airtable&#x2019;s API, I have found myself playing around a lot more with Postman. It&#x2019;s my new favorite app/service.</p><p>I have obviously been living under a huge rock all this while, cause I didn&#x2019;t realize the power Postman. Not only can you explore APIs, but they also support things like generating docs automatically for your APIs (which I did for greetingsapi.com). They&#x2019;re also working closely with popular APIs like Twitter, so publicly available endpoints are included by default inside Postman. No need to shuttle back and forth between the docs and where your code is.</p><p>Another new thing I am discovering is that they automatically generate code samples for popular languages for each endpoint. So you can essentially just copy/paste these to see them in action. This is pretty huge.</p><p>Here&#x2019;s an example for Python. You can also choose Curl, and several other languages.</p><figure class="kg-card kg-image-card"><img src="https://ajot.me/content/images/2022/04/CleanShot-2022-04-21-at-12.04.17@2x.png" class="kg-image" alt="Playing with APIs in Postman: Getting Code Samples and Curl Commands Quickly" loading="lazy"></figure>]]></content:encoded></item><item><title><![CDATA[Playing around with Postman]]></title><description><![CDATA[<p>I have known of <a href="https://www.postman.com/?ref=ajot.me">Postman</a> for a while now - it&apos;s a tool you can use to explore and test APIs. I just never got down to using it much. That changed quite a bit in the last week or so. </p><p>I started playing around with it while</p>]]></description><link>https://ajot.me/playing-around-with-postman/</link><guid isPermaLink="false">64d926d6f48ed035ee97f1c2</guid><category><![CDATA[playground]]></category><category><![CDATA[apis]]></category><category><![CDATA[postman]]></category><dc:creator><![CDATA[Amit Jotwani]]></dc:creator><pubDate>Wed, 20 Apr 2022 12:29:57 GMT</pubDate><media:content url="https://images.unsplash.com/photo-1521575107034-e0fa0b594529?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=MnwxMTc3M3wwfDF8c2VhcmNofDF8fHBvc3R8ZW58MHx8fHwxNjUwNDU3NzM2&amp;ixlib=rb-1.2.1&amp;q=80&amp;w=2000" medium="image"/><content:encoded><![CDATA[<img src="https://images.unsplash.com/photo-1521575107034-e0fa0b594529?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=MnwxMTc3M3wwfDF8c2VhcmNofDF8fHBvc3R8ZW58MHx8fHwxNjUwNDU3NzM2&amp;ixlib=rb-1.2.1&amp;q=80&amp;w=2000" alt="Playing around with Postman"><p>I have known of <a href="https://www.postman.com/?ref=ajot.me">Postman</a> for a while now - it&apos;s a tool you can use to explore and test APIs. I just never got down to using it much. That changed quite a bit in the last week or so. </p><p>I started playing around with it while exploring the Airtable API, and then the Twitter API this week. I also used it to create the docs for greetingsapi.com. Also, I didn&apos;t know Postman could generate docs and sample code for the endpoints automatically. Pretty cool.</p><figure class="kg-card kg-embed-card"><blockquote class="twitter-tweet"><p lang="en" dir="ltr">Call me ignorant, but I didn&#x2019;t realize that you can publish API docs using <a href="https://twitter.com/getpostman?ref_src=twsrc%5Etfw&amp;ref=ajot.me">@getpostman</a>.<br><br>What&#x2019;s really cool is that it automatically generates sample code for a bunch of languages.<br><br>Just created a docs page for <a href="https://t.co/ZbfX6mTqKt?ref=ajot.me">https://t.co/ZbfX6mTqKt</a><br><br>Check it out - <a href="https://t.co/ZlSfEG3NOB?ref=ajot.me">https://t.co/ZlSfEG3NOB</a> <a href="https://t.co/Fy9ot2rOOl?ref=ajot.me">pic.twitter.com/Fy9ot2rOOl</a></p>&#x2014; Amit Jotwani (@amit) <a href="https://twitter.com/amit/status/1516612341214887939?ref_src=twsrc%5Etfw&amp;ref=ajot.me">April 20, 2022</a></blockquote>
<script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>
</figure><h3 id="variables">Variables</h3><p>I am still new to this concept in Postman, but it lets you created environment variables of sorts that you can use to make API calls without actually punching the API key etc for each of the endpoints. </p><p>Here&apos;s an example for Twitter API I was exploring yesterday. More about variables <a href="https://learning.postman.com/docs/sending-requests/variables/?ref=ajot.me">here</a> on Postman&apos;s docs.</p><figure class="kg-card kg-image-card"><img src="https://ajot.me/content/images/2022/04/CleanShot-2022-04-20-at-07.37.59.png" class="kg-image" alt="Playing around with Postman" loading="lazy" width="1036" height="313" srcset="https://ajot.me/content/images/size/w600/2022/04/CleanShot-2022-04-20-at-07.37.59.png 600w, https://ajot.me/content/images/size/w1000/2022/04/CleanShot-2022-04-20-at-07.37.59.png 1000w, https://ajot.me/content/images/2022/04/CleanShot-2022-04-20-at-07.37.59.png 1036w" sizes="(min-width: 720px) 720px"></figure><p>You then use it in the API call with {{variable_name}}.</p><figure class="kg-card kg-image-card"><img src="https://ajot.me/content/images/2022/04/CleanShot-2022-04-20-at-07.39.22.png" class="kg-image" alt="Playing around with Postman" loading="lazy" width="1044" height="660" srcset="https://ajot.me/content/images/size/w600/2022/04/CleanShot-2022-04-20-at-07.39.22.png 600w, https://ajot.me/content/images/size/w1000/2022/04/CleanShot-2022-04-20-at-07.39.22.png 1000w, https://ajot.me/content/images/2022/04/CleanShot-2022-04-20-at-07.39.22.png 1044w" sizes="(min-width: 720px) 720px"></figure><p></p>]]></content:encoded></item><item><title><![CDATA[Create a Content extension for Canva using Flask]]></title><description><![CDATA[<p></p><p>Saw this <a href="https://docs.developer.canva.com/apps/extensions/content-extensions/quick-start?ref=ajot.me">Quick Start guide</a> on Canva&apos;s developer portal that walks you through creating a content extension for Canva&apos;s users. It uses Node.js and Express.js as the server backend. I wanted to replicate this example using Python and Flask as the server.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://ajot.me/content/images/2022/04/CleanShot-2022-04-19-at-16.50.47.png" class="kg-image" alt loading="lazy" width="1117" height="972" srcset="https://ajot.me/content/images/size/w600/2022/04/CleanShot-2022-04-19-at-16.50.47.png 600w, https://ajot.me/content/images/size/w1000/2022/04/CleanShot-2022-04-19-at-16.50.47.png 1000w, https://ajot.me/content/images/2022/04/CleanShot-2022-04-19-at-16.50.47.png 1117w" sizes="(min-width: 720px) 720px"><figcaption>My Flask</figcaption></figure>]]></description><link>https://ajot.me/create-a-content-extension-for-canva-using-flask/</link><guid isPermaLink="false">64d926d6f48ed035ee97f1c0</guid><category><![CDATA[flask]]></category><category><![CDATA[python]]></category><category><![CDATA[tutorials]]></category><dc:creator><![CDATA[Amit Jotwani]]></dc:creator><pubDate>Tue, 19 Apr 2022 20:54:39 GMT</pubDate><media:content url="https://images.unsplash.com/photo-1526379095098-d400fd0bf935?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=MnwxMTc3M3wwfDF8c2VhcmNofDE2fHxweXRob24lMjBmbGFza3xlbnwwfHx8fDE2NTAzMzA4NTc&amp;ixlib=rb-1.2.1&amp;q=80&amp;w=2000" medium="image"/><content:encoded><![CDATA[<img src="https://images.unsplash.com/photo-1526379095098-d400fd0bf935?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=MnwxMTc3M3wwfDF8c2VhcmNofDE2fHxweXRob24lMjBmbGFza3xlbnwwfHx8fDE2NTAzMzA4NTc&amp;ixlib=rb-1.2.1&amp;q=80&amp;w=2000" alt="Create a Content extension for Canva using Flask"><p></p><p>Saw this <a href="https://docs.developer.canva.com/apps/extensions/content-extensions/quick-start?ref=ajot.me">Quick Start guide</a> on Canva&apos;s developer portal that walks you through creating a content extension for Canva&apos;s users. It uses Node.js and Express.js as the server backend. I wanted to replicate this example using Python and Flask as the server.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://ajot.me/content/images/2022/04/CleanShot-2022-04-19-at-16.50.47.png" class="kg-image" alt="Create a Content extension for Canva using Flask" loading="lazy" width="1117" height="972" srcset="https://ajot.me/content/images/size/w600/2022/04/CleanShot-2022-04-19-at-16.50.47.png 600w, https://ajot.me/content/images/size/w1000/2022/04/CleanShot-2022-04-19-at-16.50.47.png 1000w, https://ajot.me/content/images/2022/04/CleanShot-2022-04-19-at-16.50.47.png 1117w" sizes="(min-width: 720px) 720px"><figcaption>My Flask server listening and responding to POST request from Canvac</figcaption></figure><p><strong>What is a content extension?</strong></p><p>This is how Canva describes it:</p><blockquote>A content extension allows you to import third-party content, such as photos and illustrations, into Canva. Users can access this content via the side panel and add it to their designs.</blockquote><figure class="kg-card kg-image-card"><img src="https://ajot.me/content/images/2022/04/CleanShot-2022-04-19-at-09.10.46.png" class="kg-image" alt="Create a Content extension for Canva using Flask" loading="lazy" width="1281" height="1281" srcset="https://ajot.me/content/images/size/w600/2022/04/CleanShot-2022-04-19-at-09.10.46.png 600w, https://ajot.me/content/images/size/w1000/2022/04/CleanShot-2022-04-19-at-09.10.46.png 1000w, https://ajot.me/content/images/2022/04/CleanShot-2022-04-19-at-09.10.46.png 1281w" sizes="(min-width: 720px) 720px"></figure><h2 id="how-content-extensions-work">How content extensions work</h2><ol><li>You visit the Canva Developer Portal and create an app.</li><li>You provide the endpoint/base URL for your app. This is the endpoint where Canva will send a POST request. One thing to note is that Canva adds &quot;/content/resources/find&quot; to the base URL, so your service should be answering the POST request at - <code>your-service.com/any-custom-route/content/resources/find</code></li><li>Your service should then return the content in the following format. The content that your service responds with is used to render the content in the side panel. </li></ol><pre><code class="language-json">{
	&quot;type&quot;: &quot;SUCCESS&quot;,
    &quot;resources&quot;:[
		{
            &quot;contentType&quot;: &quot;image/jpeg&quot;,
            &quot;id&quot;: &quot;0&quot;,
            &quot;name&quot;: &quot;Alejandro Escamilla&quot;,
            &quot;thumbnail&quot;: {
                &quot;url&quot;: &quot;https://picsum.photos/id/0/5616/3744&quot;
            },
            &quot;type&quot;: &quot;IMAGE&quot;,
            &quot;url&quot;: &quot;https://picsum.photos/id/0/5616/3744&quot;
        },
        {
            &quot;contentType&quot;: &quot;image/jpeg&quot;,
            &quot;id&quot;: &quot;1&quot;,
            &quot;name&quot;: &quot;Alejandro Escamilla&quot;,
            &quot;thumbnail&quot;: {
                &quot;url&quot;: &quot;https://picsum.photos/id/1/5616/3744&quot;
            },
            &quot;type&quot;: &quot;IMAGE&quot;,
            &quot;url&quot;: &quot;https://picsum.photos/id/1/5616/3744&quot;
        }
       ]
}</code></pre><p></p><p><strong>Step 1: Build the backend service</strong></p><p>If you already have a service that has the content, you can skip this step, and just make sure that it returns data in the format Canva is expecting (see above). </p><p>Here&apos;s the code you can use to fire up a Flask server - </p><pre><code class="language-python">#inside main.py

from flask import Flask, request, render_template
import requests 
import json

app = Flask(__name__)
app.config[&quot;DEBUG&quot;] = True

# Define the route which will receive the POST request from Canva
@app.route(&quot;/canva/content/resources/find&quot;, methods=[&apos;POST&apos;])
def get_content():
	# Gather the content you want to send back. Here we are using picsum.photos, a placeholder API for pictures.
    url = &quot;https://picsum.photos/v2/list&quot;
    r = requests.get(url)
    results = json.loads(r. text)

    resources = []
	
    # Loop through the results received from picsum API, and prepare the content in the format expected by Canva.
    for resource in results:
        content = {
            &quot;type&quot;: &quot;IMAGE&quot;,
            &quot;id&quot;: resource[&quot;id&quot;],
            &quot;name&quot;: resource[&quot;author&quot;],
            &quot;thumbnail&quot;: {
                &quot;url&quot;: resource[&quot;download_url&quot;],
            },
            &quot;url&quot;: resource[&quot;download_url&quot;],
            &quot;contentType&quot;: &quot;image/jpeg&quot;
        }
        resources.append(content)

    print(resources)

    return {&quot;type&quot;: &quot;SUCCESS&quot;,&quot;resources&quot;:resources}

if __name__ == &quot;__main__&quot;:
    app.run(host=&quot;127.0.0.1&quot;, port=8080, debug=True)</code></pre><p>Run the server with <code>python3 main.py</code>, and expose it to the internet using a tuneling software like ngrok, or you can do what I did, and hosted it on PythonAnywhere.</p><p>You can see it in action here - <code>https://ajotwani.pythonanywhere.com/canva/content/resources/find</code></p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://ajot.me/content/images/2022/04/CleanShot-2022-04-19-at-16.50.47-1.png" class="kg-image" alt="Create a Content extension for Canva using Flask" loading="lazy" width="1117" height="972" srcset="https://ajot.me/content/images/size/w600/2022/04/CleanShot-2022-04-19-at-16.50.47-1.png 600w, https://ajot.me/content/images/size/w1000/2022/04/CleanShot-2022-04-19-at-16.50.47-1.png 1000w, https://ajot.me/content/images/2022/04/CleanShot-2022-04-19-at-16.50.47-1.png 1117w" sizes="(min-width: 720px) 720px"><figcaption>My Flask server listening and responding to POST request from Canvac</figcaption></figure><p><strong>Step 2: Build the app on Canva developer portal. </strong></p><p>Same as step 2 <a href="https://docs.developer.canva.com/apps/extensions/content-extensions/quick-start?ref=ajot.me">here</a></p><p><strong>Step 3: Configure the content extension</strong></p><p>Same as step 3 <a href="https://docs.developer.canva.com/apps/extensions/content-extensions/quick-start?ref=ajot.me">here</a></p>]]></content:encoded></item><item><title><![CDATA[How to deploy a Flask app to Digital Ocean's app platform]]></title><description><![CDATA[<p>I recently went throught the process to put my side project <a href="https://howbigisthebaby.io/?ref=ajot.me">howbigisthebaby.io</a> on Digital Ocean&apos;s new app platform, and wanted to document it in case anyone else found it helpful (or atleast me revisiting this later).</p><!--kg-card-begin: html--><aside class="toc"></aside><!--kg-card-end: html--><!--kg-card-begin: markdown--><hr>
<h2 id="part-1-setup-the-environment-for-the-flask-app">Part 1: Setup the environment for the Flask app</h2>
<p><strong>Step 1:</strong></p>]]></description><link>https://ajot.me/how-to-deploy-a-flask-app-to-digital-oceans-app-platform/</link><guid isPermaLink="false">64d926d6f48ed035ee97f1bf</guid><category><![CDATA[programming]]></category><category><![CDATA[tutorials]]></category><category><![CDATA[python]]></category><category><![CDATA[flask]]></category><dc:creator><![CDATA[Amit Jotwani]]></dc:creator><pubDate>Tue, 19 Apr 2022 01:36:46 GMT</pubDate><media:content url="https://images.unsplash.com/photo-1624953587687-daf255b6b80a?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=MnwxMTc3M3wwfDF8c2VhcmNofDJ8fHB5dGhvbiUyMGNvZGV8ZW58MHx8fHwxNjUwMzMwOTEz&amp;ixlib=rb-1.2.1&amp;q=80&amp;w=2000" medium="image"/><content:encoded><![CDATA[<img src="https://images.unsplash.com/photo-1624953587687-daf255b6b80a?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=MnwxMTc3M3wwfDF8c2VhcmNofDJ8fHB5dGhvbiUyMGNvZGV8ZW58MHx8fHwxNjUwMzMwOTEz&amp;ixlib=rb-1.2.1&amp;q=80&amp;w=2000" alt="How to deploy a Flask app to Digital Ocean&apos;s app platform"><p>I recently went throught the process to put my side project <a href="https://howbigisthebaby.io/?ref=ajot.me">howbigisthebaby.io</a> on Digital Ocean&apos;s new app platform, and wanted to document it in case anyone else found it helpful (or atleast me revisiting this later).</p><!--kg-card-begin: html--><aside class="toc"></aside><!--kg-card-end: html--><!--kg-card-begin: markdown--><hr>
<h2 id="part-1-setup-the-environment-for-the-flask-app">Part 1: Setup the environment for the Flask app</h2>
<p><strong>Step 1: Create the virtual environment on your machine</strong></p>
<pre><code>$ virtualenv venv -p Python3
</code></pre>
<p><strong>Step 2: Activate the virtual environment</strong></p>
<pre><code>$ source venv/bin/activate
</code></pre>
<p><strong>Step 3: Install Flask and gunicorn</strong></p>
<pre><code>$ pip install Flask gunicorn
</code></pre>
<p><strong>Step 4: Freeze the requirements in requirements.txt file</strong></p>
<p>Now that you have the flask package installed, you will need to save this requirement and its dependencies so App Platform can install them later. Do this now using pip and then saving the information to a requirements.txt file:</p>
<pre><code>$ pip freeze &gt; requirements.txt
</code></pre>
<p><strong>Step 5: Write code for your Flask app in app.py. Here&apos;s a simple &quot;hello world&quot; app</strong></p>
<pre><code>from flask import Flask
app = Flask(__name__)

@app.route(&apos;/&apos;)
def hello_world():
    return &apos;Hello World!&apos;
</code></pre>
<hr>
<h2 id="part-2-setting-up-your-gunicorn-configuration">Part 2: Setting Up Your Gunicorn Configuration</h2>
<p>Since we cannot use Flask&apos;s built-in server in production environment, we will use Gunicron web server <a href="https://www.reddit.com/r/Python/comments/68phcu/why_nginxgunicornflask/?ref=ajot.me">Why Nginx/Gunicorn/Flask?</a></p>
<blockquote>
<p>While lightweight and easy to use, Flask&#x2019;s built-in server is not suitable for production as it doesn&#x2019;t scale well and by default serves only one request at a time.<br>
<a href="https://flask.palletsprojects.com/en/2.0.x/deploying/?ref=ajot.me">Source: Flask&apos;s docs</a></p>
</blockquote>
<p><strong>Step 1: Create the gunicorn config file</strong></p>
<pre><code>$ touch gunicorn_config.py
</code></pre>
<p><strong>Step 2: Add this configuration to gunicorn_config.py file</strong></p>
<pre><code># inside gunicorn_config.py
bind = &quot;0.0.0.0:8080&quot;
workers = 2
</code></pre>
<hr>
<h2 id="part-3-pushing-the-site-to-github">Part 3: Pushing the Site to GitHub</h2>
<p>The way the Digital Ocean app platform works is that it syncs with a GitHub repo. Any changes to this GitHub repo will automatically be pushed to your app living on Digital Ocean&apos;s app platform.</p>
<p><strong>Step 1: Create a new repo on GitHub.com</strong><br>
Open your browser and navigate to GitHub, log in with your profile, and create a new repository called flask-app.</p>
<p><strong>Step 2: Then, create a .gitignore file, and add .pyc so these files are not pushed to GitHub.</strong></p>
<pre><code>$ git init
$ nano .gitignore
</code></pre>
<pre><code># inside .gitignore
.pyc
</code></pre>
<p><strong>Step 3: Add files to your repository, and commit the changes</strong></p>
<pre><code>$ git add app.py gunicorn_config.py requirements.txt .gitignore
$ git commit -m &quot;Initial Flask App&quot;
</code></pre>
<p><strong>Step 4: Add the GitHub repo you created earlier as the remote repository</strong></p>
<pre><code>$ git remote add origin https://github.com/your_username/flask-app
</code></pre>
<p>example: git remote add origin <a href="https://github.com/ajot/hbb-flask-webhook?ref=ajot.me">https://github.com/ajot/hbb-flask-webhook</a></p>
<p><strong>Step 5: Push to GitHub</strong></p>
<pre><code>$ git branch -M main
$ git push -u origin main
</code></pre>
<hr>
<h2 id="part-4-deploying-to-digital-oceans-app-platform">Part 4: Deploying to Digital Ocean&apos;s app platform</h2>
<ol>
<li>Create a new &quot;app&quot; on Digital Ocean.</li>
<li>Choose GitHub as source</li>
<li>Choose the GitHub repo you just created</li>
<li>Follow the promots to create the app</li>
<li>Edit the run command to this - gunicorn --worker-tmp-dir /dev/shm app:app</li>
</ol>
<hr>
<p><strong>Bonus Tip:</strong> You can also set environment variables in Digital Ocean, and then access them in your code like this -</p>
<pre><code class="language-python">import OS

airtable_api_key = os.environ.get(&apos;airtable_api_key&apos;)
airtable_base = os.environ.get(&apos;airtable_base&apos;)
</code></pre>
<hr>
<p><strong>Resources:</strong><br>
<a href="https://www.digitalocean.com/community/tutorials/how-to-deploy-a-flask-app-using-gunicorn-to-app-platform?ref=ajot.me">How To Deploy a Flask App Using Gunicorn to App Platform | DigitalOcean</a></p>
<!--kg-card-end: markdown-->]]></content:encoded></item></channel></rss>