Skip to content

Commit

Permalink
Merge branch 'main' into LLMFilterData
Browse files Browse the repository at this point in the history
  • Loading branch information
apgupta3303 committed Mar 11, 2024
2 parents 8c68d04 + 28194c1 commit 802ee93
Show file tree
Hide file tree
Showing 7 changed files with 373 additions and 6 deletions.
12 changes: 12 additions & 0 deletions Intake.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,7 @@
A9DFE8A92ABE551400428242 /* AccountButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9DFE8A82ABE551400428242 /* AccountButton.swift */; };
A9FE7AD02AA39BAB0077B045 /* AccountSheet.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9FE7ACF2AA39BAB0077B045 /* AccountSheet.swift */; };
ACAA47812B571C800032D21F /* Questionnaire.json in Resources */ = {isa = PBXBuildFile; fileRef = ACAA47802B571C7F0032D21F /* Questionnaire.json */; };
ACF862BE2B96E29600ACBA1E /* ExportView.swift in Sources */ = {isa = PBXBuildFile; fileRef = ACF862BD2B96E29600ACBA1E /* ExportView.swift */; };
ACDF32ED2B9D0F4300B127E2 /* MenstrualHistory.swift in Sources */ = {isa = PBXBuildFile; fileRef = ACFFA1D02B8FD8BB0006E6D4 /* MenstrualHistory.swift */; };
ACFFA1CE2B8FD7190006E6D4 /* SmokingHistory.swift in Sources */ = {isa = PBXBuildFile; fileRef = ACFFA1CD2B8FD7190006E6D4 /* SmokingHistory.swift */; };
F42AB1D22B6379B5002E13A6 /* SpeziLLM in Frameworks */ = {isa = PBXBuildFile; productRef = F42AB1D12B6379B5002E13A6 /* SpeziLLM */; };
Expand Down Expand Up @@ -228,6 +229,7 @@
A9DFE8A82ABE551400428242 /* AccountButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccountButton.swift; sourceTree = "<group>"; };
A9FE7ACF2AA39BAB0077B045 /* AccountSheet.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccountSheet.swift; sourceTree = "<group>"; };
ACAA47802B571C7F0032D21F /* Questionnaire.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = Questionnaire.json; sourceTree = "<group>"; };
ACF862BD2B96E29600ACBA1E /* ExportView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ExportView.swift; sourceTree = "<group>"; };
ACFFA1CD2B8FD7190006E6D4 /* SmokingHistory.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SmokingHistory.swift; sourceTree = "<group>"; };
ACFFA1D02B8FD8BB0006E6D4 /* MenstrualHistory.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MenstrualHistory.swift; sourceTree = "<group>"; };
F42AB1DB2B637C8C002E13A6 /* LLMOnboardingView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LLMOnboardingView.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -501,6 +503,7 @@
653A254F283387FE005D4D48 /* Intake */ = {
isa = PBXGroup;
children = (
ACF862BC2B96E28400ACBA1E /* Export */,
F4F4F8802B8C6FC5008FBEED /* Elements.swift */,
519E830A2B7C4F1600A2D92D /* Medication View */,
511827942B740191002033A0 /* Surgery */,
Expand Down Expand Up @@ -573,6 +576,14 @@
path = SocialHistory;
sourceTree = "<group>";
};
ACF862BC2B96E28400ACBA1E /* Export */ = {
isa = PBXGroup;
children = (
ACF862BD2B96E29600ACBA1E /* ExportView.swift */,
);
path = Export;
sourceTree = "<group>";
};
F42AB1DA2B637C5F002E13A6 /* ChiefComplaint */ = {
isa = PBXGroup;
children = (
Expand Down Expand Up @@ -878,6 +889,7 @@
F42AB1E52B6383F9002E13A6 /* LLMOpenAITokenOnboarding.swift in Sources */,
51805C122B81853800D17109 /* IntakeMedication.swift in Sources */,
A9FE7AD02AA39BAB0077B045 /* AccountSheet.swift in Sources */,
ACF862BE2B96E29600ACBA1E /* ExportView.swift in Sources */,
51A027672B82CDA300A195C8 /* MedicationContentView.swift in Sources */,
653A2551283387FE005D4D48 /* Intake.swift in Sources */,
2FE5DC3629EDD7CA004B9AB4 /* HealthKitPermissions.swift in Sources */,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -149,8 +149,8 @@
"kind" : "remoteSourceControl",
"location" : "https://github.com/StanfordBDHG/ResearchKit",
"state" : {
"revision" : "e4afb10ec636a70e06afc4a84fb98e457818439a",
"version" : "2.2.27"
"revision" : "64512d0a0a5cc3e9d5b3fc5217c54f11d0dc044c",
"version" : "2.2.28"
}
},
{
Expand Down Expand Up @@ -275,8 +275,8 @@
"kind" : "remoteSourceControl",
"location" : "https://github.com/StanfordSpezi/SpeziQuestionnaire.git",
"state" : {
"revision" : "f25580e95bfdad02383980dcb94406cf97b08ea8",
"version" : "1.0.2"
"revision" : "f9d9b6d99bb1e00bda2974b440dca8367733d591",
"version" : "1.1.0"
}
},
{
Expand Down
286 changes: 286 additions & 0 deletions Intake/Export/ExportView.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,286 @@
// This source file is part of the Intake based on the Stanford Spezi Template Application project
//
// SPDX-FileCopyrightText: 2023 Stanford University
//
// SPDX-License-Identifier: MIT
//
//

// swiftlint disable: closure_body_length
import PDFKit
import SpeziFHIR
import SwiftUI
import UIKit

// swiftlint:disable file_types_order
struct ExportView: View {
@Environment(DataStore.self) var data
@State private var isSharing = false
@State private var pdfData: PDFDocument?

// swiftlint:disable closure_body_length
var body: some View {
ScrollView {
self.wrappedBody
}
.toolbar {
ToolbarItem(placement: .topBarTrailing) {
Button {
Task { await shareButtonTapped() }
} label: {
Image(systemName: "square.and.arrow.up")
.accessibilityLabel("Share Intake form")
}
}
}
.sheet(isPresented: $isSharing) {
if let pdfData = self.pdfData {
ShareSheet(sharedItem: pdfData)
.presentationDetents([.medium])
} else {
ProgressView()
.padding()
.presentationDetents([.medium])
}
}
.onChange(of: pdfData) {
print("PDF data changed")
}
}

// FOR UPDATED SURGERY STRUCT
// ForEach(data.surgeries, id: \.self) { item in
// if !item.startDate.isEmpty && !item.endDate.isEmpty && !item.complications.isEmpty{
// HStack {
// Text(item.surgeryName)
// Text(item.startDate)
// Text(item.endDate)
// Text(item.complications)
// }
// }
// }

// ForEach([1,2,3], id: \.self) { item in
// Text(String(item))
// }


private var wrappedBody: some View {
VStack {
Text("MEDICAL HISTORY").fontWeight(.bold)

Spacer()
.frame(height: 20)

VStack(alignment: .leading) {
Group {
HStack {
Text("Date:").fontWeight(.bold)
Text(todayDateString())
}
HStack {
Text("Name:").fontWeight(.bold)
Text("John Doe")
}
HStack {
Text("Date of Birth:").fontWeight(.bold)
Text("January 1, 1980")
}
HStack {
Text("Age:").fontWeight(.bold)
Text("35")
}
HStack {
Text("Sex:").fontWeight(.bold)
Text("Female")
}

Spacer()
.frame(height: 20)


VStack(alignment: .leading) {
Text("Chief Complaint:").fontWeight(.bold)
if data.chiefComplaint.isEmpty {
Text("Patient did not enter chief complaint.")
} else {
Text(data.chiefComplaint)
}
}

Spacer()
.frame(height: 20)


VStack(alignment: .leading) {
Text("Past Medical History:").fontWeight(.bold)
if data.conditionData.isEmpty {
Text("No medical conditions")
} else {
List(data.conditionData, id: \.id) { item in
Text(item.condition)
}
}
}

Spacer()
.frame(height: 20)

VStack(alignment: .leading) {
Text("Past Surgical History:").fontWeight(.bold)
if data.surgeries.isEmpty {
Text("No past surgeries")
} else {
List(data.surgeries, id: \.id) { item in
Text(item.surgeryName)
}
}
}

Spacer()
.frame(height: 20)

VStack(alignment: .leading) {
Text("Medications:").fontWeight(.bold)
if data.medicationData.isEmpty {
Text("No medications")
} else {
List(Array(data.medicationData), id: \.id) { item in
HStack {
Text(item.type.localizedDescription)
Text(item.dosage.localizedDescription)
}
}
}
}

Spacer()
.frame(height: 20)

VStack(alignment: .leading) {
Text("Allergies:").fontWeight(.bold)
if data.allergyData.isEmpty {
Text("No known allergies")
} else {
List(data.allergyData, id: \.id) { item in
HStack {
Text(item.allergy)
List(item.reaction, id: \.id) { reactionItem in
Text(reactionItem.reaction)
}
}
}
}
}

Spacer()
.frame(height: 20)

VStack(alignment: .leading) {
Text("Review of Systems:").fontWeight(.bold)
HStack {
Text("Last Menstrural Period")
Text("Date")
}

HStack {
Text("Smoking history")
Text("0 pack years")
}
}
}
}
// swiftlint:enable:closure_body_length
Spacer()
}
}

@MainActor
private func shareButtonTapped() async {
self.pdfData = await self.exportToPDF()
self.isSharing = true
}


@MainActor
func exportToPDF() async -> PDFDocument? {
let renderer = ImageRenderer(content: self.wrappedBody)

// issue: proposed height is not expanding as necessary. uncomment to attempt to fix this.

// var proposedHeightOptional = renderer.uiImage?.size.height

// guard let proposedHeight = proposedHeightOptional else {
// return nil
// }

// let pageSize = CGSize(width: 612, height: proposedHeight)

let pageSize = CGSize(width: 612, height: 920)

renderer.proposedSize = .init(pageSize)

return await withCheckedContinuation { continuation in
renderer.render { _, context in
var box = CGRect(origin: .zero, size: pageSize)

/// Create in-memory `CGContext` that stores the PDF
guard let mutableData = CFDataCreateMutable(kCFAllocatorDefault, 0),
let consumer = CGDataConsumer(data: mutableData),
let pdf = CGContext(consumer: consumer, mediaBox: &box, nil) else {
continuation.resume(returning: nil)
return
}

pdf.beginPDFPage(nil)
pdf.translateBy(x: 50, y: -50)

context(pdf)

pdf.endPDFPage()
pdf.closePDF()

continuation.resume(returning: PDFDocument(data: mutableData as Data))
}
}
}
}


struct ShareSheet: UIViewControllerRepresentable {
let sharedItem: PDFDocument


func makeUIViewController(context: Context) -> UIActivityViewController {
// Note: Need to write down the PDF to storage as in-memory PDFs are not recognized properly
let temporaryPath = FileManager.default.temporaryDirectory.appendingPathComponent(
LocalizedStringResource("Intake Form").localizedString() + ".pdf"
)
try? sharedItem.dataRepresentation()?.write(to: temporaryPath)

let controller = UIActivityViewController(
activityItems: [temporaryPath],
applicationActivities: nil
)
controller.completionWithItemsHandler = { _, _, _, _ in
try? FileManager.default.removeItem(at: temporaryPath)
}

return controller
}

func updateUIViewController(_ uiViewController: UIActivityViewController, context: Context) {}
}

func todayDateString() -> String {
let today = Date()
let formatter = DateFormatter()
formatter.dateStyle = .long
return formatter.string(from: today)
}

struct ExportView_Previews: PreviewProvider {
static var previews: some View {
ExportView()
}
}
Loading

0 comments on commit 802ee93

Please sign in to comment.