From eddbdfcccf44aa9e6017a83e54e74f539ca12967 Mon Sep 17 00:00:00 2001 From: RunasSudo Date: Sat, 4 Feb 2023 15:48:44 +1100 Subject: [PATCH] Add selected non-PBS medicines --- export_db.sh | 2 +- ..._pbs_brand_names.py => find_brand_names.py | 41 ++++++++++++------- full_build_db.sh | 3 +- html/index.html | 35 ++++++++++++---- import_non_pbs.py | 35 ++++++++++++++++ non_pbs.csv | 27 ++++++++++++ 6 files changed, 119 insertions(+), 24 deletions(-) rename find_pbs_brand_names.py => find_brand_names.py (58%) create mode 100755 import_non_pbs.py create mode 100644 non_pbs.csv diff --git a/export_db.sh b/export_db.sh index d7fa7c5..c02e464 100755 --- a/export_db.sh +++ b/export_db.sh @@ -1,4 +1,4 @@ #!/bin/bash rm html/database.db -sqlite3 database.db '.dump meta pbs_item pbs_mp pbs_mp_brand_name pbs_mpp pbs_item_restriction pbs_restriction pbs_restriction_criteria pbs_criteria pbs_criteria_parameter' | sqlite3 html/database.db +sqlite3 database.db '.dump meta pbs_item pbs_mp mp_brand_name pbs_mpp pbs_item_restriction pbs_restriction pbs_restriction_criteria pbs_criteria pbs_criteria_parameter non_pbs_tpp' | sqlite3 html/database.db diff --git a/find_pbs_brand_names.py b/find_brand_names.py similarity index 58% rename from find_pbs_brand_names.py rename to find_brand_names.py index b5242b2..4265e73 100755 --- a/find_pbs_brand_names.py +++ b/find_brand_names.py @@ -25,10 +25,11 @@ con.row_factory = sqlite3.Row cur = con.cursor() # Init schema -cur.execute('DROP TABLE IF EXISTS pbs_mp_brand_name') -cur.execute('CREATE TABLE pbs_mp_brand_name (id INTEGER PRIMARY KEY AUTOINCREMENT, mp_code STRING, brand_name STRING)') +cur.execute('DROP TABLE IF EXISTS mp_brand_name') +cur.execute('CREATE TABLE mp_brand_name (id INTEGER PRIMARY KEY AUTOINCREMENT, mp_preferred_term TEXT, brand_name TEXT)') -cur.execute('SELECT * FROM pbs_tpp LEFT JOIN (SELECT code, mp_code FROM pbs_mpp) AS pbs_mpp ON pbs_tpp.mpp_code = pbs_mpp.code LEFT JOIN (SELECT code, preferred_term as mp_preferred_term FROM pbs_mp) AS pbs_mp ON pbs_mpp.mp_code = pbs_mp.code') +# Get PBS brand names +cur.execute('SELECT brand_name, mp_preferred_term FROM pbs_tpp LEFT JOIN (SELECT code, mp_code FROM pbs_mpp) AS pbs_mpp ON pbs_tpp.mpp_code = pbs_mpp.code LEFT JOIN (SELECT code, preferred_term as mp_preferred_term FROM pbs_mp) AS pbs_mp ON pbs_mpp.mp_code = pbs_mp.code') brand_names = {} for tpp in cur.fetchall(): @@ -53,21 +54,31 @@ for tpp in cur.fetchall(): # OK! brand_name = ' '.join(words) - if tpp['mp_code'] not in brand_names: - brand_names[tpp['mp_code']] = set() + if tpp['mp_preferred_term'] not in brand_names: + brand_names[tpp['mp_preferred_term']] = set() - brand_names[tpp['mp_code']].add(brand_name) + brand_names[tpp['mp_preferred_term']].add(brand_name) + +# Get non-PBS brand names +cur.execute('SELECT * FROM non_pbs_tpp') +for tpp in cur.fetchall(): + # This is manually curated so no need for cleaning + + if tpp['mp_preferred_term'] not in brand_names: + brand_names[tpp['mp_preferred_term']] = set() + + brand_names[tpp['mp_preferred_term']].add(tpp['brand_name']) # Reduce names with unambiguous prefixes -for mp_code in sorted(brand_names.keys()): - for brand_name in list(brand_names[mp_code]): +for mp_preferred_term in sorted(brand_names.keys()): + for brand_name in list(brand_names[mp_preferred_term]): # Can we reduce the length of the name? words = brand_name.split() for i in range(1, len(words)): short_name = ' '.join(words[0:i]) - if any(b.startswith(short_name) for m in brand_names.keys() if m != mp_code for b in brand_names[m]): + if any(b.startswith(short_name) for m in brand_names.keys() if m != mp_preferred_term for b in brand_names[m]): # Conflict continue @@ -76,14 +87,14 @@ for mp_code in sorted(brand_names.keys()): continue # Can shorten - if brand_name in brand_names[mp_code]: - brand_names[mp_code].remove(brand_name) - brand_names[mp_code].add(short_name) + if brand_name in brand_names[mp_preferred_term]: + brand_names[mp_preferred_term].remove(brand_name) + brand_names[mp_preferred_term].add(short_name) break # Add to database -for mp_code in sorted(brand_names.keys()): - for brand_name in sorted(list(brand_names[mp_code])): - cur.execute('INSERT INTO pbs_mp_brand_name (mp_code, brand_name) VALUES (?, ?)', (mp_code, brand_name)) +for mp_preferred_term in sorted(brand_names.keys()): + for brand_name in sorted(list(brand_names[mp_preferred_term])): + cur.execute('INSERT INTO mp_brand_name (mp_preferred_term, brand_name) VALUES (?, ?)', (mp_preferred_term, brand_name)) con.commit() diff --git a/full_build_db.sh b/full_build_db.sh index 47f5d9b..878cd58 100755 --- a/full_build_db.sh +++ b/full_build_db.sh @@ -2,5 +2,6 @@ ./import_pbs_xml.py || exit 1 ./render_pbs_criteria.py || exit 1 -./find_pbs_brand_names.py || exit 1 +./import_non_pbs.py || exit 1 +./find_brand_names.py || exit 1 ./export_db.sh diff --git a/html/index.html b/html/index.html index 93f2a38..ded0dca 100644 --- a/html/index.html +++ b/html/index.html @@ -97,11 +97,11 @@ document.getElementById('pbs-date').innerText = {'01': 'January', '02': 'February', '03': 'March', '04': 'April', '05': 'May', '06': 'June', '07': 'July', '08': 'August', '09': 'September', '10': 'October', '11': 'November', '12': 'December'}[pbs_date_bits[1]] + ' ' + pbs_date_bits[0]; // Initialise search bar - const mp_preferred_terms = execAsScalars(db.prepare('SELECT preferred_term FROM pbs_mp ORDER BY LOWER(preferred_term)')); + const mp_preferred_terms = execAsScalars(db.prepare('SELECT * FROM (SELECT preferred_term FROM pbs_mp UNION SELECT mp_preferred_term AS preferred_term FROM non_pbs_tpp) ORDER BY LOWER(preferred_term)')); let data = mp_preferred_terms.map(mp_preferred_term => ({'label': mp_preferred_term, 'preview': mp_preferred_term, 'value': mp_preferred_term})); - const tpp_brand_names = execAsObjects(db.prepare('SELECT * FROM pbs_mp_brand_name LEFT JOIN pbs_mp ON pbs_mp_brand_name.mp_code = pbs_mp.code ORDER BY LOWER(brand_name)')); - data = data.concat(tpp_brand_names.map(tpp_brand_name => ({'label': tpp_brand_name['brand_name'], 'preview': tpp_brand_name['brand_name'] + ' (' + tpp_brand_name['preferred_term'] + ')', 'value': tpp_brand_name['preferred_term']}))); + const tpp_brand_names = execAsObjects(db.prepare('SELECT * FROM mp_brand_name ORDER BY LOWER(brand_name)')); + data = data.concat(tpp_brand_names.map(tpp_brand_name => ({'label': tpp_brand_name['brand_name'], 'preview': tpp_brand_name['brand_name'] + ' (' + tpp_brand_name['mp_preferred_term'] + ')', 'value': tpp_brand_name['mp_preferred_term']}))); const autocomplete = new Autocomplete(document.getElementById('search-input'), { data: data, @@ -120,7 +120,15 @@ document.getElementById('search-input').value = item.value; // Find matching PBS items - let stmt = db.prepare('SELECT * FROM pbs_item LEFT JOIN (SELECT code AS mpp_code, preferred_term AS mpp_preferred_term, mp_code FROM pbs_mpp) AS pbs_mpp ON pbs_item.mpp_code = pbs_mpp.mpp_code LEFT JOIN (SELECT code AS mp_code, preferred_term AS mp_preferred_term FROM pbs_mp) AS pbs_mp ON pbs_mpp.mp_code = pbs_mp.mp_code WHERE LOWER(mp_preferred_term) = ?'); + let stmt = db.prepare( + 'SELECT * FROM (' + + ' SELECT code, mp_preferred_term, mpp_preferred_term, benefit_type, maximum_prescribable_units, number_repeats, program FROM pbs_item' + + ' LEFT JOIN (SELECT code AS mpp_code, preferred_term AS mpp_preferred_term, mp_code FROM pbs_mpp) AS pbs_mpp ON pbs_item.mpp_code = pbs_mpp.mpp_code' + + ' LEFT JOIN (SELECT code AS mp_code, preferred_term AS mp_preferred_term FROM pbs_mp) AS pbs_mp ON pbs_mpp.mp_code = pbs_mp.mp_code' + + ' UNION SELECT DISTINCT NULL AS code, mp_preferred_term, mpp_preferred_term, "unrestricted" AS benefit_type, NULL AS maximum_prescribable_units, NULL AS number_repeats, "NA" AS program FROM non_pbs_tpp' + + ' )' + + ' WHERE LOWER(mp_preferred_term) = ?' + ); stmt.bind([item.value.toLowerCase()]); const items = execAsObjects(stmt); @@ -136,7 +144,11 @@ const restrictions = execAsObjects(stmt); const tr = document.createElement('tr'); - let td = document.createElement('td'); td.innerHTML = '' + item['code'] + ''; tr.appendChild(td); + let td = document.createElement('td'); + if (item['code']) { + td.innerHTML = '' + item['code'] + ''; + } + tr.appendChild(td); td = document.createElement('td'); let div = document.createElement('div'); div.innerText = item['mpp_preferred_term']; td.appendChild(div); @@ -174,8 +186,17 @@ td = document.createElement('td'); td.innerText = item['maximum_prescribable_units']; tr.appendChild(td); td = document.createElement('td'); td.innerText = item['number_repeats']; tr.appendChild(td); - if (item['program'] === 'R1') { - td = document.createElement('td'); td.innerHTML = 'RPBS'; tr.appendChild(td); + if (item['program'] !== 'GE') { + td = document.createElement('td'); + if (item['program'] === 'R1') { + td.innerHTML = 'RPBS'; + } else if (item['program'] === 'NA') { + td.innerHTML = 'Non-PBS'; + } else { + alert('Unknown program: ' + item['program']); + throw 'Unknown program: ' + item['program']; + } + tr.appendChild(td); tr.classList.add('table-secondary'); } else if (item['benefit_type'] === 'unrestricted') { td = document.createElement('td'); tr.appendChild(td); diff --git a/import_non_pbs.py b/import_non_pbs.py new file mode 100755 index 0000000..d4742b9 --- /dev/null +++ b/import_non_pbs.py @@ -0,0 +1,35 @@ +#!/usr/bin/env python3 +# Copyright © 2023 Lee Yingtong Li (RunasSudo) +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . + +import csv +import sqlite3 + +# Open database +con = sqlite3.connect('database.db') +cur = con.cursor() + +# Init schema +cur.execute('DROP TABLE IF EXISTS non_pbs_tpp') +cur.execute('CREATE TABLE non_pbs_tpp (mp_preferred_term TEXT, mpp_preferred_term TEXT, brand_name TEXT)') + +with open('non_pbs.csv', 'r', newline='') as f: + reader = csv.DictReader(f) + + for tpp in reader: + if not tpp['Hide']: + cur.execute('INSERT INTO non_pbs_tpp (mp_preferred_term, mpp_preferred_term, brand_name) VALUES (?, ?, ?)', (tpp['MP'], tpp['MPP'], tpp['TPP'])) + +con.commit() diff --git a/non_pbs.csv b/non_pbs.csv new file mode 100644 index 0000000..fe77a34 --- /dev/null +++ b/non_pbs.csv @@ -0,0 +1,27 @@ +MP,MPP,TPP,Source,Accessed,Hide +colecalciferol,"colecalciferol 25 microgram (1000 units) tablet, 60",OsteVit-D One-A-Day,https://www.mimsonline.com.au.acs.hcn.com.au/Search/AbbrPI.aspx?ID=74470001_2,2023-02-24, +colecalciferol,"colecalciferol 25 microgram (1000 units) tablet, 250",OsteVit-D One-A-Day,https://www.mimsonline.com.au.acs.hcn.com.au/Search/AbbrPI.aspx?ID=74470001_2,2023-02-24,Hide +colecalciferol,"colecalciferol 25 microgram (1000 units) capsule, 60",Caltrate Vitamin D Daily,https://www.mimsonline.com.au.acs.hcn.com.au/Search/AbbrPI.aspx?ID=2930001_2,2023-02-24, +colecalciferol,"colecalciferol 25 microgram (1000 units) capsule, 60",Ostelin,https://www.mimsonline.com.au.acs.hcn.com.au/Search/AbbrPI.aspx?ID=21920001_2,2023-02-24, +colecalciferol,"colecalciferol 25 microgram (1000 units) capsule, 130",Ostelin,https://www.mimsonline.com.au.acs.hcn.com.au/Search/AbbrPI.aspx?ID=21920001_2,2023-02-24,Hide +colecalciferol,"colecalciferol 25 microgram (1000 units) capsule, 250",Ostelin,https://www.mimsonline.com.au.acs.hcn.com.au/Search/AbbrPI.aspx?ID=21920001_2,2023-02-24,Hide +calcium + colecalciferol,"calcium 600 mg + colecalciferol 12.5 microgram (500 units) tablet, 60",Caltrate Bone Health,https://amhonline.amh.net.au.acs.hcn.com.au/chapters/endocrine-drugs/drugs-affecting-bone/other-drugs-affecting-bone/calcium,2023-02-24, +calcium + colecalciferol,"calcium 600 mg + colecalciferol 12.5 microgram (500 units) tablet, 100",Caltrate Bone Health,https://amhonline.amh.net.au.acs.hcn.com.au/chapters/endocrine-drugs/drugs-affecting-bone/other-drugs-affecting-bone/calcium,2023-02-24,Hide +calcium + colecalciferol,"calcium 600 mg + colecalciferol 12.5 microgram (500 units) tablet, 120",Caltrate Bone Health,https://amhonline.amh.net.au.acs.hcn.com.au/chapters/endocrine-drugs/drugs-affecting-bone/other-drugs-affecting-bone/calcium,2023-02-24,Hide +calcium + colecalciferol,"calcium 600 mg + colecalciferol 12.5 microgram (500 units) tablet, 60",Cal-600 + D,https://amhonline.amh.net.au.acs.hcn.com.au/chapters/endocrine-drugs/drugs-affecting-bone/other-drugs-affecting-bone/calcium,2023-02-24, +calcium + colecalciferol,"calcium 600 mg + colecalciferol 12.5 microgram (500 units) tablet, 100",Cal-600 + D,https://amhonline.amh.net.au.acs.hcn.com.au/chapters/endocrine-drugs/drugs-affecting-bone/other-drugs-affecting-bone/calcium,2023-02-24,Hide +calcium + colecalciferol,"calcium 600 mg + colecalciferol 12.5 microgram (500 units) tablet, 60",Ostelin Calcium & Vitamin D3,https://amhonline.amh.net.au.acs.hcn.com.au/chapters/endocrine-drugs/drugs-affecting-bone/other-drugs-affecting-bone/calcium,2023-02-24, +calcium + colecalciferol,"calcium 600 mg + colecalciferol 12.5 microgram (500 units) tablet, 130",Ostelin Calcium & Vitamin D3,https://amhonline.amh.net.au.acs.hcn.com.au/chapters/endocrine-drugs/drugs-affecting-bone/other-drugs-affecting-bone/calcium,2023-02-24,Hide +calcium + colecalciferol,"calcium 600 mg + colecalciferol 12.5 microgram (500 units) tablet, 250",Ostelin Calcium & Vitamin D3,https://amhonline.amh.net.au.acs.hcn.com.au/chapters/endocrine-drugs/drugs-affecting-bone/other-drugs-affecting-bone/calcium,2023-02-24,Hide +calcium + colecalciferol,"calcium 600 mg + colecalciferol 12.5 microgram (500 units) chewable tablet, 60",Ostelin Calcium & Vitamin D3,https://amhonline.amh.net.au.acs.hcn.com.au/chapters/endocrine-drugs/drugs-affecting-bone/other-drugs-affecting-bone/calcium,2023-02-24,Hide +ferrous sulfate + folic acid,"ferrous sulfate 270 mg (iron 87.4 mg) + folic acid 300 microgram capsule, 30",Fefol,https://www.mimsonline.com.au.acs.hcn.com.au/Search/AbbrPI.aspx?ID=2850001_2,2023-02-24, +ferrous sulfate + folic acid,"ferrous sulfate 270 mg (iron 87.4 mg) + folic acid 300 microgram capsule, 60",Fefol,https://www.mimsonline.com.au.acs.hcn.com.au/Search/AbbrPI.aspx?ID=2850001_2,2023-02-24,Hide +ferrous fumarate + ascorbic acid,"ferrous fumarate 304 mg (iron 100 mg) + ascorbic acid 20 mg capsule, 30",Ferropods,https://www.mimsonline.com.au.acs.hcn.com.au/Search/AbbrPI.aspx?ID=135900001_2,2023-02-24, +ferrous sulfate,"ferrous sulfate 325 mg (iron 105 mg) tablet, 30",Ferro-grad,https://www.mimsonline.com.au.acs.hcn.com.au/Search/AbbrPI.aspx?ID=2870001_2,2023-02-24, +ferrous sulfate + ascorbic acid,"ferrous sulfate 325 mg (iron 105 mg) + ascorbic acid 500 mg tablet, 30",Ferro-grad C,https://www.mimsonline.com.au.acs.hcn.com.au/Search/AbbrPI.aspx?ID=48460001_2,2023-02-24, +ferrous sulfate + folic acid,"ferrous sulfate 250 mg (iron 80 mg) + folic acid 300 microgram tablet, 30",Ferro-grad F,https://www.mimsonline.com.au.acs.hcn.com.au/Search/AbbrPI.aspx?ID=2900001_2,2023-02-24, +iron polymaltose,"iron (as polymaltose) 100 mg tablet, 30",Maltofer,https://www.mimsonline.com.au.acs.hcn.com.au/Search/AbbrPI.aspx?ID=6800001_2,2023-02-24, +sodium chloride,"sodium chloride 600 mg tablet, 100",Saltabs,https://www.mimsonline.com.au.acs.hcn.com.au/Search/AbbrPI.aspx?ID=130430001_2,2023-02-24, +sodium chloride,"sodium chloride 600 mg tablet, 100",Toppin,https://www.mimsonline.com.au.acs.hcn.com.au/Search/AbbrPI.aspx?ID=21820001_2,2023-02-24, +zinc sulfate,"zinc (as sulfate) 50 mg capsule, 100",Zincaps,https://amhonline.amh.net.au.acs.hcn.com.au/chapters/blood-electrolytes/tables/oral-electrolytes-table,2023-02-24, +tapentadol,"tapentadol 50 mg tablet, 20",Palexia IR,https://amhonline.amh.net.au.acs.hcn.com.au/chapters/analgesics/drugs-pain-relief/opioid-analgesics/tapentadol,2023-02-24,