library(shiny) library(bslib) library(mrgsolve) library(dplyr) library(ggplot2) model_code <- ' $PARAM @annotated CL : 2.94 : Clearance (L/h) [0.042 L/h/kg x 70 kg] V : 35 : Volume of distribution (L) [0.50 L/kg x 70 kg] KA : 1.4 : Absorption rate constant (1/h) F1 : 1.0 : Bioavailability WT : 70 : Body weight (kg) SMOK : 1.0 : Smoking status (1=non-smoker, 1.5=smoker) $CMT @annotated DEPOT : Oral depot (mg) CENT : Central (mg) $MAIN double CLi = CL * (WT/70.0) * SMOK; double Vi = V * (WT/70.0); F_DEPOT = F1; $ODE dxdt_DEPOT = -KA * DEPOT; dxdt_CENT = KA * DEPOT - (CLi/Vi) * CENT; $TABLE double CP = CENT / Vi; $CAPTURE CP ' mod <- mcode("theophylline_rovei", model_code) app_theme <- bs_theme( version = 5, bootswatch = "flatly", primary = "#8b5cf6" ) |> bs_add_rules(" .metric-card { background: #f8f9fa; border-radius: 8px; padding: 15px; margin: 5px; text-align: center; border: 1px solid #dee2e6; } .metric-value { font-size: 24px; font-weight: bold; color: #2c3e50; } .metric-label { font-size: 12px; color: #7f8c8d; } .metric-success .metric-value { color: #10b981; } .metric-warning .metric-value { color: #f59e0b; } .metric-primary .metric-value { color: #8b5cf6; } .metric-info .metric-value { color: #0dcaf0; } .ref-box { background: #f0f4ff; border-left: 4px solid #8b5cf6; padding: 12px 16px; border-radius: 4px; margin-top: 10px; font-size: 13px; } .ref-box a { color: #8b5cf6; } ") ui <- page_sidebar( title = "Theophylline PK Simulator", theme = app_theme, sidebar = sidebar( title = "Simulation Settings", width = 340, h6("Dosing"), sliderInput("dose", "Dose (mg)", min = 100, max = 600, value = 250, step = 25), sliderInput("interval", "Dosing Interval (h)", min = 6, max = 24, value = 12, step = 1), numericInput("n_days", "Duration (days)", value = 5, min = 1, max = 14), hr(), h6("Patient Characteristics"), sliderInput("wt", "Weight (kg)", min = 40, max = 120, value = 70, step = 5), selectInput("smoking", "Smoking Status", choices = c("Non-smoker" = 1.0, "Smoker (+50% CL)" = 1.5), selected = 1.0), hr(), checkboxInput("log_scale", "Log Scale (Y-axis)", value = FALSE) ), navset_card_tab( title = "Theophylline PK Simulator", full_screen = TRUE, nav_panel("Simulation", layout_column_wrap( width = 1/4, fill = FALSE, div(class="metric-card metric-success", div(class="metric-value", textOutput("cmax")), div(class="metric-label","Cmax (mg/L)")), div(class="metric-card metric-warning", div(class="metric-value", textOutput("ctrough")), div(class="metric-label","Ctrough (mg/L)")), div(class="metric-card metric-primary", div(class="metric-value", textOutput("auc")), div(class="metric-label","AUC0-τ (mg·h/L)")), div(class="metric-card metric-info", div(class="metric-value", textOutput("thalf")), div(class="metric-label","t½ (h)")) ), plotOutput("pkPlot", height = "500px") ), nav_panel("Model Information", markdown(" ## Theophylline — 1-Compartment Oral PK Model ### Source Rovei V, Chanoine F, Strolin Benedetti M. *Pharmacokinetics of theophylline: a dose-range study.* Br J Clin Pharmacol. 1982;14:769-778. ### Study Design - **Subjects:** 8 healthy adult volunteers (non-smokers, xanthine-free diet) - **Doses:** 125, 250, 375, and 500 mg single oral doses (crossover) - **Formulation:** Theodel® tablets - **Model:** One-compartment open model with first-order absorption ### PK Parameters (Mean ± SD across doses) | Parameter | Value | Unit | |-----------|-------|------| | CL | 0.042 ± 0.010 | L/h/kg | | Vd | 0.50 ± 0.07 | L/kg | | Ka | ~1.4 | 1/h | | F | ~1.0 | (complete absorption) | | t½ (elimination) | 8.2 ± 1.5 | h | | Protein binding | 53–65% | — | ### Therapeutic Window - **Therapeutic range:** 10–20 mg/L (bronchodilator effect) - **Toxicity risk:** > 20 mg/L (GI, CNS, cardiovascular adverse effects) ### Key Findings - Linear pharmacokinetics across 125–500 mg dose range - Clearance unchanged across all doses (no dose-dependent kinetics) - Primary elimination via hepatic metabolism (~85% of clearance) - Renal clearance: 13–17% of total clearance - Smoking increases theophylline clearance by ~50% ")), nav_panel("References", div(class = "ref-box", tags$p(tags$strong("Primary Reference:")), tags$p("Rovei V, Chanoine F, Strolin Benedetti M. Pharmacokinetics of theophylline: a dose-range study. Br J Clin Pharmacol. 1982;14:769-778."), tags$p(tags$strong("FDA Label:")), tags$p(tags$a(href="https://www.accessdata.fda.gov/drugsatfda_docs/label/2009/019211s042lbl.pdf", target="_blank", "Theophylline FDA Prescribing Information")), tags$p(tags$strong("Population PK Review:")), tags$p(tags$a(href="https://pubmed.ncbi.nlm.nih.gov/27578172/", target="_blank", "Ma et al. Theophylline: a review of population pharmacokinetic analyses. J Clin Pharm Ther. 2016.")), tags$p(tags$strong("Additional References:")), tags$ul( tags$li("Ogilvie RI. Clinical pharmacokinetics of theophylline. Clin Pharmacokinet. 1978;3:267-293."), tags$li("Hendeles L, Weinberger M, Johnson G. Monitoring serum theophylline levels. Clin Pharmacokinet. 1978;3:294-312.") ) ) ) ), # ── PKPDBuilder Branding Footer ── div(style = "text-align: center; padding: 20px; margin-top: 30px; border-top: 1px solid #e9ecef; color: #6c757d; font-size: 12px;", "Powered by ", tags$a(href = "https://www.pkpdbuilder.com", target = "_blank", style = "color: #8b5cf6; font-weight: 500;", "PKPDBuilder.com"), " • Built by Sunny ☀️ (Husain Attarwala's AI Assistant)", br(), tags$span(style = "font-size: 10px;", "For research and educational purposes only. Not for clinical decision-making.") ) ) server <- function(input, output, session) { sim_data <- reactive({ doses <- expand.grid(amt = as.numeric(input$dose), time = seq(0, by = as.numeric(input$interval), length.out = as.numeric(input$n_days) * 24 / as.numeric(input$interval))) doses$evid <- 1 doses$cmt <- 1 doses$ID <- 1 total_h <- as.numeric(input$n_days) * 24 out <- mod %>% param(WT = as.numeric(input$wt), SMOK = as.numeric(input$smoking)) %>% data_set(doses) %>% mrgsim(end = total_h, delta = 0.25) %>% as.data.frame() out }) last_interval <- reactive({ d <- sim_data() tau <- as.numeric(input$interval) total_h <- as.numeric(input$n_days) * 24 start <- total_h - tau d %>% filter(time >= start & time <= total_h) }) output$cmax <- renderText({ sprintf("%.1f", max(last_interval()$CP)) }) output$ctrough <- renderText({ li <- last_interval() sprintf("%.1f", li$CP[nrow(li)]) }) output$auc <- renderText({ li <- last_interval() auc_val <- sum(diff(li$time) * (head(li$CP, -1) + tail(li$CP, -1)) / 2) sprintf("%.1f", auc_val) }) output$thalf <- renderText({ ke <- (as.numeric(input$wt) * 0.042 * as.numeric(input$smoking)) / (as.numeric(input$wt) * 0.50) sprintf("%.1f", log(2)/ke) }) output$pkPlot <- renderPlot({ d <- sim_data() p <- ggplot(d, aes(x = time, y = CP)) + annotate("rect", xmin = -Inf, xmax = Inf, ymin = 10, ymax = 20, fill = "#10b981", alpha = 0.10) + annotate("rect", xmin = -Inf, xmax = Inf, ymin = 20, ymax = Inf, fill = "#ef4444", alpha = 0.08) + geom_hline(yintercept = c(10, 20), linetype = "dashed", color = c("#10b981","#ef4444"), linewidth = 0.5) + geom_line(color = "#8b5cf6", linewidth = 1.2) + annotate("text", x = max(d$time)*0.98, y = 10.5, label = "Therapeutic min (10 mg/L)", hjust = 1, size = 3, color = "#10b981") + annotate("text", x = max(d$time)*0.98, y = 20.5, label = "Toxic threshold (20 mg/L)", hjust = 1, size = 3, color = "#ef4444") + labs(x = "Time (h)", y = "Plasma Concentration (mg/L)", title = "Theophylline Plasma Concentration–Time Profile") + theme_minimal(base_size = 14) + theme(plot.title = element_text(face = "bold", size = 16)) if (input$log_scale) p <- p + scale_y_log10() p }) } shinyApp(ui, server)