<?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: khush chaudhari</title>
    <description>The latest articles on DEV Community by khush chaudhari (@khush_chaudhari_0cb689c86).</description>
    <link>https://dev.to/khush_chaudhari_0cb689c86</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%2F4156208%2F248f38a2-47d7-40d0-aa9f-eafce6fa0568.png</url>
      <title>DEV Community: khush chaudhari</title>
      <link>https://dev.to/khush_chaudhari_0cb689c86</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/khush_chaudhari_0cb689c86"/>
    <language>en</language>
    <item>
      <title>friends's prescription said 0-1-0, so I built a local Gemma app that turns it into a medicine card</title>
      <dc:creator>khush chaudhari</dc:creator>
      <pubDate>Sun, 04 Oct 2026 17:59:40 +0000</pubDate>
      <link>https://dev.to/khush_chaudhari_0cb689c86/friendss-prescription-said-0-1-0-so-i-built-a-local-gemma-app-that-turns-it-into-a-medicine-card-3cn1</link>
      <guid>https://dev.to/khush_chaudhari_0cb689c86/friendss-prescription-said-0-1-0-so-i-built-a-local-gemma-app-that-turns-it-into-a-medicine-card-3cn1</guid>
      <description>&lt;p&gt;&lt;em&gt;This is a submission for the &lt;a href="https://dev.to/challenges/hacktoberfest-weekend-2026-10-01"&gt;Hacktoberfest Weekend Challenge: Build for a Friend&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

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

&lt;p&gt;&lt;strong&gt;DoseCard&lt;/strong&gt; turns a doctor's prescription (a photo or typed text) into a large-print daily medicine card in Hindi and English that you can print or send on WhatsApp.&lt;/p&gt;

&lt;p&gt;I built it for a friend, my neighbour's wife. She manages her prescriptions, and the instructions are often written in doctor's shorthand: &lt;code&gt;1-0-1&lt;/code&gt;, &lt;code&gt;0-1-0&lt;/code&gt;, &lt;code&gt;HS&lt;/code&gt;. If nobody has taught you the code, &lt;code&gt;0-1-0&lt;/code&gt; means one tablet in the afternoon and &lt;code&gt;HS&lt;/code&gt; means at bedtime. It's easy to get wrong, and a wrong guess means taking a medicine at the wrong time.&lt;/p&gt;

&lt;p&gt;DoseCard does three things:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Reads the prescription with Gemma&lt;/strong&gt;, running on the laptop.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Shows her what it read&lt;/strong&gt; and makes her check every medicine against the paper before anything goes on the card.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Makes the card&lt;/strong&gt;: सुबह / दोपहर / शाम / रात (morning / afternoon / evening / night), before or after food, with a separate box for "only when needed" medicines.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;It doesn't give medical advice. It only organises what the doctor wrote.&lt;/p&gt;

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

&lt;p&gt;&lt;strong&gt;Live:&lt;/strong&gt; &lt;a href="https://dosecard.onrender.com/" rel="noopener noreferrer"&gt;https://dosecard.onrender.com/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The page is hosted on Render, but the AI isn't. Your browser sends the prescription to Ollama on &lt;em&gt;your own&lt;/em&gt; computer (&lt;code&gt;localhost:11434&lt;/code&gt;). So the live link only works if you have Ollama and the model installed, and the page walks you through that one-time setup. The video shows it running on my 8 GB MacBook.&lt;/p&gt;

&lt;h2&gt;
  
  
  Code
&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/khushthecoder" rel="noopener noreferrer"&gt;
        khushthecoder
      &lt;/a&gt; / &lt;a href="https://github.com/khushthecoder/cheetah_26" rel="noopener noreferrer"&gt;
        cheetah_26
      &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;DoseCard&lt;/h1&gt;
&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;Photograph a doctor's prescription. Get a large-print, Hindi + English daily medicine card for the fridge — read by Gemma running on your own laptop.&lt;/strong&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Built for the Hacktoberfest 2026 Weekend Challenge: &lt;em&gt;Build for a Friend&lt;/em&gt;.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;strong&gt;Live:&lt;/strong&gt; &lt;a href="https://dosecard.onrender.com" rel="nofollow noopener noreferrer"&gt;https://dosecard.onrender.com&lt;/a&gt; — the page is hosted; the AI runs on your own computer (see &lt;a href="https://github.com/khushthecoder/cheetah_26#deployment-render" rel="noopener noreferrer"&gt;Deployment&lt;/a&gt;).&lt;/p&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Why I built this&lt;/h2&gt;
&lt;/div&gt;

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

&lt;/div&gt;
&lt;p&gt;Indian prescriptions are written in shorthand: &lt;code&gt;1-0-1&lt;/code&gt;, &lt;code&gt;BD&lt;/code&gt;, &lt;code&gt;TDS&lt;/code&gt;, &lt;code&gt;HS&lt;/code&gt;, &lt;code&gt;OD&lt;/code&gt;, &lt;code&gt;SOS&lt;/code&gt;, &lt;code&gt;AC/PC&lt;/code&gt;, often by hand.
Someone in the family has to translate that into &lt;em&gt;"which pill, when, before or after food, until when"&lt;/em&gt; — and then
re-explain it over the phone, or write it out again on paper, every time the prescription changes.&lt;/p&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;What DoseCard does&lt;/h2&gt;

&lt;/div&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Add the prescription&lt;/strong&gt; — a photo (up to 3 pages) or typed / pasted text.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Gemma reads it on-device&lt;/strong&gt; — an open-weight model running in…&lt;/li&gt;
&lt;/ol&gt;&lt;/div&gt;
  &lt;/div&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/khushthecoder/cheetah_26" 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;p&gt;&lt;strong&gt;Stack:&lt;/strong&gt; Gemma 4 E2B (&lt;code&gt;gemma4:e2b-it-qat&lt;/code&gt;, a 4.3 GB download) running locally in Ollama · React + TypeScript + Vite · Zod · Vitest · GitHub Actions · Render&lt;/p&gt;

&lt;p&gt;My laptop is an 8 GB M3. That ruled out bigger models (the 6 GB E4B would push it into swap), so the whole design had to assume a &lt;em&gt;small&lt;/em&gt; model.&lt;/p&gt;

&lt;h3&gt;
  
  
  Rule one: the model reads, code decides
&lt;/h3&gt;

&lt;p&gt;My first version asked Gemma for each medicine &lt;em&gt;and&lt;/em&gt; when to take it. I wrote a small eval (made-up prescriptions in different styles: GP shorthand, Latin codes, a Hinglish WhatsApp message) and measured it.&lt;/p&gt;

&lt;p&gt;Gemma found all 23 medicines. But its own answer for &lt;em&gt;when&lt;/em&gt; to take them was right only &lt;strong&gt;9 times out of 23&lt;/strong&gt;. It read &lt;code&gt;HS&lt;/code&gt; (bedtime) as morning and &lt;code&gt;0-0-1&lt;/code&gt; as evening.&lt;/p&gt;

&lt;p&gt;The same model copying the dosing text &lt;em&gt;exactly as written&lt;/em&gt; got all 23 right. So I split the job. Gemma only transcribes (&lt;code&gt;1-0-1&lt;/code&gt;, &lt;code&gt;BD&lt;/code&gt;, &lt;code&gt;only if fever&lt;/code&gt;), and a deterministic, unit-tested decoder turns that into a schedule. If the decoder can't read something, the row is left blank for a person to fill in. The model is never asked to guess.&lt;/p&gt;

&lt;p&gt;The eval caught my bugs too:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;My decoder read &lt;code&gt;Glimisave M1 1-0-1&lt;/code&gt; as three times a day, and would have read &lt;code&gt;Telma 40 1-0-0&lt;/code&gt; as &lt;strong&gt;40 tablets&lt;/strong&gt; in the morning.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;OD&lt;/code&gt; (once a day) was silently turned into "morning". The doctor never wrote a time, so now the app asks.&lt;/li&gt;
&lt;li&gt;"Cetirizine at bedtime &lt;strong&gt;if itching&lt;/strong&gt;" landed on the daily schedule. Any written condition now keeps a medicine in the "only when needed" box.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Then I tried my friend's real prescription
&lt;/h3&gt;

&lt;p&gt;Typed test cases are one thing. Her prescription is handwritten, crumpled, in two columns, and the photo I got was sideways.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Attempt 1, the photo as it was:&lt;/strong&gt; it ran for 98 seconds, got stuck repeating "T. Vit", and &lt;em&gt;invented a medicine&lt;/em&gt;: "Telma 40, 1-0-1, after food, 5 days, with warm water". None of that is on her prescription. It was the example line from my own prompt.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Attempt 2, rotated upright, straight to JSON:&lt;/strong&gt; most names came back, but none of the doses, and the header scribbles became "medicines".&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Attempt 3, two passes.&lt;/strong&gt; First I asked Gemma for a plain transcription, one medicine per line with its dose on the same line. About 6 seconds later:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;T. Vite 0-1-0
T. zul (ad) 1-0-0
Butonim 0.05 ointment
BPO 2.5% gel.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Both doses were right. But when the second pass turned that text into medicines, it gave Vit C the dose &lt;code&gt;1-0-0&lt;/code&gt;, which is Zinc's dose from the next line. None of my checks caught it, because "1-0-0" really is on the page.&lt;/p&gt;

&lt;h3&gt;
  
  
  The fix that made it safe
&lt;/h3&gt;

&lt;p&gt;Now &lt;em&gt;code&lt;/em&gt; decides which dose belongs to which medicine. It finds the medicine's own line and takes the dose written on it, or on a dose-only line right below it. If the model paired them differently, the written position wins and the row is flagged.&lt;/p&gt;

&lt;p&gt;The other fixes from that night:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A ↻ button, so the photo is upright before the model sees it&lt;/li&gt;
&lt;li&gt;No real medicine names in the prompts (a test checks this)&lt;/li&gt;
&lt;li&gt;A cap on output length, so a repetition loop fails in seconds, not minutes&lt;/li&gt;
&lt;li&gt;She sees and can edit the transcription before anything else happens&lt;/li&gt;
&lt;li&gt;Any name, strength or dose that isn't actually in the text gets flagged&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The result on her prescription: &lt;strong&gt;Vit C → afternoon, Zinc → morning&lt;/strong&gt;, matching the paper, with no wrong doses. It still misread names ("Vite" for Vit C, "Butonim" for Tretinoin) and missed the face wash entirely. That's exactly why the app won't make a card until every row has been ticked by a human.&lt;/p&gt;

&lt;h3&gt;
  
  
  Results
&lt;/h3&gt;

&lt;p&gt;Typed eval: 8 prescriptions, 34 medicines. 3 of the prescriptions were written &lt;em&gt;after&lt;/em&gt; the fixes as a held-out check.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Medicines found&lt;/td&gt;
&lt;td&gt;34 / 34&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Invented medicines&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Schedule correct&lt;/td&gt;
&lt;td&gt;32 / 34&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Schedule wrong&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;0&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Left for the person (&lt;code&gt;OD&lt;/code&gt; with no time written)&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Average time per prescription&lt;/td&gt;
&lt;td&gt;7.2 s&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;98 unit tests run in GitHub Actions on every push, and Render only deploys after they pass.&lt;/p&gt;

&lt;h3&gt;
  
  
  What friends said
&lt;/h3&gt;

&lt;p&gt;The first real-world test showed that DoseCard was useful, but it was not a magic one-click solution.&lt;/p&gt;

&lt;p&gt;The biggest point of friction was the handwritten prescription itself. The AI could identify most of the medicines and correctly read the written dosing patterns, but some medicine names were unclear and needed to be corrected by the user.&lt;/p&gt;

&lt;p&gt;The most useful part of the experience was the review step: instead of silently trusting the AI, the user could see what had been read, correct anything that was wrong, and only then generate the final daily schedule card.&lt;/p&gt;

&lt;p&gt;That test changed the product in an important way. We stopped treating AI output as the final answer and designed DoseCard around &lt;strong&gt;AI transcription + human verification&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Does Open Innovation Matter?
&lt;/h2&gt;

&lt;p&gt;Because this is a photo of someone's prescription.&lt;/p&gt;

&lt;p&gt;With a closed API, step one would be uploading her health record (her name, her doctor, her medicines) to a company's server. With an open-weight model, it never leaves the laptop. That's not a privacy setting; there is simply no server in the path. Even the deployed site is just static files. Render serves the page, the browser talks to Gemma on your own machine, and the site's Content-Security-Policy only lets the page connect to itself and &lt;code&gt;localhost&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Open weights also made the engineering possible:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;I could measure the model honestly.&lt;/strong&gt; I ran the eval and her prescription again and again while fixing things, at temperature 0, for free. That's how I found the model's timing guesses were right only 9 times out of 23, and designed around it instead of trusting it.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;I could constrain it.&lt;/strong&gt; Ollama's structured output forces Gemma to answer in my JSON schema, with limits on list length and output tokens.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;It runs on what people actually have.&lt;/strong&gt; A 4.3 GB model on an 8 GB laptop, offline, with no API key and no bill per prescription.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;It can be swapped.&lt;/strong&gt; One environment variable changes the model, and the eval tells me whether a new one is actually better.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Prize Categories
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Gemma&lt;/strong&gt;: DoseCard runs Gemma 4 E2B (open weights, QAT) locally through Ollama, both to read the photo and to structure the text.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Render&lt;/strong&gt;: deployed as a Render static site from a Blueprint (&lt;code&gt;render.yaml&lt;/code&gt;) with security headers, auto-deploying only after GitHub Actions CI passes.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>devchallenge</category>
      <category>weekendchallenge</category>
      <category>hf26challenge</category>
    </item>
  </channel>
</rss>
