Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions .github/workflows/deploy-to-cloud-run.yml
Original file line number Diff line number Diff line change
Expand Up @@ -41,3 +41,10 @@ jobs:
--platform managed \
--region us-central1 \
--allow-unauthenticated

- name: Notify Slack on failure
if: failure()
run: |
curl -X POST -H 'Content-type: application/json' \
--data '{"text": "Deployment failed for branch ${{ github.ref }}. Here are the details:\n\n Branch: ${{ github.ref }}\n Commit: ${{ github.sha }}\n Author: ${{ github.actor }}\n"}' \
${{ secrets.SLACK_WEBHOOK_URL }}
1 change: 1 addition & 0 deletions .python-version
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
3.10.2
26 changes: 26 additions & 0 deletions UI/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
## Pipeline Overview

The CI/CD pipeline automates the deployment of the Streamlit application to Google Cloud Platform (GCP). Here's an overview of the steps:

1. **Trigger on Push**:
- The pipeline is triggered when changes are pushed to the `UI/` directory in the `feature/UI-deploy`, `dev`, or `main` branches.

2. **Authenticate with Google Cloud**:
- The pipeline uses a service account key stored in GitHub Secrets to authenticate with GCP.

3. **Build and Push Docker Image**:
- The pipeline builds a Docker image for the Streamlit application using the `Dockerfile` in the `UI/` directory.
- The Docker image is tagged and pushed to **Google Artifact Registry** under the repository `us-docker.pkg.dev/medscript-437117/gcr.io/streamlit-app`.

4. **Deploy to Cloud Run**:
- The Docker image is deployed to **Cloud Run**, which provides a managed, scalable environment to host the Streamlit app.
- The application is exposed to the internet and configured to allow unauthenticated access.

This pipeline ensures that any updates to the `UI/` directory in the specified branches are automatically deployed to the production environment.

### Slack Alert Notification Integration

We’ve integrated Slack alert notifications into the deployment pipeline to keep developers informed about deployment failures. If a deployment fails, an alert is sent to a specified Slack channel with details including the branch, commit and author. Slack notifications are triggered through a webhook and provide real-time information about the deployment status. Screenshot of sample notification is below:
<img width="1280" alt="Screenshot 2024-12-03 at 6 37 47 PM" src="https://github.com/user-attachments/assets/ca66cb1f-8871-4695-89b6-52db59ccdbe9">


99 changes: 99 additions & 0 deletions UI/generate_patient_report_pdf.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
import json
from fpdf import FPDF

class PatientReportPDF(FPDF):
def header(self):
# Add a title
self.set_font('Times', 'B', 16)
self.cell(0, 10, 'Patient report', border=False, ln=1, align='C')
self.set_line_width(0.5)
self.line(10, self.get_y(), 200, self.get_y())
self.ln(5)

def footer(self):
# Add a footer with page number
self.set_y(-15)
self.set_font('Times', 'I', 10)
self.cell(0, 10, f'{self.page_no()}', align='C')

def draw_patient_id(self, value):
# Write the patient ID
self.set_font("Times", "I", 12)
self.cell(0, 10, f"Patient ID: {value}", 0, 1)
self.ln(5)

def draw_patient_record(self, value):
# Write the patient record
self.set_font("Times", "B", 12)
self.cell(0, 10, "Patient Record", 0, 1)
self.ln(2)

self.set_font("Times", "", 10)
for record_key, record_value in value.items():
self.multi_cell(0, 8, u"{0}: {1}".format(record_key, record_value))
self.ln(5)

def draw_possible_diagnosis(self, value):
# Write possible diagnosis
self.set_font("Times", "B", 11)
self.cell(0, 8, "Possible Diagnosis:", 0, 1)

# Write primary diagnosis
self.set_font("Times", "B", 10)
self.cell(0, 8, "Primary Diagnosis:", 0, 1)
self.set_font("Times", "", 10)
self.multi_cell(0, 8, "- {0}".format(value['Primary Diagnosis']))

# Write Differential Diagnosis
self.set_font("Times", "B", 11)
self.cell(0,8,"Differential Diagnosis", 0, 1)
self.set_font("Times", "", 10)

# Loop through the list and write to pdf
for index, item in enumerate(value['Differential Diagnoses']):
self.cell(5)
self.multi_cell(0, 10, f"{index}. {item}")

def draw_diagnosis_report(self, value):
# Write diagnosis report
self.set_font("Times", "B", 12)
self.cell(0, 10, "Diagnosis Report", 0, 1)
self.ln(2)

self.set_font("Times", "", 10)
for report_key, report_value in value.items():
# Write the possible diagnosis
if report_key == 'Possible Diagnoses':
self.draw_possible_diagnosis(report_value)

# Write all other elements in the possible diagnosis
else:
self.set_font("Times", "B", 10)
self.cell(0, 8, report_key, 0, 1)
self.set_font("Times", "", 10)

self.multi_cell(0, 8, f"{report_value}".encode().decode('unicode-escape'))
self.ln(3)

def create_pdf():
# Create an instance of the PDF class
pdf = PatientReportPDF()
pdf.add_page()

# Set font and add some content
# pdf.set_font('Times', '', 12)
# pdf.multi_cell(0, 10, "This is a sample PDF generated using Python and the FPDF library. "
# "You can add multiple lines of text, and the text will automatically wrap.")

with open('../models/test_records_with_med42.json') as file:
data = json.load(file)[1]

pdf.draw_patient_id(data['patientId'])

pdf.draw_patient_record(data['patientRecord'])

pdf.draw_diagnosis_report(data['diagnosisReport'])

print(f"Report created succesfully.")

return pdf.output(dest="S").encode("latin1")
13 changes: 12 additions & 1 deletion UI/patient_form.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import streamlit as st
from generate_patient_report_pdf import create_pdf

st.title("Welcome to MedScript")
# Streamlit application
Expand Down Expand Up @@ -60,7 +61,17 @@

# Button to view diagnostic report
if st.button("View Diagnostic Report"):
st.write("Diagnostic report is not yet available. Please contact your healthcare provider.")
try:
pdf = create_pdf()
st.download_button(
label="Download PDF",
data=pdf,
file_name="diagnostic_report.pdf",
mime="application/pdf"
)
except Exception as e:
print(f"An error occured: {e}")
# st.write("Diagnostic report is not yet available. Please contact your healthcare provider.")

st.subheader("Diagnosis")
primary_diagnosis = st.text_area("Primary Diagnosis", placeholder="Enter the primary diagnosis...", height=150)
Expand Down
1 change: 1 addition & 0 deletions UI/requirements.txt
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
fpdf=1.7.2
streamlit==1.24.0
1 change: 1 addition & 0 deletions requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ filelock==3.16.1
Flask==2.2.5
flatten-dict==0.4.2
flufl.lock==8.1.0
fpdf==1.7.2
fonttools==4.54.1
frozenlist==1.5.0
fsspec==2024.9.0
Expand Down