<?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: SPG Medisafe</title>
    <description>The latest articles on DEV Community by SPG Medisafe (@spgmedisafe).</description>
    <link>https://dev.to/spgmedisafe</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%2F4171298%2F1ae2fa1a-9dd3-486e-8a87-b8fd17fbd56a.png</url>
      <title>DEV Community: SPG Medisafe</title>
      <link>https://dev.to/spgmedisafe</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/spgmedisafe"/>
    <language>en</language>
    <item>
      <title>Validating Medical Device Specs in Python: A Stone Basket Example</title>
      <dc:creator>SPG Medisafe</dc:creator>
      <pubDate>Tue, 13 Oct 2026 12:15:19 +0000</pubDate>
      <link>https://dev.to/spgmedisafe/validating-medical-device-specs-in-python-a-stone-basket-example-jgb</link>
      <guid>https://dev.to/spgmedisafe/validating-medical-device-specs-in-python-a-stone-basket-example-jgb</guid>
      <description>&lt;p&gt;If you maintain a product catalogue or ERP feed for medical devices, bad specification data is a quiet source of errors. A basket listed with the wrong length or a missing diameter can end up in a tender or an order. Here is a small, practical approach to validating device specs before they are published.&lt;/p&gt;

&lt;h2&gt;
  
  
  The problem
&lt;/h2&gt;

&lt;p&gt;A ureteroscopic stone retrieval basket has a handful of attributes that buyers filter on: shaft diameter (Fr), working length (cm), cage style, wire count and sterility. Suppliers send these in spreadsheets with inconsistent units and spellings, such as &lt;code&gt;1.9F&lt;/code&gt;, &lt;code&gt;1.9 Fr&lt;/code&gt; or &lt;code&gt;1.9 french&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Model the data
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;dataclasses&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;dataclass&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;typing&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Literal&lt;/span&gt;

&lt;span class="n"&gt;Cage&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Literal&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;helical&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;flat-wire&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;tipless&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;fixed-tip&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="nd"&gt;@dataclass&lt;/span&gt;
&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;BasketSpec&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;sku&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;
    &lt;span class="n"&gt;shaft_fr&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;float&lt;/span&gt;
    &lt;span class="n"&gt;length_cm&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;
    &lt;span class="n"&gt;cage&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Cage&lt;/span&gt;
    &lt;span class="n"&gt;wires&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;
    &lt;span class="n"&gt;sterile&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;bool&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Normalise before you validate
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;re&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;parse_fr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;float&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;m&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;re&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;search&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;r&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;(\d+(?:\.\d+)?)&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="nc"&gt;ValueError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;No diameter in &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="si"&gt;!r}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;float&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;group&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Add sanity checks
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;validate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;spec&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;BasketSpec&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;list&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
    &lt;span class="n"&gt;errors&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="mf"&gt;1.0&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="n"&gt;spec&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;shaft_fr&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="mf"&gt;4.0&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;errors&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;shaft_fr outside expected range&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="mi"&gt;50&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="n"&gt;spec&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;length_cm&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="mi"&gt;150&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;errors&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;length_cm outside expected range&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;spec&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;wires&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;errors&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;wires must be 2 or more&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;spec&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sterile&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;errors&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;sterile flag missing&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;errors&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The ranges are placeholders. Take the real limits from each manufacturer's specification sheet rather than hard-coding assumptions.&lt;/p&gt;

&lt;h2&gt;
  
  
  Fail loudly in your pipeline
&lt;/h2&gt;

&lt;p&gt;Run the validator in CI or in your import job, and reject the batch if any row returns errors. A report listing the SKU and the failing field is much easier for a catalogue team to fix than a silent bad record.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why it matters
&lt;/h2&gt;

&lt;p&gt;Buyers compare devices on exactly these fields, so clean data means fewer wrong orders and faster quotes. If you want the buyer's side of the story, including cage designs and quality checks, see the full guide: &lt;a href="https://www.spgmedisafe.com/stone-retrieval-basket-guide/" rel="noopener noreferrer"&gt;stone retrieval basket guide for buyers&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Note: this is general software advice and not clinical guidance.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>python</category>
      <category>healthcare</category>
      <category>datavalidation</category>
      <category>ecommerce</category>
    </item>
    <item>
      <title>Modelling Medical Device Sizes: Encoding Ureteral Access Sheaths in a Product Catalogue</title>
      <dc:creator>SPG Medisafe</dc:creator>
      <pubDate>Fri, 09 Oct 2026 07:30:20 +0000</pubDate>
      <link>https://dev.to/spgmedisafe/modelling-medical-device-sizes-encoding-ureteral-access-sheaths-in-a-product-catalogue-4co2</link>
      <guid>https://dev.to/spgmedisafe/modelling-medical-device-sizes-encoding-ureteral-access-sheaths-in-a-product-catalogue-4co2</guid>
      <description>&lt;p&gt;If you build catalogue, ERP or e-commerce systems for medical device companies, you will meet products whose "size" is not one number. Ureteral access sheaths are a good example.&lt;/p&gt;

&lt;h2&gt;
  
  
  Two diameters and a length
&lt;/h2&gt;

&lt;p&gt;A sheath is labelled like &lt;code&gt;12/14 Fr, 45 cm&lt;/code&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;12 Fr&lt;/strong&gt;: inner diameter (the working channel for a scope)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;14 Fr&lt;/strong&gt;: outer diameter (what sits in the patient)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;45 cm&lt;/strong&gt;: working length&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;French units convert simply: &lt;code&gt;mm = Fr / 3&lt;/code&gt;. So 12/14 Fr is roughly 4.0 / 4.7 mm.&lt;/p&gt;

&lt;h2&gt;
  
  
  A simple data model
&lt;/h2&gt;

&lt;p&gt;Storing the size as a single string makes filtering painful. Split it into fields instead:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"product_type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"ureteral_access_sheath"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"inner_fr"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;12&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"outer_fr"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;14&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"length_cm"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;45&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"coating"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"hydrophilic"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"sterile"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"single_use"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now the UI can offer filters such as "inner diameter at least 11 Fr" or "length 35 to 46 cm", and you can validate that &lt;code&gt;outer_fr &amp;gt; inner_fr&lt;/code&gt; on import.&lt;/p&gt;

&lt;h2&gt;
  
  
  Compatibility rules
&lt;/h2&gt;

&lt;p&gt;Real buyers search by the instrument they own. A useful rule is: show sheaths where &lt;code&gt;inner_fr&lt;/code&gt; is greater than the scope's outer diameter, so there is space for fluid to drain. Store scope diameters in a separate table and join on that rule rather than hard-coding pairs.&lt;/p&gt;

&lt;h2&gt;
  
  
  Labels and SKUs
&lt;/h2&gt;

&lt;p&gt;Generate the SKU and the pack label from the fields, not by hand, for example &lt;code&gt;UAS-12-14-45&lt;/code&gt;. This keeps printed labels, barcodes and the website in sync and avoids the classic error of a 35 cm product shipped in a 45 cm box.&lt;/p&gt;

&lt;h2&gt;
  
  
  Import validation
&lt;/h2&gt;

&lt;p&gt;Supplier price lists rarely follow one format. Some write &lt;code&gt;12/14Fr&lt;/code&gt;, others &lt;code&gt;12-14 F&lt;/code&gt; or &lt;code&gt;Fr 12/14&lt;/code&gt;. A small parser with a regex such as &lt;code&gt;(\d+(?:\.\d)?)\s*[/-]\s*(\d+(?:\.\d)?)&lt;/code&gt; handles most variants. Reject rows where the outer value is not larger than the inner one, and flag lengths outside a sensible range, for example 15 to 70 cm, for manual review instead of importing them silently.&lt;/p&gt;

&lt;h2&gt;
  
  
  Search and SEO
&lt;/h2&gt;

&lt;p&gt;Buyers search with phrases like "12/14 access sheath 45 cm". Generate product titles from the fields in one consistent pattern, and include both the Fr value and the millimetre conversion in the description. Structured data with &lt;code&gt;additionalProperty&lt;/code&gt; for each dimension also helps search engines understand the product.&lt;/p&gt;

&lt;h2&gt;
  
  
  Further reading
&lt;/h2&gt;

&lt;p&gt;For the clinical side, which sizes are common and why, see this guide on &lt;a href="https://www.spgmedisafe.com/ureteral-access-sheath-sizes/" rel="noopener noreferrer"&gt;ureteral access sheath sizes&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>healthtech</category>
      <category>database</category>
      <category>ecommerce</category>
      <category>datamodeling</category>
    </item>
  </channel>
</rss>
