inou/scrape_mychart/import.go

193 lines
5.3 KiB
Go
Executable File

package main
import (
"database/sql"
"encoding/json"
"fmt"
"os"
"path/filepath"
_ "github.com/mattn/go-sqlite3"
)
const sophiaPatientID = 1
type Response struct {
OrderName string `json:"orderName"`
Key string `json:"key"`
Results []Result `json:"results"`
}
type Result struct {
OrderMetadata OrderMeta `json:"orderMetadata"`
ResultComponents []Component `json:"resultComponents"`
IsAbnormal bool `json:"isAbnormal"`
}
type OrderMeta struct {
OrderProviderName string `json:"orderProviderName"`
AuthorizingProviderName string `json:"authorizingProviderName"`
PrioritizedInstantISO string `json:"prioritizedInstantISO"`
CollectionTimestampsDisplay string `json:"collectionTimestampsDisplay"`
ResultStatus string `json:"resultStatus"`
ResultType string `json:"resultType"`
ResultingLab Lab `json:"resultingLab"`
AssociatedDiagnoses []string `json:"associatedDiagnoses"`
SpecimensDisplay string `json:"specimensDisplay"`
}
type Lab struct {
Name string `json:"name"`
Address []string `json:"address"`
CliaNumber string `json:"cliaNumber"`
}
type Component struct {
ComponentInfo CompInfo `json:"componentInfo"`
ComponentResultInfo CompResultInfo `json:"componentResultInfo"`
ComponentComments CompComments `json:"componentComments"`
}
type CompInfo struct {
Name string `json:"name"`
CommonName string `json:"commonName"`
Units string `json:"units"`
}
type CompResultInfo struct {
Value string `json:"value"`
NumericValue *float64 `json:"numericValue,omitempty"`
ReferenceRange RefRange `json:"referenceRange"`
AbnormalFlag string `json:"abnormalFlagCategoryValue"`
}
type RefRange struct {
Low *float64 `json:"low,omitempty"`
High *float64 `json:"high,omitempty"`
FormattedReferenceRange string `json:"formattedReferenceRange"`
}
type CompComments struct {
HasContent bool `json:"hasContent"`
ContentAsString string `json:"contentAsString"`
}
func main() {
dbPath := "labs.db"
if len(os.Args) > 1 {
dbPath = os.Args[1]
}
db, err := sql.Open("sqlite3", dbPath)
if err != nil {
fmt.Printf("Failed to open %s: %v\n", dbPath, err)
os.Exit(1)
}
defer db.Close()
files, _ := filepath.Glob("output/*.json")
fmt.Printf("Processing %d files\n", len(files))
labCount, skipCount, testCount := 0, 0, 0
for _, f := range files {
data, _ := os.ReadFile(f)
var resp Response
if err := json.Unmarshal(data, &resp); err != nil {
continue
}
if len(resp.Results) == 0 {
continue
}
r := resp.Results[0]
if r.OrderMetadata.ResultType != "LAB" {
skipCount++
continue
}
provider := r.OrderMetadata.OrderProviderName
if provider == "" {
provider = r.OrderMetadata.AuthorizingProviderName
}
labAddr := ""
if len(r.OrderMetadata.ResultingLab.Address) > 0 {
for i, a := range r.OrderMetadata.ResultingLab.Address {
if i > 0 {
labAddr += ", "
}
labAddr += a
}
}
diagnoses := ""
for i, d := range r.OrderMetadata.AssociatedDiagnoses {
if i > 0 {
diagnoses += "; "
}
diagnoses += d
}
isAbnormal := 0
if r.IsAbnormal {
isAbnormal = 1
}
res, err := db.Exec(`INSERT OR IGNORE INTO lab_orders
(patient_id, order_name, order_key, collection_date, provider, lab_name, lab_address, lab_clia, specimen, status, is_abnormal, diagnoses)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
sophiaPatientID, resp.OrderName, resp.Key, r.OrderMetadata.PrioritizedInstantISO, provider,
r.OrderMetadata.ResultingLab.Name, labAddr, r.OrderMetadata.ResultingLab.CliaNumber,
r.OrderMetadata.SpecimensDisplay, r.OrderMetadata.ResultStatus, isAbnormal, diagnoses)
if err != nil {
fmt.Printf("err %s: %v\n", f, err)
continue
}
rowsAffected, _ := res.RowsAffected()
var orderID int64
if rowsAffected == 0 {
// Already exists, get existing ID
row := db.QueryRow(`SELECT id FROM lab_orders WHERE order_key = ?`, resp.Key)
if err := row.Scan(&orderID); err != nil {
fmt.Printf("err getting existing order %s: %v\n", f, err)
continue
}
} else {
orderID, _ = res.LastInsertId()
labCount++
}
for _, c := range r.ResultComponents {
var numVal, refLow, refHigh *float64
if c.ComponentResultInfo.NumericValue != nil {
numVal = c.ComponentResultInfo.NumericValue
}
if c.ComponentResultInfo.ReferenceRange.Low != nil {
refLow = c.ComponentResultInfo.ReferenceRange.Low
}
if c.ComponentResultInfo.ReferenceRange.High != nil {
refHigh = c.ComponentResultInfo.ReferenceRange.High
}
comments := ""
if c.ComponentComments.HasContent {
comments = c.ComponentComments.ContentAsString
}
db.Exec(`INSERT INTO lab_tests
(order_id, name, common_name, value, numeric_value, units, ref_low, ref_high, ref_range_display, abnormal_flag, comments)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
orderID, c.ComponentInfo.Name, c.ComponentInfo.CommonName,
c.ComponentResultInfo.Value, numVal, c.ComponentInfo.Units,
refLow, refHigh, c.ComponentResultInfo.ReferenceRange.FormattedReferenceRange,
c.ComponentResultInfo.AbnormalFlag, comments)
testCount++
}
}
fmt.Printf("Done: %d lab orders, %d tests (%d non-lab skipped)\n", labCount, testCount, skipCount)
}