<?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"><channel><title><![CDATA[Sadra Yahyapour]]></title><description><![CDATA[Backend Developer. Machine Learning Lover. Open Source Contributor. Making Handy Stuff. Native Python Coder.]]></description><link>https://blog.imsadra.dev</link><image><url>https://cdn.hashnode.com/res/hashnode/image/upload/v1770820703379/906c31b9-317c-4429-be3b-2171baa056ca.png</url><title>Sadra Yahyapour</title><link>https://blog.imsadra.dev</link></image><generator>RSS for Node</generator><lastBuildDate>Tue, 14 Apr 2026 12:22:56 GMT</lastBuildDate><atom:link href="https://blog.imsadra.dev/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[Notio - Hashnode Post-Publication LLM Toolkit]]></title><description><![CDATA[As someone who manages various communities with different languages, personalities, and audiences, I've always wanted them all to benefit my Hashnode blogs, but I haven't been successful.
Some readers found my articles boring, while others thought th...]]></description><link>https://blog.imsadra.dev/notio-hashnode-post-publication-llm-toolkit</link><guid isPermaLink="true">https://blog.imsadra.dev/notio-hashnode-post-publication-llm-toolkit</guid><category><![CDATA[ModusHack]]></category><category><![CDATA[modus]]></category><category><![CDATA[hypermode]]></category><category><![CDATA[pyaction]]></category><category><![CDATA[hackathon]]></category><category><![CDATA[Python]]></category><category><![CDATA[TypeScript]]></category><category><![CDATA[GitHub]]></category><category><![CDATA[github-actions]]></category><category><![CDATA[GitHub Actions]]></category><category><![CDATA[cicd]]></category><category><![CDATA[llm]]></category><category><![CDATA[AI]]></category><category><![CDATA[Hashnode]]></category><category><![CDATA[language models]]></category><dc:creator><![CDATA[Sadra Yahyapour]]></dc:creator><pubDate>Wed, 27 Nov 2024 06:56:47 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1732620416331/30cbe3d1-fa64-4763-99ce-1d0ac1fb68ae.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>As someone who manages various communities with different languages, personalities, and audiences, I've always wanted them all to benefit my Hashnode blogs, but I haven't been successful.</p>
<p>Some readers found my articles boring, while others thought they were too technical and hard to understand. Some people even had trouble reading articles in English.</p>
<p>So I thought, why not use LLMs for some post-production work and publish different versions of an article I write on Hashnode?</p>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://youtu.be/0CZMhxBLAak">https://youtu.be/0CZMhxBLAak</a></div>
<p> </p>
<h3 id="heading-theory">Theory</h3>
<p>Hashnode uses a GitHub repository to create backups for the articles you publish on Hashnode. If you enable this feature in your Hashnode dashboard, a <code>username/hashnode</code> repository will be created on your GitHub profile.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1732632981123/93afed7c-78ea-47b3-ba23-949dd8c34119.png" alt class="image--center mx-auto" /></p>
<p>From then on, whenever you publish or edit an article, it will automatically be pushed to that GitHub repository, and here is when magic happens! 🪄</p>
<div data-node-type="callout">
<div data-node-type="callout-emoji">✨</div>
<div data-node-type="callout-text">As soon as Hashnode updates the repository, Notio gets triggered on the article.</div>
</div>

<p>Based on the configuration given to Notio, it carries out different tasks on the article. In the end, the result is a markdown file with the new version of the article inside the backup repository.</p>
<h3 id="heading-how-notio-works">How Notio Works</h3>
<p>Notio functions as a GitHub CI pipeline (GitHub Action). It connects to a Hypermode GraphQL instance and processes the article by making API calls to the instance.</p>
<p>Once the transformed article is received, it is saved as a Markdown file in the backup directory. Since both Hashnode and GitHub use similar Markdown syntax, the output articles stay readable on GitHub.</p>
<h3 id="heading-llm-features">LLM Features</h3>
<p>Notio provides several LLM features. Let's begin with the most important one.</p>
<h4 id="heading-article-translation">Article translation</h4>
<p>You can receive a translated version of your article in any language on your GitHub just minutes after publishing it on Hashnode. (both RTL and LTR languages are supported)</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1732631964161/fd105315-5211-4489-83e4-85e45b9350b2.png" alt class="image--center mx-auto" /></p>
<h4 id="heading-generating-questions">Generating questions</h4>
<p>This feature allows you to generate any number of questions, with or without answers, about the published article and save them as a markdown document.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1732634125931/8481a995-1f36-4304-a9b0-2af6f22bfe06.png" alt class="image--center mx-auto" /></p>
<h4 id="heading-generate-abstraction">Generate abstraction</h4>
<p>If you need to create a brief summary of your article, this feature is perfect for you. Just a minute after you publish your article on Hashnode, you'll have its summary in the repository.</p>
<h4 id="heading-simplify-article">Simplify article</h4>
<p>You can use this feature to create a simpler version of your article for readers who need a basic understanding of the topic. This version might include fewer code blocks and lighter discussion topics, but it still covers all sections of the article.</p>
<h3 id="heading-setup-amp-usage">Setup &amp; Usage</h3>
<p>If you want to set up Notio for your Hashnode blog, follow these steps.</p>
<h4 id="heading-1-enable-blog-backup-from-hashnode">1. Enable blog backup from Hashnode</h4>
<p>Navigate to your blog settings. From the sidebar, select "<strong>GitHub</strong>” and follow the instructions.</p>
<h4 id="heading-2-create-a-hypermode-instance">2. Create a Hypermode instance</h4>
<p>Clone <a target="_blank" href="https://github.com/lnxpy/notio-model">this Hypermode sample instance</a> repository. Follow <a target="_blank" href="https://docs.hypermode.com/deploy">this document</a> to deploy your own GraphQL instance on Hypermode.</p>
<h4 id="heading-3-grab-the-endpoint-url-and-api-key">3. Grab the endpoint URL and API key</h4>
<p>Once your instance is deployed to Hypermode, retrieve the endpoint URL from the Hypermode dashboard and the API key from the instance settings.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1732648927287/de8ad1d3-3ed9-4dd0-801a-10fa8fae3d94.png" alt class="image--center mx-auto" /></p>
<h4 id="heading-4-store-the-secrets">4. Store the secrets</h4>
<p>Navigate to the backup repository that Hashnode has just created for you on your GitHub profile. Go to "<strong>Settings</strong>" &gt; "<strong>Secrets and variables</strong>" &gt; "<strong>Actions</strong>" and create the following secrets.</p>
<ul>
<li><p><code>HYPERMODE_ENDPOINT_URL</code>: The endpoint URL–taken from the Hypermode dashboard.</p>
</li>
<li><p><code>HYPERMODE_API_TOKEN</code>: The API token–taken from the Hypermode instance settings.</p>
</li>
</ul>
<h4 id="heading-5-use-notio">5. Use Notio</h4>
<p>Now that everything is set up and the model is ready to respond, create a workflow file in the backup repository at <code>.github/workflows/notio-ci.yml</code>. For now, paste the following YAML configuration into it.</p>
<pre><code class="lang-yaml"><span class="hljs-attr">name:</span> <span class="hljs-string">Notio</span> <span class="hljs-string">LLM</span> <span class="hljs-string">CI</span>

<span class="hljs-attr">on:</span>
  <span class="hljs-attr">push:</span>
    <span class="hljs-attr">branches:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">main</span>

<span class="hljs-attr">jobs:</span>
  <span class="hljs-attr">build:</span>
    <span class="hljs-attr">runs-on:</span> <span class="hljs-string">ubuntu-latest</span>

    <span class="hljs-attr">name:</span> <span class="hljs-string">Running</span> <span class="hljs-string">the</span> <span class="hljs-string">action</span>
    <span class="hljs-attr">steps:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">checkout</span>
        <span class="hljs-attr">uses:</span> <span class="hljs-string">actions/checkout@v4</span>
        <span class="hljs-attr">with:</span>
          <span class="hljs-attr">fetch-depth:</span> <span class="hljs-number">0</span>
</code></pre>
<p>Two more <code>steps</code> need to be added to this YAML file. The next step involves using Notio. The steps below show how to use each LLM feature. Choose one and configure it according to your needs.</p>
<ul>
<li><p><code>translate-article</code>: To translate the article.</p>
<pre><code class="lang-yaml">  <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Running</span> <span class="hljs-string">Notio</span>
    <span class="hljs-attr">uses:</span> <span class="hljs-string">lnxpy/notio@main</span>
    <span class="hljs-attr">with:</span>
      <span class="hljs-attr">method:</span> <span class="hljs-string">translate-article</span>
      <span class="hljs-attr">path:</span> <span class="hljs-string">dutch/</span>  <span class="hljs-comment"># default: transformed_articles/</span>
      <span class="hljs-attr">translate_to:</span> <span class="hljs-string">Dutch</span> <span class="hljs-comment"># default: English</span>
      <span class="hljs-attr">hypermode_endpoint:</span> <span class="hljs-string">${{</span> <span class="hljs-string">secrets.HYPERMODE_ENDPOINT</span> <span class="hljs-string">}}</span>
      <span class="hljs-attr">hypermode_api_token:</span> <span class="hljs-string">${{</span> <span class="hljs-string">secrets.HYPERMODE_API_TOKEN</span> <span class="hljs-string">}}</span>
</code></pre>
</li>
<li><p><code>question-generation</code>: To generate questions about the article.</p>
<pre><code class="lang-yaml">  <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Running</span> <span class="hljs-string">Notio</span>
    <span class="hljs-attr">uses:</span> <span class="hljs-string">lnxpy/notio@main</span>
    <span class="hljs-attr">with:</span>
      <span class="hljs-attr">method:</span> <span class="hljs-string">generate-questions</span>
      <span class="hljs-attr">path:</span> <span class="hljs-string">questions/</span>
      <span class="hljs-attr">question_limit:</span> <span class="hljs-number">10</span> <span class="hljs-comment"># default: 20</span>
      <span class="hljs-attr">include_answers:</span> <span class="hljs-literal">false</span> <span class="hljs-comment"># default: true</span>
      <span class="hljs-attr">hypermode_endpoint:</span> <span class="hljs-string">${{</span> <span class="hljs-string">secrets.HYPERMODE_ENDPOINT</span> <span class="hljs-string">}}</span>
      <span class="hljs-attr">hypermode_api_token:</span> <span class="hljs-string">${{</span> <span class="hljs-string">secrets.HYPERMODE_API_TOKEN</span> <span class="hljs-string">}}</span>
</code></pre>
</li>
<li><p><code>abtract-article</code>: To summarize and generate an abstraction from the article.</p>
<pre><code class="lang-yaml">  <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Running</span> <span class="hljs-string">Notio</span>
    <span class="hljs-attr">uses:</span> <span class="hljs-string">lnxpy/notio@main</span>
    <span class="hljs-attr">with:</span>
      <span class="hljs-attr">method:</span> <span class="hljs-string">abstract-article</span>
      <span class="hljs-attr">path:</span> <span class="hljs-string">abstractions/</span>
      <span class="hljs-attr">hypermode_endpoint:</span> <span class="hljs-string">${{</span> <span class="hljs-string">secrets.HYPERMODE_ENDPOINT</span> <span class="hljs-string">}}</span>
      <span class="hljs-attr">hypermode_api_token:</span> <span class="hljs-string">${{</span> <span class="hljs-string">secrets.HYPERMODE_API_TOKEN</span> <span class="hljs-string">}}</span>
</code></pre>
</li>
<li><p><code>simplify-article</code>: To simplify the article.</p>
<pre><code class="lang-yaml">  <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Running</span> <span class="hljs-string">Notio</span>
    <span class="hljs-attr">uses:</span> <span class="hljs-string">lnxpy/notio@main</span>
    <span class="hljs-attr">with:</span>
      <span class="hljs-attr">method:</span> <span class="hljs-string">simplify-article</span>
      <span class="hljs-attr">path:</span> <span class="hljs-string">simplified_articles/</span>
      <span class="hljs-attr">hypermode_endpoint:</span> <span class="hljs-string">${{</span> <span class="hljs-string">secrets.HYPERMODE_ENDPOINT</span> <span class="hljs-string">}}</span>
      <span class="hljs-attr">hypermode_api_token:</span> <span class="hljs-string">${{</span> <span class="hljs-string">secrets.HYPERMODE_API_TOKEN</span> <span class="hljs-string">}}</span>
</code></pre>
</li>
</ul>
<p>And finally, add the commit step to ensure the Notio articles are submitted to the repository and visible to your audiences.</p>
<pre><code class="lang-yaml"><span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Commiting</span>
  <span class="hljs-attr">uses:</span> <span class="hljs-string">EndBug/add-and-commit@v9</span>
  <span class="hljs-attr">with:</span>
    <span class="hljs-attr">default_author:</span> <span class="hljs-string">github_actions</span>
    <span class="hljs-attr">message:</span> <span class="hljs-string">'article updated'</span>
</code></pre>
<p>Here is a sample <code>notio-ci.yml</code> file showing how to use the <code>question-generation</code> method.</p>
<pre><code class="lang-yaml"><span class="hljs-attr">name:</span> <span class="hljs-string">Notio</span> <span class="hljs-string">LLM</span> <span class="hljs-string">CI</span>

<span class="hljs-attr">on:</span>
  <span class="hljs-attr">push:</span>
    <span class="hljs-attr">branches:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">main</span>

<span class="hljs-attr">jobs:</span>
  <span class="hljs-attr">build:</span>
    <span class="hljs-attr">runs-on:</span> <span class="hljs-string">ubuntu-latest</span>

    <span class="hljs-attr">name:</span> <span class="hljs-string">Running</span> <span class="hljs-string">the</span> <span class="hljs-string">action</span>
    <span class="hljs-attr">steps:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">checkout</span>
        <span class="hljs-attr">uses:</span> <span class="hljs-string">actions/checkout@v4</span>
        <span class="hljs-attr">with:</span>
          <span class="hljs-attr">fetch-depth:</span> <span class="hljs-number">0</span>

      <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Running</span> <span class="hljs-string">Notio</span>
        <span class="hljs-attr">uses:</span> <span class="hljs-string">lnxpy/notio@main</span>
        <span class="hljs-attr">with:</span>
          <span class="hljs-attr">method:</span> <span class="hljs-string">generate-questions</span>
          <span class="hljs-attr">path:</span> <span class="hljs-string">questions/</span>
          <span class="hljs-attr">question_limit:</span> <span class="hljs-number">20</span>
          <span class="hljs-attr">include_answers:</span> <span class="hljs-literal">true</span>
          <span class="hljs-attr">hypermode_endpoint:</span> <span class="hljs-string">${{</span> <span class="hljs-string">secrets.HYPERMODE_ENDPOINT</span> <span class="hljs-string">}}</span>
          <span class="hljs-attr">hypermode_api_token:</span> <span class="hljs-string">${{</span> <span class="hljs-string">secrets.HYPERMODE_API_TOKEN</span> <span class="hljs-string">}}</span>

      <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Commiting</span>
        <span class="hljs-attr">uses:</span> <span class="hljs-string">EndBug/add-and-commit@v9</span>
        <span class="hljs-attr">with:</span>
          <span class="hljs-attr">default_author:</span> <span class="hljs-string">github_actions</span>
          <span class="hljs-attr">message:</span> <span class="hljs-string">'article updated'</span>
</code></pre>
<p>Now, whenever you publish or edit an article on Hashnode, Notio will begin processing it and perform the LLM operation you have specified.</p>
<h3 id="heading-example">Example</h3>
<p>I'm using Notio for my Hashnode blog. When I published this article, Notio generated 10 questions about the article and stored them in my Hashnode backup repository. You can visit it here.</p>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://github.com/lnxpy/hashnode">https://github.com/lnxpy/hashnode</a></div>
<p> </p>
<h3 id="heading-tech-stacks">Tech Stacks</h3>
<ul>
<li><p>PyAction: <a target="_blank" href="https://pyaction.imsadra.me/">https://pyaction.imsadra.me/</a></p>
</li>
<li><p>Hypermode: <a target="_blank" href="https://hypermode.com/">https://hypermode.com/</a></p>
</li>
</ul>
<h3 id="heading-useful-links">Useful Links</h3>
<ul>
<li><p>Notio repository: <a target="_blank" href="https://github.com/lnxpy/notio">https://github.com/lnxpy/notio</a></p>
</li>
<li><p>Hypermode model instance: <a target="_blank" href="https://github.com/lnxpy/notio-model">https://github.com/lnxpy/notio-model</a></p>
</li>
</ul>
<h3 id="heading-conclusion">Conclusion</h3>
<p>With the rapid growth of AI and LLMs, we'll see significant improvements in our CI cycles. We won't need to review and rewrite articles ourselves because we have AI!</p>
<p>Special thanks to Hypermode and Hashnode for setting up this awesome hackathon. 🍻</p>
]]></content:encoded></item><item><title><![CDATA[I Switched to Hashnode Docs]]></title><description><![CDATA[A few months ago, Hashnode launched a new app called "Docs", and I decided to move the documentation of one of my open-source projects to Hashnode.
https://pyaction.imsadra.me/docs
 
Working with it is as simple as writing blogs on Hashnode but in an...]]></description><link>https://blog.imsadra.dev/i-switched-to-hashnode-docs</link><guid isPermaLink="true">https://blog.imsadra.dev/i-switched-to-hashnode-docs</guid><category><![CDATA[Hashnode]]></category><category><![CDATA[docs]]></category><category><![CDATA[documentation]]></category><category><![CDATA[Documents]]></category><category><![CDATA[pyaction]]></category><category><![CDATA[GitHub]]></category><category><![CDATA[material]]></category><category><![CDATA[mkdocs]]></category><category><![CDATA[Python]]></category><category><![CDATA[github-actions]]></category><dc:creator><![CDATA[Sadra Yahyapour]]></dc:creator><pubDate>Sat, 23 Nov 2024 10:47:28 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1732354287948/1ffa69bc-4c81-4e71-8835-6d10ff1901ec.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>A few months ago, Hashnode launched a new app called "Docs", and I decided to move the documentation of one of my open-source projects to Hashnode.</p>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://pyaction.imsadra.me/docs">https://pyaction.imsadra.me/docs</a></div>
<p> </p>
<p>Working with it is as simple as writing blogs on Hashnode but in an organized way. I decided to write my fresh experience of using Docs for a public open-source tool.</p>
<h3 id="heading-i-shouldve-reconsidered-my-decision">I Should’ve Reconsidered My Decision</h3>
<p>As an open-source developer, I think I should’ve reconsidered my decision when I first decided to deploy on Hashnode Docs.</p>
<h4 id="heading-the-separation-of-docs-and-project">The separation of docs and project</h4>
<p>I’m feeling a few concerns about Hashnode Docs. The main reason is that I’m separating the documentation from my codebase and hosting it elsewhere.</p>
<p>It's like a double-edged sword. You can modify and deploy it very quickly, but only your team has access to the docs, and not everyone can contribute to it.</p>
<p>There is a "GitHub" feature that will be available soon, but I don't think it will be the same as keeping the documentation within the scope of the project itself. It seems more like a backup system for the documentation in my opinion.</p>
<h4 id="heading-lack-of-customizability">Lack of customizability</h4>
<p>I moved my documentation from <a target="_blank" href="https://squidfunk.github.io/mkdocs-material/">MkDocs Material</a>, which was highly customizable. It offered many plugins and extensions, and you could modify existing features, which was really nice.</p>
<p>I haven't seen these options on Hashnode. Remember, Hashnode Docs <em>isn't just for developers</em>. In my view, it's designed to let you document anything, regardless of the level of programming knowledge. On the other hand, tools like <a target="_blank" href="https://www.mkdocs.org/">MkDocs</a> and <a target="_blank" href="https://www.sphinx-doc.org/en/master/">Sphinx</a> are meant for developers who prefer to work directly in a text editor.</p>
<h4 id="heading-code-highlighting-feature">Code highlighting feature</h4>
<p>You include many code snippets in your docs, and this is another area where Hashnode could improve. The syntax highlighting feature is somewhat lacking, as it doesn't recognize all the keywords in some languages, which can be frustrating at first. Considering the fresh modern look of (<code>/callout</code>), the code snippets could be more visually appealing. I strongly feel that code snippets haven't changed much since the first release of Hashnode, and Hashnode Docs is still using the same setup.</p>
<h4 id="heading-including-badges-icons-and-in-line-images">Including badges, icons, and in-line images</h4>
<p>Including badges is another issue with Docs. I wish I could add some <a target="_blank" href="https://shields.io/">Shields.io</a> badges to my docs to show the current status of my project.</p>
<p>I tried to use tables to put badges in them but you can’t put images inside the table cells. I tried buttons, although I managed to put the badges in them but the looking wasn’t too good.</p>
<h4 id="heading-no-versioning-or-tagging-feature">No versioning or tagging feature</h4>
<p>There is no recovery feature yet. If you delete any part of your document, you can't retrieve it back. Tagging and versioning in documents are very important features. Fortunately, Hashnode offers "Revision History" for blogs. I hope they add the same functionality for documents soon, perhaps with the ability to tag specific states of the docs.</p>
<h4 id="heading-no-translation-feature">No translation feature</h4>
<p>If you want to translate your docs into another language, there is currently no feature available for that.</p>
<h3 id="heading-things-i-love-about-hashnode-docs">Things I Love About Hashnode Docs</h3>
<p>I switched to Hashnode Docs for a reason. Let's look at what really caught my attention and convinced me to move to Hashnode Docs.</p>
<h4 id="heading-new-commands-and-markdown-items">New commands and markdown items</h4>
<p>There are many features that help you present your docs effectively. You can use <code>/steps</code>, <code>/card</code>, <code>/accordion</code>, <code>/tabs</code>, and even <code>/button</code> to customize your docs and make the reading experience more engaging. None of these markdown items are available on blogs which makes docs more unique.</p>
<h4 id="heading-no-more-ci-pipeline-or-deployment">No more CI pipeline or deployment</h4>
<p>You don't need a CI pipeline to deploy your documentation after each change, which allows for continuous publication—this is fantastic. Additionally, making changes to your documents is easy and straightforward.</p>
<h4 id="heading-seo-is-the-next-level">SEO is the next level</h4>
<p>In a word, Hashnode’s SEO team is outstanding. Even if you try to harm your docs' SEO, Hashnode still manages to share your docs with the world. It's amazing how powerful this is.</p>
<h4 id="heading-perfect-choice-for-open-source-projects">Perfect choice for open-source projects</h4>
<p>If you're looking for a platform to publish the documentation for your open-source project, Hashnode Docs is a great choice. It includes features that read various parameters from the GitHub repository you specify, such as the number of stars your repository has.</p>
<h4 id="heading-fast-to-navigate-and-engage">Fast to navigate and engage</h4>
<p>Its routing system is quick, letting you move instantly between pages and documents without waiting for the full page to load. Only the content section reloads.</p>
<h3 id="heading-final-word">Final Word</h3>
<p>Keep in mind, it's only been a few months since Hashnode Docs was released. So, many of my complaints and concerns might disappear in a few months.</p>
]]></content:encoded></item><item><title><![CDATA[Generic Typing in Python]]></title><description><![CDATA[Since Python introduced the ability to add generic types to functions, the language has become much more type-friendly and encourages you to follow this convention for more maintainable code. Obviously, this will lead to a better development experien...]]></description><link>https://blog.imsadra.dev/generic-typing-in-python</link><guid isPermaLink="true">https://blog.imsadra.dev/generic-typing-in-python</guid><category><![CDATA[Python Generics]]></category><category><![CDATA[Type Generics]]></category><category><![CDATA[python typing]]></category><category><![CDATA[Python]]></category><category><![CDATA[Python 3]]></category><category><![CDATA[python beginner]]></category><category><![CDATA[typing]]></category><category><![CDATA[Types]]></category><category><![CDATA[generics]]></category><category><![CDATA[TypeScript Generics]]></category><category><![CDATA[pydantic]]></category><category><![CDATA[Mypy]]></category><category><![CDATA[type checking]]></category><dc:creator><![CDATA[Sadra Yahyapour]]></dc:creator><pubDate>Sun, 03 Nov 2024 21:08:56 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1730558938928/8df0d8ab-a51d-48e3-a1e4-cdc9c0965b9c.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Since Python introduced the ability to add generic types to functions, the language has become much more type-friendly and encourages you to follow this convention for more maintainable code. Obviously, this will lead to a better development experience.</p>
<p>Stay with me as we discuss the basics of Generic Typing in Python and some best practices at the end.</p>
<h3 id="heading-what-does-generic-typing-help-with">What Does Generic Typing Help With?</h3>
<p>This fascinating feature allows you to make type-checking more dynamic. Sometimes, we don't know the types of input parameters.</p>
<p>Before we continue, I encourage you to take a moment to try solving this problem.</p>
<p><strong>How would you type-hint a function that takes one parameter and that’s a list of objects of any type (</strong><code>int</code><strong>,</strong> <code>str</code><strong>,</strong> <code>User</code><strong>,</strong> <code>Book</code><strong>, etc) and returns just one random object of that type?</strong></p>
<h3 id="heading-any-turns-off-the-type-checking"><code>Any</code> Turns off the Type-Checking!</h3>
<p>During that challenge, you might have considered the following thought processes. Let's take a look.</p>
<h4 id="heading-thought-process-1">Thought Process 1:</h4>
<p>My function should take one parameter, which is a list of any type (<code>List[Any]</code>), and return a random single item from that list (<code>Any</code>).</p>
<pre><code class="lang-python"><span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">select</span>(<span class="hljs-params">items: List[Any]</span>) -&gt; Any:</span>
    <span class="hljs-keyword">return</span> random.choice(items)
</code></pre>
<h4 id="heading-answer">Answer:</h4>
<p>This is incorrect. Although your type-checker might still pass the type-checking, there is a significant mistake in this typing. <code>List[Any]</code> means a list that can be empty or filled with items of any type. This allows the function to be called in a way that violates the requirement that the parameter should be a list of items of the same type.</p>
<pre><code class="lang-python">select([<span class="hljs-number">1</span>, <span class="hljs-string">"b"</span>, <span class="hljs-string">"c"</span>, <span class="hljs-string">"d"</span>, <span class="hljs-literal">False</span>, <span class="hljs-number">3.14</span>])
</code></pre>
<p>The type <code>Any</code> somehow neutralizes the type-checker and hides its pointer from being type-checked.</p>
<pre><code class="lang-python">item: Any = ...  <span class="hljs-comment"># just like we've put `type: ignore` here</span>
</code></pre>
<p>Be very careful with <code>Any</code> and make sure you really need its effect.</p>
<hr />
<h4 id="heading-thought-process-2">Thought Process 2:</h4>
<p>I can explicitly define what type my parameter can take. That way, not only I haven’t used the type <code>Any</code>, I have explicitly defined the input and output type.</p>
<pre><code class="lang-python"><span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">select</span>(<span class="hljs-params">items: List[int | str | bool | float]</span>) -&gt; int | str | bool | float:</span>
    <span class="hljs-keyword">return</span> random.choice(items)
</code></pre>
<h4 id="heading-answer-1">Answer:</h4>
<p>This is a better solution, but it still doesn't fully meet the challenge where we said the function parameter could be anything. It might not be a string or an integer, for example. This function only works for the built-in types. What if I wanted to call it like this?</p>
<pre><code class="lang-python"><span class="hljs-keyword">from</span> typing <span class="hljs-keyword">import</span> NewType

<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">select</span>(<span class="hljs-params">...</span>):</span>
    .

User = NewType(<span class="hljs-string">"User"</span>, str)
users = [User(<span class="hljs-string">"Abby"</span>), User(<span class="hljs-string">"Chris"</span>), User(<span class="hljs-string">"Nick"</span>)]

user = select(users)
</code></pre>
<pre><code class="lang-plaintext">file.py:12: error: Argument 1 to "select" has incompatible type "list[User]"; expected "list[int | str | bool | float]"  [arg-type]
</code></pre>
<p>Not only did our type-checker (<code>mypy</code>) fail to check it, but it also doesn't meet the challenge.</p>
<h3 id="heading-using-generic-typing">Using Generic Typing</h3>
<p>It’s a newly added syntax to Python. Yet I haven’t seen many people using it as they are still releasing for older Python versions like 3.8, 3.9, 3.10, and 3.11. Generic typing was introduced in Python 3.12.</p>
<p>Now, let’s talk about how we can use this feature to solve the challenge we described earlier.</p>
<h4 id="heading-structure-and-syntax">Structure and Syntax</h4>
<p>The structure is quite simple. You define new type variables and use them accordingly when defining the function. Just like defining variables that don't have a value at the time of declaration, you define placeholders in brackets right before the parentheses. (Here, I've declared only one variable/placeholder named <code>T</code>)</p>
<pre><code class="lang-python"><span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">FUNCTION_NAME</span>[<span class="hljs-title">TYPES</span>](<span class="hljs-params">ARGS..</span>) -&gt; TYPE:</span>
</code></pre>
<pre><code class="lang-python"><span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">select</span>[<span class="hljs-title">T</span>](<span class="hljs-params">items...</span></span>
</code></pre>
<p>And you can use it later when specifying the type of parameters and the return value of your function.</p>
<pre><code class="lang-python"><span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">select</span>[<span class="hljs-title">T</span>](<span class="hljs-params">items: List[T]</span>) -&gt; T:</span>
    ...
</code></pre>
<p>Now, let’s define the above function. It has declared one <code>T</code> type variable and says, the <code>items</code> parameter can be a list of anything and I’ll consider that as <code>T</code> for now. That <code>T</code> is the placeholder for any time that items might have.</p>
<pre><code class="lang-python">select(items=[<span class="hljs-number">1</span>, <span class="hljs-number">2</span>, <span class="hljs-number">3</span>, <span class="hljs-number">4</span>, <span class="hljs-number">5</span>]) <span class="hljs-comment"># T=int   &amp;  List[T] = List[int]</span>
select(items=[<span class="hljs-string">"a"</span>, <span class="hljs-string">"b"</span>, <span class="hljs-string">"c"</span>]) <span class="hljs-comment"># T=str   &amp;  List[T] = List[str]</span>
select(items=[<span class="hljs-number">1.2</span>, <span class="hljs-number">1.3</span>, <span class="hljs-number">1.4</span>]) <span class="hljs-comment"># T=float &amp;  List[T] = List[Float]</span>

<span class="hljs-comment"># Therefore, we have..</span>
users = [User(<span class="hljs-string">"Abby"</span>), User(<span class="hljs-string">"Chris"</span>), User(<span class="hljs-string">"Nick"</span>)]
select(items=users)           <span class="hljs-comment"># T=User  &amp;  List[T] = List[User]</span>
</code></pre>
<p>As you can see, the type represented by <code>T</code> changes dynamically based on the values we put in the <code>items</code> parameter. We can now explain the <code>select()</code> function like this:</p>
<ul>
<li><p>Function definition: <code>def select[T](items: List[T]) -&gt; T:</code></p>
</li>
<li><p>Type variable(s)/placeholder(s): <code>T</code></p>
</li>
<li><p>Input type: List of any similar type</p>
</li>
<li><p>Output type: A type similar to one of the list items</p>
</li>
</ul>
<h3 id="heading-lets-practice">Let’s Practice</h3>
<p>P1: Now, let's say our function takes only one item and returns a tuple containing three of that item.</p>
<p>Answer:</p>
<pre><code class="lang-python"><span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">convert</span>[<span class="hljs-title">B</span>](<span class="hljs-params">item: B</span>) -&gt; Tuple[B, B, B]:</span>
    <span class="hljs-keyword">return</span> (item, item, item)
</code></pre>
<blockquote>
<p>Hint: We use the letter "T" as the default primary type placeholder by convention, but you can choose any name you prefer as in the above example, we used the letter “B”.</p>
</blockquote>
<p>P2: Let’s say we need a function that takes two parameters of different types and returns a tuple containing those items.</p>
<p>Answer:</p>
<pre><code class="lang-python"><span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">convert</span>[<span class="hljs-title">A</span>, <span class="hljs-title">B</span>](<span class="hljs-params">first: A, second: B</span>) -&gt; Tuple[A, B]:</span>
    <span class="hljs-keyword">return</span> (first, second)
</code></pre>
<p>P3: A function that takes an ID and a list of dictionaries, where each dictionary has exactly three pairs (<code>id</code>, <code>username</code>, and <code>is_verified</code>), and returns the item that matches the given ID.</p>
<blockquote>
<p>Hint: Avoid defining the type <code>Users</code> in this example to better understand Generic Typing. Defining it would make Generics seem unnecessary in this case.</p>
</blockquote>
<p>Answer: You figure it out! :))</p>
<h3 id="heading-conclusion">Conclusion</h3>
<p>Generic Typing is a pretty new and hot topic in Python. It's helpful when defining functions and classes. In this article, we explored the function side of this powerful syntax. Hopefully, you found it useful.</p>
]]></content:encoded></item><item><title><![CDATA[Display Your Package Download Rate on GitHub]]></title><description><![CDATA[In this quick tutorial, I’ll show you a GitHub action that lets you add a small, cool-looking chart badge that shows your Python packages' download rate over the past weeks. This method is completely free and open-source.
https://github.com/lnxpy/pyp...]]></description><link>https://blog.imsadra.dev/display-your-package-download-rate-on-github</link><guid isPermaLink="true">https://blog.imsadra.dev/display-your-package-download-rate-on-github</guid><category><![CDATA[GitHub]]></category><category><![CDATA[github-actions]]></category><category><![CDATA[Open Source]]></category><category><![CDATA[cicd]]></category><category><![CDATA[ci-cd]]></category><category><![CDATA[Python]]></category><category><![CDATA[python projects]]></category><category><![CDATA[pypi]]></category><category><![CDATA[charts]]></category><category><![CDATA[plotly]]></category><category><![CDATA[pyaction]]></category><dc:creator><![CDATA[Sadra Yahyapour]]></dc:creator><pubDate>Fri, 11 Oct 2024 07:38:22 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1728628884162/9c2665e0-d3e6-4100-a39b-082690426d51.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>In this quick tutorial, I’ll show you a GitHub action that lets you add a small, cool-looking chart badge that shows your Python packages' download rate over the past weeks. This method is completely free and open-source.</p>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://github.com/lnxpy/pypi-chart-badge">https://github.com/lnxpy/pypi-chart-badge</a></div>
<p> </p>
<p>Here's a quick preview of what we'll have by the end of this tutorial. Take a look at this example below.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1728629377874/b996dc85-d4d0-4996-9244-fbd60549ebf6.png" alt class="image--center mx-auto" /></p>
<p>In the example above, <a target="_blank" href="https://github.com/lnxpy/pyaction">PyAction</a> is a Python library (which I maintain and used to create this project), and I used the action mentioned earlier to create that green graph badge that displays the PyPI download rate of the package over the past 15 days.</p>
<p>Follow the following steps to create and show this badge on your own repositories.</p>
<h4 id="heading-1-create-a-new-ci-pipeline">1. Create a new CI pipeline</h4>
<p>To use this action, you need a dedicated CI pipeline. To do this, create a <code>.yml</code> or <code>.yaml</code> file in the <code>.github/workflows/</code> directory of your repository. (If this directory doesn’t exist, create it)</p>
<p>I’m going to name it <code>pypi_chart.yml</code> so its full path would be <code>.github/workflows/pypi_chart.yml</code> in my repository.</p>
<h4 id="heading-2-paste-this-action-usage-in-the-yml-file">2. Paste this action usage in the yml file</h4>
<p>Paste the following pipeline usage in the yml file of yours.</p>
<pre><code class="lang-yaml"><span class="hljs-attr">name:</span> <span class="hljs-string">Update</span> <span class="hljs-string">the</span> <span class="hljs-string">PyPI</span> <span class="hljs-string">chart</span> <span class="hljs-string">badge</span>

<span class="hljs-attr">on:</span>
  <span class="hljs-attr">schedule:</span>
    <span class="hljs-bullet">-</span> <span class="hljs-attr">cron:</span> <span class="hljs-string">"0 0 1 * *"</span>  <span class="hljs-comment"># &lt;= runs every month (cron pattern)</span>
  <span class="hljs-attr">workflow_dispatch:</span>

<span class="hljs-attr">jobs:</span>
  <span class="hljs-attr">update-chart-badge:</span>
    <span class="hljs-attr">name:</span> <span class="hljs-string">Updating</span> <span class="hljs-string">the</span> <span class="hljs-string">pypi</span> <span class="hljs-string">chart</span> <span class="hljs-string">badge</span>
    <span class="hljs-attr">runs-on:</span> <span class="hljs-string">ubuntu-latest</span>

    <span class="hljs-attr">steps:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Checkout</span>
        <span class="hljs-attr">uses:</span> <span class="hljs-string">actions/checkout@v4</span>

      <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Updating</span> <span class="hljs-string">the</span> <span class="hljs-string">badge</span>
        <span class="hljs-attr">uses:</span> <span class="hljs-string">lnxpy/pypi-chart-badge@v1</span>
        <span class="hljs-attr">with:</span>
          <span class="hljs-attr">package_name:</span> <span class="hljs-string">&lt;PACKAGE-NAME&gt;</span> <span class="hljs-comment"># &lt;= here goes your package name</span>

      <span class="hljs-bullet">-</span> <span class="hljs-attr">uses:</span> <span class="hljs-string">EndBug/add-and-commit@v9</span>
        <span class="hljs-attr">with:</span>
          <span class="hljs-attr">default_author:</span> <span class="hljs-string">github_actions</span>
          <span class="hljs-attr">message:</span> <span class="hljs-string">'chart badge updated'</span>
</code></pre>
<p>This pipeline will run every month to create or update the badge. You can use <a target="_blank" href="https://crontab.guru/">any cron pattern</a> and replace it with the one on line 5.</p>
<p>Since this pipeline includes <code>workflow_dispatch:</code>, you can run it manually at any time. We will first run it manually to generate the badge, and then let it run automatically for future executions.</p>
<p>Don't forget to update the <code>package_name</code> parameter on line 20 with the name of your Python package.</p>
<p>On the last line, you can set any commit message you want when the badge is updated in your repository.</p>
<h4 id="heading-3-giving-access-to-the-workflow">3. Giving access to the workflow</h4>
<p>Now, you need to grant write access to the workflow so it can push the updated badge to your repository.</p>
<p>Simply on the repository page, navigate to “<strong>Settings”</strong>. From the side panel, choose “<strong>Actions</strong>” &gt; “<strong>General</strong>”.</p>
<p>Scroll all the way down until you find “<strong>Workflow permissions</strong>”. Select “<strong>Read and write permissions</strong>" and hit save.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1728630704160/f8ca2ae2-4748-4524-9da0-067c0da65ec3.png" alt class="image--center mx-auto" /></p>
<p>Now, your workflow has writing access to your repository.</p>
<h4 id="heading-4-testing">4. Testing</h4>
<p>Go to the “<strong>Actions</strong>” panel of your repository. From the side panel, choose the “<strong>Update the PyPI chart badge</strong>” workflow and run it.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1728631171988/b4ad7495-772a-4c9d-8f56-46da0af3fbdd.png" alt class="image--center mx-auto" /></p>
<p>After a few seconds, you’ll see the <code>.pypi_chart/badge.svg</code> is added to your repository. Now if you edit your <code>README.md</code> file and add that SVG file, you’ll see the badge showing up perfectly.</p>
<pre><code class="lang-markdown"><span class="hljs-section"># Package ![<span class="hljs-string">badge</span>](<span class="hljs-link">.pypi_chart/badge.svg</span>)</span>
</code></pre>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1728631569416/0a6378bf-2d16-4d7a-8790-32896eae37a5.png" alt class="image--center mx-auto" /></p>
<h4 id="heading-options">Options</h4>
<p>There are a bunch of options that you can use to customize the badge. Copy and paste any of the following options into your pipeline.</p>
<pre><code class="lang-yaml">      <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Updating</span> <span class="hljs-string">the</span> <span class="hljs-string">badge</span>
        <span class="hljs-attr">uses:</span> <span class="hljs-string">lnxpy/pypi-chart-badge@v1</span>
        <span class="hljs-attr">with:</span>
          <span class="hljs-attr">package_name:</span> <span class="hljs-string">&lt;PACKAGE-NAME&gt;</span>

          <span class="hljs-comment"># badge width size in px (default: 60)</span>
          <span class="hljs-attr">badge_width:</span> <span class="hljs-number">200</span>

          <span class="hljs-comment"># badge height size in px (default: 20)</span>
          <span class="hljs-attr">badge_height:</span> <span class="hljs-number">65</span>

          <span class="hljs-comment"># plot color (CSS color or HEX)</span>
          <span class="hljs-attr">badge_color:</span> <span class="hljs-string">'#ff05e6'</span>

          <span class="hljs-comment"># days limit (default: 15)</span>
          <span class="hljs-attr">days_limit:</span> <span class="hljs-number">25</span>

          <span class="hljs-comment"># output path (default: .pypi_chart/)</span>
          <span class="hljs-attr">output_path:</span> <span class="hljs-string">media/</span>

          <span class="hljs-comment"># output file name (default: badge.svg)</span>
          <span class="hljs-attr">file_name:</span> <span class="hljs-string">badge.png</span>
</code></pre>
<h4 id="heading-links">Links</h4>
<ul>
<li><p>Action repository: <a target="_blank" href="https://github.com/lnxpy/pypi-chart-badge">https://github.com/lnxpy/pypi-chart-badge</a></p>
</li>
<li><p>Action on GitHub marketplace: <a target="_blank" href="https://github.com/marketplace/actions/pypi-chart-badge">https://github.com/marketplace/actions/pypi-chart-badge</a></p>
</li>
<li><p>PyAction repository: <a target="_blank" href="https://github.com/lnxpy/pyaction">https://github.com/lnxpy/pyaction</a></p>
</li>
<li><p>PyAction docs: <a target="_blank" href="https://pyaction.imsadra.me">https://pyaction.imsadra.me</a></p>
</li>
</ul>
]]></content:encoded></item><item><title><![CDATA[How I Made It to the 2nd Product of the Day on Product Hunt]]></title><description><![CDATA[Months ago, I participated in a Hashnode hackathon where I had to build a project powered by MindsDB. I ended up submitting a project named "Hey!". It's an open-source CLI tool that allows users to interact with ChatGPT through their terminals and CM...]]></description><link>https://blog.imsadra.dev/how-i-made-it-to-the-2nd-product-of-the-day-on-product-hunt</link><guid isPermaLink="true">https://blog.imsadra.dev/how-i-made-it-to-the-2nd-product-of-the-day-on-product-hunt</guid><dc:creator><![CDATA[Sadra Yahyapour]]></dc:creator><pubDate>Sat, 31 Aug 2024 05:48:27 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1724603395333/5585c779-5949-4b42-8d04-880a03ac7637.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Months ago, I participated in a Hashnode hackathon where I had to build a project powered by MindsDB. I ended up submitting a project named "Hey!". It's an open-source CLI tool that allows users to interact with ChatGPT through their terminals and CMDs.</p>
<p>Here is the article showcasing Hey:</p>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://blog.imsadra.me/introducing-hey-your-ai-powered-pair-programming-friend">https://blog.imsadra.me/introducing-hey-your-ai-powered-pair-programming-friend</a></div>
<p> </p>
<h3 id="heading-i-was-the-only-user-of-my-product">I Was the Only User of My Product 😞</h3>
<p>At first, I was the only user. Despite sharing it on every social media platform, I felt disappointed when nobody noticed it. No one seemed to care about what I was doing, and my repo had only 8 stars (mine included).</p>
<p>Fortunately, I had a network of smart, awesome people. Since Hey was a hackathon idea, I felt hesitant about sharing posts about it after the event ended. I made something for the hackathon, and it did its job. Everyone who participated in the hackathon seemed to enjoy it at the time. But do I really want to keep maintaining the project? Is my network going to keep ignoring it? Does anybody else want to hear about Hey?</p>
<h3 id="heading-i-gave-it-a-shot">I Gave It a Shot 🎯</h3>
<p>Hey was presented very well. In fact, I believe the intro video about Hey was one of the key reasons for its success during the hackathon. I couldn't let that masterpiece fade away. I had to show it off once again!</p>
<p>Here is the intro:</p>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://www.youtube.com/watch?v=fhO34PVa-38">https://www.youtube.com/watch?v=fhO34PVa-38</a></div>
<p> </p>
<p>After the hackathon ended, I was maintaining only a few repositories. So, I decided to work on multiple projects. I planned to add some cool features to Hey and improve it a bit thinking that might attract some attention.</p>
<p>The very first version of Hey was difficult to set up. As a user, I would never install it!</p>
<p>If you wanted to use Hey, you had to go through several steps, like creating an account on MindsDB, copying and pasting some SQL queries, setting up the LLM model, and installing Hey. Developers might be okay with this, but what about anyone else?</p>
<p>The first step was to improve the accessibility. I developed Hey top to bottom in 4 days. I assumed it would be a one-time development process, so I built it just to work.</p>
<p>Fortunately, its codebase wasn't too large to refactor. I managed to redesign it using a completely different approach for connecting to the LLM. The project saw massive performance improvements in different aspects.</p>
<ul>
<li><p>Accessibility: Now, all you need to run it is a token!</p>
</li>
<li><p>Performance: The new redesign resulted in much faster response time.</p>
</li>
<li><p>Design: The new codebase design allowed me to focus more on the UI and UX.</p>
</li>
</ul>
<h3 id="heading-what-to-do-next">What To Do Next 🤔</h3>
<p>I had posted about the initial version of Hey with my network before, but it didn't get much attention as I said. Maybe it was because it was an app created for a hackathon, or perhaps they found it a bit hard to use. Either way, it didn't seem promising to me.</p>
<p>I had to try something new, something fair with a decent chance of engagement. I was familiar with <a target="_blank" href="https://producthunt.com">Product Hunt</a>. I knew people used it to share their startup ideas or demonstrate an MVP. I didn't realize that open-source projects were also part of that community.</p>
<p>So I went for it!</p>
<h3 id="heading-i-had-almost-no-strategy">I Had Almost No Strategy 🤯</h3>
<p>At first, I didn't know much about the Product Hunt platform. I assumed it was mainly about the number of followers and the network you had, which would help you get more attention and engagement. As a newcomer, I thought my project wouldn't stand a chance of being noticed by others so I didn't take it seriously and didn’t put %100 of myself into it. Soon I realized that it made my first mistake.</p>
<p>I was still a bit upset about the lack of engagement Hey received on other social platforms before, so I didn't plan any specific strategy for this one and just went through it.</p>
<p>I looked at some of the top-voted products of the day to see how they were designed. Meanwhile, I wondered, what if my project could make it up there?</p>
<p>I also read a few articles about best practices for launching on Product Hunt to at least have an idea of what I was doing. Noticed these key points.</p>
<ul>
<li><p>Choose a simple precise slogan for your product.</p>
</li>
<li><p>Use a graphic designing tool to create a few templates for its homepage.</p>
</li>
<li><p>Your product's logo and slogan matter the most.</p>
</li>
<li><p>Keep your product's description short, simple, and informative.</p>
</li>
</ul>
<p>So I followed almost everything I learned from those resources, but I still had some ignorance!</p>
<p>I launched it, posted about it on LinkedIn and Twitter, and then went to bed.</p>
<p>This is pretty much the strategy that I followed. It took me about three hours to launch. Looking back, I realize I could have done way better with more planning. Some entrepreneurs wait to launch at specific times, even if their product has been ready for months, intentionally missing out on potential income their product could have made.</p>
<p>I'm not an entrepreneur, and this article isn't the best place to learn about entrepreneurship. I'm just sharing my experience with one (seemingly) successful launch on Product Hunt. 😅</p>
<h3 id="heading-product-hunt-is-a-fair-deal">Product Hunt Is a Fair Deal 🤌</h3>
<p>I woke up the next day and turned off the airplane mode of my phone. Immediately, I was flooded with dozens of notifications on LinkedIn and X. So many email notifications from Product Hunt. I had over 30 new messages in my inbox. At first, I thought these were bots crawling Product Hunt to message creators and advertise their work. This seemed reasonable to me, so I didn't take it seriously.</p>
<p>At first, I didn't expect any difference between launching on Product Hunt and sharing a post about the product on social media. That's why I was surprised when I opened my browser to Google something and suddenly saw my project listed as the 3rd product of the day with 110 upvotes!!</p>
<p><img src="https://pbs.twimg.com/media/GVwOuW9XMAAUanP?format=jpg&amp;name=medium" alt="Image" /></p>
<p>I was really surprised. I had put only three hours for this launch and didn't even put in much effort, but it still did great. I shared this image everywhere and tagged all my friends to support and check it out. I even reshared the intro video to get more engagement. Now, I was seeing it work! 🥳</p>
<p>Luckily, with the help of my network, I even managed to reach 1st place for a couple of hours but eventually secured the 2nd spot, earning the title <strong>"The 2nd Product of the Day"</strong>. This was more than enough for me, especially since I had only one follower when I launched.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1725079808558/1511858c-ebf2-4394-b01f-d264be881c91.png" alt class="image--center mx-auto" /></p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1725079841422/643a0838-7a36-4e6a-9a83-21709ed882b7.png" alt class="image--center mx-auto" /></p>
<p>I was amazed at how (fairly) competitive Product Hunt is. Everyone has an equal chance to showcase their products. (Let's ignore those who pay for subscriptions)</p>
<p>With a bit of hard work, you can succeed. Just like how I did it with only 1 follower and almost no engagement at the time on the platform. Of course, you’re idea and implementation matter. 🙃</p>
<h3 id="heading-getting-upvotes-and-attentions">Getting Upvotes and Attentions ⬆️</h3>
<p>Your product's logo, name, and slogan are the first things users will notice, so you should put serious effort into them. Use your design skills to <strong>convince them to click on it</strong>. If you're not skilled enough in those areas, let others do them for you. Ask your friends. Luckily, I had a bit of knowledge.</p>
<p>When a user opens your product page, the first thing that he/she notices is the images and media you've attached. My YouTube video did a good job there. It was less than two minutes long and effectively conveyed the message to the users. So, upload a video if you can. (It doesn't need to be recorded with the best high-tech microphone and laptop)</p>
<p>If you can't upload a video, design eye-catching images to convey your message. Don't overload them with information. They should be bite-sized but informative, using just a bold keyword or two. Use graphical elements to highlight and demonstrate the strengths of your product.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1725080313501/0ffd3754-be68-4fdc-b258-2edffe1c095b.png" alt class="image--center mx-auto" /></p>
<p>The next thing that catches their attention is the description of your product. This is your most valuable shot right there as users will decide here whether to upvote and comment or close the tab and never return!</p>
<p>Always start strong in the first paragraph. Grab their attention and convince them to keep reading. Most people get bored while reading, so I kept it shot, very simple, and informative. If your product has a longer description, make sure to begin with your best sentences. It's markdown-supported, so remember to use bullet points and emojis.</p>
<p>One thing that I didn’t know about at the time was that whenever someone launches on Product Hunt, he also writes the first comment on the product and talks about the idea and puts more information there. I’d highly recommend that if your product description is too long.</p>
<h3 id="heading-what-then">What Then 🔮</h3>
<p>Well, I don't know what to do then as for me, it's the first time I'm experiencing "<em>my then"</em>. I've got so many comments on my launch. So many feature requests.</p>
<p>The only expectation I have is to see fewer engagements in my future launches. Usually, the first launch makes the biggest impact unless you take a major step forward and offer something truly impressive, like how OpenAI introduced GPT-4o. Therefore, I need to attract more people from other fields while also working on cool features for the next launch.</p>
<p>Here I've listed the steps that I'd take for now.</p>
<ul>
<li><p>Expand the project to cover more areas and engage people from both tech and non-tech fields.</p>
</li>
<li><p>Work on the new feature requests.</p>
</li>
<li><p>Demonstrate other aspects of my project by creating more content.</p>
</li>
<li><p>Ease the accessibility.</p>
</li>
</ul>
<p>These steps seem to be positively effective. They might be the worst decisions to make right now, I don't know. But I'm not experienced and I don't have time to study <em>"Things to do after a successful launch on Product Hunt"</em>. Most importantly, I wouldn't blame myself if I fail because it's my first time and I'm seeking experience.</p>
<blockquote>
<p>I can learn from failure, not success. Success only makes me temporarily happy, but failure teaches me the path to lasting happiness.</p>
</blockquote>
<h3 id="heading-the-last-word">The Last Word ✍️</h3>
<p>Always take your shot. If it doesn't work, try again. Fortunately, you have an unlimited supply of arrows. One day, you'll see it hit.</p>
<p>Thank you for reading this article. I've included links to my product here. Please take a look and share your thoughts.</p>
<ul>
<li><p>Hey! on Product Hunt: <a target="_blank" href="https://www.producthunt.com/products/hey-30620d3c-3330-4c6a-86cd-8cf9930a2490/launches">https://www.producthunt.com/products/hey-30620d3c-3330-4c6a-86cd-8cf9930a2490/launches</a></p>
</li>
<li><p>Hey! on GitHub: <a target="_blank" href="https://github.com/lnxpy/hey">https://github.com/lnxpy/hey</a></p>
</li>
</ul>
]]></content:encoded></item><item><title><![CDATA[Python 3.13 - New Features & Deprecations]]></title><description><![CDATA[We introduced several cool typing and interactive interpreting features in Python 3.12. I discussed all of them here:
https://blog.imsadra.me/python312-is-happening
 
Before we go ahead, this is the full patch note about this new release.The biggest ...]]></description><link>https://blog.imsadra.dev/python-313-new-features-deprecations</link><guid isPermaLink="true">https://blog.imsadra.dev/python-313-new-features-deprecations</guid><category><![CDATA[python 3.13]]></category><category><![CDATA[3.13]]></category><category><![CDATA[3.12]]></category><category><![CDATA[python 3.12]]></category><category><![CDATA[Python]]></category><category><![CDATA[Python 3]]></category><category><![CDATA[newrelease]]></category><category><![CDATA[versioning]]></category><category><![CDATA[update ]]></category><category><![CDATA[features]]></category><category><![CDATA[deprecation]]></category><category><![CDATA[python beginner]]></category><category><![CDATA[python projects]]></category><dc:creator><![CDATA[Sadra Yahyapour]]></dc:creator><pubDate>Fri, 16 Aug 2024 01:24:48 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1723555843235/a2ef4e78-aea9-4318-ab42-de2b71c4ca30.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>We introduced several cool typing and interactive interpreting features in Python 3.12. I discussed all of them here:</p>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://blog.imsadra.me/python312-is-happening">https://blog.imsadra.me/python312-is-happening</a></div>
<p> </p>
<p>Before we go ahead, <a target="_blank" href="https://docs.python.org/3.13/whatsnew/3.13.html">this</a> is the full patch note about this new release.<br /><em>The biggest change</em> introduced in the early 3.13 patch notes is that the Global Interpreter Lock (GIL) is now droppable, and you can disable it in certain cases!</p>
<h3 id="heading-1-gil-amp-jit">1. GIL &amp; JIT</h3>
<p>CPython will run with the GIL disabled when configured using the <code>--disable-gil</code> option at build time. To check if the current interpreter is configured with <code>--disable-gil</code>, run the following command:</p>
<pre><code class="lang-bash">... -c <span class="hljs-string">"import sys; sysconfig.get_config_var('Py_GIL_DISABLED')"</span>
</code></pre>
<p>What's more, there will be an experimental version of a just-in-time compiler (JIT) that is disabled by default. You can use as easy as passing an option or by creating your own specific build of CPython 3.13.</p>
<p>It's been quite a while since PyPy introduced its high-performance garbage collector along a JIT compiler. These features have shown great promises.</p>
<p>All of this leads us to realize that CPython 3.14 will be a significant performance improvement, not only for users but also for all frameworks built on top of Python.</p>
<p>Now, without further ado, let's talk about some of the changes.</p>
<h3 id="heading-2-a-better-interactive-interpreter">2. A Better Interactive Interpreter</h3>
<p>The new interpreter provides more detailed error messages and tracebacks, helping you catch bugs and issues even faster. They are more colorful. You can disable this behavior by setting <code>PYTHON_COLORS</code> and <code>NO_COLOR</code> environment variables.</p>
<pre><code class="lang-bash">PYTHON_COLORS=0 python3.13 main.py
</code></pre>
<p>If you write a script inside a <code>.py</code> file with a file name similar to one of the standard library names, such as <code>random</code>, you would typically get a circular import error.</p>
<pre><code class="lang-bash">$ python3.12 random.py
Traceback (most recent call last):
  File <span class="hljs-string">"/home/random.py"</span>, line 1, <span class="hljs-keyword">in</span> &lt;module&gt;
    import random; <span class="hljs-built_in">print</span>(random.randint(5))
    ^^^^^^^^^^^^^
  File <span class="hljs-string">"/home/random.py"</span>, line 1, <span class="hljs-keyword">in</span> &lt;module&gt;
    import random; <span class="hljs-built_in">print</span>(random.randint(5))
                         ^^^^^^^^^^^^^^
AttributeError: partially initialized module <span class="hljs-string">'random'</span> has no attribute <span class="hljs-string">'randint'</span> (most likely due to a circular import)
</code></pre>
<p>But now, you get a more precise exception.</p>
<pre><code class="lang-bash">$ python3.13 random.py
Traceback (most recent call last):
  File <span class="hljs-string">"/home/random.py"</span>, line 1, <span class="hljs-keyword">in</span> &lt;module&gt;
    import random; <span class="hljs-built_in">print</span>(random.randint(5))
    ^^^^^^^^^^^^^
  File <span class="hljs-string">"/home/random.py"</span>, line 1, <span class="hljs-keyword">in</span> &lt;module&gt;
    import random; <span class="hljs-built_in">print</span>(random.randint(5))
                        ^^^^^^^^^^^^^^
AttributeError: module <span class="hljs-string">'random'</span> has no attribute <span class="hljs-string">'randint'</span> (consider renaming <span class="hljs-string">'/home/random.py'</span> since it has the same name as the standard library module named <span class="hljs-string">'random'</span> and the import system gives it precedence)
</code></pre>
<p>The same rule applies to third-party packages.</p>
<p>If you call a function with an incorrect parameter name, the interpreter will guess what you meant and suggest the correct name.</p>
<pre><code class="lang-python-repl"><span class="hljs-meta">&gt;&gt;&gt;</span> <span class="python"><span class="hljs-string">"better error messages!"</span>.split(max_split=<span class="hljs-number">1</span>)</span>
Traceback (most recent call last):
  File "&lt;stdin&gt;", line 1, in &lt;module&gt;
    "better error messages!".split(max_split=1)
    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^
TypeError: split() got an unexpected keyword argument 'max_split'. Did you mean 'maxsplit'?
</code></pre>
<h3 id="heading-3-no-more-functions-in-repl">3. No More Functions in REPL</h3>
<p>Now, you can call <code>help()</code>, <code>exit()</code>, and <code>quit()</code> commands without the parentheses.</p>
<h3 id="heading-4-deprecation-warnings">4. Deprecation Warnings</h3>
<p>The <code>warning</code> standard library is used to alert developers about deprecations, future removals, and similar issues. In Python 3.13, there is a <code>@deprecation</code> decorator that allows you to add deprecation warning messages to any of your components.</p>
<pre><code class="lang-python"><span class="hljs-keyword">from</span> warnings <span class="hljs-keyword">import</span> deprecated

<span class="hljs-meta">@deprecated("Use PayPalPaymentProcessor instead")</span>
<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">PaymentProcessor</span>:</span>
    ...

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">PayPalPaymentProcessor</span>:</span>
    ...
</code></pre>
<p>Once a user tries to use any of the deprecated components, a warning will appear in the prompt. For functions, that happens on calls; for classes, on instantiation and on creation of subclasses.</p>
<h3 id="heading-5-new-typing-features">5. New Typing Features</h3>
<p>We all know that Python itself doesn't enforce types. You can define a function that takes a parameter <code>name: str</code> and still pass an integer to that function. So, working on the <code>typing</code> module means improving static type checkers even more, and you should use them.</p>
<h4 id="heading-readonly-type"><code>ReadOnly</code> type</h4>
<p>In Python 3.13, we have <code>typing.ReadOnly</code>, which can be applied to items of a <code>TypedDict</code> to make them read-only. If you try to change the item value, you can, but your type checker should raise an error.</p>
<pre><code class="lang-python"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Student</span>(<span class="hljs-params">TypedDict</span>):</span>
   idn: ReadOnly[str]
   gpa: float

<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">modify_student</span>(<span class="hljs-params">s: Student</span>) -&gt; <span class="hljs-keyword">None</span>:</span>
   s[<span class="hljs-string">"gpa"</span>] = <span class="hljs-number">4.0</span>  <span class="hljs-comment"># allowed</span>
   s[<span class="hljs-string">"idn"</span>] = <span class="hljs-string">"24315"</span>  <span class="hljs-comment"># typechecker error</span>
</code></pre>
<h4 id="heading-typeis-type"><code>TypeIs</code> type</h4>
<p>This type is assigned to the return value of callables (like functions) that return booleans, indicating whether a variable is an object of a specific type or not.</p>
<p>Consider the following simple function.</p>
<pre><code class="lang-python"><span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">is_student</span>(<span class="hljs-params">s: Student</span>):</span>
    <span class="hljs-keyword">return</span> isinstance(s, Student)
</code></pre>
<p>Similar to <code>typing.List</code> and <code>typing.Union</code>, it accepts any generic or custom types, so you should use it like <code>TypeIs[SomeType]</code>. Now, let's add a type hint to the return value of that function using <code>TypeIs</code>.</p>
<pre><code class="lang-python"><span class="hljs-keyword">from</span> typing <span class="hljs-keyword">import</span> TypeIs

<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">is_student</span>(<span class="hljs-params">s: Student</span>) -&gt; TypeIs[Student]:</span>
    <span class="hljs-keyword">return</span> isinstance(s, Student)
</code></pre>
<p>Now, if <code>is_student(val)</code> returns <code>True</code>, the type checker knows that <code>val</code> is of type <code>Student</code>. If it returns <code>False</code>, the type checker knows that <code>val</code> is not of type <code>Student</code>.</p>
<p>Generally speaking, you should use it when..</p>
<ul>
<li><p>The function returns a boolean value.</p>
</li>
<li><p>The function checks an object's type using <code>isinstance()</code> or any other mechanism.</p>
</li>
</ul>
<h3 id="heading-6-official-support-for-ios-amp-android">6. Official Support for iOS &amp; Android</h3>
<p>Python 3.13 is going to have a separate official release for the iOS platform. The team is working on the Android one as well, but not planned for the 3.13 release.</p>
<h3 id="heading-7-version-support">7. Version Support</h3>
<ul>
<li><p>Python 3.9 - 3.12 have one and a half years of full support, followed by three and a half years of security fixes.</p>
</li>
<li><p>Python 3.13 and later have two years of full support, followed by three years of security fixes.</p>
</li>
</ul>
<h3 id="heading-python-314-315-and-316-expected-removals">Python 3.14, 3.15, and 3.16 Expected Removals</h3>
<p>We're going to see many removals as some components in the standard library are deprecated. Here, I've listed the libraries most affected by these future component removals.</p>
<ul>
<li><p><code>argparse</code></p>
</li>
<li><p><code>asyncio</code></p>
</li>
<li><p><code>pathlib</code></p>
</li>
<li><p><code>importlib</code></p>
</li>
</ul>
<h3 id="heading-conclusion">Conclusion</h3>
<p>GIL has been both a blessing and a mess, in my opinion. It's great to see the community moving forward, considering alternatives and solutions for allowing users to optionally use the components. I'm confident that the JIT compiler will make Python even faster in the next few releases. There will be significant performance improvements in high-scale web frameworks like Django. I hope this article has given you a clear insight into the upcoming Python 3.13 release.</p>
]]></content:encoded></item><item><title><![CDATA[Lambda: The Single-line Function]]></title><description><![CDATA[Lambda functions are an essential feature in Python, allowing developers to create small, anonymous functions without formally defining them using the def keyword. These functions are typically used in situations where a small, throwaway function is ...]]></description><link>https://blog.imsadra.dev/lambda-the-single-line-function</link><guid isPermaLink="true">https://blog.imsadra.dev/lambda-the-single-line-function</guid><category><![CDATA[Python]]></category><category><![CDATA[Python 3]]></category><category><![CDATA[lambda]]></category><category><![CDATA[Lambda Expression]]></category><category><![CDATA[Lambda function]]></category><category><![CDATA[best practices]]></category><category><![CDATA[software development]]></category><category><![CDATA[tips]]></category><dc:creator><![CDATA[Sadra Yahyapour]]></dc:creator><pubDate>Sat, 10 Aug 2024 23:10:27 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1723331351839/c48730a9-cd07-48e3-ad34-38bbaedaf955.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Lambda functions are an essential feature in Python, allowing developers to create small, anonymous functions without formally defining them using the <code>def</code> keyword. These functions are typically used in situations where a small, throwaway function is needed for a short period.</p>
<h3 id="heading-what-is-a-lambda-function">What is a Lambda Function?</h3>
<p>In Python, a lambda function is a small function defined using the <code>lambda</code> keyword. The syntax for a lambda function is straightforward:</p>
<pre><code class="lang-python"><span class="hljs-keyword">lambda</span> arguments: expression
</code></pre>
<ul>
<li><p><strong>Arguments</strong>: The input parameters to the function.</p>
</li>
<li><p><strong>Expression</strong>: The single expression that is evaluated and returned by the function.</p>
</li>
</ul>
<p>A simple example is:</p>
<pre><code class="lang-python">add = <span class="hljs-keyword">lambda</span> x, y: x + y
print(add(<span class="hljs-number">2</span>, <span class="hljs-number">3</span>))  <span class="hljs-comment"># Output: 5</span>
</code></pre>
<p>Here, <code>lambda x, y: x + y</code> creates a function that takes two arguments, <code>x</code> and <code>y</code>, and returns their sum.</p>
<p>This simple lambda function is equivalent to the following <code>def</code> function in Python:</p>
<pre><code class="lang-python"><span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">add</span>(<span class="hljs-params">x, y</span>):</span>
    <span class="hljs-keyword">return</span> x + y
</code></pre>
<h3 id="heading-when-to-use-lambda-functions">When to Use Lambda Functions?</h3>
<p>Lambda functions are best used in scenarios where a small, simple function is required temporarily, especially when passing functions as arguments to higher-order functions like <code>map()</code> and <code>filter()</code>.</p>
<h4 id="heading-common-use-cases">Common Use Cases</h4>
<ol>
<li><p><strong>Using Lambda with</strong> <code>map()</code>:</p>
<pre><code class="lang-python"> numbers = [<span class="hljs-number">1</span>, <span class="hljs-number">2</span>, <span class="hljs-number">3</span>, <span class="hljs-number">4</span>]
 squared = list(map(<span class="hljs-keyword">lambda</span> x: x ** <span class="hljs-number">2</span>, numbers))
 print(squared)  <span class="hljs-comment"># Output: [1, 4, 9, 16]</span>
</code></pre>
</li>
<li><p><strong>Using Lambda with</strong> <code>filter()</code>:</p>
<pre><code class="lang-python"> numbers = [<span class="hljs-number">1</span>, <span class="hljs-number">2</span>, <span class="hljs-number">3</span>, <span class="hljs-number">4</span>, <span class="hljs-number">5</span>, <span class="hljs-number">6</span>]
 evens = list(filter(<span class="hljs-keyword">lambda</span> x: x % <span class="hljs-number">2</span> == <span class="hljs-number">0</span>, numbers))
 print(evens)  <span class="hljs-comment"># Output: [2, 4, 6]</span>
</code></pre>
</li>
<li><p><strong>Using Lambda with</strong> <code>sorted()</code>:</p>
<pre><code class="lang-python"> points = [(<span class="hljs-number">2</span>, <span class="hljs-number">3</span>), (<span class="hljs-number">1</span>, <span class="hljs-number">2</span>), (<span class="hljs-number">3</span>, <span class="hljs-number">1</span>)]
 sorted_points = sorted(points, key=<span class="hljs-keyword">lambda</span> point: point[<span class="hljs-number">1</span>])
 print(sorted_points)  <span class="hljs-comment"># Output: [(3, 1), (1, 2), (2, 3)]</span>
</code></pre>
</li>
</ol>
<h3 id="heading-best-practices-for-using-lambda-functions">Best Practices for Using Lambda Functions</h3>
<p>While lambda functions can be a powerful tool, they are not without their limitations and potential pitfalls. Following best practices can help you avoid common issues.</p>
<ol>
<li><p><strong>Keep Lambda Functions Simple</strong>: Lambda functions should be simple and concise. If the function requires more than one expression or is complex, it's better to use a standard function defined with <code>def</code>.</p>
<pre><code class="lang-python"> <span class="hljs-comment"># Good</span>
 increment = <span class="hljs-keyword">lambda</span> x: x + <span class="hljs-number">1</span>

 <span class="hljs-comment"># Bad</span>
 complex_lambda = <span class="hljs-keyword">lambda</span> x: (x + <span class="hljs-number">1</span>, x ** <span class="hljs-number">2</span>)  <span class="hljs-comment"># Better as a regular function</span>
</code></pre>
</li>
<li><p><strong>Use Descriptive Names</strong>: When assigning lambda functions to variables, use descriptive names to indicate the purpose of the function. Just like a normal function.</p>
<pre><code class="lang-python"> multiply_by_two = <span class="hljs-keyword">lambda</span> x: x * <span class="hljs-number">2</span>
</code></pre>
</li>
<li><p><strong>Avoid Overusing Lambda Functions</strong>: While lambda functions can make code more concise, overuse can lead to code that is difficult to read and maintain. Use them judiciously and prefer regular functions when the logic is complex.</p>
</li>
<li><p><strong>Use Lambdas in Context</strong>: Lambda functions shine when used in context, such as within <code>map()</code>, <code>filter()</code>, or <code>sorted()</code>. If the lambda is being assigned to a variable and used multiple times, consider defining a proper function.</p>
</li>
</ol>
<h3 id="heading-worst-practices-with-lambda-functions">Worst Practices with Lambda Functions</h3>
<ol>
<li><p><strong>Complex Lambda Expressions</strong>: A lambda function should only contain a single expression. Using complex logic within a lambda function can make the code unreadable.</p>
<pre><code class="lang-python"> <span class="hljs-comment"># Bad practice</span>
 func = <span class="hljs-keyword">lambda</span> x: x <span class="hljs-keyword">if</span> x &gt; <span class="hljs-number">10</span> <span class="hljs-keyword">else</span> x + <span class="hljs-number">10</span> <span class="hljs-keyword">if</span> x &gt; <span class="hljs-number">5</span> <span class="hljs-keyword">else</span> x + <span class="hljs-number">5</span>
</code></pre>
</li>
<li><p><strong>Ignoring Readability</strong>: Lambda functions can sometimes obscure the meaning of the code, especially if overused or if they contain nested lambdas.</p>
<pre><code class="lang-python"> <span class="hljs-comment"># Hard to read</span>
 result = (<span class="hljs-keyword">lambda</span> x: (<span class="hljs-keyword">lambda</span> y: x + y)(<span class="hljs-number">10</span>))(<span class="hljs-number">5</span>)
</code></pre>
</li>
<li><p><strong>Using Lambdas Where</strong> <code>def</code> <strong>is Clearer</strong>: If a lambda function spans multiple lines or performs multiple operations, using a standard function definition is more appropriate.</p>
<pre><code class="lang-python"> <span class="hljs-comment"># Better as a regular function</span>
 <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">add_and_square</span>(<span class="hljs-params">x, y</span>):</span>
     <span class="hljs-keyword">return</span> (x + y) ** <span class="hljs-number">2</span>
</code></pre>
</li>
</ol>
<h3 id="heading-advanced-usage-of-lambda-functions">Advanced Usage of Lambda Functions</h3>
<p>While lambda functions are typically used for simple tasks, there are some advanced use cases where they can be applied creatively and effectively.</p>
<ol>
<li><p><strong>Lambda with Higher-Order Functions</strong>: Lambda functions are often used in conjunction with higher-order functions that take other functions as arguments. It's quite similar to decorators, isn't it?!</p>
<pre><code class="lang-python"> <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">apply_function</span>(<span class="hljs-params">func, value</span>):</span>
     <span class="hljs-keyword">return</span> func(value)

 result = apply_function(<span class="hljs-keyword">lambda</span> x: x ** <span class="hljs-number">2</span>, <span class="hljs-number">4</span>)
 print(result)  <span class="hljs-comment"># Output: 16</span>
</code></pre>
</li>
<li><p><strong>Lambda with</strong> <code>reduce()</code>: The <code>reduce()</code> function applies a lambda function cumulatively to the items of a sequence.</p>
<pre><code class="lang-python"> <span class="hljs-keyword">from</span> functools <span class="hljs-keyword">import</span> reduce
 numbers = [<span class="hljs-number">1</span>, <span class="hljs-number">2</span>, <span class="hljs-number">3</span>, <span class="hljs-number">4</span>]
 product = reduce(<span class="hljs-keyword">lambda</span> x, y: x * y, numbers)
 print(product)  <span class="hljs-comment"># Output: 24</span>
</code></pre>
</li>
<li><p><strong>Lambda in Key Functions</strong>: Lambdas are often used as the key argument in functions like <code>sorted()</code> or <code>min()</code> to determine the sorting or selection criteria.</p>
<pre><code class="lang-python"> names = [<span class="hljs-string">'Alice'</span>, <span class="hljs-string">'Bob'</span>, <span class="hljs-string">'Charlie'</span>]
 longest_name = max(names, key=<span class="hljs-keyword">lambda</span> name: len(name))
 print(longest_name)  <span class="hljs-comment"># Output: Charlie</span>
</code></pre>
</li>
<li><p><strong>Currying with Lambda Functions</strong>: Lambda functions can be used to create curried functions, where a function with multiple arguments is transformed into a series of functions each with a single argument.</p>
<pre><code class="lang-python"> <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">multiply</span>(<span class="hljs-params">x</span>):</span>
     <span class="hljs-keyword">return</span> <span class="hljs-keyword">lambda</span> y: x * y

 double = multiply(<span class="hljs-number">2</span>)
 print(double(<span class="hljs-number">5</span>))  <span class="hljs-comment"># Output: 10</span>
</code></pre>
</li>
<li><p><strong>Using to Define Immediately Invoked Functions (IIFs)</strong>: Some functions get invoked as they get defined. They are so popular in Javascript. In Python, there is a chance to define these types of functions using lambda functions.</p>
<pre><code class="lang-python"><span class="hljs-meta"> @lambda _:_()</span>
 <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">my_iif</span>():</span>
     print(<span class="hljs-string">"Don't call me. I'm already invoked!"</span>)
</code></pre>
</li>
</ol>
<h3 id="heading-conclusion">Conclusion</h3>
<p>Lambda functions are a versatile and powerful feature in Python, ideal for situations where you need a small, one-off function. While they can lead to more concise code, using them wisely is essential to maintain readability and avoid complexity. Understanding when and how to use lambda functions effectively can enhance your Python programming skills and make your code more expressive and functional.</p>
]]></content:encoded></item><item><title><![CDATA[Python Open-Source Development: How I Build and Maintain Open-Source Repositories!]]></title><description><![CDATA[Right now, you're probably using several open-source tools and benefiting from them. As an open-source enthusiast, I often follow a clear and reliable method for developing and maintaining my Python repositories.
In this article, I will discuss the s...]]></description><link>https://blog.imsadra.dev/python-open-source-development-how-i-build-and-maintain-open-source-repositories</link><guid isPermaLink="true">https://blog.imsadra.dev/python-open-source-development-how-i-build-and-maintain-open-source-repositories</guid><category><![CDATA[Python]]></category><category><![CDATA[Python 3]]></category><category><![CDATA[Developer]]></category><category><![CDATA[development]]></category><category><![CDATA[Open Source]]></category><category><![CDATA[GitHub]]></category><category><![CDATA[opensource]]></category><category><![CDATA[Collaboration]]></category><category><![CDATA[contribution to open source]]></category><category><![CDATA[github-actions]]></category><category><![CDATA[Testing]]></category><category><![CDATA[code coverage]]></category><category><![CDATA[pytest]]></category><category><![CDATA[documentation]]></category><category><![CDATA[Git]]></category><dc:creator><![CDATA[Sadra Yahyapour]]></dc:creator><pubDate>Fri, 09 Aug 2024 16:00:13 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1723205138856/8b22c609-1f20-4cbe-b7b7-20475912d3a9.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Right now, you're probably using several open-source tools and benefiting from them. As an open-source enthusiast, I often follow a clear and reliable method for developing and maintaining my Python repositories.</p>
<p>In this article, I will discuss the steps I take to create a clean and impressive Python repository with over 90% code coverage and an excellent Development Experience (DX).</p>
<h3 id="heading-first-i-ask">First, I Ask..</h3>
<p>Before I take any steps, I have to ask myself a few questions. Initially, I think about my users and determine who I am releasing the tool for. Am I developing a tool for developers, like a package or library? Or am I working on a service for users with little to no programming knowledge?</p>
<p>Will I be the only developer working on this tool, or will it be open for contributions from developers of all experience levels? Which development flow will I follow?</p>
<p>Regardless of the tools, these are the basic initial questions I ask before starting. Today, we'll explore the steps for creating a sample open-source repository that other developers can contribute to and develop.</p>
<p>We will explore the tools I use, how I use them, and the value they bring to your workspace and repository.</p>
<h3 id="heading-i-use-git-amp-github">I Use Git &amp; GitHub</h3>
<p>There are so many version-controlling software. I prefer Git as GitHub fully supports that. It's also easy to learn for beginners. Also, some other tools on my machine rely on <code>git</code>. So for me, <code>git</code> is the perfect solution.</p>
<h3 id="heading-dx-matters-a-lot">DX Matters A Lot</h3>
<p>Development experience is one of those great feelings that not all open-source contributors get to enjoy when contributing, often due to poor code quality and terrible development flows from some projects.</p>
<p>For some repositories, it takes days to understand the project's flow and build momentum to contribute. On the other hand, there are very neat projects where it only takes a few minutes to find the area you want to contribute to.</p>
<p>It's about documentation, clean code, excellent code structure, and a solid design. These key concepts help you find the issue you want to fix in someone's repository very quickly.</p>
<h3 id="heading-tips-for-improving-the-development-experience">Tips For Improving the Development Experience</h3>
<p>As a new contributor, what bothers you the most about a repository you just switched to? What features would you like that new repository to have to make you feel great about returning and contributing even later?</p>
<p>It's very common not to know the entire codebase in a repository. It might even be a framework you want to contribute to, so studying the entire framework and reviewing its codebase is obviously not a good practice. How do you want to know whether your change is healthy and not breaking any other part of the codebase?</p>
<p>In the next sections, we'll explore tools and best practices to help you maintain a clean, solid Python repository that every developer would love to open pull requests on.</p>
<h3 id="heading-testing-amp-code-coverage">Testing &amp; Code Coverage</h3>
<p>In the open-source world, you don't know much about your contributors' expertise levels. That's why reliability is crucial. Testing every change in your repository helps ensure your code's reliability. While it's not a perfect solution, it will catch many issues and vulnerabilities at the right time.</p>
<p>It doesn't really matter which development flow you follow, whether it's TDD or DDD, or if you write tests after your components are working. Make sure all related components are tested with each change.</p>
<p>Now, whenever someone makes a change in your repository on their local machine, they can test that change by running a single command to ensure everything is fine and the codebase is still working as expected.</p>
<p>He doesn't have to test every part of the project, and neither do you. But how do I achieve this in my repositories?</p>
<p>In a Python repository, I usually use either the standard <code>unittest</code> library or <a target="_blank" href="https://docs.pytest.org/en/stable/">PyTest</a>, which is an excellent tool for writing unit tests.</p>
<p>On top of that, I use <a target="_blank" href="https://coverage.readthedocs.io/en/7.6.1/">Coverage.py</a> to check how much of my code is covered by tests. It's not the smartest solution, but it works well for small to medium-sized projects.</p>
<p>If you want to use a third-party tool with more options, details, and great accessibility in your repository, <a target="_blank" href="https://about.codecov.io/">CodeCov</a> is a good solution for your Python projects.</p>
<h3 id="heading-linting-amp-formatting">Linting &amp; Formatting</h3>
<p>I don't think it needs any additional explanation on how important linting and formatting are. So, hands down, <a target="_blank" href="https://docs.astral.sh/ruff/">Ruff</a> is the best tool for maintaining consistent formatting and linting across your entire repository. I love this tool because it handles all the cleaning tasks, so you won't need any other tools. Most of the time, I find myself ignoring some PEP statements, but that's fine.</p>
<p>How do I make the contributors to use Ruff and deliver their changes formatted and linted? Well, it's described in the next section.</p>
<h3 id="heading-i-use-pre-commit-for-automate-evaluations">I Use Pre-commit For Automate Evaluations</h3>
<p><a target="_blank" href="https://pre-commit.com/">Pre-commit</a> is essentially a <a target="_blank" href="https://git-scm.com/book/en/v2/Customizing-Git-Git-Hooks">Git Hook</a> that runs automatically with each <code>git commit</code> by default. How does it help with open-source maintenance?</p>
<p>I use <code>pre-commit</code> to run Ruff and check the format and lint of any changes made by contributors. Actually, I don't run it myself; the contributors use it on their own!</p>
<p>As the maintainer of the repository, all I need to do is add a pre-commit configuration file to the root of my repository. Contributors then have to install <code>pre-commit</code> and enable it. From that point on, commits will only go through if Ruff runs successfully. If Ruff fails, the commit won't happen.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1723218037165/d3bee08e-30b5-4cb3-8530-cc5117f87bbf.png" alt class="image--center mx-auto" /></p>
<p>If there are contributors who don't want to install <code>pre-commit</code> and add any hooks to the <code>.git/hooks/</code> directory, that's still fine. We can set up a CI/CD pipeline to run pre-commit hooks and catch the linting and formatting issues when a PR is opened. We'll discuss that later.</p>
<h3 id="heading-multiple-python-version-support">Multiple Python Version Support</h3>
<p>There are times when I want my repository to work in environments with different Python versions. Naturally, before any deployment, my project should be tested in all those environments, right?</p>
<p>I use <a target="_blank" href="https://tox.wiki/en">Tox</a> for that purpose. Consider <code>tox</code> as an environment (<code>venv</code>) manager in your repository. You can define the following environments and set a custom set of commands, it'll create those virtual environments and run those commands.</p>
<pre><code class="lang-ini"><span class="hljs-section">[tox]</span>
<span class="hljs-attr">env_list</span> = py3{<span class="hljs-number">8</span>,<span class="hljs-number">9</span>,<span class="hljs-number">10</span>,<span class="hljs-number">11</span>,<span class="hljs-number">12</span>}
<span class="hljs-attr">skipsdist</span> = <span class="hljs-literal">true</span>
<span class="hljs-attr">skip_install</span> = <span class="hljs-literal">true</span>

<span class="hljs-section">[testenv]</span>
<span class="hljs-attr">description</span> = run tests
<span class="hljs-attr">deps</span> = -rdev_requirements.txt
<span class="hljs-attr">commands</span> =
    coverage run -m pytest {posargs:tests}
    coverage report -m
    coverage erase
</code></pre>
<p>This is a very basic <code>tox.ini</code> config file. It shows that I have five environments: <code>py38</code>, <code>py39</code>, <code>py310</code>, <code>py311</code>, and <code>py312</code>. For all these environments, the following process will occur.</p>
<ol>
<li><p>Tox creates the environment.</p>
</li>
<li><p>Uses <code>pip</code> to install the dependencies described like <code>-r dev_requirements.txt</code>.</p>
</li>
<li><p>Runs <code>coverage run -m pytest {posargs:tests}</code></p>
</li>
<li><p>Runs <code>coverage report -m</code></p>
</li>
<li><p>Runs <code>coverage erase</code></p>
</li>
</ol>
<p>This procedure happens for each environment, meaning we should expect <code>tox</code> to be executed five times.</p>
<p>There is a cool fact about Tox. It creates virtual environments for each environment defined inside the <code>tox.ini</code> file. Running the <code>tox</code> command goes through the process, so if you've already run that command, you should have a <code>.tox/</code> directory in the root of your project, containing all the environments.</p>
<pre><code class="lang-plaintext">.tox/
├── py38/
├── py39/
├── py310/
├── py311/
└── py312/
</code></pre>
<p>It makes the further <code>tox</code> calls way faster because the dependencies are already installed in the environments. Note that if you want to test your changes in all these environments, you should have all these Python versions installed on your machine so that Tox can create those environments and test your changes. I use <code>pyenv</code> for that purpose.</p>
<h3 id="heading-cicd-pipelines">CI/CD Pipelines</h3>
<p>What if the contributors forget to install <code>pre-commit</code> or run <code>tox</code> to make sure their changes are tested in all supported environments and don't break anything in the repository?</p>
<p>Well, we need to double-check and ensure everything happens correctly. Since I'm developing my repository on GitHub, I use GitHub Actions. Almost all open-source repositories follow the Fork Flow strategy for contributions. This means if you want to contribute to someone's repository, you first need to clone it, make your changes, and then open a pull request.</p>
<p>As a maintainer, you can run both <code>tox</code> and <code>pre-commit</code> inside those pull requests. But wait a second. Why not use <code>pre-commit</code> inside a new <code>tox</code> environment? That way, your pipeline would be easier to follow. If any contributor regrets using <code>pre-commit</code>, it's still part of the <code>tox</code> configuration.</p>
<pre><code class="lang-ini"><span class="hljs-section">[tox]</span>
<span class="hljs-attr">env_list</span> = py3{<span class="hljs-number">8</span>,<span class="hljs-number">9</span>,<span class="hljs-number">10</span>,<span class="hljs-number">11</span>,<span class="hljs-number">12</span>}, pre-commit   <span class="hljs-comment"># &lt;- new</span>
...

<span class="hljs-section">[testenv:pre-commit]</span>
<span class="hljs-attr">description</span> = run pre-commit
<span class="hljs-attr">deps</span> = pre-commit
<span class="hljs-attr">commands</span> = pre-commit run --all-files --show-diff-<span class="hljs-literal">on</span>-failure
</code></pre>
<p>Now, our <code>tox</code> has six environment and in the last environment, it first installs <code>pre-commit</code> and then runs <code>pre-commit run --all-files --show-diff-on-failure</code>. Nice!</p>
<p>There is a useful feature of Tox that I forgot to mention earlier. When you run <code>tox</code>, it only runs the environments for which you have the corresponding Python interpreter version installed on your machine. What does that mean?</p>
<p>If you only have Python 3.8 installed and you run <code>tox</code>, only one environment will be created, and the codebase will be tested in that environment. Tox won't return any status code other than 0 if you don't have the other Python versions installed. That's great.</p>
<p>I use <code>matrix</code> in my actions pipeline to run <code>tox</code> for each Python environment.</p>
<pre><code class="lang-yaml"><span class="hljs-attr">name:</span> <span class="hljs-string">Testing</span>

<span class="hljs-attr">on:</span>
  <span class="hljs-attr">pull_request:</span>
    <span class="hljs-attr">branches:</span> [<span class="hljs-string">main</span>]

<span class="hljs-attr">jobs:</span>
  <span class="hljs-attr">Testing:</span>
    <span class="hljs-attr">runs-on:</span> <span class="hljs-string">ubuntu-latest</span>
    <span class="hljs-attr">strategy:</span>
      <span class="hljs-attr">matrix:</span>
        <span class="hljs-attr">python:</span> [<span class="hljs-string">"3.8"</span>, <span class="hljs-string">"3.9"</span>, <span class="hljs-string">"3.10"</span>, <span class="hljs-string">"3.11"</span>, <span class="hljs-string">"3.12"</span>]

    <span class="hljs-attr">steps:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-attr">uses:</span> <span class="hljs-string">actions/checkout@v4</span>

      <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Setup</span> <span class="hljs-string">Python</span>
        <span class="hljs-attr">uses:</span> <span class="hljs-string">actions/setup-python@v5</span>
        <span class="hljs-attr">with:</span>
          <span class="hljs-attr">python-version:</span> <span class="hljs-string">${{</span> <span class="hljs-string">matrix.python</span> <span class="hljs-string">}}</span>

      <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Install</span> <span class="hljs-string">tox</span>
        <span class="hljs-attr">run:</span> <span class="hljs-string">pip</span> <span class="hljs-string">install</span> <span class="hljs-string">tox</span>

      <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Run</span> <span class="hljs-string">tox</span>
        <span class="hljs-attr">run:</span> <span class="hljs-string">tox</span> <span class="hljs-string">-e</span> <span class="hljs-string">py</span>
</code></pre>
<p>Now, whenever someone opens a new pull request, a pipeline will get triggered and test the changes in all the environments.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1723218025717/65aacc5a-f2d2-46d6-90c0-2dd7430d4318.png" alt class="image--center mx-auto" /></p>
<p>We're almost there. The next section is optional. If you want to include documentation for your Python repository, then don't skip it.</p>
<h3 id="heading-documentation">Documentation</h3>
<p>I've used <a target="_blank" href="https://www.sphinx-doc.org/">Sphinx</a> with reStructuredText (rST) format before. It's a good solution if you want to add quick documentation. You can gather and display information about internal APIs effectively.</p>
<p>You can then deploy your documentation on <a target="_blank" href="https://readthedocs.org/">ReadTheDocs</a> for free. But if you're looking for more customization and flexibility, my best advice would be <a target="_blank" href="https://www.mkdocs.org/">MkDocs</a>.</p>
<p><a target="_blank" href="https://squidfunk.github.io/mkdocs-material/">Material MkDocs</a> has taken the project further by adding more flexibility, color options, and visual plugins. Both MkDocs and Material MkDocs support Markdown, which can speed up your workflow if you're not familiar with rST.</p>
<h3 id="heading-see-in-practice">See in Practice</h3>
<p>If you want to see all the tools and practices described in this article in action, I highly recommend checking out this repository of mine.</p>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://github.com/lnxpy/pyaction">https://github.com/lnxpy/pyaction</a></div>
<p> </p>
<h3 id="heading-conclusion">Conclusion</h3>
<p>In conclusion, adopting best practices and utilizing tools to maintain Python repositories not only enhances developer experience (DX) but also contributes to the overall efficiency and quality of the software development process. By prioritizing simplicity, clean code, and thoughtful design decisions, developers can streamline their workflows, reduce technical debt, and foster a collaborative and inclusive development environment. Embracing these principles not only benefits the individual developer but also the entire team and the long-term success of the project.</p>
<p>Ultimately, investing in maintaining clean and well-organized code will pay off in the form of increased productivity, easier maintenance, and more robust and resilient software applications.</p>
]]></content:encoded></item><item><title><![CDATA[Unlock Your Brain’s Potential: Discover How to Read Faster!]]></title><description><![CDATA[We are flooded with information in this century. Many valuable resources can have a big impact on your life and even lead to success!
I believe we all agree that wisdom is the most valuable thing anyone can have over time. We've always been told by o...]]></description><link>https://blog.imsadra.dev/unlock-your-brains-potential-discover-how-to-read-faster</link><guid isPermaLink="true">https://blog.imsadra.dev/unlock-your-brains-potential-discover-how-to-read-faster</guid><category><![CDATA[sighted]]></category><category><![CDATA[brain]]></category><category><![CDATA[AI]]></category><category><![CDATA[bionic]]></category><category><![CDATA[bioinformatics]]></category><category><![CDATA[Artificial Intelligence]]></category><category><![CDATA[Python]]></category><category><![CDATA[reading]]></category><category><![CDATA[Science ]]></category><dc:creator><![CDATA[Sadra Yahyapour]]></dc:creator><pubDate>Wed, 07 Aug 2024 06:27:34 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1723036023747/d33fa247-f544-4764-89fc-3d60500d0423.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>We are flooded with information in this century. Many valuable resources can have a big impact on your life and even lead to success!</p>
<p>I believe we all agree that wisdom is the most valuable thing anyone can have over time. We've always been told by our parents, school, and society that books are the real deal. They are pure valuable knowledge in your hands. But why? Is it true? If it is, then why are we so obsessed with other types of learning, such as watching tutorials, videos, and podcasts?</p>
<p>Why do we find reading frustrating? Is it really is or we've changed over time and our brain presumes that it's a boring activity?!</p>
<p>Let's find out how you can hack your mind and unlock its full potential when it comes to reading!</p>
<h3 id="heading-understanding-the-mechanics-of-reading">Understanding the Mechanics of Reading</h3>
<p>Reading is a complex process involving the coordination of various ocular movements and cognitive functions. Two key components of eye movements in reading are <strong>fixations</strong> and <strong>saccades</strong>. Fixation refers to the moment when our eyes pause on a specific point in the text, allowing the brain to process the information. These pauses typically last between 200 to 300 milliseconds. During fixation, the visual information is sent to the brain for decoding and comprehension.</p>
<p>Saccades are the rapid movements our eyes make between fixations. These jumps can range from a few characters to several words, depending on the reader's proficiency and the complexity of the text. Saccades occur in approximately 20 to 40 milliseconds. The efficiency of these movements significantly impacts reading speed and comprehension. Inefficient saccadic movements can lead to slower reading and reduced comprehension as the brain struggles to keep up with the visual input.</p>
<h4 id="heading-eye-tracking-and-reading-patterns">Eye Tracking and Reading Patterns</h4>
<p>Eye-tracking technology has provided valuable insights into reading patterns and behaviors. By monitoring where and how long a reader's gaze rests on a page, researchers can analyze the efficiency of their reading process. Eye-tracking studies have revealed that proficient readers exhibit more efficient saccadic movements and shorter fixation durations compared to less skilled readers. These insights have informed the development of bionic reading methods aimed at optimizing these ocular movements.</p>
<h3 id="heading-the-brains-role-in-reading">The Brain's Role in Reading</h3>
<p>The brain plays a crucial role in reading, and decoding visual information into meaningful language. This process involves multiple regions of the brain working in concert. The primary visual cortex processes the raw visual input, while the fusiform gyrus, particularly the Visual Word Form Area (VWFA), specializes in recognizing letters and words. Once recognized, the brain's language centers, including Broca's and Wernicke's areas, work to decode and comprehend the text.</p>
<p>Research has shown that our brain does not read every letter in a word sequentially. Instead, it uses contextual clues and pattern recognition to interpret words quickly. This phenomenon, known as the "word superiority effect," allows the brain to recognize words as whole units rather than individual letters. This ability underpins the effectiveness of bionic reading methods, which leverage the brain's capacity for rapid word recognition.</p>
<h4 id="heading-the-importance-of-context">The Importance of Context</h4>
<p>Context plays a vital role in reading comprehension. The brain uses surrounding words and prior knowledge to predict and infer meaning, reducing the cognitive load required to decode each word individually. This predictive processing enables faster and more efficient reading. Bionic reading methods enhance this natural ability by presenting text in a way that highlights key information, aiding the brain's predictive and inferential processes.</p>
<h3 id="heading-the-psychological-aspects-of-reading">The Psychological Aspects of Reading</h3>
<p>Reading involves significant cognitive load, as the brain must decode visual information, access stored knowledge, and integrate new information into existing mental frameworks. High cognitive load can impede reading speed and comprehension. Bionic reading methods aim to reduce cognitive load by optimizing the presentation of text, making it easier for the brain to process information.</p>
<p>One psychological concept relevant to reading is "cognitive ease." When information is presented in a way that aligns with our brain's natural processing capabilities, it feels easier to understand and remember. Bionic reading methods exploit this by structuring text to align with our brain's strengths, enhancing cognitive ease and improving reading efficiency.</p>
<h4 id="heading-motivation-and-engagement">Motivation and Engagement</h4>
<p>Motivation and engagement are critical factors in reading success. Readers who are motivated and engaged with the text are more likely to comprehend and retain information. Bionic reading methods can enhance engagement by making reading feel less effortful and more rewarding. By reducing the strain associated with traditional reading, these methods can help maintain motivation and sustain attention over longer periods.</p>
<h3 id="heading-bionic-reading-methods">Bionic Reading Methods</h3>
<p>There are several methods to convert text into its bionic version, and in almost every solution, you are free to apply whatever convention you think will work best for you. We're about to showcase them here.</p>
<h4 id="heading-highlighting-key-information">Highlighting Key Information</h4>
<p>One of the fundamental principles of bionic reading is the strategic highlighting of key information. By emphasizing important words or phrases, bionic reading methods guide the reader's focus and facilitate faster comprehension. This technique leverages the brain's ability to recognize words as whole units and enhances the efficiency of fixation and saccadic movements.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1723002357082/e9d1b470-20f0-40ad-acd5-24974eae241b.png" alt class="image--center mx-auto" /></p>
<h4 id="heading-chunking-and-text-formatting">Chunking and Text Formatting</h4>
<p>Chunking refers to breaking down text into manageable units, or chunks, to improve readability. This method is based on the idea that the brain can process chunks of information more efficiently than individual letters or words. By organizing text into grammatical chunks, bionic reading methods reduce cognitive load and make it easier for the brain to decode and comprehend information.</p>
<p>Text formatting is another crucial aspect of bionic reading. This includes adjusting font size, spacing, and line length to optimize readability. Studies have shown that certain fonts and text arrangements can enhance reading speed and comprehension. Bionic reading methods often incorporate these findings to create text formats that align with the brain's natural processing abilities.</p>
<h4 id="heading-the-role-of-technology">The Role of Technology</h4>
<p>Technology plays a significant role in implementing bionic reading methods. Digital platforms and applications can dynamically adjust text presentation to suit individual reading preferences and needs. Eye tracking technology, for example, can be used to tailor text formatting in real time, providing a personalized reading experience that maximizes efficiency and comprehension.</p>
<h3 id="heading-are-partial-words-enough">Are Partial Words Enough?</h3>
<p>One intriguing aspect of bionic reading is the question of whether only a few letters of each word are sufficient for comprehension. Research suggests that the brain can indeed recognize words even when parts of them are obscured or altered, as long as the first and last letters remain intact. This phenomenon is known as "typoglycemia." For example, the sentence "Aoccdrnig to a rscheearch at Cmabrigde Uinervtisy" can still be understood despite the jumbled letters.</p>
<p>Bionic reading methods can exploit this by selectively highlighting or modifying parts of words to enhance readability without compromising comprehension. By presenting just enough information for the brain to recognize and process words, these methods can significantly speed up reading.</p>
<h3 id="heading-what-is-sighted">What is Sighted?</h3>
<p><a target="_blank" href="https://sighted.vercel.app/">Sighted</a> is an AI-powered solution to improve reading quality with the help of NLP and bionic algorithms.</p>
<p><a target="_blank" href="https://blog.imsadra.me/sighted-read-faster-comprehend-better-with-ai#heading-useful-links">Read this article to find out more about Sighted!</a></p>
<h3 id="heading-case-studies-and-applications">Case Studies and Applications</h3>
<h4 id="heading-educational-settings">Educational Settings</h4>
<p>In educational settings, bionic reading methods have shown promise in improving reading speed and comprehension among students. By reducing cognitive load and enhancing engagement, these methods can help students process information more efficiently, leading to better academic performance.</p>
<h4 id="heading-professional-environments">Professional Environments</h4>
<p>Professionals who need to process large volumes of text, such as lawyers, researchers, and journalists, can benefit from bionic reading methods. By optimizing reading efficiency, these methods can save time and improve productivity, allowing professionals to stay on top of information without sacrificing comprehension.</p>
<h4 id="heading-accessibility">Accessibility</h4>
<p>Bionic reading methods also hold the potential to enhance accessibility for individuals with reading difficulties, such as dyslexia. By presenting text in a way that aligns with the brain's natural processing capabilities, these methods can make reading more accessible and less challenging for those with cognitive impairments.</p>
<h3 id="heading-conclusion">Conclusion</h3>
<p>Bionic reading methods represent a significant advancement in our understanding and enhancement of the reading process. By leveraging the brain's natural abilities and optimizing ocular movements, these methods can significantly improve reading speed and comprehension. The integration of psychological insights, such as cognitive load and motivation, further enhances their effectiveness. As technology continues to evolve, the potential for personalized and adaptive bionic reading solutions will only grow, offering exciting possibilities for readers of all backgrounds and abilities.</p>
]]></content:encoded></item><item><title><![CDATA[Boosting Sighted Accessibility: Building a Python GitHub Action]]></title><description><![CDATA[It's been a couple of months since I created PyActon, an open-source tool that allows you to create GitHub Actions using Python. With this tool, you can install and use any Python package or library within your CI/CD pipelines.

The implementation de...]]></description><link>https://blog.imsadra.dev/boosting-sighted-accessibility-building-a-python-github-action</link><guid isPermaLink="true">https://blog.imsadra.dev/boosting-sighted-accessibility-building-a-python-github-action</guid><category><![CDATA[sighted]]></category><category><![CDATA[Python]]></category><category><![CDATA[Python 3]]></category><category><![CDATA[GitHub]]></category><category><![CDATA[github-actions]]></category><category><![CDATA[GitHub Actions]]></category><category><![CDATA[ci-cd]]></category><category><![CDATA[CI/CD]]></category><category><![CDATA[automation]]></category><category><![CDATA[pyaction]]></category><dc:creator><![CDATA[Sadra Yahyapour]]></dc:creator><pubDate>Tue, 30 Jul 2024 15:53:37 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1722329776666/aab0a17b-3660-495e-88bb-fb7b0b8d2cfa.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>It's been a couple of months since I created <a target="_blank" href="https://pyaction.imsadra.me">PyActon</a>, an open-source tool that allows you to create GitHub Actions using Python. With this tool, you can install and use any Python package or library within your CI/CD pipelines.</p>
<blockquote>
<p><a target="_blank" href="https://github.com/lnxpy/sighted-action">The implementation described in this article is live hosted on GitHub.</a></p>
</blockquote>
<p>You can use it with GitHub's official <a target="_blank" href="https://github.com/actions/cache"><code>action/cache</code></a> action to cache your action's dependencies. This is helpful when working with environments that have many or large dependencies that take time to load. This way, you can quickly get TensorFlow running inside your pipelines! We're not going to use it today though as our action is quite light and fast in terms of response.</p>
<p>In this article, we'll create a new action using PyAction that triggers when an issue is opened or reopened. It will read the issue data, use <a target="_blank" href="https://sighted.vercel.app">Sighted</a> to analyze the text in the issue form, and finally, display the result as a comment based on the analysis.</p>
<h3 id="heading-installation">Installation</h3>
<p>Let's install both <code>pyaction</code> and <code>sighted</code>. They both require you to have <code>pip</code> and Python &gt;= 3.8 installed.</p>
<pre><code class="lang-bash">pip install <span class="hljs-string">"pyaction[cli"</span> sighted
</code></pre>
<p>To get started, we use the <code>init</code> command in <code>pyaction</code> to provide a base template for our action.</p>
<pre><code class="lang-bash">pyaction init
</code></pre>
<pre><code class="lang-plaintext">🎤 Action name
   Sighted Action
🎤 Action's slug
   sighted-action
🎤 Short description
   Turning your issue content into bionic text
🎤 Author's name
   Sadra Yahyapour
🎤 Include workflow testing pipeline
   Yes
</code></pre>
<p>Change the current directory to the action's path.</p>
<pre><code class="lang-bash"><span class="hljs-built_in">cd</span> sighted-action/
</code></pre>
<h3 id="heading-development">Development</h3>
<p>We need to interact with the repository issues meaning we need to have access to the issue ID. Our action doesn't have any user-related inputs. Let's start with the <code>action.yml</code> file. I need to add one input and one output.</p>
<pre><code class="lang-yaml"><span class="hljs-attr">inputs:</span>
  <span class="hljs-string">...</span>
  <span class="hljs-attr">issue_number:</span>
    <span class="hljs-attr">description:</span> <span class="hljs-string">The</span> <span class="hljs-string">id</span> <span class="hljs-string">of</span> <span class="hljs-string">the</span> <span class="hljs-string">issue</span>
    <span class="hljs-attr">required:</span> <span class="hljs-literal">true</span>

<span class="hljs-attr">outputs:</span>
  <span class="hljs-attr">bionic_text:</span>
    <span class="hljs-attr">description:</span> <span class="hljs-string">The</span> <span class="hljs-string">converted</span> <span class="hljs-string">bionic</span> <span class="hljs-string">text</span>
</code></pre>
<p>Leave the rest of the file as it is. Add the moment, we're using <code>sighted==0.0.4</code>. Add it to the <code>requirements.txt</code> file.</p>
<pre><code class="lang-plaintext">...
sighted==0.0.4
</code></pre>
<p>Create <code>.github/ISSUE_TEMPLATE/sighted_text.yml</code> file. It contains the structure of the issue template.</p>
<pre><code class="lang-yaml"><span class="hljs-attr">name:</span> <span class="hljs-string">Sighted</span> <span class="hljs-string">Text</span>
<span class="hljs-attr">description:</span> <span class="hljs-string">Using</span> <span class="hljs-string">Sighted</span> <span class="hljs-string">to</span> <span class="hljs-string">convert</span> <span class="hljs-string">your</span> <span class="hljs-string">input</span> <span class="hljs-string">into</span> <span class="hljs-string">Markdown</span> <span class="hljs-string">bionic</span> <span class="hljs-string">text</span>
<span class="hljs-attr">title:</span> <span class="hljs-string">"Sighted: Bionic Text"</span>
<span class="hljs-attr">body:</span>
  <span class="hljs-bullet">-</span> <span class="hljs-attr">type:</span> <span class="hljs-string">textarea</span>
    <span class="hljs-attr">attributes:</span>
      <span class="hljs-attr">label:</span> <span class="hljs-string">Text</span>
      <span class="hljs-attr">description:</span> <span class="hljs-string">Write</span> <span class="hljs-string">or</span> <span class="hljs-string">paste</span> <span class="hljs-string">your</span> <span class="hljs-string">text</span> <span class="hljs-string">here</span>
    <span class="hljs-attr">validations:</span>
      <span class="hljs-attr">required:</span> <span class="hljs-literal">true</span>
  <span class="hljs-bullet">-</span> <span class="hljs-attr">type:</span> <span class="hljs-string">dropdown</span>
    <span class="hljs-attr">id:</span> <span class="hljs-string">color</span>
    <span class="hljs-attr">attributes:</span>
      <span class="hljs-attr">label:</span> <span class="hljs-string">Color</span>
      <span class="hljs-attr">description:</span> <span class="hljs-string">Set</span> <span class="hljs-string">the</span> <span class="hljs-string">color</span> <span class="hljs-string">for</span> <span class="hljs-string">fixations</span>
      <span class="hljs-attr">options:</span>
        <span class="hljs-bullet">-</span> <span class="hljs-string">Gray</span>
        <span class="hljs-bullet">-</span> <span class="hljs-string">Light</span> <span class="hljs-string">Gray</span>
        <span class="hljs-bullet">-</span> <span class="hljs-string">Dark</span> <span class="hljs-string">Gray</span>
        <span class="hljs-bullet">-</span> <span class="hljs-string">Red</span>
        <span class="hljs-bullet">-</span> <span class="hljs-string">Yellow</span>
        <span class="hljs-bullet">-</span> <span class="hljs-string">Green</span>
      <span class="hljs-attr">default:</span> <span class="hljs-number">1</span>
  <span class="hljs-bullet">-</span> <span class="hljs-attr">type:</span> <span class="hljs-string">dropdown</span>
    <span class="hljs-attr">id:</span> <span class="hljs-string">fixation</span>
    <span class="hljs-attr">attributes:</span>
      <span class="hljs-attr">label:</span> <span class="hljs-string">Fixation</span>
      <span class="hljs-attr">description:</span> <span class="hljs-string">Set</span> <span class="hljs-string">the</span> <span class="hljs-string">fixation</span>
      <span class="hljs-attr">options:</span>
        <span class="hljs-bullet">-</span> <span class="hljs-number">1</span>
        <span class="hljs-bullet">-</span> <span class="hljs-number">2</span>
        <span class="hljs-bullet">-</span> <span class="hljs-number">3</span>
        <span class="hljs-bullet">-</span> <span class="hljs-number">4</span>
        <span class="hljs-bullet">-</span> <span class="hljs-number">5</span>
      <span class="hljs-attr">default:</span> <span class="hljs-number">1</span>
  <span class="hljs-bullet">-</span> <span class="hljs-attr">type:</span> <span class="hljs-string">dropdown</span>
    <span class="hljs-attr">id:</span> <span class="hljs-string">saccade</span>
    <span class="hljs-attr">attributes:</span>
      <span class="hljs-attr">label:</span> <span class="hljs-string">Saccade</span>
      <span class="hljs-attr">description:</span> <span class="hljs-string">Set</span> <span class="hljs-string">the</span> <span class="hljs-string">saccade</span>
      <span class="hljs-attr">options:</span>
        <span class="hljs-bullet">-</span> <span class="hljs-number">1</span>
        <span class="hljs-bullet">-</span> <span class="hljs-number">2</span>
        <span class="hljs-bullet">-</span> <span class="hljs-number">3</span>
        <span class="hljs-bullet">-</span> <span class="hljs-number">4</span>
        <span class="hljs-bullet">-</span> <span class="hljs-number">5</span>
      <span class="hljs-attr">default:</span> <span class="hljs-number">0</span>
</code></pre>
<p>Issue template allows you to design a form and force users to fill the form if they want to open any issue on your repository. The above template would create something like this in the issues tab.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1722353174089/5de913aa-8f63-4f51-a482-3fc4f365aa7c.png" alt class="image--center mx-auto" /></p>
<p>We're almost there. All we need to do is to implement the actual action and make it usable by completing the <code>.github/workflows/test.yml</code> pipeline.</p>
<p>Open the <code>main.py</code> file and import the following <code>sighted</code> components.</p>
<pre><code class="lang-python"><span class="hljs-keyword">from</span> string <span class="hljs-keyword">import</span> Template               <span class="hljs-comment"># new</span>

<span class="hljs-keyword">from</span> pyaction <span class="hljs-keyword">import</span> PyAction
<span class="hljs-keyword">from</span> pyaction.auth <span class="hljs-keyword">import</span> Auth            <span class="hljs-comment"># new</span>
<span class="hljs-keyword">from</span> pyaction.issues <span class="hljs-keyword">import</span> IssueForm     <span class="hljs-comment"># new</span>
<span class="hljs-keyword">from</span> sighted <span class="hljs-keyword">import</span> Literal               <span class="hljs-comment"># new</span>
<span class="hljs-keyword">from</span> sighted.language <span class="hljs-keyword">import</span> PoS          <span class="hljs-comment"># new</span>
</code></pre>
<p>Notice we're importing <code>pyaction.auth.Auth</code> and <code>pyaction.issues.IssueForm</code>. We need the <code>IssueForm</code> class to pass it an <code>Auth</code> object and interact with the issues.</p>
<p>We're going to use LaTeX to colorize text in GitHub's markdown platform. You probably know, there is no way you could colorize a text in your markdown unless you use LaTeX. That's not a common way, but for the sake of experiments, that's fine.</p>
<p>For that matter, we need this const variable defined.</p>
<pre><code class="lang-python">...
COLORS = {
    <span class="hljs-string">"Gray"</span>: <span class="hljs-string">"{gray}"</span>,
    <span class="hljs-string">"Light Gray"</span>: <span class="hljs-string">"{lightgray}"</span>,
    <span class="hljs-string">"Dark Gray"</span>: <span class="hljs-string">"{lightgray}"</span>,
    <span class="hljs-string">"Red"</span>: <span class="hljs-string">"{red}"</span>,
    <span class="hljs-string">"Yellow"</span>: <span class="hljs-string">"{yellow}"</span>,
    <span class="hljs-string">"Green"</span>: <span class="hljs-string">"{green}"</span>,
}
</code></pre>
<p>Let's focus on the <code>main</code> function. Consider this one.</p>
<pre><code class="lang-python">...
workflow = PyAction()

<span class="hljs-meta">@workflow.action()</span>
<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">my_action</span>(<span class="hljs-params">github_token: str, repository: str, issue_number: int</span>) -&gt; <span class="hljs-keyword">None</span>:</span>
    auth = Auth(token=github_token)

    repo = auth.github.get_repo(repository)
    user_input = IssueForm(repo=repo, number=issue_number).render()

    language = Literal(
        text=user_input[<span class="hljs-string">"Text"</span>],
        fixation=int(user_input[<span class="hljs-string">"Fixation"</span>]),
        saccade=int(user_input[<span class="hljs-string">"Saccade"</span>]),
        ignore_pos=[PoS.PUNCT],
    )

    color = COLORS[user_input[<span class="hljs-string">"Color"</span>]]
    prefix, postfix = <span class="hljs-string">f"$`\color<span class="hljs-subst">{color}</span>"</span>, <span class="hljs-string">"`$"</span>

    transformed_text = language.transform(
        template=Template(<span class="hljs-string">f" $`$fix`$$<span class="hljs-subst">{prefix}</span>$unfix<span class="hljs-subst">{postfix}</span>"</span>)
    )

    workflow.write({<span class="hljs-string">"bionic_text"</span>: <span class="hljs-string">""</span>.join(list(transformed_text))})
</code></pre>
<p>It first reads the issue. Serializes that and takes <code>Text</code>, <code>Fixation</code>, <code>Saccade</code>, and <code>Color</code>. Then it creates <code>prefix</code> and <code>postfix</code> based on the <code>color</code>. Finally, the whole bionic transformed content will be returned under the variable name of <code>bionic_text</code>.</p>
<p>Open the <code>.github/workflows/test.yml</code>. Paste the following workflow steps.</p>
<pre><code class="lang-yaml"><span class="hljs-attr">name:</span> <span class="hljs-string">Testing</span>

<span class="hljs-attr">on:</span>
  <span class="hljs-attr">issues:</span>
    <span class="hljs-attr">types:</span> [<span class="hljs-string">opened</span>, <span class="hljs-string">reopened</span>]

<span class="hljs-attr">jobs:</span>
  <span class="hljs-attr">Test:</span>
    <span class="hljs-attr">runs-on:</span> <span class="hljs-string">ubuntu-latest</span>
    <span class="hljs-attr">name:</span> <span class="hljs-string">Testing</span> <span class="hljs-string">the</span> <span class="hljs-string">action</span>
    <span class="hljs-attr">steps:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Checkout</span>
        <span class="hljs-attr">uses:</span> <span class="hljs-string">actions/checkout@v2</span>

      <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Create</span> <span class="hljs-string">processing</span> <span class="hljs-string">comment</span>
        <span class="hljs-attr">uses:</span> <span class="hljs-string">peter-evans/create-or-update-comment@v4</span>
        <span class="hljs-attr">id:</span> <span class="hljs-string">comment</span>
        <span class="hljs-attr">with:</span>
          <span class="hljs-attr">issue-number:</span> <span class="hljs-string">${{</span> <span class="hljs-string">github.event.issue.number</span> <span class="hljs-string">}}</span>
          <span class="hljs-attr">body:</span> <span class="hljs-string">Processing..</span>

      <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Running</span> <span class="hljs-string">the</span> <span class="hljs-string">action</span>
        <span class="hljs-attr">uses:</span> <span class="hljs-string">./</span>
        <span class="hljs-attr">id:</span> <span class="hljs-string">sighted</span>
        <span class="hljs-attr">with:</span>
          <span class="hljs-attr">issue_number:</span> <span class="hljs-string">${{</span> <span class="hljs-string">github.event.issue.number</span> <span class="hljs-string">}}</span>

      <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Update</span> <span class="hljs-string">comment</span>
        <span class="hljs-attr">uses:</span> <span class="hljs-string">peter-evans/create-or-update-comment@v4</span>
        <span class="hljs-attr">with:</span>
          <span class="hljs-attr">comment-id:</span> <span class="hljs-string">${{</span> <span class="hljs-string">steps.comment.outputs.comment-id</span> <span class="hljs-string">}}</span>
          <span class="hljs-attr">body:</span> <span class="hljs-string">${{</span> <span class="hljs-string">steps.sighted.outputs.bionic_text</span> <span class="hljs-string">}}</span>
          <span class="hljs-attr">reactions:</span> <span class="hljs-string">rocket</span>
          <span class="hljs-attr">edit-mode:</span> <span class="hljs-string">replace</span>
</code></pre>
<p>It gets triggered whenever an issue gets opened or reopened. Creates a comment in the issue form. Runs the action. Updates the comment that it just created. Here is the result.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1722354455443/0278f05f-56a2-4e2c-8793-bc9a1fd39643.png" alt class="image--center mx-auto" /></p>
<p>Hopefully, you enjoyed this practice!</p>
<h3 id="heading-conclusion">Conclusion</h3>
<p>In this article, we created a GitHub Action using PyAction that triggers when an issue is opened or reopened, reads the issue data, uses Sighted to analyze and transform the text into bionic text, and displays the result in a comment. We walked through the installation, setup, and development process, including defining inputs and outputs, creating an issue template, implementing the main functionality with Python imports, and setting up a GitHub workflow to automate the process.</p>
]]></content:encoded></item><item><title><![CDATA[10 Python Easter Eggs]]></title><description><![CDATA[Python, the versatile and widely used programming language, is known for its readability and simplicity. However, beneath its surface lies a whimsical side, with numerous Easter eggs that reflect the playful nature of its developers. These Easter egg...]]></description><link>https://blog.imsadra.dev/10-python-easter-eggs</link><guid isPermaLink="true">https://blog.imsadra.dev/10-python-easter-eggs</guid><category><![CDATA[Python]]></category><category><![CDATA[Python 3]]></category><category><![CDATA[easter eggs]]></category><category><![CDATA[best practices]]></category><category><![CDATA[fun]]></category><category><![CDATA[software]]></category><dc:creator><![CDATA[Sadra Yahyapour]]></dc:creator><pubDate>Mon, 29 Jul 2024 17:05:23 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1722274117791/2a0c192a-ac8a-4dd4-9f17-3a87b4f5d572.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Python, the versatile and widely used programming language, is known for its readability and simplicity. However, beneath its surface lies a whimsical side, with numerous Easter eggs that reflect the playful nature of its developers. These Easter eggs range from humorous references to historical nods and clever tricks, each adding a touch of personality to the language. In this article, we'll dive into ten fascinating Python Easter eggs, complete with code snippets to illustrate these hidden gems.</p>
<h3 id="heading-1-the-zen-of-python">1. The Zen of Python</h3>
<p>One of the most famous Easter eggs in Python is the "Zen of Python," a collection of guiding principles for writing computer programs. These aphorisms were written by Tim Peters and can be displayed by importing the <code>this</code> module.</p>
<pre><code class="lang-python"><span class="hljs-keyword">import</span> this
</code></pre>
<p>Upon executing this code, you'll be greeted with the Zen of Python:</p>
<pre><code class="lang-plaintext">The Zen of Python, by Tim Peters

Beautiful is better than ugly.
Explicit is better than implicit.
Simple is better than complex.
Complex is better than complicated.
Flat is better than nested.
Sparse is better than dense.
Readability counts.
Special cases aren't special enough to break the rules.
Although practicality beats purity.
Errors should never pass silently.
Unless explicitly silenced.
In the face of ambiguity, refuse the temptation to guess.
There should be one-- and preferably only one --obvious way to do it.
Although that way may not be obvious at first unless you're Dutch.
Now is better than never.
Although never is often better than *right* now.
If the implementation is hard to explain, it's a bad idea.
If the implementation is easy to explain, it may be a good idea.
Namespaces are one honking great idea -- let's do more of those!
</code></pre>
<p>Devs are sometimes good philosophers. Check this out.</p>
<pre><code class="lang-python"><span class="hljs-meta">&gt;&gt;&gt; </span><span class="hljs-keyword">import</span> this
<span class="hljs-meta">&gt;&gt;&gt; </span>love = this
<span class="hljs-meta">&gt;&gt;&gt; </span>this <span class="hljs-keyword">is</span> love
<span class="hljs-literal">True</span>
<span class="hljs-meta">&gt;&gt;&gt; </span>love <span class="hljs-keyword">is</span> <span class="hljs-literal">True</span>
<span class="hljs-literal">False</span>
<span class="hljs-meta">&gt;&gt;&gt; </span>love <span class="hljs-keyword">is</span> <span class="hljs-literal">False</span>
<span class="hljs-literal">False</span>
<span class="hljs-meta">&gt;&gt;&gt; </span>love <span class="hljs-keyword">is</span> <span class="hljs-keyword">not</span> <span class="hljs-literal">True</span> <span class="hljs-keyword">or</span> <span class="hljs-literal">False</span>
<span class="hljs-literal">True</span>
<span class="hljs-meta">&gt;&gt;&gt; </span>love <span class="hljs-keyword">is</span> <span class="hljs-keyword">not</span> <span class="hljs-literal">True</span> <span class="hljs-keyword">or</span> <span class="hljs-literal">False</span>; love <span class="hljs-keyword">is</span> love
<span class="hljs-literal">True</span>
</code></pre>
<p>Poetic. Isn't it?!</p>
<h3 id="heading-2-importing-antigravity">2. Importing Antigravity</h3>
<p>Python takes you on a literal journey when you import the <code>antigravity</code> module. This Easter egg references a famous XKCD comic strip about Python.</p>
<pre><code class="lang-python"><span class="hljs-keyword">import</span> antigravity
</code></pre>
<p>Executing this code opens your web browser and takes you to the XKCD comic about Python:</p>
<p><img src="https://imgs.xkcd.com/comics/python.png" alt="XKCD Python" class="image--center mx-auto" /></p>
<h3 id="heading-3-the-walrus-operator">3. The Walrus Operator</h3>
<p>Introduced in Python 3.8, the walrus operator (<code>:=</code>) allows assignment expressions. The fun part is that it's named after it resembles the eyes and tusks of a walrus.</p>
<pre><code class="lang-python"><span class="hljs-keyword">if</span> (n := len(<span class="hljs-string">'walrus'</span>)) &gt; <span class="hljs-number">5</span>:
    print(<span class="hljs-string">f"The length of 'walrus' is <span class="hljs-subst">{n}</span>"</span>)
</code></pre>
<p>Output:</p>
<pre><code class="lang-plaintext">The length of 'walrus' is 6
</code></pre>
<h3 id="heading-4-barrys-eggs">4. Barry's Eggs</h3>
<p>There's a quirky Easter egg that references Barry Warsaw, one of Python's developers. By using the <code>from __future__ import barry_as_FLUFL</code> statement, you can enable an alternative form of assignment using the <code>&lt;&gt;</code> operator, reminiscent of the older days of Python.</p>
<pre><code class="lang-python"><span class="hljs-keyword">from</span> __future__ <span class="hljs-keyword">import</span> barry_as_FLUFL

<span class="hljs-comment"># This will work with the import above</span>
<span class="hljs-number">2</span> &lt;&gt; <span class="hljs-number">3</span>
</code></pre>
<p>Output:</p>
<pre><code class="lang-plaintext">True
</code></pre>
<p>But if you do this, it'll raise an exception.</p>
<pre><code class="lang-python"><span class="hljs-number">2</span> != <span class="hljs-number">3</span>
<span class="hljs-comment"># SyntaxError: with Barry as BDFL, use '&lt;&gt;' instead of '!='</span>
</code></pre>
<h3 id="heading-5-hello-world">5. Hello, World!</h3>
<p>The classic "Hello, World!" program has a playful twist when using the <code>__hello__</code> module.</p>
<pre><code class="lang-python"><span class="hljs-keyword">import</span> __hello__
</code></pre>
<p>Output:</p>
<pre><code class="lang-plaintext">Hello world!
</code></pre>
<p>Whenever you install Python in a fresh environment, you can also do this to ensure your Python setup process is successful.</p>
<pre><code class="lang-bash">$ python -c <span class="hljs-string">"import __hello__; __hello__.main()"</span>
Hello world!
</code></pre>
<h3 id="heading-6-braces-in-python">6. Braces in Python</h3>
<p>Python's design philosophy famously excludes the use of braces (<code>{}</code>) for code blocks, preferring indentation instead. However, there's an Easter egg that humorously acknowledges this design choice.</p>
<pre><code class="lang-python"><span class="hljs-keyword">from</span> __future__ <span class="hljs-keyword">import</span> braces
</code></pre>
<p>Instead of enabling braces, this code raises a <code>SyntaxError</code> with the message "not a chance."</p>
<p>Output:</p>
<pre><code class="lang-plaintext">  File "&lt;stdin&gt;", line 1
    from __future__ import braces
                                  ^
SyntaxError: not a chance
</code></pre>
<p>I guess we'll never see anyone using braces in Python ever.</p>
<h3 id="heading-7-time-travel-with-timeit">7. Time Travel with <code>timeit</code></h3>
<p>The <code>timeit</code> module is used for timing the execution of small code snippets. But did you know it also contains a cheeky reference to "time travel"?</p>
<pre><code class="lang-python"><span class="hljs-keyword">import</span> timeit

timeit.timeit(<span class="hljs-string">'"-".join(str(n) for n in range(100))'</span>, number=<span class="hljs-number">10000</span>)
</code></pre>
<p>Output:</p>
<pre><code class="lang-plaintext">0.28043669999999996
</code></pre>
<p>While this example shows the normal use of <code>timeit</code>, the module's documentation contains whimsical references to time travel, such as "If you can time travel, then you're doing it wrong."</p>
<h3 id="heading-8-python-philosophy">8. Python Philosophy</h3>
<p>Python's <code>import this</code> reveals the Zen of Python, but there's also another philosophical Easter egg when you try to import <code>__phello__</code>.</p>
<pre><code class="lang-python"><span class="hljs-keyword">import</span> __phello__.world
</code></pre>
<p>Output:</p>
<pre><code class="lang-plaintext">Hello world!
</code></pre>
<p>This playful module echoes the traditional "Hello, World!" while embedding it in a mysterious, underscore-laden package name. Somehow similar to <code>import __hello__</code>.</p>
<h3 id="heading-9-swapping-values">9. Swapping Values</h3>
<p>Python allows for elegant value swapping without a temporary variable. While not strictly an Easter egg, this feature often surprises newcomers.</p>
<pre><code class="lang-python">a, b = <span class="hljs-number">1</span>, <span class="hljs-number">2</span>
a, b = b, a
print(a, b)
</code></pre>
<p>Output:</p>
<pre><code class="lang-plaintext">2 1
</code></pre>
<h3 id="heading-10-exception-thrower">10. Exception Thrower</h3>
<p>In Python 3.9, <code>__peg_parser__</code> would throw a syntax error with a certain message of "You found it!".</p>
<pre><code class="lang-plaintext">&gt;&gt;&gt; __peg_parser__
  File "&lt;stdin&gt;", line 1
    __peg_parser__
    ^
SyntaxError: You found it!
</code></pre>
<h2 id="heading-conclusion">Conclusion</h2>
<p>Python's Easter eggs add a layer of charm and character to the language, reminding us that programming can be both a serious and fun endeavor. These hidden gems not only showcase the creativity of Python's developers but also invite us to explore and enjoy the lighter side of coding. Whether you're a seasoned Pythonista or a curious newcomer, these Easter eggs are sure to bring a smile to your face and a deeper appreciation for the delightful intricacies of Python. Happy coding!</p>
]]></content:encoded></item><item><title><![CDATA[Sighted - Read Faster, Comprehend Better With AI!]]></title><description><![CDATA[Highly recommended article for individuals with ADHD (Attention Deficit Hyperactivity Disorder) and Dyslexia.

Sighted is LIVE here!

Distraction. A very familiar term in recent decades. The Internet has been a blessing for humanity, but it has also ...]]></description><link>https://blog.imsadra.dev/sighted-read-faster-comprehend-better-with-ai</link><guid isPermaLink="true">https://blog.imsadra.dev/sighted-read-faster-comprehend-better-with-ai</guid><category><![CDATA[bionic]]></category><category><![CDATA[AIForTomorrow]]></category><category><![CDATA[AI]]></category><category><![CDATA[#ai-tools]]></category><category><![CDATA[nlp]]></category><category><![CDATA[nlp transformers]]></category><category><![CDATA[Open Source]]></category><category><![CDATA[hackathon]]></category><category><![CDATA[Python]]></category><category><![CDATA[React]]></category><dc:creator><![CDATA[Sadra Yahyapour]]></dc:creator><pubDate>Mon, 22 Jul 2024 12:31:56 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1721404415261/18f56e42-3b8d-486e-abeb-6473d3166c80.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p><strong>Highly recommended article for individuals with ADHD (Attention Deficit Hyperactivity Disorder) and Dyslexia.</strong></p>
<ul>
<li><a target="_blank" href="https://sighted.vercel.app">Sighted is LIVE here!</a></li>
</ul>
<p>Distraction. A very familiar term in recent decades. The Internet has been a blessing for humanity, but it has also caused several issues. When was the last time you managed to finish a book? How did it go? Have you noticed any difference between reading today and reading in 2000? How engrossed in a book are you today compared to before?</p>
<p>In this article, we'll introduce an AI-powered solution to improve reading quality. This is not just a basic introduction or a project demo. I'll guide you through all the decision-making phases I went through during the development of this project.</p>
<h3 id="heading-problem">Problem</h3>
<p>I had ADHD as a child. My parents always said that there was nothing I could do without rushing. I was like Road Runner (the animation). 😅</p>
<p>Almost all of those behaviors disappeared as I grew up. However, one habit that remains is my inability to stick with books for a reasonable amount of time.</p>
<p>I read a paragraph, then take a break. I get back to it for a few minutes, take another break, and continue this way. Although my every smart device is on DND (Do Not Disturb) mode. I did some research and observed myself in different situations.</p>
<p>I realized that I needed to change myself first, but I had already tried that many times. It didn't seem to work. So, I thought, what if I change the subject? What if I modify the book to make it easier to follow, understand, and finish more quickly?</p>
<p>Then I came up with a concept called "Focus Reading" or "Bionic Reading".</p>
<h3 id="heading-bionic-reading">Bionic Reading</h3>
<p>Bionic solutions use technology inspired by nature to solve problems. Bionic Reading is one such method, designed to help people read more efficiently. It highlights parts of the text, making it easier for the eyes to follow and understand. By emphasizing key elements, Bionic Reading helps readers process information faster, reducing cognitive load and improving reading speed. This technique benefits those with attention difficulties and anyone wanting to enhance their reading skills and retention.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1721653855199/e809af6c-88d6-445f-8d88-3d3d5f58d8fd.png" alt="Sighted demo" class="image--center mx-auto" /></p>
<h3 id="heading-usage">Usage</h3>
<p>Sighted is available as an installable Python package. I designed it to be reusable and easy to install on various instances, making it compatible with front-end frameworks like React and Vue. This approach allows you to use Sighted even on language learning platforms.</p>
<h4 id="heading-installation">Installation</h4>
<p>Make sure you have <code>pip</code> and <code>python&gt;=3.8</code> installed on your computer or instance, then run the following command.</p>
<pre><code class="lang-bash">pip install sighted
</code></pre>
<p>Sighted comes with a ~12MB pre-trained very efficient model. It's built into the library, so you won't need to download anything extra.</p>
<h4 id="heading-usage-1">Usage</h4>
<p>Create a Python file where we will import the necessary classes and functions.</p>
<pre><code class="lang-python"><span class="hljs-keyword">from</span> sighted <span class="hljs-keyword">import</span> Literal
<span class="hljs-keyword">from</span> sighted.language <span class="hljs-keyword">import</span> PoS

<span class="hljs-keyword">from</span> string <span class="hljs-keyword">import</span> Template
</code></pre>
<p>We're using the <code>Literal</code> approach for transforming. Let's create an object.</p>
<pre><code class="lang-python">text = <span class="hljs-string">"Hello there. This is an experimental text to showcase \
        how fast you can read with Sighted."</span>

language = Literal(
    text=text, 
    fixation=<span class="hljs-number">2</span>,
)
</code></pre>
<p>I've defined my <code>text</code> and created the <code>language</code> object, which will be transformed using the pattern I will specify later. Sighted analyzes every character, glyph, and symbol in the given text and applies the chosen pattern to it.</p>
<p>If you want to ignore certain parts of speech in the text, use the <code>PoS</code> enum, which includes almost all necessary parts of speech. In our example, I'm going to ignore all punctuation marks (<strong>period, comma, apostrophe, quotation, question mark, exclamation mark, brackets, braces, parentheses, dash, hyphen, ellipsis, colon, and semicolon</strong>).</p>
<pre><code class="lang-python">language = Literal(
    text=text, 
    fixation=<span class="hljs-number">2</span>,
    ignore_pos=[PoS.PUNCT]   <span class="hljs-comment">#  &lt;-- new</span>
)
</code></pre>
<p>I'll discuss <code>saccade</code> and <code>fixation</code> later, but for now, let's focus on the transformation process.</p>
<pre><code class="lang-python">transformed_text = language.transform(
    template=Template(<span class="hljs-string">' &lt;span class="$pos" id="$dep"&gt;&lt;b&gt;$fix&lt;/b&gt;$unfix&lt;/span&gt;'</span>)
)
</code></pre>
<p>I'm using the native <code>string.Template</code> for template definition because Sighted can safely insert information into it. For each word in my text, I'm transforming it into a <code>&lt;span /&gt;</code> HTML tag.</p>
<ul>
<li><p><code>$fix</code> : The fixed (bold) part of the word.</p>
</li>
<li><p><code>$unfix</code> : The part of the text that shouldn't be bolded.</p>
</li>
<li><p><code>$pos</code> : Part of speech of the word in the text.</p>
</li>
<li><p><code>$dep</code> : Syntactic dependency of the word in the text.</p>
</li>
</ul>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1721647312740/4d47fd60-c66f-4888-9603-3d618542f62d.png" alt class="image--center mx-auto" /></p>
<p>The <code>transformed_text</code> is an iterable object. If I loop over it and print what it contains, I'll get this result.</p>
<pre><code class="lang-xml"><span class="hljs-tag">&lt;<span class="hljs-name">span</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"intj"</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"ROOT"</span>&gt;</span><span class="hljs-tag">&lt;<span class="hljs-name">b</span>&gt;</span>He<span class="hljs-tag">&lt;/<span class="hljs-name">b</span>&gt;</span>llo<span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">span</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"adv"</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"advmod"</span>&gt;</span><span class="hljs-tag">&lt;<span class="hljs-name">b</span>&gt;</span>th<span class="hljs-tag">&lt;/<span class="hljs-name">b</span>&gt;</span>ere<span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span>
. <span class="hljs-comment">&lt;!-- Look how this punctuation is ignored --&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">span</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"pron"</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"nsubj"</span>&gt;</span><span class="hljs-tag">&lt;<span class="hljs-name">b</span>&gt;</span>Th<span class="hljs-tag">&lt;/<span class="hljs-name">b</span>&gt;</span>is<span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">span</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"aux"</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"ROOT"</span>&gt;</span><span class="hljs-tag">&lt;<span class="hljs-name">b</span>&gt;</span>i<span class="hljs-tag">&lt;/<span class="hljs-name">b</span>&gt;</span>s<span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">span</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"det"</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"det"</span>&gt;</span><span class="hljs-tag">&lt;<span class="hljs-name">b</span>&gt;</span>a<span class="hljs-tag">&lt;/<span class="hljs-name">b</span>&gt;</span>n<span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">span</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"adj"</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"amod"</span>&gt;</span><span class="hljs-tag">&lt;<span class="hljs-name">b</span>&gt;</span>exper<span class="hljs-tag">&lt;/<span class="hljs-name">b</span>&gt;</span>imental<span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">span</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"noun"</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"attr"</span>&gt;</span><span class="hljs-tag">&lt;<span class="hljs-name">b</span>&gt;</span>te<span class="hljs-tag">&lt;/<span class="hljs-name">b</span>&gt;</span>xt<span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">span</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"part"</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"aux"</span>&gt;</span><span class="hljs-tag">&lt;<span class="hljs-name">b</span>&gt;</span>t<span class="hljs-tag">&lt;/<span class="hljs-name">b</span>&gt;</span>o<span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">span</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"verb"</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"relcl"</span>&gt;</span><span class="hljs-tag">&lt;<span class="hljs-name">b</span>&gt;</span>show<span class="hljs-tag">&lt;/<span class="hljs-name">b</span>&gt;</span>case<span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">span</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"space"</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"dep"</span>&gt;</span><span class="hljs-tag">&lt;<span class="hljs-name">b</span>&gt;</span>    <span class="hljs-tag">&lt;/<span class="hljs-name">b</span>&gt;</span>    <span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">span</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"sconj"</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"advmod"</span>&gt;</span><span class="hljs-tag">&lt;<span class="hljs-name">b</span>&gt;</span>ho<span class="hljs-tag">&lt;/<span class="hljs-name">b</span>&gt;</span>w<span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">span</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"adv"</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"advmod"</span>&gt;</span><span class="hljs-tag">&lt;<span class="hljs-name">b</span>&gt;</span>fa<span class="hljs-tag">&lt;/<span class="hljs-name">b</span>&gt;</span>st<span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">span</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"pron"</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"nsubj"</span>&gt;</span><span class="hljs-tag">&lt;<span class="hljs-name">b</span>&gt;</span>yo<span class="hljs-tag">&lt;/<span class="hljs-name">b</span>&gt;</span>u<span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">span</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"aux"</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"aux"</span>&gt;</span><span class="hljs-tag">&lt;<span class="hljs-name">b</span>&gt;</span>ca<span class="hljs-tag">&lt;/<span class="hljs-name">b</span>&gt;</span>n<span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">span</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"verb"</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"ccomp"</span>&gt;</span><span class="hljs-tag">&lt;<span class="hljs-name">b</span>&gt;</span>re<span class="hljs-tag">&lt;/<span class="hljs-name">b</span>&gt;</span>ad<span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">span</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"adp"</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"prep"</span>&gt;</span><span class="hljs-tag">&lt;<span class="hljs-name">b</span>&gt;</span>wi<span class="hljs-tag">&lt;/<span class="hljs-name">b</span>&gt;</span>th<span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">span</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"propn"</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"pobj"</span>&gt;</span><span class="hljs-tag">&lt;<span class="hljs-name">b</span>&gt;</span>Sig<span class="hljs-tag">&lt;/<span class="hljs-name">b</span>&gt;</span>hted<span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span>
. <span class="hljs-comment">&lt;!-- Look how this punctuation is ignored --&gt;</span>
</code></pre>
<p>Notice the <code>class</code> attributes as well as all the IDs. If I place this result within a <code>&lt;p /&gt;</code> tag inside a very basic HTML structure, you'll get something like this.</p>
<p><img src="https://github.com/lnxpy/sighted/raw/main/media/img1.png" alt="Basic result" /></p>
<h4 id="heading-styling">Styling</h4>
<p>With a little bit of styling, you can have control over each and all parts of speech and dependencies in your text.</p>
<pre><code class="lang-css"><span class="hljs-comment">/* unfixed letters */</span>
<span class="hljs-selector-tag">p</span> { <span class="hljs-attribute">color</span>: gray; }

<span class="hljs-comment">/* fixed letters */</span>
<span class="hljs-selector-tag">b</span> { <span class="hljs-attribute">color</span>: black; }

<span class="hljs-comment">/* adverbs */</span>
<span class="hljs-selector-class">.adv</span> { <span class="hljs-attribute">border-bottom</span>: solid <span class="hljs-number">1px</span>; }

<span class="hljs-comment">/* pronouns */</span>
<span class="hljs-selector-class">.pron</span> { <span class="hljs-attribute">color</span>: red; }
<span class="hljs-selector-class">.pron</span> &gt; <span class="hljs-selector-tag">b</span> { <span class="hljs-attribute">color</span>: purple; }

<span class="hljs-comment">/* verbs */</span>
<span class="hljs-selector-class">.verb</span> {
    <span class="hljs-attribute">background-color</span>: yellow;
    <span class="hljs-attribute">padding</span>: <span class="hljs-number">3px</span>;
    <span class="hljs-attribute">border-radius</span>: <span class="hljs-number">2px</span>;
}
</code></pre>
<p><img src="https://github.com/lnxpy/sighted/raw/main/media/img2.png" alt="Styled result" /></p>
<h4 id="heading-terminal-usage">Terminal usage</h4>
<p>It's not just about HTML. You can use any template as the input for the transformer function. See how I've used this feature to display bionic outputs in my terminal!</p>
<pre><code class="lang-python"><span class="hljs-keyword">from</span> string <span class="hljs-keyword">import</span> Template

<span class="hljs-keyword">from</span> rich.console <span class="hljs-keyword">import</span> Console
<span class="hljs-keyword">from</span> rich.markdown <span class="hljs-keyword">import</span> Markdown

<span class="hljs-keyword">from</span> sighted <span class="hljs-keyword">import</span> Literal
<span class="hljs-keyword">from</span> sighted.language <span class="hljs-keyword">import</span> PoS

console = Console()
text = <span class="hljs-string">"Hello there. This is an experimental text to showcase how fast you can read with Sighted."</span>

language = Literal(
    text=text,
    fixation=<span class="hljs-number">2</span>,
    ignore_pos=[PoS.PUNCT],
)

template = Template(<span class="hljs-string">" **$fix**$unfix"</span>)
transformed_text = language.transform(template)

md = Markdown(<span class="hljs-string">""</span>.join(list(transformed_text)))

console.print(md, style=<span class="hljs-string">"white dim"</span>)
</code></pre>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1721648522685/b9613ee9-d87e-4377-86c1-8024f317df4e.png" alt="Sighted in terminal" class="image--center mx-auto" /></p>
<h3 id="heading-fixation-amp-saccade">Fixation &amp; Saccade</h3>
<p>This parameter indicates how many letters from the beginning of each word should be considered as fixation (<code>$fix</code>). It's a value between 1 to 5. As you increase it, you see that the amount of fixed letters will increase from the beginning of each word.</p>
<p><img src="https://github.com/lnxpy/sighted/raw/main/media/img3.png" alt="Fixation differences" /></p>
<p>As I mentioned, I needed to determine the fixation formula. For each word length, I selected ten words and examined the fixation on them. Each fixation value should correspond to any word length.</p>
<p>$$L = \begin{cases} 1 &amp; \text{if } W = 2 \text{ or } W &lt; 2 \\ \min\left(\left\lceil \frac{W \cdot F}{5} \right\rceil, W\right) &amp; \text{if } W \geq 3 \end{cases}$$</p><p>For words with a length of one or two, only the first letter is bolded. For words longer than three letters, the bottom equation will be considered.</p>
<p>Saccades are rapid, involuntary eye movements that play a crucial role in reading by allowing the eyes to jump quickly from one word or section of text to another.</p>
<p><img src="https://github.com/lnxpy/sighted/raw/main/media/img5.png" alt="Eye tracking and saccade" /></p>
<p>This attribute reduces the cognitive load on the brain by making it easier to recognize and process words, thereby improving reading speed and comprehension.</p>
<p><img src="https://github.com/lnxpy/sighted/raw/main/media/img4.png" alt="Saccade differences" /></p>
<p>If you want to know more about saccade, I highly recommend watching <a target="_blank" href="https://www.youtube.com/watch?v=VFIZDZwdf-0">this video on YouTube</a>.</p>
<h3 id="heading-sighted-helps-with-adhd-amp-dyslexia">Sighted Helps with ADHD &amp; <strong>Dyslexia</strong></h3>
<p>By presenting the text with one highly styled word at a chunk of space and controlled saccade and fixation, Sighted helps reduce visual distractions and improves focus.</p>
<ul>
<li><p>Improved focus and attention: By controlling the pace at which text is bolded, Sighted helps individuals with ADHD maintain focus and avoid distractions.</p>
</li>
<li><p>Reduced visual stress: The controlled presentation of text reduces the visual overload experienced by individuals with Dyslexia, making it easier for them to process and comprehend information.</p>
</li>
<li><p>Enhanced reading comprehension: It can help individuals with Dyslexia and ADHD improve their reading speed and comprehension by minimizing cognitive load and maximizing text accessibility.</p>
</li>
</ul>
<h3 id="heading-development-process">Development Process</h3>
<p>This section covers the decisions I made during the development of this project. I don't see myself as a nerd, but I hope this gives you a clear idea of the process.</p>
<h4 id="heading-day-1-starting">Day 1 - Starting</h4>
<p>I discovered that social media has caused teens, including myself, to lose their connection with books. This might be due to the problems social media caused us. So, I came up with the idea.</p>
<ul>
<li><p>Researching the idea, use cases, and similar projects.</p>
</li>
<li><p>Finding out, how it can solve a problem and who can benefit from it.</p>
</li>
<li><p>Thinking about the possible ways that the idea can be delivered.</p>
</li>
<li><p>How useful the idea is going to be. Is it even implementable?!</p>
</li>
</ul>
<h4 id="heading-day-2-researching">Day 2 - Researching</h4>
<p>I was already familiar with tokenization, literal processing, and literature to some extent. I knew some frameworks and libraries, so diving deeper into this process wasn't a big deal for me. Instead, I focused on the tools and explored how they could benefit me.</p>
<ul>
<li><p>Learned about some keywords in this specific field.</p>
<ul>
<li>Learned about terms like fixation, saccade, lemmatization, eye tracking, and how the human brain processes text. I also worked on a formula that calculates the fixation for each word based on its importance in the context of the sentence.</li>
</ul>
</li>
</ul>
<h4 id="heading-day-3-feedback">Day 3 - Feedback</h4>
<p>Well, I couldn't rely solely on my own thoughts as a customer since I won't be the only user of this service. So, I made some parts public and shared a short teaser on socials to gauge the crowd's interest. Turned out they loved it so I kept developing.</p>
<p>I also tried the same technique on RTL and cursive-written languages, but it was not as effective as in English.</p>
<ul>
<li><p>Deployed a very basic MVP and asked some friends for feedback.</p>
</li>
<li><p>Shared a teaser clip on socials to see the people's reaction.</p>
</li>
</ul>
<h4 id="heading-day-4-branding">Day 4 - Branding</h4>
<p>Branding matters, especially for my idea, which was going to be a Python package, a website, and a repository. It needed to be unique. I asked ChatGPT for some help and ultimately chose "Sighted" because it comes from "Sight" which is a fitting word in the world of biology.</p>
<ul>
<li>Choosing a brand name, slogan, logo design, and theming.</li>
</ul>
<h4 id="heading-day-5-deployment">Day 5 - Deployment 🎉</h4>
<p>Last but not least, I hit the big green button to deploy everything on the cloud. There were a few minor issues to fix, but nothing too serious. I made it through.</p>
<p>Hopefully, this section has given you a clear understanding of what a hackathon submission journey may look like!</p>
<h3 id="heading-tech-stacks">Tech Stacks</h3>
<p>For the website, I used <strong>React (TypeScript)</strong> with <strong>Aceternity UI</strong> and <strong>Shadcn UI</strong> for the UI part deployed on <strong>Vercel</strong>.</p>
<p>I used <strong>Python</strong> and <strong>SpaCy</strong> for the package development.</p>
<h3 id="heading-useful-links">Useful Links</h3>
<ul>
<li><p>Website -&gt; <a target="_blank" href="https://sighted.vercel.app">https://sighted.vercel.app</a></p>
</li>
<li><p>Website repo on GitHub -&gt; <a target="_blank" href="https://github.com/lnxpy/sighted-web">https://github.com/lnxpy/sighted-web</a></p>
</li>
<li><p>Package -&gt; <a target="_blank" href="https://pypi.org/project/sighted">https://pypi.org/project/sighted</a></p>
</li>
<li><p>Package repo on GitHub -&gt; <a target="_blank" href="https://github.com/lnxpy/sighted">https://github.com/lnxpy/sighted</a></p>
</li>
</ul>
<h3 id="heading-hashnode-bionic-reading">Hashnode + Bionic Reading</h3>
<p>What if Hashnode could publish articles in bionic mode? Let me know what you think about this cool effective feature!</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1721592018795/83aa44e3-3f89-41ab-9c91-ca920308518a.png" alt="Using Sighted on Hashnode blogs" class="image--center mx-auto" /></p>
<h3 id="heading-conclusion">Conclusion</h3>
<p>In conclusion, bionic technology offers a groundbreaking solution for individuals struggling with reading difficulties. By harnessing the power of bionic reading with AI (NLP), these individuals can access a more efficient and accessible means of consuming written content. The future of bionic reading holds significant promise in revolutionizing how we interact with written text, offering a beacon of hope for those seeking a more enriching and fulfilling reading experience.</p>
]]></content:encoded></item><item><title><![CDATA[PyAction - Create GitHub Actions Using Python!]]></title><description><![CDATA[GitHub Actions was introduced in 2018. Since then, GitHub Marketplace has witnessed numerous brilliant ideas that have impacted the Continuous Integration and Delivery world.
As GitHub says..

Actions are individual tasks that you can combine to crea...]]></description><link>https://blog.imsadra.dev/pyaction-create-github-actions-using-python</link><guid isPermaLink="true">https://blog.imsadra.dev/pyaction-create-github-actions-using-python</guid><category><![CDATA[pyaction]]></category><category><![CDATA[Python]]></category><category><![CDATA[Python 3]]></category><category><![CDATA[GitHub]]></category><category><![CDATA[github-actions]]></category><category><![CDATA[GitHub Actions]]></category><category><![CDATA[cicd]]></category><category><![CDATA[Continuous Integration]]></category><category><![CDATA[continuous deployment]]></category><category><![CDATA[Devops]]></category><category><![CDATA[Open Source]]></category><category><![CDATA[mindsdb]]></category><dc:creator><![CDATA[Sadra Yahyapour]]></dc:creator><pubDate>Sat, 11 May 2024 16:52:38 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1715301543194/b148ed33-3be3-4237-b430-ff652da393f0.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>GitHub Actions was introduced in 2018. Since then, GitHub Marketplace has witnessed numerous brilliant ideas that have impacted the Continuous Integration and Delivery world.</p>
<p>As GitHub says..</p>
<blockquote>
<p>Actions are individual tasks that you can combine to create jobs and customize your workflow. You can create your own actions, or use and customize actions shared by the GitHub community [aka Marketplace].</p>
</blockquote>
<p>In this article, we will demonstrate a simple implementation using PyAction, a tool that allows you to create actions using Python. This way, you can take advantage of Python libraries and bring your ideas to pipelines!</p>
<p>Additionally, we will discuss the recent release of PyAction and the new features that have been introduced in version 0.6.x.</p>
<h3 id="heading-actions-in-a-nutshell">Actions in a Nutshell</h3>
<p>In the following example, you see a simple pipeline in GitHub Actions that triggers on the <code>push</code> event and executes the bash script <code>echo Hello world!</code>. You can place any bash script there to run, including an API call using <code>curl</code> maybe!</p>
<pre><code class="lang-yaml"><span class="hljs-attr">name:</span> <span class="hljs-string">CI</span> <span class="hljs-string">Example</span>

<span class="hljs-attr">on:</span>
  <span class="hljs-attr">push:</span>
    <span class="hljs-attr">branches:</span> [<span class="hljs-string">main</span>]

<span class="hljs-attr">jobs:</span>
  <span class="hljs-attr">Builder:</span>
    <span class="hljs-attr">runs-on:</span> <span class="hljs-string">ubuntu-latest</span>

    <span class="hljs-attr">steps:</span>
    <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Greeting</span>
      <span class="hljs-attr">run:</span> <span class="hljs-string">echo</span> <span class="hljs-string">Hello</span> <span class="hljs-string">world!</span>
</code></pre>
<p>We have a <code>Builder</code> job with just one step, which is executing a shell script. Actions are like functions. Considering the earlier example, what if you wanted to send an email instead of running a bash script?!</p>
<pre><code class="lang-yaml">    <span class="hljs-string">...</span>
    <span class="hljs-attr">steps:</span>
    <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Sending</span> <span class="hljs-string">email</span>
      <span class="hljs-attr">run:</span> <span class="hljs-string">python</span> <span class="hljs-string">send_email.py</span> <span class="hljs-comment"># hmm.. let's see.</span>
</code></pre>
<p>Well, thanks to <a class="user-mention" href="https://hashnode.com/@dawidd6">Dawid Dziurla</a>, there is already an action designed for this purpose. You can modify your pipeline to use it in this manner.</p>
<pre><code class="lang-yaml">    <span class="hljs-string">...</span>
    <span class="hljs-attr">steps:</span>
    <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Send</span> <span class="hljs-string">mail</span>
      <span class="hljs-attr">uses:</span> <span class="hljs-string">dawidd6/action-send-mail@v3</span>
      <span class="hljs-attr">with:</span>
        <span class="hljs-attr">subject:</span> <span class="hljs-string">Github</span> <span class="hljs-string">Actions</span> <span class="hljs-string">job</span> <span class="hljs-string">result</span>
        <span class="hljs-attr">to:</span> <span class="hljs-string">someone@somewhere.com</span>
        <span class="hljs-attr">from:</span> <span class="hljs-string">Sadra</span> <span class="hljs-string">Yahyapour</span>
        <span class="hljs-attr">body:</span> <span class="hljs-string">CI</span> <span class="hljs-string">workflow</span> <span class="hljs-string">executed</span> <span class="hljs-string">successfully!</span>
</code></pre>
<p>As you can see, we are now using the <code>dawidd6/action-send-mail</code> release <code>v3</code> action with certain input parameters (<code>subject</code>, <code>to</code>, <code>from</code>, and <code>body</code>).</p>
<p>The aim here is to introduce a solution for creating actions using Python and have the ability to call it with the desired input parameters.</p>
<p>Worth noting that the <code>action-send-email</code> is written in JavaScript and we're talking snakes here. <a target="_blank" href="https://emojipedia.org/snake">🐍</a></p>
<h3 id="heading-creating-an-action">Creating an Action</h3>
<p>Technically, GitHub offers three ways to develop custom actions.</p>
<ul>
<li><p>JavaScript actions</p>
</li>
<li><p>Docker container actions</p>
</li>
<li><p>Composite actions</p>
</li>
</ul>
<p>Let's focus on the Docker container and Composite options. Composite actions are actions created using only bash scripts. Essentially, you have the freedom to perform various tasks within a Composite action. You can compile and execute C programs, install tools based on the optionally-limited <code>runs-on</code> image, make API calls, and more. In essence, Composite actions offer flexibility.</p>
<p>On the other hand, Docker container actions are restricted to the base image specified in the <code>Dockerfile</code>. You can then add additional layers. PyAction follows the Docker container approach and runs your action within a <code>python:3-slim</code> environment. This is what PyAction uses to run your Python actions.</p>
<h3 id="heading-using-pyaction">Using PyAction</h3>
<p>PyAction is delivered as a third-party package needed for your action to function. Despite this, it is very stable in the local environment for testing. Therefore, you can use it on your local machine easily and even test your action before deploying it.</p>
<h4 id="heading-installation">Installation</h4>
<p>Make sure you have <code>pip</code> and <code>python&gt;=3.8</code> available on your machine and run the following command to install <code>pyaction</code>.</p>
<pre><code class="lang-bash">pip install -U <span class="hljs-string">"pyaction[cli]"</span>
</code></pre>
<h4 id="heading-creating-a-base-template">Creating a base template</h4>
<p>It is highly recommended to use the <code>init</code> command to generate a base template for your action. You can then build your action on top of this structure by answering some questions.</p>
<pre><code class="lang-bash">pyaction init
</code></pre>
<div class="hn-table">
<table>
<thead>
<tr>
<td><strong>Question</strong></td><td><strong>Description</strong></td><td><strong>Example</strong></td></tr>
</thead>
<tbody>
<tr>
<td>Action name</td><td>Action name</td><td>SMS Action</td></tr>
<tr>
<td>Action's slug</td><td>Slugged version of the <code>Action name</code> (better left with the default value)</td><td>sms-action</td></tr>
<tr>
<td>Short description</td><td>Short description about the action</td><td>Sending SMS Messages</td></tr>
<tr>
<td>Author's name</td><td>Author's name</td><td>John Doe</td></tr>
<tr>
<td>Include workflow testing pipeline</td><td>Creates a <code>.github/workflows/test.yml</code> for self-testing the action</td><td><code>y</code></td></tr>
</tbody>
</table>
</div><p>This prompting would give us the following action tree.</p>
<pre><code class="lang-bash">sms-action/
├── Dockerfile
├── README.md
├── action.yml
├── main.py
└── requirements.txt

1 directory, 5 files
</code></pre>
<h4 id="heading-hello-world-example">Hello world example</h4>
<p>Let's take a look at a simple <code>hello-world</code> example created with PyAction. The goal is to set up an input parameter for users to send their names, and then we will greet them!</p>
<p>After you've initialized the template using the <code>init</code> command, it's time to define the input/output parameters. Open the <code>action.yml</code> file to declare them.</p>
<pre><code class="lang-yaml"><span class="hljs-string">...</span>
<span class="hljs-attr">inputs:</span>
  <span class="hljs-attr">name:</span>
    <span class="hljs-attr">description:</span> <span class="hljs-string">user's</span> <span class="hljs-string">name</span>
    <span class="hljs-attr">default:</span> <span class="hljs-string">World</span>

<span class="hljs-attr">outputs:</span>
  <span class="hljs-attr">phrase:</span>
    <span class="hljs-attr">description:</span> <span class="hljs-string">greeting</span> <span class="hljs-string">message</span>
</code></pre>
<p>Open the <code>main.py</code> file. And create the very basic <code>greeting_action</code> inside it.</p>
<pre><code class="lang-python"><span class="hljs-keyword">from</span> pyaction <span class="hljs-keyword">import</span> PyAction


workflow = PyAction()

<span class="hljs-meta">@workflow.action()</span>
<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">greeting_action</span>(<span class="hljs-params">name: str</span>) -&gt; <span class="hljs-keyword">None</span>:</span>
    workflow.write(
        {
            <span class="hljs-string">"phrase"</span>: <span class="hljs-string">f"Hello <span class="hljs-subst">{name}</span>!"</span>
        }
    )
</code></pre>
<p>As you can see, the <code>greeting_action</code> takes one parameter called <code>name</code> which is a string. It also writes to the workflow the variable <code>phrase</code>. We've defined them both in the <code>action.yml</code> file.</p>
<h4 id="heading-testing">Testing</h4>
<p>To test if the action works as expected, there is a <code>run</code> command that does the job for us. However, before we proceed with the execution, we need to simulate how action runners invoke the actions with the defined input parameters.</p>
<p>Create a <code>.env</code> file inside the action to simulate the input parameters of the action.</p>
<pre><code class="lang-bash">INPUT_NAME=Sadra
</code></pre>
<p>Now, by running the <code>run</code> command, you'll see the variables that the <code>workflow.write()</code> method writes to the workflow.</p>
<pre><code class="lang-bash">pyaction run
phrase=Hello Sadra!
</code></pre>
<p>The <code>phrase</code> variable is accessible in other steps of the user's workflow.</p>
<pre><code class="lang-yaml">    <span class="hljs-string">...</span>
    <span class="hljs-attr">steps:</span>
    <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Greeting</span> <span class="hljs-string">action</span>
      <span class="hljs-attr">id:</span> <span class="hljs-string">greeting</span>
      <span class="hljs-attr">uses:</span> <span class="hljs-string">you/greeting-action@main</span>

   <span class="hljs-attr">steps:</span>
    <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Showing</span> <span class="hljs-string">the</span> <span class="hljs-string">message</span>
      <span class="hljs-attr">run:</span> <span class="hljs-string">echo</span> <span class="hljs-string">${{</span> <span class="hljs-string">steps.greeting.outputs.phrase</span> <span class="hljs-string">}}</span>
</code></pre>
<h4 id="heading-publishing">Publishing</h4>
<p>To publish the action on the marketplace, you need to consider certain things during the development process. After developing your action, there are some post-development tasks to complete before delivering it to the GitHub Marketplace, such as tagging and releasing it. If you wish to proceed, refer to the <a target="_blank" href="https://pyaction.imsadra.me/tutorial/#publishing-in-the-marketplace"><strong>publishing in the marketplace</strong></a> section in the documentation.</p>
<p>This <code>hello-world</code> action is also available on <a target="_blank" href="https://github.com/lnxpy/pyaction-hello-world">https://github.com/lnxpy/pyaction-hello-world</a>. Feel free to check it out! ✨</p>
<h3 id="heading-new-features-in-v06">New Features in v0.6</h3>
<p>In the new v0.6 release, a few major features were announced.</p>
<h4 id="heading-issue-form-feature">Issue Form feature</h4>
<p>As GitHub describes the issue forms..</p>
<blockquote>
<p>You can define different input types, validations, default assignees, and default labels for your issue forms.</p>
</blockquote>
<p>It's a reliable solution for controlling users' input when they open issues on your repositories. By using this feature in PyAction, you can allow users to interact with your actions through the issues tab by filling out the designed form.</p>
<p>For example, take a look at the following repository. It uses the Issue Form feature to serialize the details within the opened issue. It then utilizes this information to invoke MindsDB's <a target="_blank" href="https://mdb.ai">mdb.ai</a> service and obtain the output from ChatGPT. Lastly, it comments on the result.</p>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://github.com/lnxpy/mdb-ai-prompted-text">https://github.com/lnxpy/mdb-ai-prompted-text</a></div>
<p> </p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1715446091005/11a2b4f7-943d-48f1-8691-4182c67be7be.png" alt class="image--center mx-auto" /></p>
<p>Check out <a target="_blank" href="https://pyaction.imsadra.me/tutorial/#issueform">this page</a> for more information about how this feature works.</p>
<h4 id="heading-running-additional-bash-scripts-before-invoking-the-action">Running additional bash scripts before invoking the action</h4>
<p>If the action requires some additional system dependencies or you want to run some additional commands before the action starts running, you can put all the commands inside a <code>script.sh</code> file inside the action directory. It'll be executed even before your action's Pythonic dependencies installation.</p>
<p>Check out <a target="_blank" href="https://pyaction.imsadra.me/tutorial/#running-additional-commands">this page</a> for more information.</p>
<h4 id="heading-local-testing">Local testing</h4>
<p>With the <code>run</code> command, test the action locally before publishing your action.</p>
<h3 id="heading-composite-actions-vs-pyaction">Composite Actions vs PyAction</h3>
<p>PyAction uses the Docker container convention to create an isolated environment using DockerHub-hosted images. On the other hand, with a composite approach, you have to go through several steps to set up a Python-friendly environment before implementing your idea.</p>
<p>In a composite action, you often make many external API calls. In contrast, PyAction relies on internal API calls, which are quicker and more secure.</p>
<p>Ultimately, PyAction simplifies local action testing by simulating inputs and outputs.</p>
<h3 id="heading-useful-links">Useful Links</h3>
<ul>
<li><p>PyAction Repository: https://github.com/lnxpy/pyaction</p>
</li>
<li><p>PyAction Docs: https://pyaction.imsadra.me</p>
</li>
<li><p>mdb.ai Action (that uses Issue Forms): <a target="_blank" href="https://github.com/lnxpy/mdb-ai-prompted-text">https://github.com/lnxpy/mdb-ai-prompted-text</a></p>
</li>
</ul>
<h3 id="heading-conclusion">Conclusion</h3>
<p>GitHub Actions is a platform commonly used for performing CI tasks on projects hosted on GitHub. I developed a tool called PyAction to allow users to create custom actions using Python. PyAction includes a range of features such as local testing.</p>
]]></content:encoded></item><item><title><![CDATA[Next-Gen Databases: AI meets SQL]]></title><description><![CDATA[As developers, we have experience with various database management systems (DBMS), including SQL and NoSQL. The database platforms we work with can range from advanced ones like SQL Server and PostgreSQL to simpler options like SQLite, which can be a...]]></description><link>https://blog.imsadra.dev/next-gen-databases-ai-meets-sql</link><guid isPermaLink="true">https://blog.imsadra.dev/next-gen-databases-ai-meets-sql</guid><category><![CDATA[Databases]]></category><category><![CDATA[AI]]></category><category><![CDATA[Artificial Intelligence]]></category><category><![CDATA[Machine Learning]]></category><category><![CDATA[vector database]]></category><category><![CDATA[chatgpt]]></category><dc:creator><![CDATA[Sadra Yahyapour]]></dc:creator><pubDate>Mon, 22 Apr 2024 18:30:41 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1710439622304/5868de87-7a18-447a-87f4-bcaf7921c1fd.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>As developers, we have experience with various database management systems (DBMS), including SQL and NoSQL. The database platforms we work with can range from advanced ones like SQL Server and PostgreSQL to simpler options like SQLite, which can be a smart choice in certain situations.</p>
<p>By the term "advanced," I am referring to the features and functionalities of the database that users can access, like setting up scheduled jobs, auto-indexing, and creating functions/macros and procedures when needed.</p>
<p>We have entered a new era where databases are no longer just for storing data. Developers are utilizing AI-friendly programming languages like Python to build database engines. This method has enabled them to merge AI models with DBMSs.</p>
<p>This article explains some basic concepts about the revolution happening in databases. We won't delve into very deep AI concepts. Providing a brief overview of each topic should suffice to lead us to the main subject: integrating AI and databases, the process involved, and the benefits companies and teams are reaping from it.</p>
<p>This article is based on my research on related concepts, a few months of working as an open-source contributor at MindsDB, and the paper <a target="_blank" href="https://dl.acm.org/doi/pdf/10.1145/3514221.3526142">EVA: A Symbolic Approach to Accelerating Exploratory Video Analytics with Materialized Views</a>.</p>
<h2 id="heading-supervised-learning">Supervised Learning</h2>
<p>In every real-world machine learning scenario, we design a system using a data set. We always have a data source available to us. This data might come from an external stream (like CCTVs, real-time API calls, or pub-sub systems) or be stored in a local database within the same infrastructure.b</p>
<p>Normally, in an AI project, especially in machine learning, we go through three phases. We begin with gathering the necessary data. After accumulating a sufficient amount of data for our model, we proceed to the first phase, which is pre-processing.</p>
<blockquote>
<p><strong>Pre-processing:</strong> This step initially covers all the necessary operations that need to be done on our essential data to reduce the outliers, scale them, replace the missing records, data correction, and so on.</p>
</blockquote>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1712541572956/13ab3cff-3e8b-4953-876c-0863a8ea9013.png" alt class="image--center mx-auto" /></p>
<p>Now that we have our data prepared, we would typically proceed to the next phase and train a model using this data. When we talk about <strong>"training a model based on data"</strong>, we are referring to developing a mathematical function that takes inputs and provides an output.</p>
<p>$$Predictor(x_1, x_2, x_3,\cdots) = y$$</p><p>As a developer, consider this explanation as a function named <code>predict</code> that takes a value and returns the corresponding value from a dictionary for now.</p>
<pre><code class="lang-python">data = {
    <span class="hljs-number">10</span>: <span class="hljs-number">3</span>_000,
    <span class="hljs-number">15</span>: <span class="hljs-number">5</span>_000,
    <span class="hljs-number">20</span>: <span class="hljs-number">10</span>_000,
    <span class="hljs-number">25</span>: <span class="hljs-number">15</span>_000,
    <span class="hljs-number">30</span>: <span class="hljs-number">20</span>_000
}

<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">predict</span>(<span class="hljs-params">x: int</span>) -&gt; int:</span>
    <span class="hljs-keyword">return</span> data[x]
</code></pre>
<p>There is a difference between a mathematical function and a programming function. A function in mathematics provides a result (whether accurate or not) for any input, while in programming, a function might raise an exception during runtime or return nothing (<code>None</code> or <code>null)</code>.</p>
<p>We'll see various results if we call this function with different inputs.</p>
<pre><code class="lang-python">predict(<span class="hljs-number">20</span>) <span class="hljs-comment"># 10_000</span>
predict(<span class="hljs-number">25</span>) <span class="hljs-comment"># 15_000</span>
predict(<span class="hljs-number">22</span>) <span class="hljs-comment"># ERROR &lt;KeyError&gt;</span>
</code></pre>
<p>Notice how we got an error attempting to call the function with a value that's missing from the <code>data</code> dictionary. The goal is to implement a functionality for the <code>predict()</code> function to predict any value as the key <code>x</code>. Following an approach to convert this programming function works just like a mathematical one. If a 20-square-foot room costs $10,000, then probably a 22-square-foot should cost $11,000.</p>
<details><summary>How is this Predictor function made?!</summary><div data-type="detailsContent">The data we stored can assist us in this phase. Using algorithms like <strong>Regression</strong>, we can create the most accurate <strong>y</strong> expression. This is achieved by minimizing errors and developing a function that encompasses as many data points as possible.</div></details>

<p>So far, we've seen a brief preview of how supervised Machine Learning models work. This method is used in lots of forecasting solutions in AI databases.</p>
<p>In the example that we practiced, we were dealing with pure numeric data. In some cases, we might be working with media-typed data such as photos, video tapes, audio, etc.</p>
<p>How does a machine distinguish an apple from a banana? How does a machine classify SUVs out of a road full of taxis? This is where the importance of databases shines and comes in handy in terms of storing vectors and classification.</p>
<h2 id="heading-integration-with-databases">Integration with Databases</h2>
<p>The entire process of reading, restructuring, and purifying the data used to occur on our machine, requiring us to write Python code for these tasks. We needed to assign a table column for our vectors, store the precise parameter values in variables, and so forth. This process could be tedious, particularly in production from a DevOps perspective. That's when we asked: Why not do it in databases?!</p>
<p>In most SQL databases, we have a wide range of options for creating custom macros, functions, and reusable SQL code snippets. That's where we can put our training steps; scientists thought!</p>
<p>Of course, it will make AI more accessible to developers and those interested in integrating AI into their products. However, there is another important reason behind this. In the old classic way, we had to establish the connection between our models and datasets by writing codes. This has changed now, as our models reside within our database tables.</p>
<p>This game-changing solution has two major benefits.</p>
<ul>
<li><p>Easy access to AI models from anywhere.</p>
</li>
<li><p>Models can reach out to data columns even faster.</p>
</li>
</ul>
<p>With that said, there is also a possibility to use a model based on hand-picket data from the entire dataset using SQL commands and clauses. This feature enables you to quickly evaluate your model's accuracy and tune it in the finest possible way.</p>
<h2 id="heading-how-fast-are-they">How Fast Are They</h2>
<p>Technically, when we're talking about numbers and our data is also in numerical form, training a model with this kind of dataset shouldn't be difficult. However, we live in a world where data is diverse. Many things can't be quantified. Can you measure how kind your parents are with a number? Can you express your current mood with a numeric value?</p>
<p>Well, in the sense of numerical values, it's so easy to find a value among all the data. A quick indexing would do the job for you, but how can you find a movie scene among all the movies produced by the industries?!</p>
<p>Imagine you have a table full of photos. Records contain hard-coded URLs that redirect to photos. How can you find the photos that contain an apple in the scene?</p>
<p>We use a method called <strong>Vectorizing</strong> to create a column that contains the vectorized form of each photo.</p>
<p>Well, mathematically, each photo is a matrice and each cell of that matrice is a vector showcasing the RGB values of that point.</p>
<details><summary>Why Vectorizing?!</summary><div data-type="detailsContent">That's a good question. That's because machines don't understand any other data like how we do except numbers. Machines try to find patterns between numbers.</div></details>

<p>Any type of data can be transformed into numbers (vectors). Your voice consists of frequency pulses, often represented by decibels for loudness and hertz for frequency wave height. Videos are composed of frames, with each frame being a matrix. Texts can also be represented as numbers. The real question is, where do we store the numeric form of our data?!</p>
<p>We used to define a data structure for this, but now, with the assistance of AI databases, we store everything our models require directly within the database that runs our model.</p>
<h2 id="heading-classification">Classification</h2>
<p>To find the photos that represent the figure of an apple, we can go on with two solutions.</p>
<ul>
<li><p>Parametrizing</p>
</li>
<li><p>Vectorizing</p>
</li>
</ul>
<p><strong>Parametrizing:</strong> It means we have to tag each photo based on different attributes like color, size, weight, shape, etc, and do the queries based on these values.</p>
<p><strong>Vectorizing:</strong> It helps you to define those patterns that we talked about. Their accuracy mainly depends on the quality of the photo.</p>
<h3 id="heading-conclusion">Conclusion</h3>
<p>Hopefully, this article has provided you with a brief overview of the functioning of AI databases, focusing on two key aspects: Parametrizing and Vectorizing.</p>
<p>In essence, understanding the roles of parametrizing and vectorizing is crucial in comprehending how AI databases operate and optimize data processing tasks.</p>
]]></content:encoded></item><item><title><![CDATA[RevAI: Let AI Review it First!]]></title><description><![CDATA[As a maintainer, do you need an AI that reviews your pull requests right before you check out the changes or even merge them?! Well, here it is.
RevAI is a GitHub Action that enables you to create a pipeline on each pull request opened on your reposi...]]></description><link>https://blog.imsadra.dev/revai-let-ai-review-it-first</link><guid isPermaLink="true">https://blog.imsadra.dev/revai-let-ai-review-it-first</guid><category><![CDATA[MindsDBHackathon]]></category><category><![CDATA[mindsdb]]></category><category><![CDATA[AI]]></category><category><![CDATA[GitHub]]></category><category><![CDATA[ci-cd]]></category><category><![CDATA[automation]]></category><category><![CDATA[codereview]]></category><category><![CDATA[generative ai]]></category><category><![CDATA[openai]]></category><category><![CDATA[chatgpt]]></category><category><![CDATA[llm]]></category><dc:creator><![CDATA[Sadra Yahyapour]]></dc:creator><pubDate>Sun, 14 Jan 2024 12:57:04 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1703256702376/45449a2b-bc28-4d32-9c6a-b94ea87c8833.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>As a maintainer, do you need an AI that reviews your pull requests right before you check out the changes or even merge them?! Well, here it is.</p>
<p>RevAI is a <a target="_blank" href="https://github.com/features/actions">GitHub Action</a> that enables you to create a pipeline on each pull request opened on your repositories. Underneath, it uses MindsDB to handle the requests and LLM interactions.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1705221588244/97710570-c785-472c-ba15-86228f2559b5.png" alt class="image--center mx-auto" /></p>
<h3 id="heading-case-scenario">Case Scenario</h3>
<p>Consider someone has branched off a new development line from your repository and has changed a file called <code>file.py</code>. Now, he opens a pull request from <code>him/feature</code> to <code>you/main</code>.</p>
<p>Right when he opens the PR, RevAI gets triggered and starts reviewing his changes.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1705232354944/2a714789-dbf4-4abb-a1d6-96a39d182f93.png" alt class="image--center mx-auto" /></p>
<p>Ultimately, it leaves a comment that shows what AI thinks about the changes that have been made.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1705232412240/f73cbc71-c4c6-4840-abfd-28417e5a37b3.png" alt class="image--center mx-auto" /></p>
<p>As you can see, during this PR, there was only one file (<code>file.py</code>) that got changed and RevAI has reviewed the file based on the prompt that I had given earlier. We'll go through this process later on.</p>
<h3 id="heading-customization">Customization</h3>
<p>As the maintainer of the repository, you have a full control over the factors and criterias that RevAI is going to review the code based on. You can train the model based on any prompting statement that you want. You can also change the commenting template as well.</p>
<h3 id="heading-setting-up">Setting Up</h3>
<p>In this section, we'll talk about the steps that need to be taken to have an instance of RevAI on your own repositories.</p>
<h4 id="heading-1-create-a-mindsdb-account-and-train-a-gpt-model">1. Create a MindsDB account and train a GPT model</h4>
<p>Before we begin..</p>
<ol>
<li><p>Create an account on <a target="_blank" href="https://openai.com/">OpenAI</a></p>
</li>
<li><p>Create a chatGPT instance</p>
</li>
<li><p>Generate an access token from the <a target="_blank" href="https://platform.openai.com/api-keys">dashboard</a> and keep it somewhere safe</p>
</li>
</ol>
<p>Log into <a target="_blank" href="https://cloud.mindsdb.com/">MindsDB dashboard</a>. Create an instance and run the following SQL snippet.</p>
<pre><code class="lang-sql"><span class="hljs-keyword">CREATE</span> <span class="hljs-keyword">MODEL</span> mindsdb.gpt_model
PREDICT response
<span class="hljs-keyword">USING</span>
<span class="hljs-keyword">engine</span> = <span class="hljs-string">'openai'</span>,
api_key = <span class="hljs-string">'&lt;TOKEN&gt;'</span>,
model_name = <span class="hljs-string">'gpt-3.5-turbo'</span>,
prompt_template = <span class="hljs-string">'review the {{text}} based on clean-code principles and pep rules then rate it from 1 to 10 and put it in the "score" field. Put your thoughts about it in one sentence in the "message" field.

Respond with no formatting, but in the following structure:

{
    "score": int,
    "message": str
}'</span>;
</code></pre>
<p>Replace <code>&lt;TOKEN&gt;</code> with your OpenAI token. You can also put whatever prompting statement you want, but in this case, make sure to refer to <code>{{text}}</code> as the source code content inside your prompt and your prompt should end with..</p>
<pre><code class="lang-plaintext">Respond with no formatting, but in the following structure:

{
    "score": int,
    "message": str
}
</code></pre>
<blockquote>
<p>It's quite obvious that I'm asking AI to review the code and score it based on clean-code principles and pep rules. I assume that it takes only Python files, but you can set any prompting that meets your needs.</p>
</blockquote>
<h4 id="heading-2-set-the-secrets">2. Set the secrets</h4>
<p>Navigate to your repository. Go to <strong>Settings</strong> &gt; <strong>Secrets and variables</strong> &gt; <strong>Actions</strong>. Click on <strong>New repository secrets</strong>. You need to add two secrets here. One should be named <code>EMAIL</code> that contains your MindsDB's account email and the other one is <code>PASSWORD</code> that is valued to your account's password.</p>
<p>Your repository secrets panel should look like this..</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1705233874338/fe5a873a-bc61-47d5-853f-aa2a7a52dd26.png" alt class="image--center mx-auto" /></p>
<h4 id="heading-3-set-up-the-template-and-workflow">3. Set up the template and workflow</h4>
<p>Create <code>.github/comment-template.md</code> file. Put the following markdown snippet as the commenting template.</p>
<pre><code class="lang-markdown">| 📂 <span class="hljs-strong">**File**</span>   | 💬 <span class="hljs-strong">**Comment**</span> | 🏆 <span class="hljs-strong">**Score**</span>    |
| :-----------: |---------------| :-------------: |
| <span class="hljs-code">`{{ .file }}`</span> | {{ .message }} | {{ .score }}/10 |
</code></pre>
<p>You can also modify this template and design the structure that you want RevAI to put its thoughts and reviews in.</p>
<blockquote>
<p>Don't forget to mention <code>{{ .file }}</code>, <code>{{ .message }}</code>, and <code>{{ .score }}</code> placeholders in your <code>comment-template.md</code> file.</p>
</blockquote>
<p>This is a quick look of the template that we designed..</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1705234613360/4148ac69-7ca3-4d3a-a296-ebe0e0d4017b.png" alt class="image--center mx-auto" /></p>
<p>Make a new workflow by creating <code>.github/workflows/review.yml</code> file. Put the following YAML configuration in <code>review.yml</code> file.</p>
<pre><code class="lang-yaml"><span class="hljs-attr">name:</span> <span class="hljs-string">RevAI</span> <span class="hljs-string">Reviewing</span>

<span class="hljs-attr">on:</span>
  <span class="hljs-attr">pull_request:</span>
    <span class="hljs-attr">branches:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">'main'</span>

<span class="hljs-attr">jobs:</span>
  <span class="hljs-attr">build:</span>
    <span class="hljs-attr">runs-on:</span> <span class="hljs-string">ubuntu-latest</span>

    <span class="hljs-attr">steps:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Checking</span> <span class="hljs-string">out</span>
        <span class="hljs-attr">uses:</span> <span class="hljs-string">actions/checkout@v4</span>
        <span class="hljs-attr">with:</span>
          <span class="hljs-attr">fetch-depth:</span> <span class="hljs-number">0</span>

      <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Get</span> <span class="hljs-string">changed</span> <span class="hljs-string">files</span>
        <span class="hljs-attr">id:</span> <span class="hljs-string">changed-files</span>
        <span class="hljs-attr">uses:</span> <span class="hljs-string">tj-actions/changed-files@v41</span>

      <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Use</span> <span class="hljs-string">RevAI</span>
        <span class="hljs-attr">id:</span> <span class="hljs-string">revai</span>
        <span class="hljs-attr">uses:</span> <span class="hljs-string">lnxpy/revai@0.1.0</span>
        <span class="hljs-attr">with:</span>
          <span class="hljs-attr">email:</span> <span class="hljs-string">${{</span> <span class="hljs-string">secrets.EMAIL</span> <span class="hljs-string">}}</span>
          <span class="hljs-attr">password:</span> <span class="hljs-string">${{</span> <span class="hljs-string">secrets.PASSWORD</span> <span class="hljs-string">}}</span>
          <span class="hljs-attr">files:</span> <span class="hljs-string">${{</span> <span class="hljs-string">steps.changed-files.outputs.all_changed_files</span> <span class="hljs-string">}}</span>

      <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Render</span> <span class="hljs-string">template</span>
        <span class="hljs-attr">id:</span> <span class="hljs-string">template</span>
        <span class="hljs-attr">uses:</span> <span class="hljs-string">chuhlomin/render-template@v1.4</span>
        <span class="hljs-attr">with:</span>
          <span class="hljs-attr">template:</span> <span class="hljs-string">.github/comment-template.md</span>
          <span class="hljs-attr">vars:</span> <span class="hljs-string">|
            file: ${{ steps.revai.outputs.file }}
            message: ${{ steps.revai.outputs.message }}
            score: ${{ steps.revai.outputs.score }}
</span>
      <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Create</span> <span class="hljs-string">comment</span>
        <span class="hljs-attr">uses:</span> <span class="hljs-string">peter-evans/create-or-update-comment@v3</span>
        <span class="hljs-attr">with:</span>
          <span class="hljs-attr">issue-number:</span> <span class="hljs-string">${{</span> <span class="hljs-string">github.event.number</span> <span class="hljs-string">}}</span>
          <span class="hljs-attr">body:</span> <span class="hljs-string">${{</span> <span class="hljs-string">steps.template.outputs.result</span> <span class="hljs-string">}}</span>
</code></pre>
<p>This workflow gets triggered whenever someone opens a new pull request to your <code>main</code> branch. To have a brief look at what this workflow does, we can sum them up into a few sequential jobs.</p>
<ol>
<li><p>It checks out to the commit state where the PR is.</p>
</li>
<li><p>Gets a list of changed files.</p>
</li>
<li><p>Uses RevAI and sends the changed files as well as your MindsDB credentials.</p>
</li>
<li><p>Renders the commenting template with the results that RevAI has provided.</p>
</li>
<li><p>Finally, comments out the review.</p>
</li>
</ol>
<p>You can also set (a) default reaction(s) to RevAI's comments.</p>
<pre><code class="lang-yaml">      <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Create</span> <span class="hljs-string">comment</span>
        <span class="hljs-string">...</span>
        <span class="hljs-attr">with:</span>
          <span class="hljs-string">...</span>
          <span class="hljs-attr">reactions:</span> <span class="hljs-string">|
            heart
            hooray
            laugh</span>
</code></pre>
<h3 id="heading-tech-stack">Tech Stack</h3>
<ul>
<li><p>PyAction: Creating GitHub Actions in Python. (<a target="_blank" href="http://pyaction.imsadra.me">more..</a>)</p>
</li>
<li><p>MindsDB: AI Development Cloud Platform (<a target="_blank" href="http://mindsdb.com">more..</a>)</p>
</li>
</ul>
<h3 id="heading-useful-links">Useful Links</h3>
<ul>
<li><p>RevAI on GitHub: <a target="_blank" href="https://github.com/lnxpy/revai">https://github.com/lnxpy/revai</a></p>
</li>
<li><p>RevAI on Marketplace: <a target="_blank" href="https://github.com/marketplace/actions/aireviews">https://github.com/marketplace/actions/aireviews</a></p>
</li>
</ul>
<h3 id="heading-conclusion">Conclusion</h3>
<p>With the massive growth of AI and LLMs, we'll be seeing a huge improvement in our CI and even CD cycles. There will be no need for us (humans) to review or point out any approval for minimal changes. We have AI!</p>
<p>Special thanks to Hashnode and MindsDB for sorting up this cool hackathon. 🍻</p>
]]></content:encoded></item><item><title><![CDATA[I Predicted the Future With MindsDB]]></title><description><![CDATA[One of the amazing features on MindsDB is that you have access to tens of data sources that help you access datasets from other platforms. One of the handlers that I worked on is the PyPI Handler. It uses PyPI's databases as a data source.
In this ar...]]></description><link>https://blog.imsadra.dev/i-predicted-the-future-with-mindsdb</link><guid isPermaLink="true">https://blog.imsadra.dev/i-predicted-the-future-with-mindsdb</guid><category><![CDATA[mindsdb]]></category><category><![CDATA[Machine Learning]]></category><category><![CDATA[ML]]></category><category><![CDATA[Artificial Intelligence]]></category><category><![CDATA[prediction]]></category><category><![CDATA[Python]]></category><category><![CDATA[Jupyter Notebook ]]></category><category><![CDATA[pypi]]></category><dc:creator><![CDATA[Sadra Yahyapour]]></dc:creator><pubDate>Fri, 01 Dec 2023 20:31:34 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1699569441589/c48f7ed4-41a0-4534-8824-2de75181a29a.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>One of the amazing features on MindsDB is that you have access to tens of data sources that help you access datasets from other platforms. One of the handlers that I worked on is the PyPI Handler. It uses PyPI's databases as a data source.</p>
<p>In this article, we'll be using this handler to make a prediction over the future download rate of a Python package. Keep in mind that this method uses time-series training strategies and the output is from a linear regression model meaning the output is not quite accurate.</p>
<p>To make this process faster, I'm going to start up a local MindsDB instance and train my model locally. At the end of this tutorial, you'll have access to the Notebook files.</p>
<p>In this tutorial, we need..</p>
<ul>
<li><p>A local instance of MindsDB</p>
</li>
<li><p>Jupiter Notebook (optional)</p>
</li>
</ul>
<h3 id="heading-setting-up">Setting Up</h3>
<p>As I said, we need a local MindsDB instance. You can either run up a Docker container or do it in a traditional way using a <code>venv</code> and install the requirements inside the environment.</p>
<blockquote>
<p>Follow this <a target="_blank" href="https://docs.mindsdb.com/setup/self-hosted/">official tutorial</a> to run a local MindsDB instance.</p>
</blockquote>
<p>Make sure your MindsDB engine is up and running and navigate to <a target="_blank" href="http://localhost:47334/"><code>http://localhost:47334/</code></a>. You should see the dashboard.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1701459823582/e4610028-dfa3-43cc-aa89-2e7bf1e100ab.png" alt class="image--center mx-auto" /></p>
<h3 id="heading-train-your-model">Train Your Model</h3>
<p>Now, we have to train a model before we jump into any further steps. To do so, you have to execute the following SQL code.</p>
<pre><code class="lang-sql"><span class="hljs-keyword">CREATE</span> <span class="hljs-keyword">DATABASE</span> pypi_datasource
<span class="hljs-keyword">WITH</span> <span class="hljs-keyword">ENGINE</span> = <span class="hljs-string">'pypi'</span>;

<span class="hljs-keyword">CREATE</span> <span class="hljs-keyword">MODEL</span> mindsdb.pypi_model
<span class="hljs-keyword">FROM</span> pypi_datasource
  (<span class="hljs-keyword">SELECT</span> *
<span class="hljs-keyword">FROM</span> pypi_datasource.overall <span class="hljs-keyword">WHERE</span> <span class="hljs-keyword">package</span>=<span class="hljs-string">"&lt;PACKAGE-NAME&gt;"</span> <span class="hljs-keyword">AND</span> mirrors=<span class="hljs-literal">true</span> <span class="hljs-keyword">limit</span> <span class="hljs-number">500</span>)
PREDICT downloads;
</code></pre>
<p>It creates a PyPI table and prepares a model based on the following SQL sequence.</p>
<pre><code class="lang-sql"><span class="hljs-keyword">SELECT</span> *
<span class="hljs-keyword">FROM</span> pypi_datasource.overall <span class="hljs-keyword">WHERE</span> <span class="hljs-keyword">package</span>=<span class="hljs-string">"&lt;PACKAGE-NAME&gt;"</span> <span class="hljs-keyword">AND</span> mirrors=<span class="hljs-literal">true</span> <span class="hljs-keyword">limit</span> <span class="hljs-number">500</span>)
PREDICT downloads;
</code></pre>
<p>You can refer to <a target="_blank" href="https://github.com/mindsdb/mindsdb/tree/staging/mindsdb/integrations/handlers/pypi_handler/">this</a> document for more options and parameters if you need to modify your sequence.</p>
<p>I'm trying to predict the download rate of the <code>requests</code> library. It's a famous one so let's see how is our model going to do.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1701460440852/1deafeae-6102-46f7-8cdc-750650e16601.png" alt class="image--center mx-auto" /></p>
<p>As you can see, my model's status is "generating" meaning my model is not yet ready. Once it's fully prepared and ready to be prompted, you'll see a check mark on the bottom-left part of your dashboard that says your "pypi_model" is ready to be used.</p>
<h3 id="heading-predict">Predict</h3>
<p>To do the prediction, I need a bunch of fancy tools to make an epic scene from my prediction. For that manner, I'm using <a target="_blank" href="https://plotly.com/python/">Plotly</a> which is a plotting library in Python. You can use any other tool that you need. To connect to the local MindsDB, I'm using <a target="_blank" href="https://github.com/mindsdb/mindsdb_python_sdk"><code>mindsdb_sdk</code></a>.</p>
<p>Before I jump into the connection phase, I need to set some variables.</p>
<pre><code class="lang-python"><span class="hljs-comment"># variables</span>
DAYS_TO_BE_PREDICTED = <span class="hljs-number">100</span>
MINDSDB_INSTANCE = <span class="hljs-string">"http://127.0.0.1:47334"</span>
PACKAGE_NAME = <span class="hljs-string">"requests"</span>
</code></pre>
<p>Make sure to modify <code>DAYS_TO_BE_PREDICTED</code> and <code>PACKAGE_NAME</code> variables based on your needs.</p>
<pre><code class="lang-python"><span class="hljs-keyword">from</span> datetime <span class="hljs-keyword">import</span> datetime, timedelta

<span class="hljs-keyword">import</span> mindsdb_sdk

<span class="hljs-comment"># variables</span>
<span class="hljs-comment"># ...</span>

server = mindsdb_sdk.connect()
server = mindsdb_sdk.connect(MINDSDB_INSTANCE)

databases = server.list_databases()
database = databases[<span class="hljs-number">-1</span>]
</code></pre>
<p>Let's take a batch from the download rate data to sort our model for prediction.</p>
<pre><code class="lang-python">query = database.query(
    <span class="hljs-string">f'SELECT date, downloads FROM pypi_datasource.overall WHERE package="<span class="hljs-subst">{PACKAGE_NAME}</span>" AND mirrors=true limit 500'</span>
)
overall_df = query.fetch()
</code></pre>
<p>Now, let's take our prediction data based on <code>overall_df</code>.</p>
<pre><code class="lang-python"><span class="hljs-comment"># an empty dataframe</span>
predicted_df = pd.DataFrame(columns=[<span class="hljs-string">"date"</span>, <span class="hljs-string">"downloads"</span>])

today = datetime.today()
current_date = (today - timedelta(days=<span class="hljs-number">180</span>)).date()

<span class="hljs-keyword">for</span> i <span class="hljs-keyword">in</span> range(DAYS_TO_BE_PREDICTED):
    query = database.query(
        <span class="hljs-string">f'SELECT date, downloads FROM mindsdb.pypi_model WHERE date="<span class="hljs-subst">{current_date}</span>"'</span>
    )
    predicted_value = query.fetch()
    current_date = (today + timedelta(days=i)).date()
    predicted_df = pd.concat([predicted_df, query.fetch()], ignore_index=<span class="hljs-literal">True</span>)
</code></pre>
<p>Right after the for-loop, our <code>predicted_df</code> is filled with the predicted download rates on each day. This is a quick showcase on how this variable looks like.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1701461923756/91b624e3-8ea0-40ea-9e61-daab960a827e.png" alt class="image--center mx-auto" /></p>
<p>As I said, I'll use Plotly to make a plot view from this data and the real values.</p>
<pre><code class="lang-python"><span class="hljs-keyword">import</span> pandas <span class="hljs-keyword">as</span> pd
<span class="hljs-keyword">import</span> plotly.graph_objects <span class="hljs-keyword">as</span> go

<span class="hljs-comment"># ...</span>

fig = go.Figure()

fig.add_trace(
    go.Scatter(
        x=overall_df[<span class="hljs-string">"date"</span>], y=overall_df[<span class="hljs-string">"downloads"</span>], mode=<span class="hljs-string">"lines"</span>, name=<span class="hljs-string">"Data"</span>
    )
)
fig.add_trace(
    go.Scatter(
        x=predicted_df[<span class="hljs-string">"date"</span>],
        y=predicted_df[<span class="hljs-string">"downloads"</span>],
        mode=<span class="hljs-string">"lines"</span>,
        name=<span class="hljs-string">"Prediction"</span>,
    )
)
fig.update_layout(
    title=<span class="hljs-string">"PyPI Package Download Rate Prediction"</span>,
    xaxis_title=<span class="hljs-string">"Dates"</span>,
    yaxis_title=<span class="hljs-string">"Downloads"</span>,
    template=<span class="hljs-string">"plotly_dark"</span>,
)

fig.show()
</code></pre>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1701462029817/55b6314d-0ed4-4f0a-b12b-a1fa14368076.png" alt class="image--center mx-auto" /></p>
<p>Hope you've found this implementation helpful.</p>
<h3 id="heading-useful-links">Useful Links</h3>
<ul>
<li><p>PyPI Handler: <a target="_blank" href="https://github.com/mindsdb/mindsdb/tree/staging/mindsdb/integrations/handlers/pypi_handler/">https://github.com/mindsdb/mindsdb/tree/staging/mindsdb/integrations/handlers/pypi_handler/</a></p>
</li>
<li><p>PyPI Prediction Notebook: <a target="_blank" href="https://github.com/lnxpy/pypi-prediction-examples/">https://github.com/lnxpy/pypi-prediction-examples/</a></p>
</li>
</ul>
]]></content:encoded></item><item><title><![CDATA[A Guide to Creating MindsDB Integrations]]></title><description><![CDATA[In this article, we're going to talk about how you can make new handler integrations into the MindsDB codebase.
TL;DR
MindsDB is a cloud database service with a lot of AI and data integrations that help you design, train, and predict over the cloud!
...]]></description><link>https://blog.imsadra.dev/a-guide-to-creating-mindsdb-integrations</link><guid isPermaLink="true">https://blog.imsadra.dev/a-guide-to-creating-mindsdb-integrations</guid><category><![CDATA[mindsdb]]></category><category><![CDATA[integration]]></category><category><![CDATA[contribution to open source]]></category><category><![CDATA[Open Source]]></category><category><![CDATA[Databases]]></category><dc:creator><![CDATA[Sadra Yahyapour]]></dc:creator><pubDate>Sun, 27 Aug 2023 18:46:22 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1693135720884/0c08a555-3ea4-422d-8253-f1b22144f0d9.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>In this article, we're going to talk about how you can make new handler integrations into the <a target="_blank" href="https://github.com/mindsdb/mindsdb">MindsDB</a> codebase.</p>
<h3 id="heading-tldr">TL;DR</h3>
<p><a target="_blank" href="https://mindsdb.com">MindsDB</a> is a cloud database service with a lot of AI and data integrations that help you design, train, and predict over the cloud!</p>
<p>MindsDB's community has always been welcoming all contributions over their repositories. Since they have such nice support for their products alongside their active community, I decided to write down this quick tutorial on how you can contribute to MindsDB by creating your handler integrations.</p>
<p>This guide is created for those who want to contribute to <code>mindsdb/mindsdb</code> so without further ado, let's talk about the steps.</p>
<h3 id="heading-fork-amp-clone">Fork &amp; Clone</h3>
<p>First thing first, make sure that you have <code>mindsdb/mindsdb</code> forked and cloned on your machine. Change your directory to where the codebase is on.</p>
<h3 id="heading-create-a-handler">Create A Handler</h3>
<p>I've written a tool that helps you create MindsDB handlers with a few question answerings. It's basically a Cookiecutter template that provides whatever you may need during your handler development. Follow the steps to create a basic handler template for yourself.</p>
<h4 id="heading-1-install-cookiecutter">1. Install Cookiecutter</h4>
<p>Make sure you have <code>Python</code> and <code>pip</code> installed and updated on your machine and install the <code>cookiecutter</code> package via the following command.</p>
<pre><code class="lang-bash">pip install -U cookiecutter
</code></pre>
<p>To test it out and find out whether the installation was successful, run it with the <code>-V</code> flag.</p>
<pre><code class="lang-bash">cookiecutter -V
</code></pre>
<h4 id="heading-2-generate-the-template">2. Generate the template</h4>
<p>Now, it's time to use my tool called <a target="_blank" href="https://github.com/lnxpy/cookiecutter-mindsdb"><code>lnxpy/cookiecutter-mindsdb</code></a> to generate a simple handler for yourself. MindsDB stores all the handlers in <code>mindsdb/integrations/handlers/</code> so we need to put our handler in that path too. Keep in mind that we've already changed our directory to <code>mindsdb/</code>.</p>
<pre><code class="lang-bash">cookiecutter gh:lnxpy/cookiecutter-mindsdb -o mindsdb/integrations/handlers/
</code></pre>
<p>After running the above command, it tries to retrieve the content of the cookiecutter template and then, it asks you a bunch of questions in order to create the most suitable handler for you. The following table helps you answer the questions accurately.</p>
<div class="hn-table">
<table>
<thead>
<tr>
<td>Question</td><td>Description</td><td>Type</td><td>Examples</td></tr>
</thead>
<tbody>
<tr>
<td>Handler name</td><td>Name of your handler</td><td><code>str</code></td><td><code>PyPI</code> - <code>Google</code> - <code>GitHub Copilot</code></td></tr>
<tr>
<td>Handler slug</td><td>Slug version of your handler name</td><td><code>str(slug)</code></td><td><code>[Leave this empty]</code></td></tr>
<tr>
<td>Description</td><td>A short description of your handler</td><td><code>str</code></td><td><code>MindsDB handler for doing some awesome things</code></td></tr>
<tr>
<td>Author name</td><td>Your name</td><td><code>str</code></td><td><code>John Doe</code> - <code>Sadra</code> - <code>Sara</code></td></tr>
<tr>
<td>Version</td><td>Starting version of your handler</td><td><code>str</code></td><td><code>0.0.0</code> - <code>1.0.0</code> - <code>1.0.0-b1</code> - <code>2023.03</code></td></tr>
<tr>
<td>Has dependencies</td><td>Whether your handler has dependencies (<code>requirements.txt</code>)</td><td><code>y/n</code></td><td><code>y</code> - <code>n</code></td></tr>
<tr>
<td>Need version bumper</td><td>Whether you want to use <code>bump2version</code> for version management of your handler</td><td><code>y/n</code></td><td><code>y</code> - <code>n</code></td></tr>
</tbody>
</table>
</div><p>When you see the following message, it means that your handler has been created successfully in that specific handler's path of MindsDB.</p>
<pre><code class="lang-bash">+ Sample Handler is created successfully!
</code></pre>
<p>If you navigate to the path to your handler, a quick tree view of your handler would be like the following scheme.</p>
<pre><code class="lang-bash">&lt;name&gt;_handler/
├── README.md
├── __about__.py
├── __init__.py
├── &lt;name&gt;_handler.py
├── &lt;name&gt;_tables.py
└── icon.svg

1 directory, 6 files
</code></pre>
<p>The files that need to be updated are <code>README.md</code>, <code>&lt;name&gt;_handler.py</code>, <code>&lt;name&gt;_tables.py</code>, and <code>icon.svg</code>. If your handler has any dependencies, once you've installed the dependencies, don't forget to update the <code>requirements.txt</code> file and put the output of <code>pip freeze</code> command into it as follows.</p>
<pre><code class="lang-bash">pip freeze &gt; requirements.txt
</code></pre>
<p>In the next two sections, we'll be talking about each file and method so that you can write your handlers in a flash!</p>
<h3 id="heading-develop-your-handler">Develop Your Handler</h3>
<p>In this section, we'll be going through both <code>_handler.py</code> and <code>_tables.py</code> files and their instructions. To run a local instance of MindsDB, create a virtual environment, install the dependencies, and run it with the following commands.</p>
<pre><code class="lang-bash">$ virtualenv venv &amp;&amp; <span class="hljs-built_in">source</span> venv/bin/activate
$ pip install -e .
$ pip install -e <span class="hljs-string">".[dev]"</span>
</code></pre>
<p>The following command runs the MindsDB engine on your local machine and shows the exact port that it's accessible from.</p>
<pre><code class="lang-bash">$ python -m mindsdb
</code></pre>
<p>Let's dive into the <code>&lt;name&gt;_handler.py</code> and <code>&lt;name&gt;_tables.py</code> files that contain most of our implementations. In my case, my handler name is <code>Sample</code> so I'll bring examples and practices based on this name.</p>
<h3 id="heading-handler-development">Handler Development</h3>
<p>The following handler stub structure is generated in <code>sample_handler.py</code> by default.</p>
<pre><code class="lang-python"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">SampleHandler</span>(<span class="hljs-params">APIHandler</span>):</span>
    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">__init__</span>(<span class="hljs-params">self, name: str, **kwargs</span>) -&gt; <span class="hljs-keyword">None</span>:</span> ...
    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">check_connection</span>(<span class="hljs-params">self</span>) -&gt; StatusResponse:</span> ...
    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">connect</span>(<span class="hljs-params">self</span>):</span> ...
    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">native_query</span>(<span class="hljs-params">self, query: str</span>) -&gt; StatusResponse:</span> ...
</code></pre>
<p>We'll go through each method and the way you should implement them.</p>
<h3 id="heading-samplehandlerinit-method"><code>SampleHandler.__init__()</code> Method</h3>
<p>The <code>SampleHandler.__init__()</code> method gets triggered on the following <code>CREATE DATABASE</code> query execution over in the dashboard.</p>
<pre><code class="lang-sql"><span class="hljs-keyword">CREATE</span> <span class="hljs-keyword">DATABASE</span> sample_db
<span class="hljs-keyword">WITH</span> <span class="hljs-keyword">ENGINE</span> = <span class="hljs-string">'sample'</span>;
</code></pre>
<p>Inside this method, you should create the tables that you need. As you can see, there is already a <code>SampleTable</code> class created there by default. Create your required tables inside <code>&lt;name&gt;_tables.py</code>, import them into the <code>&lt;name&gt;_handler.py</code> and add them in <code>_tables</code> variables inside the <code>__init__()</code> method.</p>
<pre><code class="lang-python">...
<span class="hljs-keyword">from</span> mindsdb.integrations.handlers.sample_handler.sample_tables <span class="hljs-keyword">import</span> SampleTable


<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">SampleHandler</span>(<span class="hljs-params">APIHandler</span>):</span>
    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">__init__</span>(<span class="hljs-params">self, name: str, **kwargs</span>) -&gt; <span class="hljs-keyword">None</span>:</span>
        <span class="hljs-string">"""initializer method

        Args:
            name (str): handler name
        """</span>
        super().__init__(name)

        self.connection = <span class="hljs-literal">None</span>
        self.is_connected = <span class="hljs-literal">False</span>

        _tables = [
            SampleTable,
            <span class="hljs-comment">#...</span>
        ]

        <span class="hljs-keyword">for</span> Table <span class="hljs-keyword">in</span> _tables:
            self._register_table(Table.name, Table(self))
</code></pre>
<p>Notice that there are two attributes called <code>self.connection</code> and <code>self.is_connected</code> inside the initializer method. We need to set them from within the <code>connect()</code> and <code>check_connection()</code> methods.</p>
<h3 id="heading-samplehandlercheckconnection-method"><code>SampleHandler.check_connection()</code> Method</h3>
<p>This method has to return a <code>StatusResponse(True/False)</code> object meaning the handler is still available to access the third-party for the further communications. This method is also where we set a bool value for <code>self.is_connected</code>. A quick sample for this method would be like this.</p>
<pre><code class="lang-python">    ...
    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">check_connection</span>(<span class="hljs-params">self</span>) -&gt; StatusResponse:</span>
            response = StatusResponse(<span class="hljs-literal">False</span>)

            <span class="hljs-keyword">try</span>:
                _ = requests.get(<span class="hljs-string">"https://google.com"</span>, timeout=<span class="hljs-number">5</span>).raise_for_status()
                response.success = <span class="hljs-literal">True</span>
                self.is_connected = <span class="hljs-literal">True</span>
            <span class="hljs-keyword">except</span> requests.exceptions.RequestException <span class="hljs-keyword">as</span> e:
                response.error_message = e

            <span class="hljs-keyword">return</span> response
</code></pre>
<h3 id="heading-samplehandlerconnect-method"><code>SampleHandler.connect()</code> Method</h3>
<p>This method is where you have to put a value into the <code>self.connection</code> attribute. A good example for this would be returning a <code>requests.get()</code> object.</p>
<pre><code class="lang-python">    ...
    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">connect</span>(<span class="hljs-params">self</span>) -&gt; Callable[..., Response]:</span>
        <span class="hljs-string">"""making the connectino object

        Returns:
            Callable: requests.get
        """</span>
        self.connection = requests.get
        <span class="hljs-keyword">return</span> self.connection
</code></pre>
<p>We need the value that this method puts into the <code>self.connection</code> later in our <code>sample_tables.py</code>.</p>
<h3 id="heading-tables-development">Tables Development</h3>
<p>Now, let's see the <code>sample_tables.py</code> and the methods and classes that live there. You can copy and paste your tables from the initial <code>SampleTable</code> or even modify it. The first step is to specify each table's columns and names. You can do this by changing the <code>name</code> and <code>columns</code> attributes of each class.</p>
<pre><code class="lang-python"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">SampleTable</span>(<span class="hljs-params">APITable</span>):</span>
    name: str = <span class="hljs-string">"sample"</span>
    columns: List[str] = ...

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">__init__</span>(<span class="hljs-params">self, handler: APIHandler</span>):</span> ...
    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">select</span>(<span class="hljs-params">self, query: ast.Select</span>) -&gt; pd.DataFrame:</span> ...
    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">get_columns</span>(<span class="hljs-params">self, ignore: List[str] = []</span>) -&gt; List[str]:</span> ...
</code></pre>
<p>There is a <code>SampleTable.select()</code> method defined by default. This is the only method that you probably need to change. This method is called whenever someone queries a <code>SELECT</code> statement over your table. All the parameters and clauses that the user specifies can be reachable from the <code>query</code> attribute.</p>
<p>You can define other methods such as <code>SampleTable.insert()</code> for the <code>INSERT INTO</code> purpose.</p>
<h3 id="heading-dont-forget-to">Don't Forget to..</h3>
<p>I highly recommend you take a look over other the handler integrations' source code. They'll give you a nice point of view about the components and how they're supposed to be implemented. As references, I suggest the <a target="_blank" href="https://github.com/mindsdb/mindsdb/tree/staging/mindsdb/integrations/handlers/mediawiki_handler">MediaWiki Handler</a> and <a target="_blank" href="https://github.com/mindsdb/mindsdb/tree/staging/mindsdb/integrations/handlers/pypi_handler">PyPI Handler</a> which I maintain!</p>
<p>What's more, <a class="user-mention" href="https://hashnode.com/@aiav">Artem Veremey</a> has published <a target="_blank" href="https://aia.hashnode.dev/building-a-new-mindsdb-integration-a-step-by-step-guide-with-examples-from-the-github-handler">"Building a New MindsDB Integration: A Step-by-Step Guide With Examples From the GitHub Handler"</a> article over on his blog that might give you more perspectives and ideas about this integration. Shout out to him!</p>
<h3 id="heading-conclusion">Conclusion</h3>
<p>In this quick tutorial, we talked about MindsDB and the way you can ship your desired handlers over it via a simple Cookiecutter template. Hopefully, this tutorial has helped you build your integrations easier.</p>
]]></content:encoded></item><item><title><![CDATA[Python3.12 is Happening..!]]></title><description><![CDATA[In this quick overview, we're going to talk about Python 3.12 and its new amazing features as well as my personal thoughts on each upgrade/downgrade. My thoughts are specified with the "IMO" (In My Opinion) keyword at the beginning of the quotes.
Nes...]]></description><link>https://blog.imsadra.dev/python312-is-happening</link><guid isPermaLink="true">https://blog.imsadra.dev/python312-is-happening</guid><category><![CDATA[Python]]></category><category><![CDATA[Python 3]]></category><category><![CDATA[release notes]]></category><category><![CDATA[news]]></category><category><![CDATA[update ]]></category><dc:creator><![CDATA[Sadra Yahyapour]]></dc:creator><pubDate>Tue, 22 Aug 2023 13:19:14 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1692704680328/48e40195-d51f-4f46-9df5-9cecf315d818.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>In this quick overview, we're going to talk about Python 3.12 and its new amazing features as well as my personal thoughts on each upgrade/downgrade. My thoughts are specified with the "IMO" (In My Opinion) keyword at the beginning of the quotes.</p>
<h3 id="heading-nested-f-strings">Nested F-Strings</h3>
<p>In Python 3.12, you can have nested f-string phrases. Check the following example.</p>
<pre><code class="lang-python">phrase = <span class="hljs-string">f"Hello <span class="hljs-subst">{<span class="hljs-string">f"<span class="hljs-subst">{name}</span>"</span>}</span>"</span>
<span class="hljs-comment"># Hello Sadra</span>
</code></pre>
<blockquote>
<p>IMO: It seems to be quite hard in term of understanding a nested f-string phrase as it might not be as practical as the simple f-strings. I would personally regret using this new feature.</p>
</blockquote>
<h3 id="heading-multiline-f-strings">Multiline F-Strings</h3>
<p>One cool feature that's been brought to the new 3.12 version is that you can have multiline f-strings.</p>
<pre><code class="lang-python">phrase = <span class="hljs-string">f"Hello <span class="hljs-subst">{
    name # User.name
}</span>"</span>
<span class="hljs-comment"># Hello Sadra</span>
</code></pre>
<blockquote>
<p>IMO: This is a handy one. As you can see, you can expand your f-strings as well as document them via comments. That's amazing!</p>
</blockquote>
<h3 id="heading-tokenization-is-re-written-in-c">Tokenization is Re-written in C</h3>
<p>Since Python 3.11, the tokenizer module has been in Python analyzing your Python lexical and keywords. From now on, due to the new nested and multiline f-strings, this module has been updated and re-written in C and it's almost 40% faster than before. With this huge improvement, all the linting and formatting tools that make use of this module can get a huge performance improvement over this update.</p>
<blockquote>
<p>IMO: Now I can run my CI worklfows way faster!!</p>
</blockquote>
<h3 id="heading-distutils-is-depricated">Distutils is Depricated</h3>
<p>There's always been a struggle among the Python community members about <code>setuptools</code> against <code>distutils</code> and the reason that everyone prefers the <code>setuptools</code> (which is a fork of <code>distutils</code>) over the official <code>distutils</code>. That's actually because <code>setuptools</code> is better with lots of more features and functionalities. That's why they decided to remove the <code>distutils</code> standard library and depreciate that.</p>
<p>What's more, <code>pip</code> actually uses <code>setuptools</code> for building the distributions. If you make a <code>venv</code> with <code>python&lt;=3.11</code> and <code>pip&lt;=22.1</code> you'll see that the <code>setuptools</code> is already installed there.</p>
<pre><code class="lang-bash">$ pip list
Package    Version
---------- -------
pip        23.2.1
setuptools 68.0.0    &lt;--
wheel      0.41.1
</code></pre>
<p>The <code>setuptools</code> package is not a standard library. It just uses <code>distutils</code>. Since <code>distutils</code> is no more available and <code>pip&gt;=22.1</code> is not dependent on <code>setuptools</code>, we'll never have a distributing tool inside a freshly installed Python environment.</p>
<p>The <code>setuptools</code> package has dropped its dependency on the <code>virtualenv</code> package as well. It means even if you install the <code>setuptools</code>, you won't be able to create a virtual environment or even run the following command.</p>
<pre><code class="lang-bash">$ python -m venv venv
<span class="hljs-comment"># ERROR: venv module is not found.</span>
</code></pre>
<p>The <code>distutils</code> package has become another whole third-party package. In order to have access to both <code>virtualenv</code> and <code>setuptools</code>, you have to install them separately.</p>
<blockquote>
<p>IMO: This deprication has made lots of changes. You won't have acces to any distributing tool anymore. No more <code>distutils</code>, <code>setuptools</code> or <code>virtualenv</code> by default. In fact, this quick change ensures you from a real isolated <code>venv</code> and that's a positive point. From now on, if you make a virtual environment, nothing is installed there except <code>pip</code>.</p>
</blockquote>
<h3 id="heading-kwargs-type-hinting"><code>**kwargs</code> Type Hinting</h3>
<p>In the new update, lots of changes have happened to the <code>typing</code> module. I used to use <code>typing.Any</code> for type hinting the <code>**kwargs</code> of my functions and method. That was actually quite a deal but in Python 3.12, you can give it a more precise type annotation using <code>typing.TypedDict</code>.</p>
<pre><code class="lang-python"><span class="hljs-keyword">from</span> typing <span class="hljs-keyword">import</span> TypedDict, Unpack

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Values</span>(<span class="hljs-params">TypedDict</span>):</span>
    name: str
    age: int

<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">main</span>(<span class="hljs-params">**kwargs: Unpack[Values]</span>):</span> ...
</code></pre>
<blockquote>
<p>IMO: I'd prefer to keep <code>kwargs</code> un-annotated for now.</p>
</blockquote>
<h3 id="heading-override-type-hinting"><code>@override</code> Type Hinting</h3>
<p>There's a new <code>@override</code> type-annotation included in the <code>typing</code> module. It actually helps you to specify the methods that are meant to be overridden in the OOP design. Using this feature helps the typing tools such as <code>mypy</code> debugging your code in another sense.</p>
<pre><code class="lang-python"><span class="hljs-keyword">from</span> typing <span class="hljs-keyword">import</span> override

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">A</span>:</span>
    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">greet</span>():</span> ...

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">B</span>(<span class="hljs-params">A</span>):</span>
<span class="hljs-meta">    @override</span>
    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">greet</span>():</span> ...
</code></pre>
<p>For instance, in the above example, if you make a typo issue and change <code>B.greet</code> to <code>B.great</code>, your <code>mypy</code> would most likely return a non-zero output in the STDOUT meaning the method that's meant to be overridden is not defined in the base class.</p>
<blockquote>
<p>IMO: I find this little trick quite useful actually. Also including a <code>mypy</code> execution in your CI pipelines would help you catch such issues.</p>
</blockquote>
<h3 id="heading-new-type-defining">New <code>type</code> Defining</h3>
<p>There has been a new syntax added to the new Python version. This new convention helps you define the `Type Aliases` easier.</p>
<ul>
<li><p>Before:</p>
<pre><code class="lang-python">  <span class="hljs-keyword">from</span> typing <span class="hljs-keyword">import</span> TypeAlias

  Students: TypeAlias = list[str]

  <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">list_students</span>(<span class="hljs-params">students: Students</span>) -&gt; <span class="hljs-keyword">None</span>:</span> ...
</code></pre>
</li>
<li><p>After:</p>
<pre><code class="lang-python">  type Students = list[str]

  <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">list_students</span>(<span class="hljs-params">students: Students</span>) -&gt; <span class="hljs-keyword">None</span>:</span> ...
</code></pre>
</li>
</ul>
<p>This new convention has changed how functions and methods look in Python as you can design your function scopes as follows.</p>
<ul>
<li><p>Before:</p>
<pre><code class="lang-python">  <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">list_students</span>(<span class="hljs-params">students: List[str], grades: List[float]</span>) -&gt; <span class="hljs-keyword">None</span>:</span> ...
</code></pre>
</li>
<li><p>After:</p>
<pre><code class="lang-python">  type Students = List[str]
  type Grades = List[float]

  <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">greet</span>[<span class="hljs-title">Students</span>, <span class="hljs-title">Grades</span>](<span class="hljs-params">students: Students, grades: Grades</span>) -&gt; <span class="hljs-keyword">None</span>:</span> ...
</code></pre>
</li>
</ul>
<p>The new pattern of the function definition is like <code>def NAME[*TYPES](*ARGS: TYPE) -&gt; TYPE</code>. Keep in mind that the types that are included in front of the name of the function are limited to the function scope.</p>
<blockquote>
<p>IMO: I think this feature just violates the readability of the new function. I'd prefer them in the old way if declaration. Highlighting tools might change my mind though. We have see how they're going to deal with these new syntaxes and keywords.</p>
</blockquote>
<h3 id="heading-per-interpreter-gil">Per-Interpreter GIL</h3>
<p>In the new update, we'll have full control over the GIL utilization in the sub-interpreters that we make. As the official docs say..</p>
<blockquote>
<p>This (Per-Interpreter GIL) allows Python programs to take full advantage of multiple CPU cores.</p>
</blockquote>
<p>We'll be talking about this new integration soon in another blog post. It seems to be a decent improvement over the performance manner.</p>
<h3 id="heading-conclusion">Conclusion</h3>
<p>In this blog post, we talked about the major features and updates that are about to happen in Python3.12 and the new syntaxes that help us code in a cleaner way. Lots of new features are coming from the Typing module meaning the lining and formatting tools have to work harder in the incoming months to release a compatible version with the new Python patch.</p>
]]></content:encoded></item><item><title><![CDATA[Writing GitHub Actions in Python]]></title><description><![CDATA[GitHub offers the maintainers and developers, three ways of developing and maintaining Custom GitHub Actions.

Docker-based Actions

Javascript Actions

Composite Actions


As you can see, GitHub's runners officially support shell-based and Javascrip...]]></description><link>https://blog.imsadra.dev/writing-github-actions-in-python</link><guid isPermaLink="true">https://blog.imsadra.dev/writing-github-actions-in-python</guid><category><![CDATA[Python]]></category><category><![CDATA[GitHub]]></category><category><![CDATA[github-actions]]></category><category><![CDATA[ci-cd]]></category><category><![CDATA[Open Source]]></category><dc:creator><![CDATA[Sadra Yahyapour]]></dc:creator><pubDate>Fri, 18 Aug 2023 17:16:47 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1692362367880/88d5d20f-f175-42f8-a6bc-c081057b447f.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>GitHub offers the maintainers and developers, three ways of developing and maintaining <a target="_blank" href="https://docs.github.com/en/actions/creating-actions/about-custom-actions">Custom GitHub Actions</a>.</p>
<ul>
<li><p>Docker-based Actions</p>
</li>
<li><p>Javascript Actions</p>
</li>
<li><p>Composite Actions</p>
</li>
</ul>
<p>As you can see, GitHub's runners officially support shell-based and Javascript-based actions. What if we need to implement the actions in Python?!</p>
<p>Of course, we can use either Docker-based or Composite types for creating our Python actions but is that the real deal?! What if we need to access the variables declared during the workflow run? Since Python is not officially recommended as a tool for writing actions, it doesn't mean we can't have actions in Python.</p>
<p>I wrote an open-source template called <a target="_blank" href="https://github.com/lnxpy/cookiecutter-pyaction">PyAction</a> that allows you to write GitHub Actions in Python. You have almost all the accesses that a Javascript developer has but in fact, you're using Python's syntax and have Python's power.</p>
<p>In this article, we will make a quick hello-world demo action project using my tool. I hope this article helps you out in case you're using PyAction.</p>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://github.com/lnxpy/cookiecutter-pyaction">https://github.com/lnxpy/cookiecutter-pyaction</a></div>
<p> </p>
<h3 id="heading-introducing-pyaction">Introducing PyAction</h3>
<p>PyAction is a <a target="_blank" href="https://www.cookiecutter.io/">Cookiecutter</a> template that helps you write Actions in Python. It's based on the Dockerfile implementation that GitHub recommends and has some workflow-related features that allow you to have access to the variables and data transferring during your workflow executions that we'll see later on.</p>
<blockquote>
<p>Note: This tutorial is based on PyAction v0.1.0</p>
</blockquote>
<h3 id="heading-creating-actions">Creating Actions</h3>
<p>In this section, we'll be implementing a <code>hello-world</code> action using PyAction from the very first step.</p>
<h4 id="heading-install-cookiecutter">Install <code>cookiecutter</code></h4>
<p>The tool that you need to generate a PyAction template is nothing but <code>cookiecutter</code>. Make sure you have <code>Python</code> and <code>pip</code> installed and updated on your machine and install <code>cookiecutter</code>.</p>
<pre><code class="lang-bash">pip install -U cookiecutter
</code></pre>
<p>Once you have this package installed, make sure that it's installed successfully by checking its version number.</p>
<pre><code class="lang-bash">cookiecutter -V
</code></pre>
<h4 id="heading-generate-the-hello-world-template">Generate the <code>hello-world</code> template</h4>
<p>Use the <code>cookiecutter</code> template to pull PyAction's template.</p>
<pre><code class="lang-bash">cookiecutter gh:lnxpy/cookiecutter-pyaction
</code></pre>
<p>At this point, Cookiecutter will ask you some questions to generate the most suited action for you. Here is how I managed to answer the questions for my <code>hello-world</code> action. I only listed the important answers.</p>
<div class="hn-table">
<table>
<thead>
<tr>
<td>Question</td><td>Description</td><td>Answer</td></tr>
</thead>
<tbody>
<tr>
<td>Action name</td><td>name of your action</td><td><code>Hello World</code></td></tr>
<tr>
<td>Project slug</td><td>directory/repo name</td><td><code>hello-world</code></td></tr>
<tr>
<td>Python version</td><td>the Python version that you want to use in your action</td><td><code>1</code> (Python3)</td></tr>
<tr>
<td>Include dependencies</td><td>whether your action has additional dependencies</td><td><code>n</code></td></tr>
<tr>
<td>Branding Icon</td><td>the <a target="_blank" href="https://feathericons.com/">Feather</a> icons that GitHub uses in its marketplace for actions</td><td><code>activity</code></td></tr>
<tr>
<td>Branding color</td><td>the color of the branding icon</td><td><code>1</code> (White)</td></tr>
</tbody>
</table>
</div><p>If you want to release your actions in the GitHub Marketplace, make sure you choose a unique action name as GitHub warns..</p>
<blockquote>
<ul>
<li><p>The <code>name</code> in the action's metadata file must be unique.</p>
<ul>
<li><p>The <code>name</code> cannot match an existing action name published on GitHub Marketplace.</p>
</li>
<li><p>The <code>name</code> cannot match a user or organization on GitHub, unless the user or organization owner is publishing the action. For example, only the GitHub organization can publish an action named <code>github</code>.</p>
</li>
<li><p>The <code>name</code> cannot match an existing GitHub Marketplace category.</p>
</li>
<li><p>GitHub reserves the names of GitHub features.</p>
</li>
</ul>
</li>
</ul>
</blockquote>
<p><strong>Adding dependencies for your action</strong>: If your action has dependencies, make sure to put <code>y</code> for the <code>Include dependencies</code> prompt. All it does is it creates a <code>requirements.txt</code> file as well as an additional layer in your Dockerfile. Install the requirements of your</p>
<p>Once you answered all the questions, your action will be in <code>hello-world/</code>. Here is a quick <code>tree</code> of what we have so far.</p>
<pre><code class="lang-bash">hello-world
  ├── Dockerfile
  ├── LICENSE
  ├── README.md
  ├── action.yml
  ├── actions
  │     ├── __init__.py
  │     └── io.py
  └── main.py
</code></pre>
<p>All we need to change are <code>action.yml</code>, <code>main.py</code>, and <code>README.md</code> files.</p>
<h4 id="heading-implementation">Implementation</h4>
<p>We want the users to be able to pass a <code>name</code> variable to our workflow and see a <code>Hello {name}</code> message in their workflow running logs tab. So, our action has only one <code>INPUT</code> and <code>OUTPUT</code>. Here is how we should change the <code>inputs</code> and <code>outputs</code> values of the <code>action.yml</code> file.</p>
<pre><code class="lang-yaml"><span class="hljs-string">...</span>
<span class="hljs-comment"># == inputs and outputs ==</span>

<span class="hljs-attr">inputs:</span>
  <span class="hljs-attr">name:</span>
    <span class="hljs-attr">required:</span> <span class="hljs-literal">false</span>
    <span class="hljs-attr">description:</span> <span class="hljs-string">the</span> <span class="hljs-string">person/thing</span> <span class="hljs-string">you</span> <span class="hljs-string">want</span> <span class="hljs-string">to</span> <span class="hljs-string">greet</span>
    <span class="hljs-attr">default:</span> <span class="hljs-string">World</span>

<span class="hljs-attr">outputs:</span>
  <span class="hljs-attr">phrase:</span>
    <span class="hljs-attr">description:</span> <span class="hljs-string">output</span> <span class="hljs-string">message</span>
</code></pre>
<p>This way, the users can call our action with their desired <code>name</code> value. After that, they will have access to the <code>phrase</code> variable which contains the greeting message.</p>
<p>Now, it's time to write a simple <code>hello world</code> program in our <code>main.py</code> file.</p>
<pre><code class="lang-python"><span class="hljs-keyword">import</span> os
<span class="hljs-keyword">import</span> sys
<span class="hljs-keyword">from</span> typing <span class="hljs-keyword">import</span> List

<span class="hljs-keyword">from</span> actions <span class="hljs-keyword">import</span> io


<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">main</span>(<span class="hljs-params">args: List[str]</span>) -&gt; <span class="hljs-keyword">None</span>:</span>
    <span class="hljs-string">"""main function

    Args:
        args: STDIN arguments
    """</span>

    <span class="hljs-comment"># reading the name variable from `with`</span>
    name = os.environ[<span class="hljs-string">"INPUT_NAME"</span>]

    <span class="hljs-comment"># writing to the buffer</span>
    io.write_to_output({<span class="hljs-string">"phrase"</span>: <span class="hljs-string">f"Hello <span class="hljs-subst">{name}</span>"</span>})

    <span class="hljs-comment"># now, people can echo `phrase`</span>


<span class="hljs-keyword">if</span> __name__ == <span class="hljs-string">"__main__"</span>:
    main(sys.argv)
</code></pre>
<h4 id="heading-testing">Testing</h4>
<p>Let's push our action to GitHub and test it in a simple workflow. I'm going to create a workflow inside my action repository to test it. You can test your action from whole another repository.</p>
<p>For that matter, I create a simple workflow before I push it to the GitHub repository that I've created before. Here is my <code>main.yml</code> workflow.</p>
<pre><code class="lang-yaml"><span class="hljs-comment"># .github/workflows/main.yml</span>
<span class="hljs-attr">name:</span> <span class="hljs-string">Testing</span> <span class="hljs-string">My</span> <span class="hljs-string">hello-world</span> <span class="hljs-string">Action</span>

<span class="hljs-attr">on:</span> <span class="hljs-string">workflow_dispatch</span>

<span class="hljs-attr">jobs:</span>
  <span class="hljs-attr">build:</span>
    <span class="hljs-attr">runs-on:</span> <span class="hljs-string">ubuntu-latest</span>

    <span class="hljs-attr">steps:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Checkout</span> <span class="hljs-string">code</span>
        <span class="hljs-attr">uses:</span> <span class="hljs-string">actions/checkout@v2</span>

      <span class="hljs-bullet">-</span> <span class="hljs-attr">id:</span> <span class="hljs-string">greetings</span>
        <span class="hljs-attr">name:</span> <span class="hljs-string">Using</span> <span class="hljs-string">hello-world</span>
        <span class="hljs-comment"># use the action that's inside the same repository</span>
        <span class="hljs-attr">uses:</span> <span class="hljs-string">./</span>
        <span class="hljs-attr">with:</span>
          <span class="hljs-attr">name:</span> <span class="hljs-string">Sadra</span>

      <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Echo</span> <span class="hljs-string">phrase</span>
        <span class="hljs-attr">run:</span> <span class="hljs-string">|</span>
          <span class="hljs-string">echo</span> <span class="hljs-string">${{</span> <span class="hljs-string">steps.greetings.outputs.phrase</span> <span class="hljs-string">}}</span>
</code></pre>
<p>We're almost set. If you head over to the <strong>Actions</strong> tab, you'll see that your workflow is listed there. Since it gets triggered on the <code>workflow_dispatch</code> event, you can run it manually.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1692369673444/b4d95603-01e4-4a63-84db-a415c1418d7b.png" alt class="image--center mx-auto" /></p>
<p>Run the workflow and go to the workflow running tab to see the steps passing. Let's see if our <code>hello-world</code> action is working correctly. It's supposed to echo out the <code>phrase</code> variable with <code>name=Sadra</code>.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1692369845278/1dbdbeb9-1ef9-4665-9df0-bc2edeb057a0.png" alt class="image--center mx-auto" /></p>
<p>As you can see, inside the "Echo phase" step, it can access the <code>name</code> variable. That's amazing.</p>
<p>This time, remove the <code>with</code> statement inside your <code>main.yml</code> workflow to check if the default <code>World</code> value that we set for <code>INPUT_NAME</code> works. Here would be the newly updated <code>main.yml</code> file.</p>
<pre><code class="lang-yaml">      <span class="hljs-string">...</span>
      <span class="hljs-bullet">-</span> <span class="hljs-attr">id:</span> <span class="hljs-string">greetings</span>
        <span class="hljs-attr">name:</span> <span class="hljs-string">Using</span> <span class="hljs-string">hello-world</span>
        <span class="hljs-comment"># use the action that's inside the same repository</span>
        <span class="hljs-attr">uses:</span> <span class="hljs-string">./</span>
        <span class="hljs-comment"># no more with</span>

      <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Echo</span> <span class="hljs-string">phrase</span>
        <span class="hljs-attr">run:</span> <span class="hljs-string">|</span>
          <span class="hljs-string">echo</span> <span class="hljs-string">${{</span> <span class="hljs-string">steps.greetings.outputs.phrase</span> <span class="hljs-string">}}</span>
</code></pre>
<p>Head back to the <strong>Actions</strong> tab and run the workflow once again. This time, we suppose our action to put <code>Hello World</code> inside the <code>phrase</code> environment variable.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1692370111347/7ffcb893-0b0f-4379-b552-408ec8a6bc40.png" alt class="image--center mx-auto" /></p>
<p>And that's it. With a little bit of changing the <code>main.py</code> and <code>action.yml</code> files, you can create awesome handy actions. You can even tag your actions and publish them on the GitHub Marketplace. Check the next section for more instructions on that.</p>
<h3 id="heading-useful-links">Useful Links</h3>
<p>Here is the PyAction repository on GitHub. Don't forget drop by and show us your love by giving the project a little shiny star. :)</p>
<ul>
<li><a target="_blank" href="https://github.com/lnxpy/cookiecutter-pyaction">PyAction on GitHub</a></li>
</ul>
<p>The example that I brought you here today is available on my GitHub repository as well. You can check it for the sake of more clarity.</p>
<ul>
<li><a target="_blank" href="https://github.com/lnxpy/pyaction-hello-world">Hello World project using PyAction on GitHub</a></li>
</ul>
<p>If you want to choose branding icons and colors, you may find this tiny tool useful. It helps you find a proper branding icon as well as a color for your action. (branding is only available for marketplace actions)</p>
<ul>
<li><a target="_blank" href="https://haya14busa.github.io/github-action-brandings/">GitHub actions branding cheat sheet</a></li>
</ul>
<p>DO NOT forget to follow the official instructions if you've aimed to publish your action on the marketplace. Here are the docs.</p>
<ul>
<li><p><a target="_blank" href="https://docs.github.com/en/actions/creating-actions/publishing-actions-in-github-marketplace">Publishing actions in GitHub Marketplace</a></p>
</li>
<li><p><a target="_blank" href="https://docs.github.com/en/actions/creating-actions/metadata-syntax-for-github-actions#brandingicon">GitHub actions branding</a></p>
</li>
</ul>
<h3 id="heading-conclusion">Conclusion</h3>
<p>This article gives you a brief showcase of how you can write GitHub Actions in Python using PyAction. Throughout this quick tutorial, we created a hello-world action via PyAction and pushed it to GitHub, and saw how our Python source code work underneath our workflows as actions.</p>
]]></content:encoded></item><item><title><![CDATA[Tips for Winning Hackathons]]></title><description><![CDATA[This article will discuss a sweet topic that helps you increase your winning rate through hackathons and coding (project-based) challenges. It's not technical nor documentary but using the tips described in this article would help you create and desi...]]></description><link>https://blog.imsadra.dev/tips-for-winning-hackathons</link><guid isPermaLink="true">https://blog.imsadra.dev/tips-for-winning-hackathons</guid><category><![CDATA[hackathon]]></category><category><![CDATA[tips]]></category><category><![CDATA[how-to]]></category><category><![CDATA[winning hackathons]]></category><category><![CDATA[winning]]></category><dc:creator><![CDATA[Sadra Yahyapour]]></dc:creator><pubDate>Sun, 04 Jun 2023 15:46:41 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1685625307420/2126a1b8-d052-4f38-b51a-7b24218ac82c.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>This article will discuss a sweet topic that helps you increase your winning rate through hackathons and coding (project-based) challenges. It's not technical nor documentary but using the tips described in this article would help you create and design unique projects for the hackathons.</p>
<p>In the last six months, I participated in two hackathons and won 3000$ in total for two different submissions (projects).</p>
<h3 id="heading-pasteme-paste-codes-from-your-terminal">PasteMe - Paste Codes From Your Terminal</h3>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://imsadra.me/pasteme-paste-codes-from-your-terminal">https://imsadra.me/pasteme-paste-codes-from-your-terminal</a></div>
<p> </p>
<h3 id="heading-hey-your-ai-powered-pair-programming-friend">Hey - Your AI-powered Pair Programming Friend</h3>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://imsadra.me/introducing-hey-your-ai-powered-pair-programming-friend">https://imsadra.me/introducing-hey-your-ai-powered-pair-programming-friend</a></div>
<p> </p>
<h3 id="heading-put-effort">Put Effort</h3>
<p>In both the projects that I submitted for the competitions, I tried my best in terms of <strong>development</strong>, <strong>maintenance</strong>, and <strong>presentation</strong>. If you keep all these three terms in mind, you will nail it.</p>
<ul>
<li><p>Development</p>
</li>
<li><p>Maintenance</p>
</li>
<li><p>Presentation</p>
</li>
</ul>
<h4 id="heading-development">Development</h4>
<p>Thinking of development as the phases you pass to finish up your project. Make sure you use the right conventions, best practices, system design, and architecture for your software product. Once you got your codes working, attempt to refactor them.</p>
<h4 id="heading-maintenance">Maintenance</h4>
<p>This term is somehow related to development. If you've had a healthy development and shipment process, you'd likely be good to go for maintenance. Writing programs is easy but making them maintainable is completely a different process. Always leave room for further developments.</p>
<h4 id="heading-presentation">Presentation</h4>
<p>This is probably the most important term in the case of a hackathon. When it comes to presentation, always think of a way you could showcase your project clearly and precisely. If you're working on a short intro video, make sure to present the whole point of your project. Showcase the problem first, then your project as the rescue!<br />Being straight to the point would help your project to get viral and well-known. There are different things you could do in this phase.</p>
<ul>
<li><p>Write an easy-to-understand article and README for your project.</p>
</li>
<li><p>Choose a theme for your project if possible.</p>
</li>
<li><p>Make a motion graphic about your project.</p>
</li>
<li><p>Make a short introduction video working with your project.</p>
</li>
</ul>
<p>As you can see, I used to obtain a galaxy-like theme for PasteMe. All Octocat images on the website are wearing spacesuits. Its logo itself reminds me of Alian's spaceships and whenever I open the website, I feel like I'm not on Earth anymore but in another dimension.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1685694522995/15b1d607-0914-472f-8b8d-a9908cb402ec.png" alt class="image--center mx-auto" /></p>
<h3 id="heading-story-telling">Story-telling</h3>
<p>Always make use of fiction in your presentation. It makes the audience more curious about the next thing you're going to talk about. As an example, I wrote the following sub-title in PasteMe.</p>
<blockquote>
<p>Legends believe that this project is developed from space..!! Let's see..<br />~PasteMe</p>
</blockquote>
<p>Once a person sees such a title, he/she becomes a little curious about the article. "It must be something to do with space and galaxy or stuff through this project.." he/she thinks. Once they open your article, it's time to get their attention via making an <strong>Agreement</strong> meaning you talk about shared struggles, concerns, and problems they've felt through their lives. Let them know that you've been hurt by those problems as well but enough is enough. Your introduction sections should start sad and painful but end epically and proudly just like there's something about to happen in the next sections. Something game-changer.</p>
<h3 id="heading-product-thinking">Product Thinking</h3>
<p>Think of something useful. Something that literally solves a concern. I personally, try making something handy for myself. It had been a few months since I'd been struggling with showing my code snippets to my friends but in a better fancy way. That was the starting point of PasteMe. In case I made Hey, I needed some sort of an in-line ChatGPT prompt when I'm working through CLIs and command-line shells. I needed them, so I made them in my way.</p>
<h3 id="heading-take-feedback">Take Feedback</h3>
<p>Having a network means standing in the frontline with hundreds of people supporting your work. You can always rely on them and ask them for their valuable feedback on your projects, ideas, and movements. Always present your work to them. If you're working on a project as a submission for a hackathon, let them know your progress and ask them for improvements, feature requests, and reviews first. Almost tens of features and modifications I made over on my projects were due to the feedback I received from my connections on LinkedIn so value them and think of having a network with people with similar interests and career experiences.</p>
<h3 id="heading-conclusion">Conclusion</h3>
<p>In conclusion, always make sure to work on something that's worthy. Blast on the stage with the presentation you've got about your project. Showcase all aspects of your idea as we went through all its steps during this article.</p>
]]></content:encoded></item></channel></rss>