<?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: Shanthi Kumar V</title>
    <description>The latest articles on DEV Community by Shanthi Kumar V (@shanthi_kumarv_1c960b42f).</description>
    <link>https://dev.to/shanthi_kumarv_1c960b42f</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.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4198274%2F6feb289d-0b3d-4ace-84f5-6e9c36b37410.jpg</url>
      <title>DEV Community: Shanthi Kumar V</title>
      <link>https://dev.to/shanthi_kumarv_1c960b42f</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/shanthi_kumarv_1c960b42f"/>
    <language>en</language>
    <item>
      <title>Building a Local Gmail Campaign Dashboard with Python, Flask, and Excel</title>
      <dc:creator>Shanthi Kumar V</dc:creator>
      <pubDate>Wed, 21 Oct 2026 12:29:43 +0000</pubDate>
      <link>https://dev.to/shanthi_kumarv_1c960b42f/building-a-local-gmail-campaign-dashboard-with-python-flask-and-excel-5g53</link>
      <guid>https://dev.to/shanthi_kumarv_1c960b42f/building-a-local-gmail-campaign-dashboard-with-python-flask-and-excel-5g53</guid>
      <description>&lt;p&gt;Building a Local Gmail Campaign Dashboard with Python, Flask, and Excel&lt;/p&gt;

&lt;p&gt;An engineering guide to integrating Excel contacts, Word templates, Google OAuth, and Gmail API operations into a local automation workflow.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The Problem&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Small teams often juggle multiple tools for email campaigns:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Contacts in Excel
&lt;/li&gt;
&lt;li&gt;Content in Word
&lt;/li&gt;
&lt;li&gt;Manual Gmail sending
&lt;/li&gt;
&lt;li&gt;Ad‑hoc tracking in spreadsheets
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This fragmented workflow leads to errors, duplicate sends, and wasted time. A local dashboard can unify these steps while keeping data private.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Architecture Overview&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Core components:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Python — workflow orchestration
&lt;/li&gt;
&lt;li&gt;Flask — local browser dashboard (&lt;code&gt;http://localhost:5000&lt;/code&gt;)
&lt;/li&gt;
&lt;li&gt;Excel — contact list + status tracking
&lt;/li&gt;
&lt;li&gt;Word — reusable templates with placeholders
&lt;/li&gt;
&lt;li&gt;Gmail API — sending messages via OAuth
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Flow:&lt;br&gt;
Excel → Word → OAuth → Flask dashboard → Gmail API → Status/logs&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Excel Contact List&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;A simple schema works well:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Column&lt;/th&gt;
&lt;th&gt;Purpose&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Name&lt;/td&gt;
&lt;td&gt;Personalization&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Email&lt;/td&gt;
&lt;td&gt;Recipient address&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Status&lt;/td&gt;
&lt;td&gt;Pending / Sent / Failed / Skipped&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Time Stamp&lt;/td&gt;
&lt;td&gt;Outcome time&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Design tip: Define states carefully. Handle edge cases like uncertain sends (e.g., Gmail accepted but app crashed before logging). Consider idempotency and reconciliation for robustness.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Word Templates&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Use placeholders like &lt;code&gt;{Name}&lt;/code&gt; for personalization:&lt;/p&gt;

&lt;p&gt;Subject: Update for {Name}&lt;/p&gt;

&lt;p&gt;Hello {Name},&lt;br&gt;
I wanted to share an update...&lt;/p&gt;

&lt;p&gt;Challenges: Word formatting runs can split placeholders. Test cases should include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Bold/italic text
&lt;/li&gt;
&lt;li&gt;Missing values
&lt;/li&gt;
&lt;li&gt;Non‑English names
&lt;/li&gt;
&lt;li&gt;Tables
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Document supported features clearly.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Gmail OAuth&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Never ask for raw passwords. Use OAuth with the &lt;code&gt;gmail.send&lt;/code&gt; scope.&lt;/p&gt;

&lt;p&gt;Setup steps:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Create Google Cloud project
&lt;/li&gt;
&lt;li&gt;Enable Gmail API
&lt;/li&gt;
&lt;li&gt;Configure OAuth consent screen
&lt;/li&gt;
&lt;li&gt;Create client credentials
&lt;/li&gt;
&lt;li&gt;Run authorization flow
&lt;/li&gt;
&lt;li&gt;Store tokens securely
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Security checklist:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Don’t commit secrets/tokens
&lt;/li&gt;
&lt;li&gt;Use minimum scopes
&lt;/li&gt;
&lt;li&gt;Provide revocation procedure
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;References:  &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Gmail API Guides &lt;a href="https://www.bing.com/search?q=" rel="noopener noreferrer"&gt;(developers.google.com in Bing)&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;OAuth 2.0 &lt;a href="https://www.bing.com/search?q=" rel="noopener noreferrer"&gt;(developers.google.com in Bing)&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;ol&gt;
&lt;li&gt;Flask Dashboard&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Expose controls and visibility:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Auth status
&lt;/li&gt;
&lt;li&gt;Start/pause campaign
&lt;/li&gt;
&lt;li&gt;Stats (sent/failed/pending)
&lt;/li&gt;
&lt;li&gt;Logs and previews
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Concurrency tip: Background workers must handle pause/resume, avoid duplicate sends, and record state reliably. For larger deployments, consider a task queue + database.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Error Handling &amp;amp; Recovery&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Differentiate:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Input validation errors
&lt;/li&gt;
&lt;li&gt;Authorization failures
&lt;/li&gt;
&lt;li&gt;API errors
&lt;/li&gt;
&lt;li&gt;Local file errors
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Avoid blind retries — duplicates are worse than delays. Record uncertain outcomes and let users reconcile.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Testing Checklist&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Cover:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Valid/invalid contacts
&lt;/li&gt;
&lt;li&gt;Template edge cases
&lt;/li&gt;
&lt;li&gt;OAuth token expiry
&lt;/li&gt;
&lt;li&gt;Operational scenarios (pause, restart, crash recovery)
&lt;/li&gt;
&lt;li&gt;Reporting accuracy
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Start with small test datasets before scaling.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Responsible Use&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Automation ≠ permission. Ensure compliance with:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Legal outreach rules
&lt;/li&gt;
&lt;li&gt;Gmail quotas
&lt;/li&gt;
&lt;li&gt;Organizational policies
&lt;/li&gt;
&lt;li&gt;Respect for unsubscribe requests
&lt;/li&gt;
&lt;/ul&gt;

&lt;ol&gt;
&lt;li&gt;Packaged Implementation&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;I’ve built a commercial product, &lt;strong&gt;Gmail Campaign Toolkit — Windows Edition&lt;/strong&gt;, which packages this architecture for small businesses. It runs locally, integrates Excel + Word, uses OAuth, and provides campaign monitoring.  &lt;/p&gt;

&lt;p&gt;👉 Available on Gum road:&lt;br&gt;
&lt;a href="https://vskdevops.gumroad.com/l/gmailcampaign" rel="noopener noreferrer"&gt;https://vskdevops.gumroad.com/l/gmailcampaign&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Conclusion&lt;/p&gt;

&lt;p&gt;This tutorial shows how Python, Flask, Excel, Word, and Gmail API can be combined into a local campaign dashboard. The hard problems are not just sending emails — they’re &lt;strong&gt;authorization, validation, reliable state tracking, concurrency, and recovery&lt;/strong&gt;.  &lt;/p&gt;

&lt;p&gt;Discussion: If you were extending this design, would you add a database‑backed status store, a task queue, stronger audit logging, or recovery for uncertain sends first? Share your thoughts in the comments.&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Flrlhslnvgwo9e4r2nbvw.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Flrlhslnvgwo9e4r2nbvw.png" alt=" " width="799" height="448"&gt;&lt;/a&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fniz5m22mkybsfqo5i4av.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fniz5m22mkybsfqo5i4av.png" alt=" " width="800" height="387"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>gmailcampaign</category>
      <category>emailautomation</category>
    </item>
  </channel>
</rss>
