MakPr016 commited on
Commit
e9170b7
·
1 Parent(s): 8deea46
Files changed (1) hide show
  1. main.py +189 -50
main.py CHANGED
@@ -19,6 +19,169 @@ os.makedirs(DATA_DIR, exist_ok=True)
19
 
20
  _MASTER_INDEX_CACHE = {}
21
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
22
  def load_master_index():
23
  global _MASTER_INDEX_CACHE
24
  if _MASTER_INDEX_CACHE: return _MASTER_INDEX_CACHE
@@ -47,45 +210,23 @@ def is_garbage_row(row_text: str) -> bool:
47
  def determine_item_type(description: str, form: str) -> str:
48
  """
49
  Determines the category of the item based on its description and form/unit.
50
- Categories: Pharmaceuticals, Medical Supplies, Medical Equipment.
51
  """
52
  text = (description + " " + form).lower()
53
 
54
- # Priority 1: Medical Supplies (Consumables)
55
- # Check these first to handle cases like "Insulin Syringe" (Supply) vs "Insulin" (Pharma)
56
- supplies_keywords = [
57
- 'syringe', 'needle', 'cannula', 'catheter', 'glove', 'mask', 'gauze',
58
- 'bandage', 'dressing', 'cotton', 'swab', 'lancet', 'strip', 'test kit',
59
- 'blade', 'suture', 'plaster', 'gown', 'sheet', 'bag', 'alcohol',
60
- 'disinfectant', 'sanitizer', 'tongue depressor', 'specula', 'paper',
61
- 'wipes', 'apron', 'cap', 'shoe cover', 'tape'
62
- ]
63
- if any(k in text for k in supplies_keywords):
64
- return 'Medical Supplies'
65
-
66
- # Priority 2: Medical Equipment (Devices/Durable)
67
- equipment_keywords = [
68
- 'thermometer', 'sphygmomanometer', 'stethoscope', 'oximeter',
69
- 'glucometer', 'nebulizer', 'otoscope', 'penlight', 'monitor',
70
- 'scale', 'microscope', 'centrifuge', 'refrigerator', 'cool box',
71
- 'freezer', 'lamp', 'bed', 'chair', 'pump', 'bp machine', 'device'
72
- ]
73
- if any(k in text for k in equipment_keywords):
74
- return 'Medical Equipment'
75
-
76
- # Priority 3: Pharmaceuticals (Medicines/Drugs)
77
- pharma_keywords = [
78
- 'tablet', 'capsule', 'cap', 'tab', 'syrup', 'suspension', 'susp',
79
- 'injection', 'inj', 'ampoule', 'amp', 'vial', 'cream', 'ointment',
80
- 'gel', 'suppository', 'supp', 'drops', 'inhaler', 'vaccine', 'sera',
81
- 'insulin', 'medicine', 'drug', 'mg', 'ml', 'mcg', 'iu', 'dose',
82
- 'solution', 'infusion', 'spray', 'lozenge'
83
- ]
84
- if any(k in text for k in pharma_keywords):
85
- return 'Pharmaceuticals'
86
-
87
- # Fallback
88
- return 'Medical Supplies'
89
 
90
  async def delete_file_safety_net(file_path: str, delay: int = 600):
91
  await asyncio.sleep(delay)
@@ -215,20 +356,18 @@ async def match_all(req: MatchRequest):
215
 
216
  matches = []
217
  for v in vendors:
218
- cats = [c.lower() for c in v.get('primary_categories', [])]
219
- # Simple matching logic - can be expanded to use item['type'] if needed
220
- if 'pharmaceuticals' in cats or 'medical devices' in cats or 'medical supplies' in cats:
221
- matches.append({
222
- 'vendor_id': v.get('vendor_id'),
223
- 'name': v.get('legal_name'),
224
- 'country': (v.get('countries_served') or ['Unknown'])[0],
225
- 'landedCost': v.get('landedCost', 10),
226
- 'deliveryDays': v.get('deliveryDays', 5),
227
- 'availableQty': v.get('availableQty', 1000),
228
- 'qualityScore': v.get('confidence_score', 80) / 10.0,
229
- 'reliabilityScore': 5,
230
- 'score': 9.5
231
- })
232
 
233
  results.append({
234
  "medicine": name,
 
19
 
20
  _MASTER_INDEX_CACHE = {}
21
 
22
+ # --- CATEGORY DEFINITIONS ---
23
+ # Ordered by specificity. "Whole word" matching will apply to these keywords.
24
+ CATEGORY_DEFINITIONS = {
25
+ "Pharmaceuticals & Biologics": [
26
+ "tablet", "tab", "capsule", "cap", "syrup", "suspension", "susp", "injection", "inj", "vial", "ampoule", "amp",
27
+ "drops", "gtt", "inhaler", "vaccine", "insulin", "dose", "drug", "medication", "ointment", "cream", "gel",
28
+ "lotion", "suppository", "supp", "antibiotic", "antiviral", "analgesic", "anesthetic", "hormone", "steroid",
29
+ "vitamin", "mineral", "supplement", "lozenge", "patch", "solution", "powder for suspension", "elixir", "serum",
30
+ "antitoxin"
31
+ ],
32
+ "Surgical Products": [
33
+ "scalpel", "forceps", "retractor", "clamp", "suture", "stapler", "surgical mesh", "hemostatic", "sealant",
34
+ "surgical drape", "surgical gown", "laparoscopic", "robotic surgery", "electrosurgical", "surgical laser",
35
+ "surgical blade", "trocar", "surgical clip", "surgical scissor", "needle holder"
36
+ ],
37
+ "Orthopedic & Spine": [
38
+ "orthopedic", "spine", "joint replacement", "trauma fixation", "bone plate", "bone screw", "intramedullary rod",
39
+ "bone nail", "spinal implant", "spinal fusion", "bone graft", "orthopedic brace", "cast", "arthroscopy",
40
+ "fixator", "prosthesis", "bone drill", "bone saw"
41
+ ],
42
+ "Cardiovascular Products": [
43
+ "cardiac stent", "pacemaker", "defibrillator", "icd", "heart valve", "vascular graft", "cardiac catheter",
44
+ "guidewire", "cardiac balloon", "ablation", "coronary", "angioplasty", "introducer sheath"
45
+ ],
46
+ "Medical Imaging Equipment": [
47
+ "mri", "ct scanner", "x-ray", "ultrasound", "mammography", "fluoroscopy", "pet scanner", "c-arm",
48
+ "medical imaging", "transducer", "x-ray film", "contrast media", "lead apron"
49
+ ],
50
+ "Diagnostic Products": [
51
+ "diagnostic", "test kit", "glucose test", "reagent", "immunoassay", "chemistry analyzer", "hematology",
52
+ "microbiology", "culture media", "pregnancy test", "covid", "rapid test", "urinalysis", "penlight",
53
+ "specula", "otoscope", "ophthalmoscope", "lancet", "glucometer strips", "test strip"
54
+ ],
55
+ "Patient Monitoring Equipment": [
56
+ "vital signs", "ecg", "ekg", "pulse oximeter", "blood pressure monitor", "sphygmomanometer",
57
+ "medical thermometer", "capnography", "fetal monitor", "telemetry", "spo2 sensor", "bp cuff", "temperature probe"
58
+ ],
59
+ "Respiratory & Anesthesia": [
60
+ "ventilator", "anesthesia machine", "oxygen concentrator", "nebulizer", "cpap", "bipap", "respiratory",
61
+ "endotracheal", "tracheostomy", "spirometer", "oxygen mask", "breathing circuit", "nasal cannula",
62
+ "resuscitator", "laryngoscope"
63
+ ],
64
+ "Infusion & Vascular Access": [
65
+ "infusion pump", "syringe pump", "iv set", "iv catheter", "venous", "picc", "iv port", "dialysis catheter",
66
+ "administration set", "extension set", "stopcock", "giving set", "saline", "dextrose", "ringer",
67
+ "sodium chloride", "water for injection"
68
+ ],
69
+ "Wound Care & Tissue Management": [
70
+ "wound dressing", "bandage", "gauze", "medical tape", "plaster", "adhesive", "wound foam", "alginate",
71
+ "hydrocolloid", "compression bandage", "ostomy", "skin substitute", "negative pressure"
72
+ ],
73
+ "Dialysis & Renal Care": [
74
+ "hemodialysis", "peritoneal", "dialyzer", "blood line", "fistula needle", "dialysis concentrate", "bicarbonate"
75
+ ],
76
+ "Ophthalmic Products": [
77
+ "intraocular", "intraocular lens", "phaco", "vitrectomy", "lasik", "contact lens", "viscoelastic",
78
+ "ophthalmic solution", "eye drops"
79
+ ],
80
+ "Dental Products": [
81
+ "dental implant", "orthodontic", "dental bracket", "dental wire", "dental drill", "dental handpiece",
82
+ "dental cement", "dental composite", "amalgam", "impression material", "teeth whitening", "dental chair"
83
+ ],
84
+ "Neurology & Neurosurgery": [
85
+ "neurostimulation", "spinal cord stimulator", "neuro coil", "flow diverter", "cranial", "shunt",
86
+ "neuro electrode", "eeg", "emg"
87
+ ],
88
+ "Laboratory Equipment & Supplies": [
89
+ "microscope", "lab centrifuge", "incubator", "autoclave", "pipette", "glassware", "test tube", "petri dish",
90
+ "flask", "beaker", "microscope slide", "cover glass", "fume hood", "biosafety cabinet"
91
+ ],
92
+ "Personal Protective Equipment (PPE)": [
93
+ "ppe", "n95", "face shield", "safety eyewear", "goggles", "protective apron", "shoe cover", "head cover",
94
+ "coverall", "isolation gown", "hazmat", "surgical mask"
95
+ ],
96
+ "Sterilization & Disinfection": [
97
+ "sterilization", "disinfectant", "antiseptic", "povidone", "iodine", "chlorhexidine", "alcohol swab",
98
+ "hand sanitizer", "medical soap", "enzymatic cleaner", "detergent", "washer disinfector", "sterilizer",
99
+ "sterilization indicator"
100
+ ],
101
+ "Hospital Furniture & Equipment": [
102
+ "hospital bed", "examination table", "stretcher", "medical trolley", "medical cart", "medical cabinet",
103
+ "bedside locker", "overbed table", "iv pole", "wheelchair"
104
+ ],
105
+ "Rehabilitation & Physical Therapy": [
106
+ "rehabilitation", "physiotherapy", "walker", "walking cane", "crutch", "exercise band", "traction",
107
+ "electrotherapy", "massage table", "orthosis"
108
+ ],
109
+ "Home Healthcare Products": [
110
+ "home care", "blood glucose meter", "hearing aid", "mobility aid", "bathroom safety", "commode"
111
+ ],
112
+ "Emergency & Trauma Care": [
113
+ "emergency kit", "trauma kit", "first aid", "aed", "defibrillator", "manual resuscitator", "suction unit",
114
+ "immobilizer", "cervical collar", "splint", "tourniquet", "crash cart"
115
+ ],
116
+ "Maternal & Neonatal Care": [
117
+ "maternal", "neonatal", "infant incubator", "infant warmer", "phototherapy", "breast pump", "obstetric",
118
+ "birthing bed", "fetal doppler", "umbilical"
119
+ ],
120
+ "Urology Products": [
121
+ "urology", "foley catheter", "urine bag", "urinary drainage", "ureteral stent", "stone basket"
122
+ ],
123
+ "Gastroenterology & Endoscopy": [
124
+ "endoscope", "gastroscope", "colonoscope", "biopsy forceps", "polypectomy snare", "gastric balloon", "ercp"
125
+ ],
126
+ "Oncology Products": [
127
+ "oncology", "chemotherapy", "radiotherapy", "brachytherapy", "port-a-cath", "cancer diagnostic"
128
+ ],
129
+ "Pain Management": [
130
+ "pain management", "pca pump", "epidural", "nerve block", "tens unit"
131
+ ],
132
+ "Sleep Medicine": [
133
+ "sleep apnea", "cpap mask", "bipap mask", "sleep tubing", "polysomnography"
134
+ ],
135
+ "Telemedicine & Digital Health": [
136
+ "telemedicine", "telehealth", "remote monitor", "medical software", "health app"
137
+ ],
138
+ "Blood Management": [
139
+ "blood bag", "blood transfusion", "blood bank", "blood warmer", "apheresis"
140
+ ],
141
+ "Mortuary & Pathology": [
142
+ "mortuary", "autopsy", "body bag", "morgue fridge", "dissection table", "microtome", "tissue processor"
143
+ ],
144
+ "Environmental Control": [
145
+ "medical gas", "medical vacuum", "medical air plant", "gas manifold", "gas outlet", "gas alarm"
146
+ ],
147
+ "Mobility & Accessibility": [
148
+ "patient lift", "patient hoist", "wheelchair ramp", "stair lift", "transfer board"
149
+ ],
150
+ "Bariatric Products": [
151
+ "bariatric bed", "bariatric wheelchair", "heavy duty scale"
152
+ ],
153
+ "Medical Textiles": [
154
+ "hospital linen", "bed sheet", "pillow case", "medical blanket", "towel", "privacy curtain", "medical uniform",
155
+ "scrub suit", "lab coat"
156
+ ],
157
+ "Infection Control Products": [
158
+ "waste bin", "sharps container", "biohazard bag", "spill kit", "air purifier"
159
+ ],
160
+ "Medical Gases & Cryogenics": [
161
+ "gas cylinder", "oxygen regulator", "flowmeter", "liquid oxygen", "nitrogen tank"
162
+ ],
163
+ "Nutrition & Feeding": [
164
+ "enteral feeding", "clinical nutrition", "nasogastric tube", "feeding pump", "feeding set", "peg tube"
165
+ ],
166
+ "Specimen Collection & Transport": [
167
+ "specimen container", "sample collection", "transport media", "transport swab", "urine container",
168
+ "stool container", "cool box", "transport bag"
169
+ ],
170
+ "Medical Software & IT": [
171
+ "emr", "ehr", "pacs", "ris", "lis", "his", "hospital information system"
172
+ ],
173
+ "Aesthetics & Dermatology": [
174
+ "dermatology", "aesthetic laser", "ipl", "dermal filler", "botulinum", "botox", "chemical peel",
175
+ "microdermabrasion"
176
+ ],
177
+ # Catch-all for basic items not caught above
178
+ "Medical Supplies & Consumables": [
179
+ "syringe", "needle", "glove", "examination glove", "disposable", "consumable", "cotton wool", "alcohol prep",
180
+ "urinal", "bedpan", "underpad", "tongue depressor", "applicator", "lubricant jelly", "cannula"
181
+ # Note: Cannula is here as fallback if not specific nasal/iv
182
+ ]
183
+ }
184
+
185
  def load_master_index():
186
  global _MASTER_INDEX_CACHE
187
  if _MASTER_INDEX_CACHE: return _MASTER_INDEX_CACHE
 
210
  def determine_item_type(description: str, form: str) -> str:
211
  """
212
  Determines the category of the item based on its description and form/unit.
213
+ Uses regex for whole-word matching to prevent substring errors (e.g. 'fusion' in 'infusion').
214
  """
215
  text = (description + " " + form).lower()
216
 
217
+ for category, keywords in CATEGORY_DEFINITIONS.items():
218
+ for k in keywords:
219
+ # \b matches word boundaries.
220
+ # This ensures "fusion" matches "spinal fusion" but NOT "infusion".
221
+ # It ensures "coat" matches "lab coat" but NOT "coated".
222
+ # We escape the keyword to handle any special characters safely.
223
+ pattern = r'\b' + re.escape(k) + r'\b'
224
+
225
+ if re.search(pattern, text):
226
+ return category
227
+
228
+ # Fallback default
229
+ return 'Medical Supplies & Consumables'
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
230
 
231
  async def delete_file_safety_net(file_path: str, delay: int = 600):
232
  await asyncio.sleep(delay)
 
356
 
357
  matches = []
358
  for v in vendors:
359
+ # Simplified matching logic for demonstration
360
+ matches.append({
361
+ 'vendor_id': v.get('vendor_id'),
362
+ 'name': v.get('legal_name'),
363
+ 'country': (v.get('countries_served') or ['Unknown'])[0],
364
+ 'landedCost': v.get('landedCost', 10),
365
+ 'deliveryDays': v.get('deliveryDays', 5),
366
+ 'availableQty': v.get('availableQty', 1000),
367
+ 'qualityScore': v.get('confidence_score', 80) / 10.0,
368
+ 'reliabilityScore': 5,
369
+ 'score': 9.5
370
+ })
 
 
371
 
372
  results.append({
373
  "medicine": name,