<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Vivek</title>
    <description>The latest articles on DEV Community by Vivek (@vivekthedev).</description>
    <link>https://dev.to/vivekthedev</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F563770%2Faa0cbb73-ffd7-4dc9-bcec-f7f7da9500bb.png</url>
      <title>DEV Community: Vivek</title>
      <link>https://dev.to/vivekthedev</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/vivekthedev"/>
    <language>en</language>
    <item>
      <title>Inbox in Discord</title>
      <dc:creator>Vivek</dc:creator>
      <pubDate>Fri, 06 Jun 2025 06:58:00 +0000</pubDate>
      <link>https://dev.to/vivekthedev/-38mo</link>
      <guid>https://dev.to/vivekthedev/-38mo</guid>
      <description>&lt;div class="ltag__link--embedded"&gt;
  &lt;div class="crayons-story "&gt;
  &lt;a href="https://dev.to/vivekthedev/inbox-innovation-with-postmark-presenting-inboxninja-2oma" class="crayons-story__hidden-navigation-link"&gt;Inbox Innovation with Postmark: Presenting InboxNinja 🥷&lt;/a&gt;


  &lt;div class="crayons-story__body crayons-story__body-full_post"&gt;
    &lt;div class="crayons-story__top"&gt;
      &lt;div class="crayons-story__meta"&gt;
        &lt;div class="crayons-story__author-pic"&gt;

          &lt;a href="/vivekthedev" class="crayons-avatar  crayons-avatar--l  "&gt;
            &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F563770%2Faa0cbb73-ffd7-4dc9-bcec-f7f7da9500bb.png" alt="vivekthedev profile" class="crayons-avatar__image"&gt;
          &lt;/a&gt;
        &lt;/div&gt;
        &lt;div&gt;
          &lt;div&gt;
            &lt;a href="/vivekthedev" class="crayons-story__secondary fw-medium m:hidden"&gt;
              Vivek
            &lt;/a&gt;
            &lt;div class="profile-preview-card relative mb-4 s:mb-0 fw-medium hidden m:inline-block"&gt;
              
                Vivek
                &lt;a href="/++"&gt;&lt;img alt="Subscriber" class="subscription-icon" src="https://assets.dev.to/assets/subscription-icon-805dfa7ac7dd660f07ed8d654877270825b07a92a03841aa99a1093bd00431b2.png"&gt;&lt;/a&gt;
              
              &lt;div id="story-author-preview-content-2561706" class="profile-preview-card__content crayons-dropdown branded-7 p-4 pt-0"&gt;
                &lt;div class="gap-4 grid"&gt;
                  &lt;div class="-mt-4"&gt;
                    &lt;a href="/vivekthedev" class="flex"&gt;
                      &lt;span class="crayons-avatar crayons-avatar--xl mr-2 shrink-0"&gt;
                        &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F563770%2Faa0cbb73-ffd7-4dc9-bcec-f7f7da9500bb.png" class="crayons-avatar__image" alt=""&gt;
                      &lt;/span&gt;
                      &lt;span class="crayons-link crayons-subtitle-2 mt-5"&gt;Vivek&lt;/span&gt;
                    &lt;/a&gt;
                  &lt;/div&gt;
                  &lt;div class="print-hidden"&gt;
                    
                      Follow
                    
                  &lt;/div&gt;
                  &lt;div class="author-preview-metadata-container"&gt;&lt;/div&gt;
                &lt;/div&gt;
              &lt;/div&gt;
            &lt;/div&gt;

          &lt;/div&gt;
          &lt;a href="https://dev.to/vivekthedev/inbox-innovation-with-postmark-presenting-inboxninja-2oma" class="crayons-story__tertiary fs-xs"&gt;&lt;time&gt;Jun 5 '25&lt;/time&gt;&lt;span class="time-ago-indicator-initial-placeholder"&gt;&lt;/span&gt;&lt;/a&gt;
        &lt;/div&gt;
      &lt;/div&gt;

    &lt;/div&gt;

    &lt;div class="crayons-story__indention"&gt;
      &lt;h2 class="crayons-story__title crayons-story__title-full_post"&gt;
        &lt;a href="https://dev.to/vivekthedev/inbox-innovation-with-postmark-presenting-inboxninja-2oma" id="article-link-2561706"&gt;
          Inbox Innovation with Postmark: Presenting InboxNinja 🥷
        &lt;/a&gt;
      &lt;/h2&gt;
        &lt;div class="crayons-story__tags"&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/devchallenge"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;devchallenge&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/postmarkchallenge"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;postmarkchallenge&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/webdev"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;webdev&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/api"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;api&lt;/a&gt;
        &lt;/div&gt;
      &lt;div class="crayons-story__bottom"&gt;
        &lt;div class="crayons-story__details"&gt;
          &lt;a href="https://dev.to/vivekthedev/inbox-innovation-with-postmark-presenting-inboxninja-2oma" class="crayons-btn crayons-btn--s crayons-btn--ghost crayons-btn--icon-left"&gt;
            &lt;div class="multiple_reactions_aggregate"&gt;
              &lt;span class="multiple_reactions_icons_container"&gt;
                  &lt;span class="crayons_icon_container"&gt;
                    &lt;img src="https://assets.dev.to/assets/multi-unicorn-b44d6f8c23cdd00964192bedc38af3e82463978aa611b4365bd33a0f1f4f3e97.svg" width="18" height="18"&gt;
                  &lt;/span&gt;
                  &lt;span class="crayons_icon_container"&gt;
                    &lt;img src="https://assets.dev.to/assets/fire-f60e7a582391810302117f987b22a8ef04a2fe0df7e3258a5f49332df1cec71e.svg" width="18" height="18"&gt;
                  &lt;/span&gt;
                  &lt;span class="crayons_icon_container"&gt;
                    &lt;img src="https://assets.dev.to/assets/sparkle-heart-5f9bee3767e18deb1bb725290cb151c25234768a0e9a2bd39370c382d02920cf.svg" width="18" height="18"&gt;
                  &lt;/span&gt;
              &lt;/span&gt;
              &lt;span class="aggregate_reactions_counter"&gt;16&lt;span class="hidden s:inline"&gt; reactions&lt;/span&gt;&lt;/span&gt;
            &lt;/div&gt;
          &lt;/a&gt;
            &lt;a href="https://dev.to/vivekthedev/inbox-innovation-with-postmark-presenting-inboxninja-2oma#comments" class="crayons-btn crayons-btn--s crayons-btn--ghost crayons-btn--icon-left flex items-center"&gt;
              Comments


              2&lt;span class="hidden s:inline"&gt; comments&lt;/span&gt;
            &lt;/a&gt;
        &lt;/div&gt;
        &lt;div class="crayons-story__save"&gt;
          &lt;small class="crayons-story__tertiary fs-xs mr-2"&gt;
            2 min read
          &lt;/small&gt;
            
              &lt;span class="bm-initial"&gt;
                

              &lt;/span&gt;
              &lt;span class="bm-success"&gt;
                

              &lt;/span&gt;
            
        &lt;/div&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;

&lt;/div&gt;


</description>
      <category>devchallenge</category>
      <category>postmarkchallenge</category>
      <category>webdev</category>
      <category>api</category>
    </item>
    <item>
      <title>Inbox Innovation with Postmark: Presenting InboxNinja 🥷</title>
      <dc:creator>Vivek</dc:creator>
      <pubDate>Thu, 05 Jun 2025 07:50:41 +0000</pubDate>
      <link>https://dev.to/vivekthedev/inbox-innovation-with-postmark-presenting-inboxninja-2oma</link>
      <guid>https://dev.to/vivekthedev/inbox-innovation-with-postmark-presenting-inboxninja-2oma</guid>
      <description>&lt;p&gt;This is a submission for the &lt;a href="https://dev.to/challenges/postmark"&gt;Postmark Challenge: Inbox Innovators&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Ever wished your incoming support, careers, or operations emails could instantly land in your team’s Discord channels so you can notify the right people, ask for input, and triage issues collaboratively?&lt;/p&gt;

&lt;p&gt;Email workflows are often rigid and slow. InboxNinja transforms that experience by giving teams a real-time, customizable email relay bot that plugs directly into their Discord workspace.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I Built
&lt;/h2&gt;

&lt;p&gt;InboxNinja is a real-time, self-hosted email relay bot designed for operational teams. It receives emails via Postmark, processes them through a webhook, summarizes the content, and neatly delivers it into specific Discord channels. Messages arrive organized in threads, with attachments and action buttons for instant response.&lt;/p&gt;

&lt;p&gt;It turns inboxes from static, disconnected workflows into real-time, actionable conversations inside Discord.&lt;/p&gt;

&lt;h2&gt;
  
  
  Demo
&lt;/h2&gt;

&lt;p&gt;&lt;iframe src="https://player.vimeo.com/video/1090759274" width="710" height="399"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;h2&gt;
  
  
  Code Repository
&lt;/h2&gt;


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://assets.dev.to/assets/github-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/vivekthedev" rel="noopener noreferrer"&gt;
        vivekthedev
      &lt;/a&gt; / &lt;a href="https://github.com/vivekthedev/inboxninja" rel="noopener noreferrer"&gt;
        inboxninja
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      a real-time, self-hosted email relay bot
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;
&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;📨 InboxNinja&lt;/h1&gt;
&lt;/div&gt;
&lt;p&gt;A powerful, real-time email-to-Discord relay system for operational teams.&lt;br&gt;
This application uses &lt;strong&gt;Postmark's Inbound Email Webhooks&lt;/strong&gt; to receive emails for different departments (Careers, Support, Legal, Operations, Partnerships), summarizes the content and forwards it to the appropriate &lt;strong&gt;Discord channel&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Built for productivity, team alignment, and instant issue management. 🚀&lt;/p&gt;

&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;🎯 Features&lt;/h2&gt;
&lt;/div&gt;
&lt;p&gt;✅ Real-time email forwarding via Postmark Webhook&lt;br&gt;
✅ Automated summarization using Gemini / LLM&lt;br&gt;
✅ Auto-routing to relevant Discord channels based on the email address&lt;br&gt;
✅ Supports attachments and file relaying&lt;br&gt;
✅ Interactive buttons for support teams to respond quickly&lt;br&gt;
✅ Modular and clean code with Quart + Discord.py&lt;/p&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;⚙️ Tech Stack&lt;/h2&gt;
&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://postmarkapp.com/" rel="nofollow noopener noreferrer"&gt;Postmark Inbound Webhooks&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://discordpy.readthedocs.io/" rel="nofollow noopener noreferrer"&gt;Discord.py&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://pgjones.gitlab.io/quart/" rel="nofollow noopener noreferrer"&gt;Quart (Async Flask Alternative)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Google Gemini / Summarizer&lt;/li&gt;
&lt;li&gt;Python Asyncio&lt;/li&gt;
&lt;li&gt;dotenv for secure config&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;🏗️ Project Architecture&lt;/h2&gt;

&lt;/div&gt;
&lt;div class="snippet-clipboard-content notranslate position-relative overflow-auto"&gt;
&lt;pre class="notranslate"&gt;&lt;code&gt;
/project-root
├── app.py                # Main application — runs both Discord bot &amp;amp; Quart server
├── bot.py                # Discord bot button views
├── gemini.py             #&lt;/code&gt;&lt;/pre&gt;…&lt;/div&gt;
&lt;/div&gt;
  &lt;/div&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/vivekthedev/inboxninja" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;


&lt;h2&gt;
  
  
  How I Built It
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Postmark Inbound Webhooks&lt;/strong&gt; for core email parsing and forwarding&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Postmark API&lt;/strong&gt; to retrieve complete message data via Message ID&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Discord.py&lt;/strong&gt; to build the Discord bot, send messages, handle buttons, and manage threads&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Quart&lt;/strong&gt; as the asynchronous web server for webhook handling&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  How it Works
&lt;/h2&gt;

&lt;p&gt;The app runs two asynchronous services. A Quart web server handles webhooks, and a Discord bot manages messages and actions.&lt;/p&gt;

&lt;h3&gt;
  
  
  Workflow Overview
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;A new email arrives at Postmark.&lt;/li&gt;
&lt;li&gt;Postmark's inbound webhook forwards the email payload to the /webhook endpoint.&lt;/li&gt;
&lt;li&gt;The server extracts key details like sender info, subject, body text, and attachments.&lt;/li&gt;
&lt;li&gt;It determines the appropriate Discord channel based on the email address.&lt;/li&gt;
&lt;li&gt;The message is summarized using Gemini AI. Attachments are decoded and a clean message template is prepared.&lt;/li&gt;
&lt;li&gt;The bot posts this into the right channel, starting a dedicated thread per sender.&lt;/li&gt;
&lt;li&gt;Action buttons like “Show Full Email” and “Mark as Resolved” are attached.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Challenges
&lt;/h2&gt;

&lt;p&gt;Managing both the asynchronous Quart server and Discord bot within a single process required careful event loop handling. Async conflicts, especially with asyncio.run(), demanded thoughtful debugging and examples from public repositories.&lt;/p&gt;

&lt;p&gt;Handling attachments was another tricky area. Postmark delivers them as base64 strings, which needed decoding and repackaging as Discord File objects while preserving file metadata.&lt;/p&gt;

&lt;p&gt;Discord.py’s documentation around custom button views was limited. After exploring community examples and third-party resources, I was able to implement this functionality effectively.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;I am proud of how this project came together. It is functional, cleanly built, and addresses a genuine workflow problem for modern operational teams. In future, I am planning to build a lightweight priority detection system for support emails and using Postmark's Transactional Email service for reply&lt;/p&gt;

</description>
      <category>devchallenge</category>
      <category>postmarkchallenge</category>
      <category>webdev</category>
      <category>api</category>
    </item>
    <item>
      <title>SecureLens 🔍: An LLM you can trust.</title>
      <dc:creator>Vivek</dc:creator>
      <pubDate>Mon, 05 May 2025 01:41:11 +0000</pubDate>
      <link>https://dev.to/vivekthedev/securelens-an-llm-you-can-trust-1ceo</link>
      <guid>https://dev.to/vivekthedev/securelens-an-llm-you-can-trust-1ceo</guid>
      <description>&lt;p&gt;&lt;em&gt;This is a submission for the &lt;a href="https://dev.to/challenges/permit_io"&gt;Permit.io Authorization Challenge&lt;/a&gt;: AI Access Control&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What I Built
&lt;/h2&gt;

&lt;p&gt;Presenting SecureLens 🔍: An internal company copilot tool to improve your productivity when dealing with several projects, clients, or internal company functions. SecureLens saves you the time for manually accessing crucial data in your day to day tasks. The best part, you can trust your copilot since all the risk of accidental data leaks and unauthorized AI outputs is handled for you using &lt;a href="https://www.permit.io/" rel="noopener noreferrer"&gt;Permit.io&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Architecture
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fiiy4h61hkmefjl9n42wv.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fiiy4h61hkmefjl9n42wv.png" alt="Architecture Diagram"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Permissions
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F30hn407oiy4hse494wbj.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F30hn407oiy4hse494wbj.png" alt="Permissions Diagram"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Example Users
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="p"&gt;[&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;username&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;alicej&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;password&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;2025DEVChallenge&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;role&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;employee&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;username&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;bobsmith&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;password&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;2025DEVChallenge&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;role&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;manager&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;username&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;charliek&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;password&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;2025DEVChallenge&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;role&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;admin&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;]&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h2&gt;
  
  
  Demo
&lt;/h2&gt;

&lt;p&gt;&lt;iframe src="https://player.vimeo.com/video/1081321247" width="710" height="399"&gt;
&lt;/iframe&gt;
&lt;/p&gt;
&lt;h2&gt;
  
  
  ABAC Rules
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fksvdsv7qb3osxrvjvdvz.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fksvdsv7qb3osxrvjvdvz.png" alt="emp can view pii"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Employees can ivew PII data for only those ctomer for which they are accountable.&lt;/p&gt;
&lt;h2&gt;
  
  
  Project Repo
&lt;/h2&gt;


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://assets.dev.to/assets/github-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/vivekthedev" rel="noopener noreferrer"&gt;
        vivekthedev
      &lt;/a&gt; / &lt;a href="https://github.com/vivekthedev/securelens" rel="noopener noreferrer"&gt;
        securelens
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;
&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;📊 Secure AI Copilot with Fine-Grained Authorization&lt;/h1&gt;

&lt;/div&gt;

&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;📌 Overview&lt;/h2&gt;

&lt;/div&gt;

&lt;p&gt;An internal AI-powered assistant (Copilot) for enterprise environments that interacts with business-critical data systems — CRM, HR, and Financial databases — while enforcing &lt;strong&gt;granular, dynamic, and secure data access controls&lt;/strong&gt; at every stage.&lt;/p&gt;

&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;📊 Data Schema&lt;/h2&gt;

&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Tables:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;users&lt;/code&gt; (id, name, email, phone, role)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;customers&lt;/code&gt; (id, name, email, phone, budget, address, assigned_emp_id)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;hr_data&lt;/code&gt; (id, emp_id, designation, salary, benefits, department)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;financial_data&lt;/code&gt; (id, vendor_name, amount, txn_date, notes)&lt;/li&gt;
&lt;/ul&gt;




&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;📌 Why This Project&lt;/h2&gt;

&lt;/div&gt;

&lt;ul&gt;
&lt;li&gt;AI copilots are becoming integral to internal enterprise tools&lt;/li&gt;
&lt;li&gt;Traditional RBAC isn’t enough for AI-generated dynamic content&lt;/li&gt;
&lt;li&gt;We demonstrate &lt;strong&gt;secure AI data operations&lt;/strong&gt; combining RBAC, ABAC, RAG filtering, and AI response enforcement — enterprise-ready and compliance-friendly&lt;/li&gt;
&lt;/ul&gt;



&lt;br&gt;
&lt;/div&gt;
&lt;br&gt;
&lt;br&gt;
  &lt;/div&gt;
&lt;br&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/vivekthedev/securelens" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;br&gt;
&lt;/div&gt;
&lt;br&gt;


&lt;h2&gt;
  
  
  My Journey
&lt;/h2&gt;

&lt;p&gt;While going through the challenge description, a thought of Copilot tool came to my mind that can use Permit IO capability to serve data to only Authorised users and wouldn't it be better to have a Multi agent pipeline that takes the prompt, filters it, check the permission and return the response in a beautiful SQL table.&lt;/p&gt;

&lt;p&gt;In any company, people spend a lot of time just searching, some technical ones could write their own queries but for the non-technical ones, they don't have this luxury and can only view the data with filters and conditions provided to them via a UI. This is where SecureLens come it, with its multi-agent capability and Authorization checks, it gives anyone the exact data you need. No risky queries, no unauthorized access, no wasted time.&lt;/p&gt;

&lt;p&gt;In the beginning, the idea began with a document based tool which check the document's metadata and give output to the user if they are authorized. Later, I expanded the scope to include a few more agents to clean the prompt and access data from a SQL database using a dedicated agent.&lt;/p&gt;

&lt;p&gt;I started with React, FastAPI and PydanticAI but due to time constraints and a little frontend knowledge. I decided to use Chainlit. &lt;a href="https://chainlit.io/" rel="noopener noreferrer"&gt;Chainlit&lt;/a&gt; handles all the frontend part which let me focus more on Agents and Permissions. &lt;/p&gt;

&lt;p&gt;In order to create agents, I used PydanticAI and Google Gemini LLM. With all the details ironed out. I began building my Policies and Agents.&lt;/p&gt;

&lt;h2&gt;
  
  
  Authorization for AI Applications with Permit.io
&lt;/h2&gt;

&lt;p&gt;Permit IO turned out to be easier to work with then I initially thought. With easy to use UI creating policies was just a work of few clicks. Permit IO has three core concepts:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Roles: which are the actors&lt;/li&gt;
&lt;li&gt;Resource: which are the entities on which the action needs to be performed&lt;/li&gt;
&lt;li&gt;Action: the actual action to be performed by the actor&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;All of this can only be checked using Permit's &lt;code&gt;check&lt;/code&gt; function. Every resources or attributes goes into this funciton and it returns True if the permission is granted or false otherwise. One other feature of Permit which I think was undercelebrated was defining resource as Parent-Child, which lets me easily define permission for parent and it get applied to the child automatically by a specific role.&lt;/p&gt;

&lt;p&gt;Lastly, I used PermitIO's &lt;code&gt;sync_user&lt;/code&gt; function to easily add all my users from the DB to the Permits workspace in a few seconds. One limitation I came across was that ABAC and ReBAC features currently work only within Docker container environments. It would be great to see native SDK support for these in local development in the future.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;SecureLens is built to solve a growing problem of access control so that enterprises can integrate AI copilots into their system without risking data leaks and unathorized access. By participating and building in this hackathon I learned crucial concepts about AI access control, working with permissions and building agent.&lt;/p&gt;

</description>
      <category>devchallenge</category>
      <category>permitchallenge</category>
      <category>webdev</category>
      <category>security</category>
    </item>
    <item>
      <title>Exporting Google NotebookLM Notes Is a Mess — So I Wrote a Python Script for It</title>
      <dc:creator>Vivek</dc:creator>
      <pubDate>Sun, 27 Apr 2025 07:11:02 +0000</pubDate>
      <link>https://dev.to/vivekthedev/exporting-google-notebooklm-notes-is-a-mess-so-i-wrote-a-python-script-for-it-3gh</link>
      <guid>https://dev.to/vivekthedev/exporting-google-notebooklm-notes-is-a-mess-so-i-wrote-a-python-script-for-it-3gh</guid>
      <description>&lt;p&gt;Google’s &lt;strong&gt;NotebookLM&lt;/strong&gt; is a handy tool for interacting with your documents and taking notes from them. But if you’ve tried exporting your saved notes, you’ll quickly run into a frustrating problem.&lt;/p&gt;

&lt;p&gt;There’s &lt;strong&gt;no export option in the app itself&lt;/strong&gt;. You can’t easily back up your notes, search through them offline, or move them to another tool. The only official way to get your notes is by downloading them all at once — but this comes with a big downside: it dumps everything together into a single file. This ruins the structure of individual notes and strips away important formatting, making the notes messy and harder to work with.&lt;/p&gt;

&lt;p&gt;I faced this issue myself, so I decided to fix it.&lt;/p&gt;

&lt;h2&gt;
  
  
  🛠️ I Created a Python Script to Fix This
&lt;/h2&gt;

&lt;p&gt;I built a simple Python script that grabs your exported NotebookLM notes and &lt;strong&gt;neatly restructures them&lt;/strong&gt;. It separates each note cleanly, preserves the original formatting as much as possible, and makes it easier to search or convert them into any format you want.&lt;/p&gt;




&lt;h2&gt;
  
  
  📌 How My Script Solves It
&lt;/h2&gt;

&lt;p&gt;Since NotebookLM doesn’t offer any public API, I had to build the solution differently.&lt;/p&gt;

&lt;p&gt;The script works by:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Parsing the raw HTML structure from the downloaded notes.&lt;/li&gt;
&lt;li&gt;Converting each note into clean, readable &lt;strong&gt;Markdown&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Exporting them as individual &lt;strong&gt;PDF files&lt;/strong&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This way, each note stays separate, well-formatted, and easy to search or archive.&lt;/p&gt;




&lt;h2&gt;
  
  
  ⚙️ How to Work With the Script
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Open the NotebookLM Notebook&lt;/strong&gt; whose notes you want to export.&lt;/li&gt;
&lt;li&gt;In the &lt;strong&gt;Studio&lt;/strong&gt; section, you’ll find an option to &lt;strong&gt;Convert all notes to source&lt;/strong&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0ntzyz4pq62fqlea9b30.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0ntzyz4pq62fqlea9b30.png" alt="studio section in notebooklm to convert notes to source" width="598" height="805"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Clicking this option will create a new file in your &lt;strong&gt;Sources&lt;/strong&gt; section.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Next: Get the Source of this file to feed it into the script.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The script uses &lt;strong&gt;BeautifulSoup4&lt;/strong&gt; to traverse through the source and converts it to &lt;strong&gt;Markdown&lt;/strong&gt;.&lt;/p&gt;




&lt;h3&gt;
  
  
  📋 How to Grab the HTML Source Code:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Press &lt;code&gt;F12&lt;/code&gt; or right-click on the screen and select &lt;strong&gt;Inspect&lt;/strong&gt;. This will open &lt;strong&gt;DevTools&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;In the top-left corner of the DevTools window, click on &lt;strong&gt;Select an element in the page to inspect it&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Hover over the elements in the &lt;strong&gt;Sources&lt;/strong&gt; section to find the HTML code of your notes.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fufw45ffbg79y07stx2vx.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fufw45ffbg79y07stx2vx.png" alt="DevTools option to hover and select" width="800" height="379"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You’ll see all the notes code placed inside a &lt;code&gt;&amp;lt;div&amp;gt;&lt;/code&gt; tag with the class &lt;code&gt;elements-container&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fugfc0vme5h755ku9s7fy.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fugfc0vme5h755ku9s7fy.png" alt="elements container div block where all the text resides" width="758" height="468"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This is the &lt;strong&gt;only manual step&lt;/strong&gt; in the process.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Right-click&lt;/strong&gt; on that &lt;code&gt;&amp;lt;div&amp;gt;&lt;/code&gt; element.&lt;/li&gt;
&lt;li&gt;Choose &lt;strong&gt;Copy &amp;gt; Copy element&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Paste it into a text file named &lt;code&gt;notes.txt&lt;/code&gt;.
(You can rename the file, but you’ll have to update the script accordingly.)&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  📦 Setting Up the Script
&lt;/h2&gt;

&lt;p&gt;You’ll need:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Python 3.10+&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;Install dependencies:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;  pip &lt;span class="nb"&gt;install &lt;/span&gt;&lt;span class="nv"&gt;beautifulsoup4&lt;/span&gt;&lt;span class="o"&gt;==&lt;/span&gt;4.13.4 &lt;span class="nv"&gt;markdown_pdf&lt;/span&gt;&lt;span class="o"&gt;==&lt;/span&gt;1.7
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://gist.github.com/vivekthedev/5bfe0655986d775d6d07661974ce414e" rel="noopener noreferrer"&gt;Download the script&lt;/a&gt; and save it in the same folder as notes.txt.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  🚀 Running the Script
&lt;/h2&gt;

&lt;p&gt;Open your terminal and run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;python export_note.py
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let the script do its thing.&lt;/p&gt;

&lt;p&gt;Once finished, you’ll find all your notes exported as separate PDF files.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzcb2r3aectovnnmlq921.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzcb2r3aectovnnmlq921.png" alt="exported pdf" width="800" height="363"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  📄 What You’ll Get
&lt;/h2&gt;

&lt;p&gt;The exported PDF files preserve the Markdown formatting — including:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Headings&lt;/li&gt;
&lt;li&gt;Bullet lists&lt;/li&gt;
&lt;li&gt;Code blocks&lt;/li&gt;
&lt;li&gt;Bold Text&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;One of the output PDF files from my notes looked like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flsv31mh6emthodvzcy8k.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flsv31mh6emthodvzcy8k.png" alt="sample output pdf screenshot" width="800" height="684"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;If you’ve been stuck with the same problem, I hope this saves you some time and frustration.&lt;br&gt;
It’s always fun turning annoying gaps in tools into working solutions.&lt;/p&gt;

&lt;p&gt;If you face any problems, feel free to reach out — &lt;a href="https://www.linkedin.com/in/vivekthedev" rel="noopener noreferrer"&gt;DMs&lt;/a&gt; are open!&lt;/p&gt;

</description>
      <category>ai</category>
      <category>notebooklm</category>
      <category>productivity</category>
      <category>python</category>
    </item>
    <item>
      <title>Presenting PageMyCV - Resume Website in one Click (using Pulumi ESC)</title>
      <dc:creator>Vivek</dc:creator>
      <pubDate>Sun, 06 Apr 2025 16:04:51 +0000</pubDate>
      <link>https://dev.to/vivekthedev/presenting-pagemycv-resume-website-in-one-click-using-pulumi-esc-422k</link>
      <guid>https://dev.to/vivekthedev/presenting-pagemycv-resume-website-in-one-click-using-pulumi-esc-422k</guid>
      <description>&lt;p&gt;&lt;em&gt;This is a submission for the &lt;a href="https://dev.to/challenges/pulumi"&gt;Pulumi Deploy and Document Challenge&lt;/a&gt;: Shhh, It's a Secret!&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Managing secrets like API Keys and Configuration in any application is not just crucial from security point of view but also essential for boosting maintainability of the application. These secrets can grow to a huge number as your application extend. This is the reason you need a reliable Secret Management Service like Pulumi ESC. &lt;/p&gt;

&lt;p&gt;In this article you will learn the importance of managing application secrets and I will introduce you to my project PageMyCV which uses Pulumi ESC to store and retrieve API Keys and Configurations. &lt;/p&gt;

&lt;h2&gt;
  
  
  Project Repo
&lt;/h2&gt;


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://assets.dev.to/assets/github-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/vivekthedev" rel="noopener noreferrer"&gt;
        vivekthedev
      &lt;/a&gt; / &lt;a href="https://github.com/vivekthedev/pagemycv" rel="noopener noreferrer"&gt;
        pagemycv
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;
&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;PageMyCV&lt;/h1&gt;
&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;PageMyCV&lt;/strong&gt; is a Flask-based web application that transforms a PDF resume into a professional-looking online portfolio page. Users can upload their resume, and the app automatically extracts key details using Google GenAI and hosts the final webpage via AWS S3 and CloudFront.&lt;/p&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Features&lt;/h2&gt;
&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;Upload a PDF resume via the web interface&lt;/li&gt;
&lt;li&gt;Extract structured information using Google GenAI&lt;/li&gt;
&lt;li&gt;Generate a clean HTML portfolio using Jinja templates&lt;/li&gt;
&lt;li&gt;Automatically host the portfolio using AWS S3 and CloudFront&lt;/li&gt;
&lt;li&gt;Uses Pulumi ESC for managing secure environment variables&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Tech Stack&lt;/h2&gt;
&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Python + Flask&lt;/strong&gt; – Web server and routing&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Fitz (PyMuPDF)&lt;/strong&gt; – PDF text extraction&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Google GenAI&lt;/strong&gt; – Resume data extraction&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Pulumi ESC SDK&lt;/strong&gt; – Environment configuration&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;AWS S3 + CloudFront&lt;/strong&gt; – Static site hosting&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Boto3&lt;/strong&gt; – AWS SDK for Python&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Prerequisites&lt;/h2&gt;

&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;Python 3.8 or higher&lt;/li&gt;
&lt;li&gt;Google GenAI API Key&lt;/li&gt;
&lt;li&gt;AWS Account with:
&lt;ul&gt;
&lt;li&gt;S3 Bucket&lt;/li&gt;
&lt;li&gt;CloudFront distribution&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Pulumi ESC configured with the required environment values&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Setting&lt;/h2&gt;…&lt;/div&gt;
&lt;/div&gt;
  &lt;/div&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/vivekthedev/pagemycv" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;


&lt;h2&gt;
  
  
  Live Link
&lt;/h2&gt;

&lt;p&gt;&lt;a href="http://18.234.236.185:8000/" rel="noopener noreferrer"&gt;Try for yourself&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Working Demo
&lt;/h2&gt;

&lt;p&gt;&lt;iframe src="https://player.vimeo.com/video/1072960996" width="710" height="399"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;h2&gt;
  
  
  My Journey
&lt;/h2&gt;

&lt;p&gt;The idea of making this application struck me when I was creating my own Developer site using Pelican. I noticed I am copying and pasting a lot of stuff from my Resume which felt repetitive and inefficient and decided to build an application which takes my resume, extract all the key details and renders a HTML page which I can host on Github Pages or any other static web page hosting service. And that’s how PageMyCV was born.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnizaom4p4rf5g43kun3z.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnizaom4p4rf5g43kun3z.jpeg"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I’ve tried to make this as simple as possible. All the user needs is a resume, regardless of the industry. The app picks up on the common details that are usually present across most fields and builds the page from there.&lt;/p&gt;

&lt;h2&gt;
  
  
  How it Works
&lt;/h2&gt;

&lt;p&gt;When a user visits the web interface, they are prompted to upload their resume in PDF format. Once submitted, the app reads the file using fitz (&lt;a href="https://pymupdf.readthedocs.io/en/latest/" rel="noopener noreferrer"&gt;PyMuPDF&lt;/a&gt;), a lightweight and efficient library for parsing PDF documents. This step extracts all textual content from the uploaded resume.&lt;/p&gt;

&lt;p&gt;The raw text is then passed to Google’s GenAI API. We use a structured &lt;a href="https://cloud.google.com/vertex-ai/generative-ai/docs/multimodal/function-calling" rel="noopener noreferrer"&gt;function calling&lt;/a&gt; mechanism to instruct the model on exactly what data we want extracted—such as the user's name, email, education, work history, technical skills, project details, and notable achievements. The model returns this data in a structured JSON format, which the app then uses to populate a pre-designed HTML template.&lt;/p&gt;

&lt;p&gt;After the HTML page is rendered, it's uploaded to an AWS S3 bucket. We use the &lt;a href="https://pypi.org/project/boto3/" rel="noopener noreferrer"&gt;boto3&lt;/a&gt; library to handle this interaction programmatically. Once the file is successfully stored in S3, it is served to the public via a CloudFront distribution. The user is then redirected to their unique CloudFront URL where their new online portfolio is instantly available.&lt;/p&gt;

&lt;p&gt;Earlier, the project used &lt;a href="https://pypi.org/project/python-dotenv/" rel="noopener noreferrer"&gt;Pydotenv&lt;/a&gt; to manage secrets, which made it so much inefficient. What if I want to rotate API_KEYs? Change Bucket Name or Use to different CloudFront Domain? &lt;/p&gt;

&lt;h2&gt;
  
  
  Pain Points of Traditional Approach
&lt;/h2&gt;

&lt;p&gt;Managing configuration manually quickly becomes cumbersome. Storing sensitive values like API keys in plain text opens the door to accidental leaks. The developer might accidently commit .env files and your Configuration are publically exposed.&lt;/p&gt;

&lt;h2&gt;
  
  
  Using Pulumi ESC
&lt;/h2&gt;

&lt;p&gt;Tools like Pulumi ESC are built to solve these exact problems. They allow you to securely manage secrets. A user can easily manage several configurtion in the same application just by switching environment which removes the need to manually changing model names to check the response when building AI applications.&lt;/p&gt;

&lt;p&gt;To begin, you’ll need to install the ESC CLI on your machine. This CLI allows you to create, view, and update your environments with ease. Once installed, authentication is handled through a single environment variable: PULUMI_ACCESS_TOKEN. This token gives your CLI access to your Pulumi organization and its managed environments.&lt;/p&gt;

&lt;p&gt;What used to be a clutter of 4–5 separate environment variables that had to be manually exported or managed in .env files, is now reduced to just one. &lt;/p&gt;

&lt;h3&gt;
  
  
  Resources
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.pulumi.com/docs/esc/download-install/" rel="noopener noreferrer"&gt;https://www.pulumi.com/docs/esc/download-install/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.pulumi.com/docs/pulumi-cloud/access-management/access-tokens/#creating-personal-access-tokens" rel="noopener noreferrer"&gt;https://www.pulumi.com/docs/pulumi-cloud/access-management/access-tokens/#creating-personal-access-tokens&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In Pulumi ESC all the magic happens in esc environments. Each environment holds a separate set of variables required by your application. &lt;br&gt;
To create a new environment run the following in terminal:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;esc env init default/myapp
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will create a new environment named &lt;code&gt;myapp&lt;/code&gt; in default namespace. Since you can manage multiple environment you can type the below command to list all the environments:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;gt; esc env ls
default/myapp
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In order to create a new variable in this enviroment run the following command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;gt; esc env set default/myapp varNmae varValue
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Note that the above command would only set the variable but to make it available you need to edit the conf file using:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;esc env edit default/myapp
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Running the above command would open a YAML file. Add the following text at the end of &lt;code&gt;values&lt;/code&gt; block to make your variables accessible in the applicaiton.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;environmentVariables:
  varName: ${varName}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To test this you can run the following command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;gt; esc env run default/myapp -- python -c "import os; print(os.getenv('varName'))"

varValue
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the above command you ran the python code with myapp environment. esc handled all the environment variables set up for you. In the above output you can see "varValue" was returned by our Python code.&lt;/p&gt;

&lt;p&gt;This is how Pulumi ESC made my environment management part easy. In my application I have stored MODEL, API_KEY, BUCKET, AND CLOUDFRONT configuration which was easily managed by Pulumi ESC.&lt;/p&gt;

&lt;h2&gt;
  
  
  Takeaway
&lt;/h2&gt;

&lt;p&gt;Using Pulumi ESC helped me move away from scattered .env files to a cleaner, safer setup. With one access token, I could securely manage secrets across multiple environments.&lt;/p&gt;

</description>
      <category>devchallenge</category>
      <category>pulumichallenge</category>
      <category>webdev</category>
      <category>cloud</category>
    </item>
    <item>
      <title>Effortless Database Migrations: Why Alembic is Your Python Must-Have</title>
      <dc:creator>Vivek</dc:creator>
      <pubDate>Fri, 23 Feb 2024 10:31:58 +0000</pubDate>
      <link>https://dev.to/vivekthedev/effortless-database-migrations-why-alembic-is-your-python-must-have-2f0n</link>
      <guid>https://dev.to/vivekthedev/effortless-database-migrations-why-alembic-is-your-python-must-have-2f0n</guid>
      <description>&lt;p&gt;SQL databases operate by defining specific fields, data types, and relationships across various models. These characteristics make SQL databases ideal for tasks like data science and record management. In Python-based web applications, SQL databases are commonly chosen, especially with Object Relational Manager (ORM) tools like SQLAlchemy. These tools facilitate the creation, querying, and modification of SQL models using Python.&lt;/p&gt;

&lt;p&gt;However, altering a production database after it’s been defined can be difficult. Frameworks such as Django provide their own database migration systems to simplify and streamline this process. It introduces various checks to ensure database updates are performed smoothly. But, Python Frameworks such as Flask and FastAPI do not come with their own database migration system, this is where Alembic comes into play.  &lt;a href="https://alembic.sqlalchemy.org/en/latest/" rel="noopener noreferrer"&gt;Alembic&lt;/a&gt; is a database Migration system for usage with the SQLAlchemy Database Toolkit.&lt;/p&gt;

&lt;p&gt;It allows you to make changes to your databases structure in a secure way using only Python without writing a single SQL statement. Additionally, with Alembic you can rollback to the previous version of your database definition with just a few commands. Alembic make migrations in Database really easy with intuitive syntax. It make easier to evolve our database with our application.&lt;/p&gt;

&lt;p&gt;In this tutorial, you will learn to use Alembic in a FastAPI application. We will cover most important features like upgrade, downgrade, addcolumn and dropcolumn to enhance our FastAPI application. For this tutorial, we have already created a sample application in FastAPI which has a CRUD functionality on a book table having the following fields.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;id: int  
title: string  
rating: integer  
price: float  
published: boolean
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h1&gt;
  
  
  Setting Up the starter code
&lt;/h1&gt;

&lt;h2&gt;
  
  
  Cloning Application from GitHub
&lt;/h2&gt;

&lt;p&gt;In order to setup the starter application, you have to first clone the application in your local directory using this command:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;git clone https://github.com/vivekthedev/blog-tuts.git&lt;/code&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Creating virtual environment
&lt;/h2&gt;

&lt;p&gt;Once you have the application on your system, create a python virtual environment in order to isolote the dependencies from the system site packages. You can create a virtual environment using the following command:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;python -m venv env&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;After running the above command, run  &lt;code&gt;env\Scripts\activate&lt;/code&gt;  if you are on Windows else run  &lt;code&gt;source env/bin/activate&lt;/code&gt;  if you are on Mac/Linux in order to activate your virtual environment.&lt;/p&gt;

&lt;h2&gt;
  
  
  Installing Dependencies
&lt;/h2&gt;

&lt;p&gt;Once you activate the virtual envrironment you can install dependencies present in  &lt;code&gt;requirements.txt&lt;/code&gt;  file by running  &lt;code&gt;pip install -r requirements.txt&lt;/code&gt;  command.&lt;/p&gt;

&lt;h2&gt;
  
  
  Running and Testing Starter Application
&lt;/h2&gt;

&lt;p&gt;At this point you are ready to run and test the application. In the same terminal, run  &lt;code&gt;uvicorn main:app --reload&lt;/code&gt;  to start the application on the default address of &lt;a href="http://127.0.0.1:8000/" rel="noopener noreferrer"&gt;http://127.0.0.1:8000/&lt;/a&gt; , visit &lt;a href="http://127.0.0.1:8000/docs" rel="noopener noreferrer"&gt;http://127.0.0.1:8000/docs&lt;/a&gt; to test your application on the Swagger UI.&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fsias4suwawnl2jnissxn.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fsias4suwawnl2jnissxn.png" width="800" height="576"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h1&gt;
  
  
  What are Database Migrations?
&lt;/h1&gt;

&lt;p&gt;As mentioned earlier, SQL databases have a strict structure with predefined rows and columns, which makes altering the database more challenging. For application developers, this process may require writing SQL statements, ensuring constraints like NOT NULL or UNIQUE are met, and specifying DEFAULT values. Moreover, if issues arise during the update process, reverting to the previous database state can be difficult, especially if TRANSACTION command aren’t explicitly defined when executing SQL statements.&lt;/p&gt;

&lt;p&gt;Alembic solves this problem by providing an easier and intuitive way to evolve your database without writing any line of SQL code. Additionally, working with Alembic is much easier compared to manually changing database structure through SQL or via GUI application.&lt;/p&gt;

&lt;p&gt;Each database update is stored in a Python file, providing flexibility to customize as needed. Reverting to a specific database state is as easy as executing a simple command, making the process more manageable and efficient.&lt;/p&gt;
&lt;h1&gt;
  
  
  Working with Alembic
&lt;/h1&gt;

&lt;p&gt;To start working with Alembic, you first need to install Alembic using the following command. Also make sure to run the command in the same virtual environment as you FastAPI starter application:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;pip install alembic&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Once you install Alembic, you now have a alembic Command Line Tool to configure your database. To integrate Alembic with your project run the following command in the same terminal:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;alembic init alembic-conf&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;The above command creates a directory named  &lt;code&gt;alembic-conf&lt;/code&gt;  in your project directory and generates the following files:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;code&gt;alembic-conf/versions/&lt;/code&gt;  : This directory stores various versions of your database, you will use this directory in order to examine or update various states of your database.&lt;/li&gt;
&lt;li&gt;  &lt;code&gt;alembic-conf/env.py&lt;/code&gt;  : This file is used by alembic to run migrations, it defines how the migrations should be made on the database.&lt;/li&gt;
&lt;li&gt;  &lt;code&gt;alembic-conf/script.py.mako&lt;/code&gt;  : It is a template file which is used to create new database versions in the versions directory. In other words every file/migration in versions directory is created using this template file.&lt;/li&gt;
&lt;li&gt;  &lt;code&gt;alembic-conf/README&lt;/code&gt;  : Contains information about the projects or the alembic. You can use it as changelog or writing something informative about the project.&lt;/li&gt;
&lt;li&gt;  &lt;code&gt;alembic.ini&lt;/code&gt;  : This is the configuration file where you define you various setting in order to configure your migration process.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  Configuring Database with Alembic
&lt;/h2&gt;

&lt;p&gt;To enable Alembic to make changes to your Database, it requires knowledge of the location of your database service, which is defined by the  &lt;code&gt;SQLALCHEMY_DATABASE_URL&lt;/code&gt;. To do this, locate the  &lt;code&gt;database.py&lt;/code&gt;file within your project and copy the value  &lt;code&gt;sqlite:///books.sqlite3&lt;/code&gt;  . Then, navigate to Line 63 of the  &lt;code&gt;alembic.ini&lt;/code&gt;  file and paste this value just after the variable  &lt;code&gt;sqlalchemy.url&lt;/code&gt;. Make sure to NOT enclose it in quotes .&lt;/p&gt;

&lt;p&gt;Now open  &lt;code&gt;alembic-conf/env.py&lt;/code&gt; file, and populate it with the following code:&lt;/p&gt;

&lt;p&gt;import sys&lt;br&gt;&lt;br&gt;
import models&lt;br&gt;&lt;br&gt;
sys.path.append("..")&lt;/p&gt;

&lt;p&gt;After the above imports find the  &lt;code&gt;target_metadata&lt;/code&gt;  variable and set it to  &lt;code&gt;models.Base.metadata&lt;/code&gt;  .&lt;/p&gt;

&lt;p&gt;Once you set up the above two configurations successfully, you are now ready to create you alembic revisions in order to make changes in your database.&lt;/p&gt;
&lt;h2&gt;
  
  
  Creating Alembic Revisions
&lt;/h2&gt;

&lt;p&gt;For your first Alembic revision, you will create a new column in your Books table named  &lt;code&gt;Description&lt;/code&gt;  which should be of type String. In order to create a revision file, open the terminal and type the following command.&lt;/p&gt;

&lt;p&gt;alembic revision -m "create book description"&lt;/p&gt;

&lt;p&gt;After running the above command, you will see that a new file with the name  &lt;code&gt;“&amp;lt;hash_value&amp;gt;_create_book_description.py”&lt;/code&gt;  has been created. This file will contain our revision logic which we will define in  &lt;code&gt;upgrade()&lt;/code&gt; function in the file.&lt;/p&gt;

&lt;p&gt;Open the newly created file and in the  &lt;code&gt;upgrade()&lt;/code&gt;  function type the following code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;def upgrade() -&amp;gt; None:  
    op.add_column('books', sa.Column('description', sa.String(), server_default="A short Description"), nullable=False)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the provided code, you’ve created a new revision that, when applied, adds a new column named ‘description’ of type string with a default value of “A short description”. It’s considered good practice to define the downgrade() function at the same time as the upgrade() function to ensure a smooth downgrading process in the future.&lt;/p&gt;

&lt;p&gt;To implement this, in the downgrade() function, we will reverse the actions performed in the upgrade() function, which in this case involves removing the ‘description’ column from the ‘books’ table. To achieve this, type the following code inside the downgrade() function:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;def downgrade() -&amp;gt; None:  
  op.drop_column('books', 'description')
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This code will instruct Alembic to remove the ‘description’ column from the ‘books’ table when the downgrade process is initiated.&lt;/p&gt;

&lt;p&gt;In this file, you can see another variable  &lt;code&gt;revision&lt;/code&gt;  which contains the hash value of the revision file. Copy this value as it will be useful when applying the revisions to the database.&lt;/p&gt;

&lt;h2&gt;
  
  
  Applying Revisions
&lt;/h2&gt;

&lt;p&gt;We have successfully created revision to make changes in the database, in order to apply these changes run the following command in your terminal:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;alembic upgrade &amp;lt;hash_value&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Note that “&lt;code&gt;&amp;lt;hash_value&amp;gt;&lt;/code&gt;” should be replaced with the hash value of your revision file, which you copied in the previous section. When you execute this command, you will receive confirmation messages indicating that your revisions have been applied successfully:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;INFO  [alembic.runtime.migration] Context impl SQLiteImpl.  
INFO  [alembic.runtime.migration] Will assume non-transactional DDL.  
INFO  [alembic.runtime.migration] Running upgrade  -&amp;gt; e9cbb68b0547, create book description
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To verify that the changes have been applied to our database, we can run the application using the following command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;uvicorn main:app --reload
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After that, proceed to  &lt;a href="http://127.0.0.1:8000/" rel="noopener noreferrer"&gt;http://127.0.0.1:8000/&lt;/a&gt;  to view the books stored in our database. However, you may notice that the models do not display the newly created ‘description’ field for the book. This is because FastAPI fetches all model information from the  &lt;code&gt;models.py&lt;/code&gt;  file. To ensure FastAPI fetches the ‘description’ field as well, you need to add a new line in the models.py file. Open the models.py file and append this line at the end of the model:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;class Books(Base):  
    __tablename__ = "books"  
    id = Column(Integer, primary_key=True, index=True)  
    title = Column(String)  
    price = Column(Float)  
    author = Column(String)  
    rating = Column(Integer)  
    published = Column(Boolean)  
    description = Column(String) # &amp;lt;- This line
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now open the  &lt;a href="http://127.0.0.1:8000/" rel="noopener noreferrer"&gt;http://127.0.0.1:8000/&lt;/a&gt;, you will see the following output:&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fs04a5b98t93gfoa1sk13.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fs04a5b98t93gfoa1sk13.png" width="528" height="679"&gt;&lt;/a&gt;&lt;br&gt;
You can also use  &lt;code&gt;sqlite3&lt;/code&gt;  command line tool or  &lt;a href="https://inloop.github.io/sqlite-viewer/" rel="noopener noreferrer"&gt;SQL Viewer&lt;/a&gt;  tool in order to inspect changes in your database.&lt;/p&gt;

&lt;p&gt;With that, you have successfully created and applied your very first migration using Alembic. This functionality proves to be highly useful for a production-level application, as it allows for seamless database schema modifications.&lt;/p&gt;

&lt;p&gt;For your next migration, the objective is to create a new table called ‘reviews’. This next part will serve as a learning opportunity to understand how to create a new table using Alembic Operations. Eventually, we will remove this table to demonstrate the downgrade functionality of Alembic, showcasing its ability to rollback changes.&lt;/p&gt;
&lt;h1&gt;
  
  
  Creating Table with Alembic
&lt;/h1&gt;

&lt;p&gt;In order to create a new revision, open the terminal and type the following following command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;alembic revision -m "create reviews table"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A new file with the name  &lt;code&gt;&amp;lt;hash_value&amp;gt;_create_reviews_table&lt;/code&gt;  should now be generated in the “alembic-conf/versions” directory. Open this file, and you’ll notice that it contains a variable called “down_revision” with a value corresponding to the hash value of the previous migration file. This setup makes it easy to track our migrations and ensures that Alembic can keep the upgrade and downgrade processes in sync.&lt;/p&gt;

&lt;h2&gt;
  
  
  Upgrade and Downgrade function
&lt;/h2&gt;

&lt;p&gt;Now let’s create Operations for creating a new table. Open the newly created migration file and populate the  &lt;code&gt;upgrade&lt;/code&gt;  function with the following code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;def upgrade() -&amp;gt; None:  
    op.create_table(  
        "reviews",  
        sa.Column("id", sa.Integer(), primary_key=True, index=True),  
        sa.Column("author", sa.String(50), nullable=False),  
        sa.Column("content", sa.String(200)),  
        sa.Column("timestamp", sa.TIMESTAMP, server_default=sa.func.now()),  
    )
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the above code, we are creating a new table named  &lt;code&gt;reviews&lt;/code&gt;&lt;br&gt;&lt;br&gt;
which has the following columns:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;code&gt;id&lt;/code&gt;  : An Integer type field for primary_key data of each row.&lt;/li&gt;
&lt;li&gt;  &lt;code&gt;author&lt;/code&gt;  : This column stores the author of the review and is defined as a  &lt;code&gt;String(50)&lt;/code&gt;  type. It means that the column value can’t exceed the length of 50 characters.&lt;/li&gt;
&lt;li&gt;  &lt;code&gt;content&lt;/code&gt;  : Another String field which stores the actual review content. This column cannot exceed the character length of 200.&lt;/li&gt;
&lt;li&gt;  &lt;code&gt;timestamp&lt;/code&gt;  : A field with default value of the current timestamp which signifies the date time when a particular review was created.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;As stated above, we should always write the  &lt;code&gt;downgrade&lt;/code&gt; function in order to revert the changes done by  &lt;code&gt;upgrade&lt;/code&gt; function. Write the following code in the  &lt;code&gt;downgrade&lt;/code&gt; function:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
def downgrade() -&amp;gt; None:  
    op.drop_table('reviews')
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The above code is pretty self-explainatory, we are just removing the table ‘reviews’ that we created in  &lt;code&gt;upgrade&lt;/code&gt; function.&lt;/p&gt;

&lt;h2&gt;
  
  
  Applying the Migration
&lt;/h2&gt;

&lt;p&gt;To apply the migration, first copy the hash value from the migration file and then open your terminal and run the following command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;alembic upgrade &amp;lt;hash_value&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once you’ve executed the provided command, you’ll receive a confirmation message in your terminal indicating that the latest migration has been successfully applied, resulting in the creation of the new “reviews” table. However, as our FastAPI application currently lacks any endpoints that query the “reviews” table, you won’t be able to observe these changes directly from the application.&lt;/p&gt;

&lt;p&gt;To inspect the changes, we’ll utilize an SQL Viewer Tool. Simply open the SQL Viewer Tool in your browser. Upon loading the page, you’ll notice a “Drop Here” text. Click on it and navigate to your project directory, where you’ll find the database file named ‘books.sqlite3’. Select this database file, and the SQL tool will read all the tables within it. You’ll then be able to view all the tables available in the dropdown menu within the tool’s interface. This allows you to inspect the newly created “reviews” table and any other tables present in your database.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F534ce39ijjzhy4dvoulk.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F534ce39ijjzhy4dvoulk.png" width="589" height="292"&gt;&lt;/a&gt;&lt;br&gt;
In the above image you can a reviews table which currently has no rows as we haven’t populated it with data.&lt;/p&gt;
&lt;h1&gt;
  
  
  Downgrade with Alembic
&lt;/h1&gt;

&lt;p&gt;Downgrading with Alembic is straightforward. Simply enter the downgrade command along with the identifier of the desired state in the terminal. Alembic will automatically handle all the necessary steps to revert your application to the specified state.&lt;/p&gt;

&lt;p&gt;Alembic supports  &lt;code&gt;alembic downgrade &amp;lt;hashvalue&amp;gt;&lt;/code&gt;  syntax, but the more common (and easier) way to downgrade the application involves relative identifiers using the -N and +N syntax. Let’s do it practically to understand how it works. Open your terminal and type the following command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;alembic downgrade -1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The above command instructs Alembic to take the database to a state one step previous from the current state. In other words it reverts the last applied migration to the database. Naturally, when you put -2 instead of -1 it reverts the last two migration and so on.&lt;/p&gt;

&lt;p&gt;After executing the command, you’ll receive a confirmation message in the terminal indicating that the downgrade operation ran successfully. To verify this, you can open the SQLite Viewer Tool and upload the database again. Upon inspection, you’ll notice that the dropdown menu displaying all the tables no longer includes the ‘reviews’ table. This confirms that the database has indeed been reverted back to its previous state.&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fipabxl67wtskzngrpzol.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fipabxl67wtskzngrpzol.png" width="603" height="480"&gt;&lt;/a&gt;&lt;br&gt;
You can also see in the above image that there is no review table in the database. Make sure to reupload the database to the viewer tool in order for them to reflect in your browser.&lt;/p&gt;

&lt;h1&gt;
  
  
  Conclusion
&lt;/h1&gt;

&lt;p&gt;Alembic is a really powerful tool to add database migration using SQLAlchemy. It make migration process more intuitve, easy and secure. Developers can easily manage and rollback to a different state in the database with ease. Alembic also supports operation like Batch processing, raw sql execution, adding constraints and much more. You can read about all the operations provided by Alembic in the  &lt;a href="https://alembic.sqlalchemy.org/en/latest/index.html" rel="noopener noreferrer"&gt;Documentation&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;In this tutorial you learnt about Alembic and how you can use it in your python projects to easily manage your database. You learnt upgrading and downgrading in Alembic, creating columns, tables and and reverting changes. With these functionalities Alembic becomes a must-have when working with Python/SQLAlchemy project in order to easily manage your database.&lt;/p&gt;

&lt;p&gt;Hope you like the tutorial, in case you have any question, shoot them in the comments. I will try my best to answer them.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Are Tuples really immutable? #PyTip03</title>
      <dc:creator>Vivek</dc:creator>
      <pubDate>Sat, 20 Aug 2022 08:15:28 +0000</pubDate>
      <link>https://dev.to/vivekthedev/are-tuples-really-immutable-pytip03-acf</link>
      <guid>https://dev.to/vivekthedev/are-tuples-really-immutable-pytip03-acf</guid>
      <description>&lt;p&gt;Tuple in Python are immutable Data Structures. Unlike sets, dicts and lists, the values in a tuple cannot be changed once initialized.&lt;/p&gt;

&lt;p&gt;But is it really the case for tuples? See, tuples are immutable for the &lt;strong&gt;contents they hold ** that means the **references of the objects&lt;/strong&gt; inside the tuple cannot be changed. But what if we change the contents of the &lt;strong&gt;without changing the object references&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;To see it in action look at the below code&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;gt;&amp;gt;&amp;gt; p = 7
&amp;gt;&amp;gt;&amp;gt; id(7)
2917349484976
&amp;gt;&amp;gt;&amp;gt;p = 8
&amp;gt;&amp;gt;&amp;gt;id(p)
2917349485008
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the above code when we changed the value of an integer &lt;code&gt;p&lt;/code&gt; the &lt;code&gt;id&lt;/code&gt; of the object changed, that means the variable &lt;code&gt;p&lt;/code&gt; started to point any other memory location. Now look at the below code.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;gt;&amp;gt;&amp;gt; a = [1,2,3]
&amp;gt;&amp;gt;&amp;gt; id(a)
2917355980416
&amp;gt;&amp;gt;&amp;gt; a.append(4)
&amp;gt;&amp;gt;&amp;gt; id(a)
2917355980416
&amp;gt;&amp;gt;&amp;gt; a += [3,4,5]
&amp;gt;&amp;gt;&amp;gt; id(a)
2917355980416
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the above code we can see that even if we modify our list the id of the list reamains same, which means our variable a still points to the same memory location. We can exploit this behaviour to make our tuple immutable.&lt;/p&gt;

&lt;p&gt;Type the below code to make a mutable tuple.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;gt;&amp;gt;&amp;gt; tup = (1,2,[3])
&amp;gt;&amp;gt;&amp;gt; tup
(1, 2, [3])
&amp;gt;&amp;gt;&amp;gt; tup[2]
[3]
&amp;gt;&amp;gt;&amp;gt; tup[2].append(4)
&amp;gt;&amp;gt;&amp;gt; tup
(1, 2, [3, 4])
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can see that since the &lt;code&gt;list&lt;/code&gt; object held its &lt;code&gt;id&lt;/code&gt; even after we appended 4 to it, so the tuple had no problem mutating itself. The int data structure in Python changes its reference once its value is changes that is why we cannot change integer value in a tuple.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;gt;&amp;gt;&amp;gt; tup[0] = 8
Traceback (most recent call last):
  File "&amp;lt;stdin&amp;gt;", line 1, in &amp;lt;module&amp;gt;
TypeError: 'tuple' object does not support item assignment
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Thanks for reading, Hope you liked it.&lt;/p&gt;

</description>
      <category>python</category>
      <category>programming</category>
      <category>tricks</category>
      <category>tips</category>
    </item>
    <item>
      <title>Iterable Unpacking in Python #PyTip02</title>
      <dc:creator>Vivek</dc:creator>
      <pubDate>Fri, 05 Aug 2022 17:59:00 +0000</pubDate>
      <link>https://dev.to/vivekthedev/iterable-unpacking-in-python-3dne</link>
      <guid>https://dev.to/vivekthedev/iterable-unpacking-in-python-3dne</guid>
      <description>&lt;p&gt;Python provides an awesome variety of sequential data structures. The most common of them are &lt;code&gt;list&lt;/code&gt; and &lt;code&gt;tuple&lt;/code&gt;. These sequences are sometimes used to store records, which later needs to be unpacked. &lt;/p&gt;

&lt;p&gt;When unpacking the values from a sequence you may encounter error like-&lt;code&gt;ValueError: too many values to unpack&lt;/code&gt;. In this short post I will share a short PyTip to avoid this error.&lt;/p&gt;

&lt;h2&gt;
  
  
  Problem
&lt;/h2&gt;

&lt;p&gt;Create a file or run the below code in the Python REPL.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;primes = [2, 3, 5, 7, 11]
first, second, third = primes
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The above code will produce the following error.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Traceback (most recent call last):
  File "&amp;lt;stdin&amp;gt;", line 1, in &amp;lt;module&amp;gt;
ValueError: too many values to unpack (expected 3)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The above error states that to unpack your iterable, your iterable size must be three. &lt;/p&gt;

&lt;h2&gt;
  
  
  Solution
&lt;/h2&gt;

&lt;p&gt;To solve this problem &lt;a href="https://peps.python.org/pep-3132/" rel="noopener noreferrer"&gt;PEP 3132&lt;/a&gt; was proposed. Now, write the following code in Python REPL to see &lt;strong&gt;Extended Iterable Unpacking&lt;/strong&gt; in action.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;gt;&amp;gt;&amp;gt; primes = [2, 3, 5, 7, 11]
&amp;gt;&amp;gt;&amp;gt; first, second, third, *_ = primes
&amp;gt;&amp;gt;&amp;gt; print(first, second, third, _)
2 3 5 [7, 11]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the above output you can see that &lt;code&gt;*_&lt;/code&gt; syntax caught all the excess values from the iterable. The &lt;code&gt;_&lt;/code&gt; variable could be anything of your choice, it doesn't have to be an underscore. Additionally, "*_" may be used anywhere in the statement. Consider the example below to get the idea.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;gt;&amp;gt;&amp;gt; primes = [2, 3, 5, 7, 11]
&amp;gt;&amp;gt;&amp;gt; first, *_ , last = primes
&amp;gt;&amp;gt;&amp;gt; print(first, last, _)
2 11 [3, 5, 7]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I find this feature extreamly useful and hope you find it useful too. Thanks for the reading today's PyTip. &lt;/p&gt;

</description>
      <category>python</category>
      <category>programming</category>
      <category>codenewbie</category>
      <category>beginners</category>
    </item>
    <item>
      <title>How to make immutable dictionaries in Python #PyTip01</title>
      <dc:creator>Vivek</dc:creator>
      <pubDate>Thu, 04 Aug 2022 05:20:09 +0000</pubDate>
      <link>https://dev.to/vivekthedev/how-to-make-immutable-dictionaries-in-python-pytip01-2gjb</link>
      <guid>https://dev.to/vivekthedev/how-to-make-immutable-dictionaries-in-python-pytip01-2gjb</guid>
      <description>&lt;p&gt;Python is known for its batteries included capability, it has solutions for every data structure requirements. Dictionaries in Python are very useful when storing a record as Key/Value pairs.&lt;br&gt;
Although dictionaries are awesome, they are mutable that means their values can be changed after their intialization. &lt;/p&gt;

&lt;p&gt;In this short post you will learn how you can use &lt;code&gt;MappingProxyType&lt;/code&gt; to make your dictionaries immutable. &lt;/p&gt;
&lt;h2&gt;
  
  
  Problem
&lt;/h2&gt;

&lt;p&gt;Below is the example of how you would use dictionaries in Python.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;gt;&amp;gt;&amp;gt; person = {"id" : 735616, "first_name": "John", "last_name":"Doe"}
&amp;gt;&amp;gt;&amp;gt; person["id"]
735616
&amp;gt;&amp;gt;&amp;gt; person["first_name"] + ' ' + person["last_name"]
'John Doe'
&amp;gt;&amp;gt;&amp;gt; person["id"] = 000000
&amp;gt;&amp;gt;&amp;gt; person["id"]
0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the above code we can see that our &lt;code&gt;person&lt;/code&gt; record was changed and we have no way to block this change. This is where &lt;code&gt;MappingProxyType&lt;/code&gt; comes in.&lt;/p&gt;

&lt;h2&gt;
  
  
  Solution
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;MappingProxyType&lt;/code&gt; is a read-only wrapper that provides a proxy to the underlying data structure. &lt;code&gt;MappingProxyType&lt;/code&gt; do not support item assignment which makes our data immutable.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;gt;&amp;gt;&amp;gt; from types import MappingProxyType
&amp;gt;&amp;gt;&amp;gt; im_person = MappingProxyType({"id":736533, "first_name":"John", "last_name":"Doe"})
&amp;gt;&amp;gt;&amp;gt; im_person["id"]
736533
&amp;gt;&amp;gt;&amp;gt; im_person["id"] = 87
Traceback (most recent call last):
  File "&amp;lt;stdin&amp;gt;", line 1, in &amp;lt;module&amp;gt;
TypeError: 'mappingproxy' object does not support item assignment
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the above example you can see that when we attempted to change the value of &lt;code&gt;id&lt;/code&gt;, &lt;code&gt;MappingProxyType&lt;/code&gt; immediately throws &lt;code&gt;TypeError&lt;/code&gt;, thus making our dictionary immutable. Please note that &lt;code&gt;MappingProxyType&lt;/code&gt; accepts any data type not just dictionaries. You can also use &lt;code&gt;str&lt;/code&gt; and &lt;code&gt;list&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Caveat
&lt;/h2&gt;

&lt;p&gt;Before using &lt;code&gt;MappingProxyType&lt;/code&gt;, you must know that it only provides a Proxy to the underlying data structure which means if the underlying data is changed then &lt;code&gt;MappingProxyType&lt;/code&gt; will reflect those changes.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;gt;&amp;gt;&amp;gt; im_person = MappingProxyType(person)
&amp;gt;&amp;gt;&amp;gt; im_person
mappingproxy({'id': 0, 'first_name': 'John', 'last_name': 'Doe'})
&amp;gt;&amp;gt;&amp;gt; person
{'id': 0, 'first_name': 'John', 'last_name': 'Doe'}
&amp;gt;&amp;gt;&amp;gt; person["id"] = 00
&amp;gt;&amp;gt;&amp;gt; im_person
mappingproxy({'id': 0, 'first_name': 'John', 'last_name': 'Doe'})
&amp;gt;&amp;gt;&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As you can see in the above code that the &lt;code&gt;MappingProxyType&lt;/code&gt; changed when the underlying data dictionary was changed.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;In this short article you learned about &lt;code&gt;types.MappingProxyType&lt;/code&gt;,&lt;br&gt;
they are useful when you want to make sure that your Key/Value pairs remains immutable. Hope you liked it.&lt;/p&gt;

</description>
      <category>python</category>
      <category>programming</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Amazing Functools Features in Python</title>
      <dc:creator>Vivek</dc:creator>
      <pubDate>Sat, 02 Apr 2022 16:35:57 +0000</pubDate>
      <link>https://dev.to/vivekthedev/amazing-functools-features-in-python-3gnc</link>
      <guid>https://dev.to/vivekthedev/amazing-functools-features-in-python-3gnc</guid>
      <description>&lt;p&gt;I was recently reading Django’s Source Code, and I came across the @wraps decorator, which led me to the functools docs, where I discovered some fantastic functools features. That discovery led to the creation of this article.&lt;/p&gt;

&lt;p&gt;This tutorial will teach you how to use some fantastic functools methods to make your life simpler.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is &lt;code&gt;functools&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;functools is a Python built-in module that contains Higher Order functions that can interact with other functions. A complete &lt;a href="https://docs.python.org/3/library/functools.html" rel="noopener noreferrer"&gt;functools&lt;/a&gt; documentation may be found here.&lt;br&gt;
Let’s see some decorators in action.&lt;/p&gt;
&lt;h1&gt;
  
  
  lru_cache
&lt;/h1&gt;

&lt;p&gt;When invoking a function with the same arguments, this decorator in the functools module saves n number of function calls in cache, which saves a lot of time.&lt;/p&gt;

&lt;p&gt;Assume for the sake of demonstration that we have a very large function that takes a long time to execute. The function a_heavy_operation() takes 3 seconds to execute in this example.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import time

start = time.time()
def a_heavy_operation():
    time.sleep(3)
    return 11 + 22


print(a_heavy_operation())
print(a_heavy_operation())

print(time.time() - start)

# Output
# 33
# 33
# 6.024240255355835
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It takes about 6 seconds to run the above code. To the above function, we’ll add lru cache.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import time
from functools import lru_cache

start = time.time()


@lru_cache()
def a_heavy_operation():
    time.sleep(3)
    return 11 + 22


print(a_heavy_operation())
print(a_heavy_operation())

print(time.time() - start)

# Output
# 33
# 33
# 3.0158064365386963

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Take a look at how using lru cache made our code run faster. Python saved the function’s cache and retrieved the cached value, reducing our execution time.&lt;/p&gt;

&lt;h1&gt;
  
  
  wraps
&lt;/h1&gt;

&lt;p&gt;Wraps is used in functools to keep the function details. When we decorate a function, the function’s information is gone. We utilise the @wraps decorator on the decorator wrapper function to prevent this.&lt;/p&gt;

&lt;p&gt;Take a look at this code to see what I mean.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;from functools import lru_cache


def my_decorator(func):
    def log(*args, **kwargs):
        print("Running ")
        return func(*args, *kwargs)

    return log


@my_decorator
def add(a, b):
    """my beautiful doc"""
    return a + b

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Run the above code in -i mode using, python -i file.py&lt;br&gt;
Let's see what we have:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;gt;&amp;gt;&amp;gt; add(1,2)
Running 
3
&amp;gt;&amp;gt;&amp;gt; add(3,4)
Running 
7
&amp;gt;&amp;gt;&amp;gt; add.__name__
log
&amp;gt;&amp;gt;&amp;gt; add.__doc__
&amp;gt;&amp;gt;&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We can see that our decorator is operating properly in the previous example, since it is consistently “Running” on each run. However, our function’s information has been lost, and it is unable to return the name or the docstring.&lt;/p&gt;

&lt;p&gt;We have @wraps to help us with this problem. Make the changes below to the code.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;from functools import wraps

def my_decorator(func):
    @wraps(func)
    def log(*args, **kwargs):
        print("Running ")
        return func(*args, *kwargs)

    return log


@my_decorator
def add(a, b):
    """my beautiful doc"""
    return a + b

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now again run the code using &lt;code&gt;python -i file.py&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;gt;&amp;gt;&amp;gt; add(1,2) 
Running 
3       
&amp;gt;&amp;gt;&amp;gt; add.__name__
'add'
&amp;gt;&amp;gt;&amp;gt; add.__doc__
'my beautiful doc'
&amp;gt;&amp;gt;&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Voila! The function information is now saved in our function.&lt;/p&gt;

&lt;h1&gt;
  
  
  singledispatch
&lt;/h1&gt;

&lt;p&gt;To create a generic function, singledispatch is utilised. Generic functions are those that perform the same operation on a variety of data types.&lt;/p&gt;

&lt;p&gt;Assume I want to create a function that returns the first value from an iterable of several data types.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
def return_first_element(data):
    if isinstance(data, list):
        print(data[0])
    elif isinstance(data, str):
        print(data.split()[0])
    elif isinstance(data, dict):
        print(list(data.values())[0] )
    else:
        print(print(data))

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now run python -i file.py to run the code in interactive mode.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;gt;&amp;gt;&amp;gt; return_first_element({"Age":20, "Height": 180})
20
&amp;gt;&amp;gt;&amp;gt; return_first_element("Hello Mr Python")
Hello
&amp;gt;&amp;gt;&amp;gt; return_first_element([12,432,563])      
12
&amp;gt;&amp;gt;&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Our function is effective, but it isn’t clean. Using if/elif/else statements to create generic functions is not recommended in Python. So, what’s the solution? singledispatch, of course.&lt;/p&gt;

&lt;p&gt;Let’s make a few modifications to our code.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;from functools import singledispatch

@singledispatch
def return_first_el(data):
    return data


@return_first_el.register(list)
def _(data):
    return data[0]


@return_first_el.register(dict)
def _(data):
    return list(data.values())[0]


@return_first_el.register(str)
def _(data):
    return data.split()[0]

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To check the results, run the code again in interactive mode with python -i file.py.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;gt;&amp;gt;&amp;gt; return_first_el({"Age":20, "Height": 180}) 
20
&amp;gt;&amp;gt;&amp;gt; return_first_el("Hello Mr Python")             
'Hello'
&amp;gt;&amp;gt;&amp;gt; return_first_el([124, 765, 897])   
124
&amp;gt;&amp;gt;&amp;gt; return_first_el({12,31,1})       
{1, 12, 31}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Look how our return_first_el function acted as a fallback function when no data type matched for ‘set’.&lt;/p&gt;

&lt;p&gt;Look at how much cleaner our code is now; the singledispatch made it easier to add more data types, and each datatype now gets its own place where we can perform further operations on the data.&lt;/p&gt;

&lt;h1&gt;
  
  
  total_ordering
&lt;/h1&gt;

&lt;p&gt;The total_ordering decorator saves a ton of time in Object Oriented Progrmming.&lt;br&gt;
Consider this example, the below class declares a class Man with name and age property and (=) &lt;code&gt;__eq__&lt;/code&gt; and (&amp;lt;) &lt;code&gt;__lt__&lt;/code&gt; dunder methods.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;class Man:
    def __init__(self, name, age):
        self.name = name
        self.age = age

    def __eq__(self, o):
        return self.age == o.age

    def __lt__(self, o):
        return self.age &amp;lt; o.age

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let's try to run the code to see what we have.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;gt;&amp;gt;&amp;gt; obj = Man("Vivek", 20)
&amp;gt;&amp;gt;&amp;gt; obj2 = Man("Alex", 24) 
&amp;gt;&amp;gt;&amp;gt; obj = obj
&amp;gt;&amp;gt;&amp;gt; obj == obj2
False
&amp;gt;&amp;gt;&amp;gt; obj &amp;lt; obj2
True
&amp;gt;&amp;gt;&amp;gt; obj &amp;gt;= obj2
Traceback (most recent call last):
  File "&amp;lt;stdin&amp;gt;", line 1, in &amp;lt;module&amp;gt;
TypeError: '&amp;gt;=' not supported between instances of 'Man' and 'Man'
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Our code worked for (==) and (&amp;lt;), but it didn’t work when we used an operator that wasn’t defined in the class. Given that we create at least one operator dunder method and &lt;strong&gt;eq&lt;/strong&gt; method, @total_ordering generates the,&amp;gt;,=,&amp;gt;=, and more comparison operators for our class.&lt;/p&gt;

&lt;p&gt;Let’s add our decorator just above the class.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;from functools import total_ordering

@total_ordering
class Man:
.....
.....
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now again run thee code in interactive mode to see the results&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;gt;&amp;gt;&amp;gt; o = Man("Vivek", 20) 
&amp;gt;&amp;gt;&amp;gt; b = Man("Alex", 24) 
&amp;gt;&amp;gt;&amp;gt; o == b
False
&amp;gt;&amp;gt;&amp;gt; o &amp;gt;= b  
False
&amp;gt;&amp;gt;&amp;gt; o &amp;lt;= b
True
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Take a look at how total ordering generated our class’s comparison operators.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;I hope you found this post useful; I strongly advise you to study the documentation in order to fully comprehend the internal mechanics of these higher level functions. If you enjoyed this, please consider following me on &lt;a href="https://twitter.com/vivekthedev" rel="noopener noreferrer"&gt;Twitter&lt;/a&gt;, where I share stuff related to Python, Web development, and open source software. I’ll see you there.&lt;/p&gt;

</description>
      <category>python</category>
      <category>programming</category>
      <category>tutorial</category>
      <category>codenewbie</category>
    </item>
    <item>
      <title>How to configure Django/PostgreSQL environment using Docker</title>
      <dc:creator>Vivek</dc:creator>
      <pubDate>Thu, 03 Mar 2022 17:26:56 +0000</pubDate>
      <link>https://dev.to/vivekthedev/how-to-configure-djangopostgresql-environment-using-docker-4olc</link>
      <guid>https://dev.to/vivekthedev/how-to-configure-djangopostgresql-environment-using-docker-4olc</guid>
      <description>&lt;p&gt;In this step-by-step article you will learn how you can set up a local development environment with Django and with PostgreSQL service using Docker.&lt;/p&gt;

&lt;h1&gt;
  
  
  What is Docker?
&lt;/h1&gt;

&lt;p&gt;Docker is a way to create an isolated environment for your software, with the growing number of variables within a software such as operating systems, versions, dependencies there is need of virtualization so that a software can run on multiple systems without any problem. This is where Docker comes in, Docker virtualizes the application layer of the OS so that every application can run on this environment, The virtual environment is referred to as Docker Container. So with Docker we only need a configuration file and Voila you can launch your software on any system, this configuration file is called Docker Image.&lt;/p&gt;

&lt;h1&gt;
  
  
  Why Docker?
&lt;/h1&gt;

&lt;p&gt;But why do I need to use Docker to create a virtual environment? Python already has &lt;code&gt;venv&lt;/code&gt; and&lt;code&gt;pipenv&lt;/code&gt;. The answer is that these virtual environments can only create environments for Python packages, not environments for non-Python packages such as PostgreSQL. In this tutorial, we will  use pipenv to track  Python dependencies.&lt;/p&gt;

&lt;h1&gt;
  
  
  Docker: Installation
&lt;/h1&gt;

&lt;p&gt;Docker is available for Windows, Mac and Linux you can download it from the &lt;a href="https://www.docker.com/products/docker-desktop" rel="noopener noreferrer"&gt;official site&lt;/a&gt;. After downloading and installing the setup file you can confirm your installation by running &lt;code&gt;docker --version&lt;/code&gt; on your terminal/command prompt, which will output something like this.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Docker version 20.10.12, build e91ed57
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h1&gt;
  
  
  Setting up Python Project
&lt;/h1&gt;

&lt;p&gt;After installing Docker,  create a virtual environment for your Python packages using &lt;code&gt;pipenv&lt;/code&gt;. Install pipenv by running the below command. We have to use pipenv to package our Python dependencies.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;pip install pipenv
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Create a new directory anywhere on your PC. Set up your Python project here and open a command prompt in  this  folder. In the example below, we have created a folder named &lt;code&gt;test&lt;/code&gt; and moved our command line to this folder.&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fa05jr3666szvptxiogxh.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fa05jr3666szvptxiogxh.png" alt="Command Prompt Screen" width="534" height="222"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Run the following command to create a Virtual Environment and install &lt;code&gt;django&lt;/code&gt; using pipenv.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;pipenv install django
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You should see a &lt;code&gt;Pipfile&lt;/code&gt; and 'Pipfile.lock' file in the directory. To enter this virtual environment, simply run &lt;code&gt;pipenv shell&lt;/code&gt;. Then you should see something like this:&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fg7z4wi079gdxmhs7y8ry.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fg7z4wi079gdxmhs7y8ry.png" alt="Pipenv Environment" width="339" height="60"&gt;&lt;/a&gt;&lt;br&gt;
The &lt;code&gt;(test-xxxxxxx)&lt;/code&gt; shows that you are in the virtual environment.&lt;/p&gt;

&lt;p&gt;Now run &lt;code&gt;django-admin startproject newproj&lt;/code&gt; to create a new Django project in this environment. There is a dot (.)  at the endwhich tells django that it should create a new project  in the current directory. &lt;/p&gt;

&lt;p&gt;Now run &lt;code&gt;python manage.py startapp newapp&lt;/code&gt; to create a new Django app in this project. In order for this application to be discoverable in your project, you must add it to the INSTALLED_APPS setting in your newproj/settings.py file.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;...

# Application definition

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'newapp',
]
...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Before jumping into docker make sure that everything is working by running &lt;code&gt;python manage.py runserver&lt;/code&gt; and goto, &lt;a href="http://127.0.0.1:8000/" rel="noopener noreferrer"&gt;http://127.0.0.1:8000/&lt;/a&gt;&lt;br&gt;
This screen should open up, this means your installation is working.&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fz935bch8l37ifp3t83h3.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fz935bch8l37ifp3t83h3.png" alt="Django Screen" width="800" height="526"&gt;&lt;/a&gt;&lt;br&gt;
This is how our project looks like at this point:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;D:\WORK\PY\TEST
│   db.sqlite3
│   manage.py
│   Pipfile
│   Pipfile.lock
│
├───newapp
│   │   admin.py
│   │   apps.py
│   │   models.py
│   │   tests.py
│   │   views.py
│   │   __init__.py
│   │
│   └───migrations
│           __init__.py
│
└───newproj
        asgi.py
        settings.py
        urls.py
        wsgi.py
        __init__.py

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;At this point, you're ready to set up your Docker image, but before that, make sure you're NOT in the pipenv environment. Run &lt;code&gt;exit&lt;/code&gt; to exit the environment.&lt;/p&gt;

&lt;h1&gt;
  
  
  Set Up Docker
&lt;/h1&gt;

&lt;h3&gt;
  
  
  What is Docker Image:
&lt;/h3&gt;

&lt;p&gt;A Docker image is  like a blueprint and tells Docker how to build an application, where its dependencies are located, and the commands to run to build the application. Now create a "Dockerfile" in the same directory where your Pipfile and Pipfile.lock are located and write the following command to the file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;FROM python:3.9

ENV PYTHONDONTWRITEBYTECODE 1

WORKDIR /project

COPY Pipfile Pipfile.lock /project/
RUN pip install pipenv &amp;amp;&amp;amp; pipenv install --system

COPY . /project/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is a Docker image. Let's go through each line and see what it means.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;FROM python:3.9&lt;/code&gt; &amp;gt; FROM command is used to get the base image from  &lt;a href="https://hub.docker.com/" rel="noopener noreferrer"&gt;Docker Hub&lt;/a&gt;. Since we are creating a Django project, we need a base Python image as Django is built on top of Python. If you are using Jenkins  written in Java, you need to import openjdk. Replace 3.9 with something else and you can install any version of Python.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;ENV PYTHONDONTWRITEBYTECODE 1&lt;/code&gt; &amp;gt; Environment variable that tells Docker not to write cache files (.pyc), which are Python bytecode files, usually stored in the &lt;code&gt;__pycache__&lt;/code&gt; folder.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;WORKDIR /project&lt;/code&gt; &amp;gt; WORKDIR is used to set the working directory of the Django project where  project files will be stored.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;COPY Pipfile Pipfile.lock /project/&lt;/code&gt; &amp;gt; The project's dependencies are stored in the Pipfile, so you need to copy them to your working directory (/project/ in this case).&lt;/p&gt;

&lt;p&gt;&lt;code&gt;pip install pipenv &amp;amp;&amp;amp; pipenv install system&lt;/code&gt; &amp;gt; This command is self-explanatory and uses &lt;code&gt;pipenv&lt;/code&gt; to install all  dependencies on your system.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;COPY . /project/&lt;/code&gt; &amp;gt; Instruct Docker to copy all  files and folders (source code) to a WORKDIR named /project/.&lt;br&gt;
Now it is time to build out Docker Image, on the terminal run the following command.&lt;/p&gt;

&lt;p&gt;Now we have to build our docker image using the Dockerfile, from the following command&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;docker build .
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here's what Docker does after running the above program: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Creating a Linux Environment &lt;/li&gt;
&lt;li&gt;Install Python as an environment from Docker Hub
&lt;/li&gt;
&lt;li&gt;Install all  dependencies on the system &lt;/li&gt;
&lt;li&gt;Copy all  code from the directory to the Linux directory &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;To check this, VS Code provide a very useful Docker inspection tool to view the Linux directory structure of a Docker container. If you have a Docker container you can see this. &lt;/p&gt;

&lt;p&gt;You have now created your Docker image. Now we need a &lt;code&gt;docker-compose.yml&lt;/code&gt; file to control how  the container is started.&lt;/p&gt;

&lt;p&gt;Make a new file with name &lt;code&gt;docker-compose.yml&lt;/code&gt; and write the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;version: '3.8'
services:
    web:
      build: .
      command: python /project/manage.py runserver 0.0.0.0:8000
      volumes:
        - .:/project
      ports:
        - 8000:8000
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let's analyze the above file line by line. The first line tells us which Docker version  we want to use. &lt;/p&gt;

&lt;p&gt;Next, we define the services we want to run in the container. In this case, we want to use only one web service. It then tells Docker how to create the container and tells it to literally start the Django server by looking at the current directory (.) and running the runserver command. Volumes is Docker's data persistence mechanism and is used to keep Docker filesystem data synchronized with local directories. 'ports' specifies the ports that this particular web service can use. &lt;/p&gt;

&lt;p&gt;docker-compose.yml job finished. Now run the &lt;code&gt;docker-compose up&lt;/code&gt; command to start the service.&lt;/p&gt;

&lt;p&gt;Now goto, &lt;a href="http://127.0.0.1:8000/" rel="noopener noreferrer"&gt;http://127.0.0.1:8000/&lt;/a&gt; if the page is working then congratulations, you just started your first Docker Container.&lt;/p&gt;

&lt;p&gt;You can see in the below picture that we can now inspect our project files in the Docker Linux Container using VS Code&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fcc8gyk73ooe60ertv6tv.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fcc8gyk73ooe60ertv6tv.png" alt="Docker Container filesystem" width="471" height="608"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Adding PostgreSQL
&lt;/h2&gt;

&lt;p&gt;Django comes with a built-in SQLite database, which is not recommended for  production applications. For production, you need a powerful database like PostgreSQL. In this next part we are just going to do that.&lt;/p&gt;

&lt;p&gt;To use PostgreSQL, you need to: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Install psycopg2 so that  Python can talk with PostgreSQL. &lt;/li&gt;
&lt;li&gt;Create a PostgreSQL service in Docker with the required environment variables. &lt;/li&gt;
&lt;li&gt;Use volumes to persist data even if service is interrupted &lt;/li&gt;
&lt;li&gt;Edit the settings.py file in your Django project to change DATABASE type.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So, let's begin. To install psycopg2, you must first stop the docker container using the  &lt;code&gt;docker-compose down&lt;/code&gt; command.&lt;/p&gt;

&lt;p&gt;To install the PostgreSQL adapter for Django "psycopg2", enter the command &lt;code&gt;pipenv install psycopg2&lt;/code&gt;. We are installing the adapter using 'pipenv' rather than 'pip' to make it usable in a docker environment that installs all  dependencies for pipfile.lock.&lt;/p&gt;

&lt;p&gt;After the installation is complete, you need to create a service for PostgreSQL in the &lt;code&gt;dockercompose.yml&lt;/code&gt; file. Open the file and type:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;version: '3.8'
services:
  web:
    build: .
    command: python /project/manage.py runserver 0.0.0.0:8000
    volumes:
      - .:/project
    ports:
      - 8000:8000
    depends_on:
      - db

  db:
    image: postgres
    environment:
      POSTGRES_PASSWORD: postgres

    volumes:
      - postgres_data:/var/lib/postgresql/data/
volumes:
  postgres_data:

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the above file here is what we have done,&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;We created a new service called db, this service will pull PostgreSQL image from the &lt;a href="https://hub.docker.com/_/postgres" rel="noopener noreferrer"&gt;Docker Hub&lt;/a&gt;,&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;We setup environment PostgresSQL password which we will use in our settings.py file&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;volumes are the location where all the data is stored&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;volumes outside the db service indentation tells the docker that we want to persist the &lt;code&gt;postgres_data&lt;/code&gt; locally even when we stop our docker container.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;we also made changes in web service by adding &lt;code&gt;depends_on&lt;/code&gt; which tells docker that to start the db service before web service.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Now the only thing that is left to do is to tell our Django project that we have a postgreSQL db service and we want to use this database not SQLite database.&lt;/p&gt;

&lt;p&gt;Open newproj/setting.py file and make the following changes in the DATABASES settings (line 77)&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;....
DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.postgresql',
        'NAME': 'postgres',
        'USER': 'postgres',
        'PASSWORD':'postgres',
        'HOST':'db',
        'PORT':5432
    }
}
....
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;First we have to change our ENGINE to 'django.db.backends.postgresql', the NAME and USER settings can be of your choice, in the PASSWORD we have to put the password we defined in the &lt;code&gt;docker-compose.yml&lt;/code&gt;, the HOST is your service name which is db and in the PORT we have to provide 5432 which is PostgreSQL default port.&lt;/p&gt;

&lt;p&gt;All done, now let's start our docker container using the following command&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;docker-compose up -d --build
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Note that we use -d flag which stands for detach mode it will be useful when we want to execute some commands in the same terminal and --build flag is used because we made changes in our Pipenv file that is why we are telling Docker to build the Container from scratch.&lt;/p&gt;

&lt;p&gt;After running the above command goto &lt;a href="http://127.0.0.1:8000/" rel="noopener noreferrer"&gt;http://127.0.0.1:8000/&lt;/a&gt; , if that opens up then that means your db and web service are working.&lt;/p&gt;

&lt;h2&gt;
  
  
  Execute commands from docker
&lt;/h2&gt;

&lt;p&gt;You may have already noticed  that you can't run commands like migrate and createsuperuser using the command line, because you're getting the error "Could not resolve hostname 'db' to address: unknown host".&lt;/p&gt;

&lt;p&gt;PostgreSQL databases can only be used in Docker containers, so all database-related commands must be run in the container.&lt;/p&gt;

&lt;h3&gt;
  
  
  Migrate Database
&lt;/h3&gt;

&lt;p&gt;Go to the same terminal and run the following command, where we run the previous command to start our container.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;docker-compose exec web python manage.py migrate
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Create Super User
&lt;/h3&gt;

&lt;p&gt;To test if our database is working correctly, let's create our database and login the user from the localhost&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;docker-compose exec web python manage.py createsuperuser
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Type the Username, Email and Password and go to &lt;a href="http://127.0.0.1:8000/admin/login/?next=/admin/" rel="noopener noreferrer"&gt;Django Admin&lt;/a&gt; to see if our Admin Site is working.&lt;/p&gt;

&lt;p&gt;If this window opens up then congratulation you have successfully created a Django/PostgreSQL environment using Docker&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgw9zbivpgzepj7ju4vr2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgw9zbivpgzepj7ju4vr2.png" alt="Django Main screen" width="800" height="509"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  Conclusion
&lt;/h1&gt;

&lt;p&gt;Docker is far more vast then this, if you want to learn about Docker in detail then I would recommend the following resources.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://diveintodocker.com/" rel="noopener noreferrer"&gt;Dive into Docker&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.youtube.com/watch?v=3c-iBn73dDE" rel="noopener noreferrer"&gt;Docker in 3 Hours&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;GitHub Code Repository for the above article is available &lt;a href="https://github.com/vivekthedev/django-postgresql-with-docker-" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;If you liked the article then also consider checking me out on &lt;a href="https://twitter.com/vivekthedev" rel="noopener noreferrer"&gt;Twitter&lt;/a&gt; where I post stuff like this in under 280 characters daily.&lt;/p&gt;

&lt;p&gt;If you are facing any problem in following up with this article then please feel free to ask in the comments or on &lt;a href="https://twitter.com/vivekthedev" rel="noopener noreferrer"&gt;Twitter&lt;/a&gt;&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>django</category>
      <category>python</category>
      <category>docker</category>
    </item>
    <item>
      <title>How to make Python Script that organises files</title>
      <dc:creator>Vivek</dc:creator>
      <pubDate>Thu, 24 Feb 2022 16:09:52 +0000</pubDate>
      <link>https://dev.to/vivekthedev/how-i-organised-my-chaotic-folders-with-a-python-script-3f31</link>
      <guid>https://dev.to/vivekthedev/how-i-organised-my-chaotic-folders-with-a-python-script-3f31</guid>
      <description>&lt;p&gt;In this tutorial, you'll learn how to create a Python script that organises your folders by file extension.&lt;/p&gt;

&lt;p&gt;The prerequisites of this article is that you should be comfortable with Basic Python programming and how to use modules in Python.&lt;/p&gt;

&lt;p&gt;The downloads folder in my PC is second Recycle Bin to me.&lt;br&gt;
Here is what it looks like.&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fe9mxkgzlhdbpdqre5wx4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fe9mxkgzlhdbpdqre5wx4.png" alt="Screenshot" width="800" height="419"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;There are 112 files in that folder, and I am not very good at moving each file one by one after validating the file type. Let's use Python to automate the task.&lt;/p&gt;
&lt;h1&gt;
  
  
  What to build?
&lt;/h1&gt;

&lt;p&gt;A script or GUI programme that asks for the folder that needs to be organised and then moves all of the files according to their file types. For instance, if I have files like these:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;presentation.pptx
index.html
cover.jpeg
header.png
project.docx
file.dll
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After running the script the folder structure should be:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Organized/
     |
     |_ Documents/
     |     |_ presentation.pptx
     |     |_ project.docx
     |
     |_ Code/
     |     |_ index.html
     |
     |_ Images/
     |     |_ cover.jpeg
     |     |_ header.png
     |
     |_ Others/
           |_ file.dll
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Looks so clean now. Each file type has its own dedicated folder, the lookup has become much easier.&lt;/p&gt;

&lt;h1&gt;
  
  
  The Planning
&lt;/h1&gt;

&lt;p&gt;Before touching the keyboard first we must design our logic, here is what we need to do:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Ask for the folder that needs to be organized.&lt;/li&gt;
&lt;li&gt;Retrieves all filepaths from the folder.&lt;/li&gt;
&lt;li&gt;Check the filetype.&lt;/li&gt;
&lt;li&gt;Move the file according to its extension.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Looks good to me, now lets code:&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 1 : Tell File Extension to the script
&lt;/h3&gt;

&lt;p&gt;First lets create a dictionary that tells our script the folder where each type of file will go.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;filetypes = {
    "Images": ["png", "jpg", "jfif", "webp", "jpeg", "bmp", "tiff", "gif", "raw", "psd"],
    "Documents": ["doc", "docx", "ppt", "pptx", "xls", "xlsx", "pdf", "txt"],
    "Videos": ["mp4", "mpeg", "mkv", "srt"],
    "Code": ["html", "css", "js", "py", "cpp", "c"],
    "Audio": ["mp3", "wav", "ogg"],
    "Compressed": ["zip", "tar", "rar"],
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 2 : Ask the Directory Path
&lt;/h3&gt;

&lt;p&gt;I am getting the Directory name using a GUI dialog box, that will be convenient. Tkinter has this amazing &lt;code&gt;askdirectory&lt;/code&gt; function which we can use here.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;from tkinter import filedialog
p = filedialog.askdirectory()
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When we run the above cade our dialog box is showing up and &lt;code&gt;p&lt;/code&gt; contains the path of the directory which we selected. Okay so we are done with asking the directory name.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 3 : Getting all the files
&lt;/h3&gt;

&lt;p&gt;Now we have to get all the files from the given directory, for this I am using Pathlib, a built-in module in Python that makes working with Paths super easy. But first we have to create a folder &lt;code&gt;Organized&lt;/code&gt; like we planned above.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;from Pathlib import Path
PATH = Path(p)
dest = PATH / "Organized"
dest.mkdir(exist_ok=True)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The above code take the path &lt;code&gt;p&lt;/code&gt; provided by the tkinter file dialog box and sends it to the Path(), we make a new folder "Oganized" only if it is not already there, we do it by using &lt;code&gt;exist_ok=True&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Now the iteration part,&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;files = []
for i in PATH.iterdir():
    if i.is_file():
        files.append(i)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The above code iterates on every directory and files in the PATH, and if it is a file then saves it in the &lt;code&gt;files&lt;/code&gt; list. (this is why we used Pathlib)&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 3 : Check Type -&amp;gt; Move File
&lt;/h3&gt;

&lt;p&gt;Now the only thing left to do is checking the file type then creating a folder for the file (not creating if it already exists) then move the file but move the file into &lt;code&gt;Others&lt;/code&gt; folder if the file extension is unknown.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;for file in files:
        # done flag tells that if the file matched to a dictionary value or not
        done = 0
        # iterate over the keys and check if the file belong to the particular key
        for k in filetypes.keys():
            # Check if the file extension is in the values of the key
            if file.suffix[1:] in filetypes[k]:
                done = 1
                # make a new folder with `key name` and move the file there
                destf = dest / f"{k}"
                destf.mkdir(exist_ok=True)
                shutil.move(str(file.resolve()), str(destf))

        if done != 1:
            # if the file was not present in the dictionary the make Others folder  and move the file there
            destf = dest / "Others"
            destf.mkdir(exist_ok=True)
            shutil.move(str(file.resolve()), str(destf))

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The above code take each file from the &lt;code&gt;files&lt;/code&gt; list and then checks if the extension is present in the dictionary or not.&lt;br&gt;
If the extension is in the dictionary, make a folder for the file type and move the file into that folder. If the file's extension isn't found in the folder, move it to the 'Others' folder.&lt;/p&gt;

&lt;h1&gt;
  
  
  Time to run the script : The moment of truth
&lt;/h1&gt;

&lt;p&gt;I ran the script in my downloads folder, look how the script asked for the directory and within seconds created Organized folder which contains all the files.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnm3102u3ky8u3x5g7ycx.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnm3102u3ky8u3x5g7ycx.gif" alt="Running Script" width="480" height="356"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What more can be done?
&lt;/h2&gt;

&lt;p&gt;The script is working fine but here are few things you can experiment with the script:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Use argparse for Command Line Input&lt;/li&gt;
&lt;li&gt;Improve the performance, by removing the second loop and creating a new search method.&lt;/li&gt;
&lt;li&gt;Show information about the files found and progress bar using tqdm&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Try extending the script and make sure to create a PR on the repository.&lt;/p&gt;

&lt;h2&gt;
  
  
  Here is the script &lt;a href="https://github.com/vivekthedev/python-experiments/tree/main/Organize%20Files" rel="noopener noreferrer"&gt;repository link&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;Thanks for sticking till the end, it means a lot.&lt;br&gt;
If you liked the article then also consider checking me out on &lt;a href="https://twitter.com/vivekthedev/" rel="noopener noreferrer"&gt;Twitter&lt;/a&gt; where I post stuff about python, javascript and open source daily.&lt;/p&gt;

</description>
      <category>python</category>
      <category>programming</category>
      <category>tutorial</category>
      <category>productivity</category>
    </item>
  </channel>
</rss>
