<?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: Abhimanyu R B</title>
    <description>The latest articles on DEV Community by Abhimanyu R B (@abhimanyu_rb_b56e9e0a67f).</description>
    <link>https://dev.to/abhimanyu_rb_b56e9e0a67f</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%2F3714586%2F0e75aaa1-448e-4355-8456-50875f72e414.jpg</url>
      <title>DEV Community: Abhimanyu R B</title>
      <link>https://dev.to/abhimanyu_rb_b56e9e0a67f</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/abhimanyu_rb_b56e9e0a67f"/>
    <language>en</language>
    <item>
      <title>CPIC publishes 11 standardised phenotype terms. Its own API emits 33.</title>
      <dc:creator>Abhimanyu R B</dc:creator>
      <pubDate>Sun, 25 Oct 2026 16:38:49 +0000</pubDate>
      <link>https://dev.to/abhimanyu_rb_b56e9e0a67f/cpic-publishes-11-standardised-phenotype-terms-its-own-api-emits-33-4ho</link>
      <guid>https://dev.to/abhimanyu_rb_b56e9e0a67f/cpic-publishes-11-standardised-phenotype-terms-its-own-api-emits-33-4ho</guid>
      <description>&lt;p&gt;If you build anything that turns a pharmacogenomic lab result into advice, somewhere in your code is a set of phenotype strings — &lt;code&gt;Normal Metabolizer&lt;/code&gt;, &lt;code&gt;Poor Metabolizer&lt;/code&gt;, and so on. Mine was one flat frozenset, validated at import time. It was wrong in four ways, and I could measure every one of them against CPIC's own API.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. The standardised list is not the emitted list
&lt;/h2&gt;

&lt;p&gt;CPIC's &lt;code&gt;term&lt;/code&gt; table defines &lt;strong&gt;11&lt;/strong&gt; standardised phenotype terms. Its &lt;code&gt;diplotype&lt;/code&gt; table emits &lt;strong&gt;33&lt;/strong&gt; distinct values across the 19 genes that have diplotype tables. &lt;strong&gt;24&lt;/strong&gt; of those 33 are absent from the standardised list entirely — &lt;code&gt;Deficient with CNSHA&lt;/code&gt;, &lt;code&gt;Likely Poor Metabolizer&lt;/code&gt;, &lt;code&gt;Malignant Hyperthermia Susceptibility&lt;/code&gt;, &lt;code&gt;ivacaftor responsive in CF patients&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. There is not one exact-case match
&lt;/h2&gt;

&lt;p&gt;Zero. The term table writes &lt;code&gt;Normal metabolizer&lt;/code&gt;. The diplotype table writes &lt;code&gt;Normal Metabolizer&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Match literally on CPIC's own published spelling and you reject every real CPIC result. Everything casefolds now.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. The vocabulary is per gene, and genes do not answer the same question
&lt;/h2&gt;

&lt;p&gt;DPYD has 3 terms. TPMT and NUDT15 have 5 each. CYP2C19 has 8. SLCO1B1 answers in &lt;em&gt;Function&lt;/em&gt; terms, G6PD in &lt;code&gt;Deficient&lt;/code&gt; / &lt;code&gt;Normal&lt;/code&gt; / &lt;code&gt;Variable&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;A global set is correct for exactly one gene — in my case DPYD, the gene I happened to write it against. Adding a second drug class broke it immediately.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. "No Result" exists at only one layer
&lt;/h2&gt;

&lt;p&gt;A gene that was not assayed has the CPIC phenotype &lt;code&gt;No Result&lt;/code&gt;. It appears &lt;strong&gt;251 times&lt;/strong&gt; as a &lt;code&gt;recommendation.phenotypes&lt;/code&gt; key and &lt;strong&gt;never once&lt;/strong&gt; as a &lt;code&gt;diplotype.generesult&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;CPIC models it at the recommendation layer alone. So a gene has &lt;em&gt;two&lt;/em&gt; vocabularies, and the recommendation one is strictly larger.&lt;/p&gt;

&lt;p&gt;This is the one that turned out to matter most. Thiopurine dosing is keyed on TPMT and NUDT15 &lt;strong&gt;jointly&lt;/strong&gt;. A panel that types one of them has not half-answered the question — and CPIC has published an actual row for the other gene being unestablished. The honest output is to render CPIC's row, not to write my own sentence about the gap.&lt;/p&gt;

&lt;h2&gt;
  
  
  The consequence
&lt;/h2&gt;

&lt;p&gt;A term can be one CPIC &lt;em&gt;uses&lt;/em&gt; without being one CPIC has &lt;em&gt;defined&lt;/em&gt;. &lt;code&gt;Possible Intermediate Metabolizer&lt;/code&gt;, emitted by both TPMT and NUDT15, is exactly that.&lt;/p&gt;

&lt;p&gt;So the code no longer invents a gloss. It returns CPIC's own &lt;code&gt;functionaldef&lt;/code&gt; or nothing at all, and the report says "CPIC publishes no standardised definition for this term" instead of printing the phenotype bare — which reads as though nothing were missing.&lt;/p&gt;

&lt;h2&gt;
  
  
  The fix
&lt;/h2&gt;

&lt;p&gt;A generator that pulls the vocabulary from the API into a committed snapshot, per gene and per layer, matched casefolded. A &lt;code&gt;--check&lt;/code&gt; mode runs in CI, so the snapshot drifting from CPIC fails the build rather than quietly answering with last year's terms.&lt;/p&gt;

&lt;p&gt;Each of the four defects is now a failing test if you revert the fix. That was the part worth the afternoon.&lt;/p&gt;




&lt;p&gt;If you want to see what it produces: &lt;a href="https://prahari.anukritiai.com" rel="noopener noreferrer"&gt;https://prahari.anukritiai.com&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Paste a genotyping export, or just pick the drug, and it says which positions were covered, which were not, and what CPIC publishes for that combination. It quotes CPIC verbatim and does not compute a dose. It is investigational, carries no clinician sign-off, and stores nothing you paste.&lt;/p&gt;

</description>
      <category>healthcare</category>
      <category>python</category>
      <category>api</category>
    </item>
  </channel>
</rss>
