<?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: Ray Basweti</title>
    <description>The latest articles on DEV Community by Ray Basweti (@raybasweti).</description>
    <link>https://dev.to/raybasweti</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%2F1376460%2F26d7944c-22d3-4a7b-923a-4efd529a9063.jpg</url>
      <title>DEV Community: Ray Basweti</title>
      <link>https://dev.to/raybasweti</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/raybasweti"/>
    <language>en</language>
    <item>
      <title>How I Built a Hybrid Cloud-to-Local Photo Workflow Using Google Sheets and Python</title>
      <dc:creator>Ray Basweti</dc:creator>
      <pubDate>Tue, 03 Feb 2026 11:31:20 +0000</pubDate>
      <link>https://dev.to/raybasweti/how-i-built-a-hybrid-cloud-to-local-photo-workflow-using-google-sheets-and-python-4cel</link>
      <guid>https://dev.to/raybasweti/how-i-built-a-hybrid-cloud-to-local-photo-workflow-using-google-sheets-and-python-4cel</guid>
      <description>&lt;h2&gt;
  
  
  Bridging the gap between mobile culling and local file management without uploading 1TB of data.
&lt;/h2&gt;

&lt;p&gt;As a developer and photographer, I faced a unique bottleneck. I wanted to select my best photos while sitting on the couch using my phone, but my 50MB RAW files lived on a massive hard drive in my office. I didn't want to pay for expensive cloud culling software, and I certainly didn't want to upload terabytes of data just to select 50 images.&lt;/p&gt;

&lt;p&gt;My solution? A Hybrid Workflow using &lt;strong&gt;Google Sheets&lt;/strong&gt; as a lightweight frontend and Python as the heavy-lifting backend.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Architecture
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. The Cloud Interface (Google Apps Script)
&lt;/h3&gt;

&lt;p&gt;I utilized Google Sheets not as a spreadsheet, but as a hosting platform for a custom HTML5 Web App.&lt;/p&gt;

&lt;p&gt;Why Sheets? It handles &lt;strong&gt;authentication&lt;/strong&gt; and &lt;strong&gt;hosting&lt;/strong&gt; for free.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The UI&lt;/strong&gt;: I built a responsive, mobile-first grid using the IBM Plex Sans design system. It allows for "Start/End" range input (e.g., IMG_8100 to IMG_8500) and renders clickable tiles.&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%2Fzei2wfbxmxujyocn3s94.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%2Fzei2wfbxmxujyocn3s94.png" alt="The selection page where a user marks the photos chosen" width="800" height="399"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Output&lt;/strong&gt;: Instead of saving data to a database, it generates a "Recipe" — a PDF file containing only the IDs of the selected photos.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. The Local Engine (Python &amp;amp; Flask)
&lt;/h3&gt;

&lt;p&gt;Once the "Recipe PDF" is generated, the local work begins.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Stack&lt;/strong&gt;: I wrapped a Python script in a Flask web server to provide a modern UI, but used Tkinter for native Windows folder dialogs (because browser file pickers are too restrictive).&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%2F4r0af5vt1igfg2chlbsw.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%2F4r0af5vt1igfg2chlbsw.png" alt=" " width="800" height="453"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Logic&lt;/strong&gt;:&lt;/p&gt;

&lt;p&gt;The app parses the PDF using &lt;code&gt;pypdf&lt;/code&gt; to extract IDs (e.g., 8104).&lt;/p&gt;

&lt;p&gt;It scans a local source directory for matches, handling multiple file extensions (.CR2, .JPG, .NEF).&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%2Frgext0pfc7fywrym5ziq.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%2Frgext0pfc7fywrym5ziq.png" alt="The local page where the user loads the generated PDF and selects the source and destination folders" width="800" height="404"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It performs a &lt;strong&gt;safe copy&lt;/strong&gt; (not cut) to a destination folder.&lt;/p&gt;

&lt;h2&gt;
  
  
  Technical Challenges &amp;amp; Solutions
&lt;/h2&gt;

&lt;h3&gt;
  
  
  The "WinError 32" Lock
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;The Problem&lt;/strong&gt;: On Windows, reading a PDF via Flask kept the file handle open. When I tried to move or delete the file later, the app crashed with &lt;em&gt;PermissionDenied&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Fix&lt;/strong&gt;: I implemented a strict &lt;code&gt;file.seek(0)&lt;/code&gt; and explicit stream closure pattern in Python to release the file handle immediately after parsing the text.&lt;/p&gt;

&lt;h3&gt;
  
  
  Mobile UX on Sheets
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;The Problem&lt;/strong&gt;: Scrolling through 2,000 grid items on a phone was sluggish, and the interface felt like... well, a spreadsheet.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Fix&lt;/strong&gt;: I wrote custom CSS media queries to switch from a flexible grid to a strict 2-column layout on mobile, with increased touch targets and a sticky footer, making it feel like a native app.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Result
&lt;/h2&gt;

&lt;p&gt;This tool reduced a 2-hour workflow to 15 minutes. I can cull photos on my phone, AirDrop the PDF to my PC, and have Python extract the files instantly.&lt;/p&gt;

&lt;h2&gt;
  
  
  Future Plans
&lt;/h2&gt;

&lt;p&gt;I am looking into packaging this as an Electron app for easier distribution and adding AI-based blur detection.&lt;/p&gt;

&lt;p&gt;Built with 💙 by Ray Basweti. Check out the code on &lt;a href="https://github.com/TaffCodes/photo-workflow" rel="noopener noreferrer"&gt;my Github&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>python</category>
      <category>webdev</category>
      <category>photographyworkflow</category>
      <category>raybasweti</category>
    </item>
  </channel>
</rss>
