notsahil commited on
Commit
c808da1
Β·
1 Parent(s): 034e714
Files changed (4) hide show
  1. Dockerfile +21 -0
  2. README.md +8 -4
  3. requirements.txt +2 -0
  4. src/app.py +348 -0
Dockerfile ADDED
@@ -0,0 +1,21 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ FROM python:3.9-slim
2
+
3
+ WORKDIR /app
4
+
5
+ RUN apt-get update && apt-get install -y \
6
+ build-essential \
7
+ curl \
8
+ software-properties-common \
9
+ git \
10
+ && rm -rf /var/lib/apt/lists/*
11
+
12
+ COPY requirements.txt ./
13
+ COPY src/ ./src/
14
+
15
+ RUN pip3 install -r requirements.txt
16
+
17
+ EXPOSE 8501
18
+
19
+ HEALTHCHECK CMD curl --fail http://localhost:8501/_stcore/health
20
+
21
+ ENTRYPOINT ["streamlit", "run", "src/app.py", "--server.port=8501", "--server.address=0.0.0.0"]
README.md CHANGED
@@ -1,10 +1,14 @@
1
  ---
2
- title: Fhir R5
3
- emoji: 🐒
4
  colorFrom: red
5
- colorTo: gray
6
  sdk: docker
 
 
 
7
  pinned: false
 
8
  ---
9
 
10
- Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
 
1
  ---
2
+ title: Fhir R5 Demo
3
+ emoji: πŸš€
4
  colorFrom: red
5
+ colorTo: red
6
  sdk: docker
7
+ app_port: 8501
8
+ tags:
9
+ - streamlit
10
  pinned: false
11
+ short_description: Fhir R5 Server DEMO with CRUD
12
  ---
13
 
14
+ # Fhir Server Demo
requirements.txt ADDED
@@ -0,0 +1,2 @@
 
 
 
1
+ pandas
2
+ streamlit
src/app.py ADDED
@@ -0,0 +1,348 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import streamlit as st
2
+ import requests
3
+ import json
4
+ import pandas as pd
5
+ import uuid
6
+ import os
7
+
8
+ # FHIR Server Base URL from environment variable
9
+ FHIR_BASE_URL = os.getenv("FHIR_BASE_URL")
10
+
11
+ st.title("FHIR API Explorer")
12
+ st.write("Explore the HAPI FHIR Server API")
13
+
14
+ st.sidebar.title("Navigation")
15
+ page = st.sidebar.radio("Select a page", ["Server Info", "Patient Search", "Resource Explorer", "CRUD Operations"])
16
+
17
+ if page == "Server Info":
18
+ st.header("Server Capabilities")
19
+
20
+ if st.button("Get Server Metadata"):
21
+ try:
22
+ response = requests.get(f"{FHIR_BASE_URL}/metadata")
23
+ if response.status_code == 200:
24
+ data = response.json()
25
+ st.json(data)
26
+ else:
27
+ st.error(f"Error: {response.status_code}")
28
+ except Exception as e:
29
+ st.error(f"Error: {str(e)}")
30
+
31
+ elif page == "Patient Search":
32
+ st.header("Patient Search")
33
+ col1, col2 = st.columns(2)
34
+ with col1:
35
+ family_name = st.text_input("Family Name")
36
+ with col2:
37
+ identifier = st.text_input("Identifier")
38
+
39
+ if st.button("Search Patients"):
40
+ params = {}
41
+ if family_name:
42
+ params["family"] = family_name
43
+ if identifier:
44
+ params["identifier"] = identifier
45
+
46
+ try:
47
+ response = requests.get(f"{FHIR_BASE_URL}/Patient", params=params)
48
+ if response.status_code == 200:
49
+ data = response.json()
50
+
51
+ if "entry" in data and len(data["entry"]) > 0:
52
+ patients = []
53
+ for entry in data["entry"]:
54
+ resource = entry["resource"]
55
+ patient = {
56
+ "id": resource.get("id", ""),
57
+ "name": ", ".join([name.get("family", "") for name in resource.get("name", [])]),
58
+ "gender": resource.get("gender", ""),
59
+ "birthDate": resource.get("birthDate", "")
60
+ }
61
+ patients.append(patient)
62
+
63
+ st.dataframe(pd.DataFrame(patients))
64
+ with st.expander("Raw Response"):
65
+ st.json(data)
66
+ else:
67
+ st.info("No patients found")
68
+ else:
69
+ st.error(f"Error: {response.status_code}")
70
+ except Exception as e:
71
+ st.error(f"Error: {str(e)}")
72
+
73
+ elif page == "Resource Explorer":
74
+ st.header("Resource Explorer")
75
+
76
+ resource_types = ["Patient", "Observation", "Medication", "Immunization", "Condition", "Procedure"]
77
+ resource_type = st.selectbox("Select Resource Type", resource_types)
78
+
79
+ resource_id = st.text_input("Resource ID (optional)")
80
+
81
+ if st.button("Fetch Resources"):
82
+ try:
83
+ if resource_id:
84
+ url = f"{FHIR_BASE_URL}/{resource_type}/{resource_id}"
85
+ else:
86
+ url = f"{FHIR_BASE_URL}/{resource_type}?_count=10"
87
+
88
+ response = requests.get(url)
89
+ if response.status_code == 200:
90
+ data = response.json()
91
+
92
+ if "resourceType" in data and data["resourceType"] != "Bundle":
93
+ st.write(f"Resource Type: {data['resourceType']}")
94
+ st.write(f"ID: {data.get('id', 'N/A')}")
95
+
96
+ with st.expander("View Full Resource"):
97
+ st.json(data)
98
+ else:
99
+ if "entry" in data and len(data["entry"]) > 0:
100
+ st.write(f"Found {len(data['entry'])} resources")
101
+
102
+ resources = []
103
+ for entry in data["entry"]:
104
+ resource = entry["resource"]
105
+ resources.append({
106
+ "id": resource.get("id", ""),
107
+ "resourceType": resource.get("resourceType", ""),
108
+ "lastUpdated": resource.get("meta", {}).get("lastUpdated", "")
109
+ })
110
+
111
+ st.dataframe(pd.DataFrame(resources))
112
+
113
+ selected_id = st.selectbox("Select a resource to view details",
114
+ [r["id"] for r in resources])
115
+
116
+ if selected_id:
117
+ for entry in data["entry"]:
118
+ if entry["resource"].get("id") == selected_id:
119
+ st.json(entry["resource"])
120
+ else:
121
+ st.info("No resources found")
122
+ else:
123
+ st.error(f"Error: {response.status_code}")
124
+ except Exception as e:
125
+ st.error(f"Error: {str(e)}")
126
+
127
+ elif page == "CRUD Operations":
128
+ st.header("CRUD Operations")
129
+
130
+ resource_types = ["Patient", "Observation", "Medication", "Immunization", "Condition", "Procedure"]
131
+ resource_type = st.selectbox("Select Resource Type", resource_types)
132
+
133
+ crud_tab = st.tabs(["Create", "Read", "Update", "Delete"])
134
+
135
+ # CREATE
136
+ with crud_tab[0]:
137
+ st.subheader(f"Create New {resource_type}")
138
+
139
+ if resource_type == "Patient":
140
+ with st.form("create_patient_form"):
141
+ family_name = st.text_input("Family Name")
142
+ given_name = st.text_input("Given Name")
143
+ gender = st.selectbox("Gender", ["male", "female", "other", "unknown"])
144
+ birth_date = st.date_input("Birth Date")
145
+
146
+ submit_button = st.form_submit_button("Create Patient")
147
+
148
+ if submit_button:
149
+ patient_data = {
150
+ "resourceType": "Patient",
151
+ "name": [
152
+ {
153
+ "family": family_name,
154
+ "given": [given_name]
155
+ }
156
+ ],
157
+ "gender": gender,
158
+ "birthDate": str(birth_date)
159
+ }
160
+
161
+ try:
162
+ headers = {"Content-Type": "application/fhir+json"}
163
+ response = requests.post(
164
+ f"{FHIR_BASE_URL}/Patient",
165
+ json=patient_data,
166
+ headers=headers
167
+ )
168
+
169
+ if response.status_code in [200, 201]:
170
+ st.success("Patient created successfully!")
171
+ st.json(response.json())
172
+ else:
173
+ st.error(f"Error: {response.status_code}")
174
+ st.write(response.text)
175
+ except Exception as e:
176
+ st.error(f"Error: {str(e)}")
177
+
178
+ elif resource_type == "Observation":
179
+ with st.form("create_observation_form"):
180
+ patient_id = st.text_input("Patient ID")
181
+ code_display = st.text_input("Observation Name", "Heart rate")
182
+ code_system = st.text_input("Code System", "http://loinc.org")
183
+ code_code = st.text_input("Code", "8867-4")
184
+ value = st.number_input("Value", value=80)
185
+ unit = st.text_input("Unit", "beats/minute")
186
+
187
+ submit_button = st.form_submit_button("Create Observation")
188
+
189
+ if submit_button:
190
+ observation_data = {
191
+ "resourceType": "Observation",
192
+ "status": "final",
193
+ "code": {
194
+ "coding": [
195
+ {
196
+ "system": code_system,
197
+ "code": code_code,
198
+ "display": code_display
199
+ }
200
+ ]
201
+ },
202
+ "subject": {
203
+ "reference": f"Patient/{patient_id}"
204
+ },
205
+ "valueQuantity": {
206
+ "value": value,
207
+ "unit": unit,
208
+ "system": "http://unitsofmeasure.org"
209
+ }
210
+ }
211
+
212
+ try:
213
+ headers = {"Content-Type": "application/fhir+json"}
214
+ response = requests.post(
215
+ f"{FHIR_BASE_URL}/Observation",
216
+ json=observation_data,
217
+ headers=headers
218
+ )
219
+
220
+ if response.status_code in [200, 201]:
221
+ st.success("Observation created successfully!")
222
+ st.json(response.json())
223
+ else:
224
+ st.error(f"Error: {response.status_code}")
225
+ st.write(response.text)
226
+ except Exception as e:
227
+ st.error(f"Error: {str(e)}")
228
+
229
+ else:
230
+ st.write(f"Create a new {resource_type} resource:")
231
+
232
+ template_json = {
233
+ "resourceType": resource_type,
234
+ }
235
+
236
+ json_str = st.text_area("Edit JSON", json.dumps(template_json, indent=2), height=300)
237
+
238
+ if st.button("Create Resource"):
239
+ try:
240
+ resource_data = json.loads(json_str)
241
+ headers = {"Content-Type": "application/fhir+json"}
242
+ response = requests.post(
243
+ f"{FHIR_BASE_URL}/{resource_type}",
244
+ json=resource_data,
245
+ headers=headers
246
+ )
247
+
248
+ if response.status_code in [200, 201]:
249
+ st.success(f"{resource_type} created successfully!")
250
+ st.json(response.json())
251
+ else:
252
+ st.error(f"Error: {response.status_code}")
253
+ st.write(response.text)
254
+ except json.JSONDecodeError:
255
+ st.error("Invalid JSON format")
256
+ except Exception as e:
257
+ st.error(f"Error: {str(e)}")
258
+
259
+ # READ
260
+ with crud_tab[1]:
261
+ st.subheader(f"Read {resource_type}")
262
+
263
+ resource_id = st.text_input(f"{resource_type} ID")
264
+
265
+ if st.button("Read Resource"):
266
+ if not resource_id:
267
+ st.warning("Please enter a resource ID")
268
+ else:
269
+ try:
270
+ response = requests.get(f"{FHIR_BASE_URL}/{resource_type}/{resource_id}")
271
+
272
+ if response.status_code == 200:
273
+ st.success(f"{resource_type} retrieved successfully!")
274
+ st.json(response.json())
275
+ else:
276
+ st.error(f"Error: {response.status_code}")
277
+ st.write(response.text)
278
+ except Exception as e:
279
+ st.error(f"Error: {str(e)}")
280
+
281
+ # UPDATE
282
+ with crud_tab[2]:
283
+ st.subheader(f"Update {resource_type}")
284
+
285
+ update_id = st.text_input(f"{resource_type} ID to update")
286
+
287
+ if update_id:
288
+ try:
289
+ response = requests.get(f"{FHIR_BASE_URL}/{resource_type}/{update_id}")
290
+
291
+ if response.status_code == 200:
292
+ resource_data = response.json()
293
+
294
+ json_str = st.text_area("Edit JSON", json.dumps(resource_data, indent=2), height=300)
295
+
296
+ if st.button("Update Resource"):
297
+ try:
298
+ updated_data = json.loads(json_str)
299
+ headers = {"Content-Type": "application/fhir+json"}
300
+ update_response = requests.put(
301
+ f"{FHIR_BASE_URL}/{resource_type}/{update_id}",
302
+ json=updated_data,
303
+ headers=headers
304
+ )
305
+
306
+ if update_response.status_code in [200, 201]:
307
+ st.success(f"{resource_type} updated successfully!")
308
+ st.json(update_response.json())
309
+ else:
310
+ st.error(f"Error: {update_response.status_code}")
311
+ st.write(update_response.text)
312
+ except json.JSONDecodeError:
313
+ st.error("Invalid JSON format")
314
+ except Exception as e:
315
+ st.error(f"Error: {str(e)}")
316
+ else:
317
+ st.error(f"Error fetching resource: {response.status_code}")
318
+ except Exception as e:
319
+ st.error(f"Error: {str(e)}")
320
+ else:
321
+ st.info(f"Enter a {resource_type} ID to update")
322
+
323
+ # DELETE
324
+ with crud_tab[3]:
325
+ st.subheader(f"Delete {resource_type}")
326
+
327
+ delete_id = st.text_input(f"{resource_type} ID to delete")
328
+
329
+ if delete_id:
330
+ st.warning(f"Are you sure you want to delete {resource_type}/{delete_id}?")
331
+
332
+ if st.button("Confirm Delete"):
333
+ try:
334
+ response = requests.delete(f"{FHIR_BASE_URL}/{resource_type}/{delete_id}")
335
+
336
+ if response.status_code in [200, 204]:
337
+ st.success(f"{resource_type} deleted successfully!")
338
+ else:
339
+ st.error(f"Error: {response.status_code}")
340
+ st.write(response.text)
341
+ except Exception as e:
342
+ st.error(f"Error: {str(e)}")
343
+ else:
344
+ st.info(f"Enter a {resource_type} ID to delete")
345
+
346
+ # Footer
347
+ st.markdown("---")
348
+ st.markdown("FHIR API Explorer - Using HAPI FHIR Server")