diff --git a/AI Guardian/Gopher_AI_FromPDFSp2TextStartstop.ipynb b/AI Guardian/Gopher_AI_FromPDFSp2TextStartstop.ipynb new file mode 100644 index 000000000..05c8c8663 --- /dev/null +++ b/AI Guardian/Gopher_AI_FromPDFSp2TextStartstop.ipynb @@ -0,0 +1,413 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": 3, + "id": "005cc82d-cc4d-4a25-9d08-bc7f59a40db6", + "metadata": {}, + "outputs": [], + "source": [ + "import tkinter as tk\n", + "from tkinter import ttk #Combobox\n", + "from tkinter import messagebox\n", + "from reportlab.platypus import SimpleDocTemplate, Paragraph, Spacer, Image, Table, TableStyle\n", + "from reportlab.lib import colors\n", + "from reportlab.lib.styles import getSampleStyleSheet\n", + "from datetime import datetime, timedelta, timezone\n", + "#---------------------------------------------------------------------------------\n", + "import sounddevice as sd\n", + "import numpy as np\n", + "from scipy.io.wavfile import write\n", + "\n", + "def record():\n", + " # ----------------------------\n", + " # Settings\n", + " # ----------------------------\n", + " duration = 10 # seconds to record\n", + " fs = 16000 # sampling frequency (Hz)\n", + " filename = \"recorded_audio.wav\" # output file\n", + "\n", + " # ----------------------------\n", + " # Record audio\n", + " # ----------------------------\n", + " print(\"Recording...\")\n", + " recording = sd.rec(int(duration * fs), samplerate=fs, channels=1)\n", + " sd.wait() # Wait until recording is finished\n", + " print(\"Recording finished!\")\n", + "\n", + " # ----------------------------\n", + " # Save as WAV file\n", + " # ----------------------------\n", + " # Convert to int16 for WAV format\n", + " write(filename, fs, np.int16(recording * 32767))\n", + " print(f\"Audio saved as {filename}\")\n", + " speech_to_text()\n", + "\n", + "#---------------------------------------------------------------------------------\n", + "\n", + "import wave\n", + "import json\n", + "from vosk import Model, KaldiRecognizer\n", + "\n", + "def speech_to_text():\n", + " \n", + "\n", + " # Load the Vosk model\n", + " model_path = r\"C:\\Users\\graem\\Downloads\\vosk-model-small-en-us-0.15\\vosk-model-small-en-us-0.15\"\n", + " model = Model(model_path)\n", + "\n", + " # Open recorded audio\n", + " wf = wave.open(\"recorded.wav\", \"rb\")\n", + "\n", + " # Initialize recognizer\n", + " rec = KaldiRecognizer(model, wf.getframerate())\n", + "\n", + " transcription = \"\"\n", + "\n", + " # Transcribe\n", + " while True:\n", + " data = wf.readframes(4000)\n", + "\n", + " if len(data) == 0:\n", + " break\n", + "\n", + " if rec.AcceptWaveform(data):\n", + " result = json.loads(rec.Result())\n", + " transcription += result.get(\"text\", \"\") + \" \"\n", + "\n", + " # Final result\n", + " result = json.loads(rec.FinalResult())\n", + " transcription += result.get(\"text\", \"\")\n", + " \n", + " text_notes.insert(tk.END, transcription)\n", + "\n", + " return transcription\n", + "\n", + "\n", + "# Run transcription\n", + "#---------------------------------------------------------------------------------\n", + "import json\n", + "import threading\n", + "import wave\n", + "import queue\n", + "\n", + "# Declare model path\n", + "model_path = r\"C:\\Users\\graem\\Downloads\\vosk-model-small-en-us-0.15\\vosk-model-small-en-us-0.15\"\n", + "model = Model(model_path)\n", + "\n", + "# Global control flag\n", + "global listening\n", + "listening = False\n", + "\n", + "global audio_frames\n", + "audio_frames = []\n", + "q = queue.Queue()\n", + "\n", + "#---------------------------------------------------------------------------------\n", + "# Function for speech\n", + "def audio_thread():\n", + " global listening, audio_frames\n", + "\n", + " rec = KaldiRecognizer(model, 16000)\n", + " audio_frames = []\n", + "\n", + " def callback(indata, frames, time, status):\n", + " if listening:\n", + " data = bytes(indata)\n", + " audio_frames.append(data)\n", + "\n", + " if rec.AcceptWaveform(data):\n", + " result = json.loads(rec.Result())\n", + " q.put(result.get(\"text\", \"\"))\n", + "\n", + " with sd.RawInputStream(samplerate=16000,\n", + " blocksize=8000,\n", + " dtype='int16',\n", + " channels=1,\n", + " callback=callback):\n", + " while listening:\n", + " sd.sleep(100) # IMPORTANT: prevents CPU lock\n", + "#---------------------------------------------------------------------------------\n", + "def update_textbox():\n", + " \"\"\"Safely update UI from queue\"\"\"\n", + " try:\n", + " while True:\n", + " text = q.get_nowait()\n", + " text_notes.insert(tk.END, text + \"\\n\")\n", + " except queue.Empty:\n", + " pass\n", + " root.after(100, update_textbox)\n", + "\n", + "#---------------------------------------------------------------------------------\n", + "def save_wav(filename=\"recorded.wav\"):\n", + " wf = wave.open(filename, 'wb')\n", + " wf.setnchannels(1)\n", + " wf.setsampwidth(2)\n", + " wf.setframerate(16000)\n", + " wf.writeframes(b''.join(audio_frames))\n", + " wf.close()\n", + "\n", + "#---------------------------------------------------------------------------------\n", + "# Start Recording\n", + "def start_listening():\n", + " global listening\n", + " listening = True\n", + " print(\"Listening...\")\n", + " threading.Thread(target=audio_thread, daemon=True).start()\n", + "\n", + "#---------------------------------------------------------------------------------\n", + "# Stop Recording\n", + "def stop_listening():\n", + " global listening\n", + " listening = False\n", + "\n", + " final = json.loads(KaldiRecognizer(model, 16000).FinalResult())\n", + " q.put(\"FINAL: \" + final.get(\"text\", \"\"))\n", + " \n", + " save_wav()\n", + " print(\"Saved to recorded.wav\")\n", + "\n", + " speech_to_text()\n", + "\n", + "\n", + "#---------------------------------------------------------------------------------\n", + "# Create PDF from data on the form\n", + "def generate_pdf():\n", + " name = entry_name.get()\n", + " age = entry_age.get()\n", + " gender = combo_gender.get()\n", + " diagnosis = entry_diagnosis.get()\n", + " bp = entry_bp.get()\n", + " avgheartrate = entry_avgheartrate.get()\n", + " avgspo2 = entry_avgspo2.get()\n", + " avgsleephours = entry_avgsleephours.get()\n", + " avgcalorieintake = entry_avgcalorieintake.get()\n", + " usualdietcompliance = entry_usualdietcompliance.get()\n", + " patientid = patient_id.get()\n", + " dt = datetime.get()\n", + " \n", + " notes = text_notes.get(\"1.0\", tk.END).strip()\n", + "\n", + " #if not name or not email:\n", + " #messagebox.showerror(\"Error\", \"Name and Email are required!\")\n", + " #return\n", + "\n", + " if not name:\n", + " messagebox.showerror(\"Error\", \"Name is required!\")\n", + " return\n", + "\n", + " pdf = SimpleDocTemplate(\"form_output.pdf\")\n", + " styles = getSampleStyleSheet()\n", + "\n", + " content = []\n", + "\n", + " # ✅ 1. Add Logo (make sure logo.png exists in same folder)\n", + " try:\n", + " logo = Image(\"logo.png\", width=120, height=60)\n", + " content.append(logo)\n", + " content.append(Spacer(1, 10))\n", + " except:\n", + " pass # skip if logo not found\n", + "\n", + " # Title\n", + " content.append(Paragraph(\"User Information Report\", styles[\"Title\"]))\n", + " content.append(Spacer(1, 20))\n", + "\n", + " # ✅ 2. Table Layout\n", + " data = [\n", + " [\"Field\", \"Value\"],\n", + " [\"Patient ID\", patientid],\n", + " [\"Date / Time\", dt],\n", + " [\"Name\", name],\n", + " [\"Age\", age],\n", + " [\"Gender\", gender],\n", + " [\"Diagnosis\", diagnosis],\n", + " [\"Blood Pressure\", bp],\n", + " [\"Avg. Heart Rate\", avgheartrate],\n", + " [\"Avg. SPO2\", avgspo2],\n", + " [\"Avg. Sleep Hours\", avgsleephours],\n", + " [\"Avg. Calorie Intake\", avgcalorieintake],\n", + " [\"Usual Diet Compliance\", usualdietcompliance]\n", + " ]\n", + "\n", + " table = Table(data, colWidths=[100, 250])\n", + "\n", + " table.setStyle(TableStyle([\n", + " (\"BACKGROUND\", (0, 0), (-1, 0), colors.grey),\n", + " (\"TEXTCOLOR\", (0, 0), (-1, 0), colors.white),\n", + "\n", + " (\"GRID\", (0, 0), (-1, -1), 1, colors.black),\n", + " (\"FONTNAME\", (0, 0), (-1, 0), \"Helvetica-Bold\"),\n", + "\n", + " (\"ALIGN\", (0, 0), (-1, -1), \"LEFT\"),\n", + " (\"PADDING\", (0, 0), (-1, -1), 8)\n", + " ]))\n", + "\n", + " content.append(table)\n", + " content.append(Spacer(1, 20))\n", + "\n", + " # Notes Section\n", + " content.append(Paragraph(\"Notes:\", styles[\"Normal\"]))\n", + " content.append(Spacer(1, 10))\n", + " content.append(Paragraph(notes, styles[\"Normal\"]))\n", + "\n", + " # Build PDF\n", + " pdf.build(content)\n", + "\n", + " messagebox.showinfo(\"Success\", \"PDF Generated Successfully!\")\n", + "#---------------------------------------------------------------------------------\n", + "def close_form():\n", + " form.destroy()\n", + "#---------------------------------------------------------------------------------\n", + "# Tkinter UI\n", + "root = tk.Tk()\n", + "form = tk.Toplevel(root)\n", + "form.title(\"Medical Diagnosis Form\")\n", + "form.geometry(\"800x800\")\n", + "form.configure(bg=\"#f4f6f7\")\n", + "\n", + "# --- Make a pop-up modal ---\n", + "form.transient(root)\n", + "#form.grab_set()\n", + "form.focus_force()\n", + "form.lift()\n", + "\n", + "# --- Title ---\n", + "tk.Label(form, text=\"Medical Diagnosis Form\",\n", + " font=(\"Arial\", 16, \"bold\"),\n", + " bg=\"#f4f6f7\").pack(pady=15)\n", + "\n", + "# --- Container ---\n", + "container = tk.Frame(form, bg=\"white\", bd=1, relief=\"solid\")\n", + "container.pack(padx=20, pady=10, fill=\"both\", expand=True)\n", + "\n", + "container.columnconfigure(0, weight=1, minsize=150)\n", + "container.columnconfigure(1, weight=2, minsize=300)\n", + "\n", + "# --- Labels ---\n", + "labels = [\"Patient ID\", \"Date / Time\", \"Patient Name *\", \"Age\", \"Gender\", \"Diagnosis *\", \"Blood Pressure\", \"Nursing Notes\", \n", + " \"Avg. Heart Rate\", \"Avg. SPO2\",\"Avg. Sleep Hours\", \"Avg. Calorie Intake\", \"Usual Diet Compliance\"]\n", + "\n", + "for i, text in enumerate(labels):\n", + " tk.Label(container, text=text, anchor=\"e\", bg=\"white\", wraplength=150, justify=\"right\")\\\n", + " .grid(row=i, column=0, padx=15, pady=10, sticky=\"e\")\n", + "\n", + "# --- Inputs ---\n", + "\n", + "patient_id = tk.Entry(container)\n", + "patient_id.insert(0,\"0011773333\")\n", + "patient_id.grid(row=0, column=1, padx=15, pady=10, sticky=\"ew\")\n", + "patient_id.config(state=\"readonly\") # Makes it read-only\n", + "\n", + "now = datetime.now()\n", + "\n", + "datetime = tk.Entry(container)\n", + "datetime.insert(1,now.strftime(\"%d/%m/%Y %H:%M:%S\"))\n", + "datetime.grid(row=1, column=1, padx=15, pady=10, sticky=\"ew\")\n", + "datetime.config(state=\"readonly\") # Makes it read-only\n", + "\n", + "entry_name = tk.Entry(container)\n", + "entry_name.insert(2,\"John Smith\")\n", + "entry_name.grid(row=2, column=1, padx=15, pady=10, sticky=\"ew\")\n", + "name = entry_name.get()\n", + "\n", + "entry_age = tk.Entry(container)\n", + "entry_age.insert(3,\"35\")\n", + "entry_age.grid(row=3, column=1, padx=15, pady=10, sticky=\"ew\")\n", + "age = entry_age.get()\n", + "\n", + "combo_gender = ttk.Combobox(container, values=[\"Male\", \"Female\", \"Other\"])\n", + "combo_gender.grid(row=4, column=1, padx=15, pady=10, sticky=\"ew\")\n", + "\n", + "entry_diagnosis = tk.Entry(container)\n", + "entry_diagnosis.grid(row=5, column=1, padx=15, pady=10, sticky=\"ew\")\n", + "\n", + "entry_bp = tk.Entry(container)\n", + "entry_bp.grid(row=6, column=1, padx=15, pady=10, sticky=\"ew\")\n", + "\n", + "text_notes = tk.Text(container, height=5)\n", + "text_notes.grid(row=7, column=1, padx=15, pady=10, sticky=\"ew\")\n", + "\n", + "entry_avgheartrate = tk.Entry(container)\n", + "entry_avgheartrate.insert(8,\"120 bpm\")\n", + "entry_avgheartrate.grid(row=8, column=1, padx=15, pady=10, sticky=\"ew\")\n", + "entry_avgheartrate.config(state=\"readonly\") # Makes it read-only\n", + "\n", + "entry_avgspo2 = tk.Entry(container)\n", + "entry_avgspo2.insert(9,\"Test\")\n", + "entry_avgspo2.grid(row=9, column=1, padx=15, pady=10, sticky=\"ew\")\n", + "entry_avgspo2.config(state=\"readonly\") # Makes it read-only\n", + "\n", + "entry_avgsleephours = tk.Entry(container)\n", + "entry_avgsleephours.insert(10,\"Test Sleep Hours\")\n", + "entry_avgsleephours.grid(row=10, column=1, padx=15, pady=10, sticky=\"ew\")\n", + "entry_avgsleephours.config(state=\"readonly\") # Makes it read-only\n", + "\n", + "entry_avgcalorieintake = tk.Entry(container)\n", + "entry_avgcalorieintake.insert(11,\"Test Calorie Intake\")\n", + "entry_avgcalorieintake.grid(row=11, column=1, padx=15, pady=10, sticky=\"ew\")\n", + "entry_avgcalorieintake.config(state=\"readonly\") # Makes it read-only\n", + "\n", + "entry_usualdietcompliance = tk.Entry(container)\n", + "entry_usualdietcompliance.insert(12,\"Test Usual Diet Compliance\")\n", + "entry_usualdietcompliance.grid(row=12, column=1, padx=15, pady=10, sticky=\"ew\")\n", + "entry_usualdietcompliance.config(state=\"readonly\") # Makes it read-only\n", + "\n", + " \n", + "# --- Buttons ---\n", + "btn_frame = tk.Frame(form, bg=\"#f4f6f7\")\n", + "btn_frame.pack(pady=15)\n", + "\n", + "\n", + "#tk.Button(btn_frame, text=\"Submit\", command=submit_form)\\\n", + " #.grid(row=0, column=0, padx=15)\n", + "\n", + "tk.Button(btn_frame, text=\"Close\", command=close_form)\\\n", + " .grid(row=0, column=1, padx=15)\n", + " \n", + "tk.Button(btn_frame, text=\"Record\", command=record)\\\n", + " .grid(row=1, column=0, padx=15)\n", + " \n", + "tk.Button(btn_frame, text=\"Generate PDF\", command=generate_pdf)\\\n", + " .grid(row=1, column=0, padx=15)\n", + "\n", + "tk.Button(btn_frame, text=\"Start Recording\", command=start_listening)\\\n", + " .grid(row=1, column=1, padx=15)\n", + "\n", + "tk.Button(btn_frame, text=\"Stop Recording\", command=stop_listening)\\\n", + " .grid(row=1, column=2, padx=15)\n", + "\n", + "root.mainloop()\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "f3d16eeb-324c-4fb0-b295-5bc92a6b835a", + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python [conda env:base] *", + "language": "python", + "name": "conda-base-py" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.12.3" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/AI Guardian/Gopher_AI_FromPDFSp2TextStartstop_v2.ipynb b/AI Guardian/Gopher_AI_FromPDFSp2TextStartstop_v2.ipynb new file mode 100644 index 000000000..1fd92a899 --- /dev/null +++ b/AI Guardian/Gopher_AI_FromPDFSp2TextStartstop_v2.ipynb @@ -0,0 +1,531 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": null, + "id": "acb2bd8c-ea7c-4dd5-8d22-8060cc1bf9ce", + "metadata": {}, + "outputs": [], + "source": [ + "import json\n", + "import tkinter as tk\n", + "from tkinter import filedialog, messagebox, ttk\n", + "from deep_translator import GoogleTranslator\n", + "from datetime import datetime, timedelta, timezone\n", + "import re\n", + "import wave\n", + "import json\n", + "from vosk import Model, KaldiRecognizer\n", + "import threading\n", + "import wave\n", + "import queue\n", + "\n", + "# Declare model path\n", + "model_path = r\"C:\\Users\\graem\\Downloads\\vosk-model-small-en-us-0.15\\vosk-model-small-en-us-0.15\"\n", + "model = Model(model_path)\n", + "\n", + "# Global control flag\n", + "global listening\n", + "listening = False\n", + "\n", + "global audio_frames\n", + "audio_frames = []\n", + "q = queue.Queue()\n", + "\n", + "# Supported languages (can be expanded)\n", + "languages = {\n", + " \"English\": \"en\",\n", + " \"Spanish\": \"es\",\n", + " \"French\": \"fr\",\n", + " \"German\": \"de\",\n", + " \"Chinese (Simplified)\": \"zh-CN\",\n", + " \"Japanese\": \"ja\",\n", + " \"Korean\": \"ko\",\n", + " \"Arabic\": \"ar\",\n", + " \"Hindi\": \"hi\",\n", + " \"Italian\": \"it\"\n", + "}\n", + "\n", + "class MedicalApp:\n", + " def __init__(self, root):\n", + " self.root = root\n", + " self.root.title(\"Electronic Medical Record System\")\n", + " self.root.geometry(\"800x600\")\n", + "\n", + " self.data = {}\n", + "\n", + " # Style\n", + " style = ttk.Style()\n", + " style.theme_use(\"clam\")\n", + "\n", + " # Top buttons\n", + " top_frame = ttk.Frame(root, padding=10)\n", + " top_frame.pack(fill=\"x\")\n", + "\n", + " now = datetime.now().strftime(\"%d/%m/%Y %H:%M:%S\")\n", + "\n", + " ttk.Button(top_frame, text=\"Load Record\", command=self.load_json).pack(side=\"left\", padx=5)\n", + " ttk.Button(top_frame, text=\"Save Record\", command=self.save_json).pack(side=\"left\", padx=5)\n", + " ttk.Label(top_frame, text=now).pack(side=\"left\", padx=5)\n", + "\n", + " # Tabs\n", + " self.notebook = ttk.Notebook(root)\n", + " self.notebook.pack(fill=\"both\", expand=True, padx=10, pady=10)\n", + "\n", + " self.create_patient_tab()\n", + " self.create_medical_tab()\n", + " self.create_nursesnotes_tab()\n", + " self.create_languagetranslator_tab()\n", + "\n", + " # -------------------------\n", + " # Patient Info Tab\n", + " # -------------------------\n", + " def create_patient_tab(self):\n", + " self.patient_tab = ttk.Frame(self.notebook)\n", + " self.notebook.add(self.patient_tab, text=\"Patient Details\")\n", + "\n", + " frame = ttk.LabelFrame(self.patient_tab, text=\"Personal Information\", padding=15)\n", + " frame.pack(fill=\"x\", padx=10, pady=10)\n", + "\n", + " self.fields = {}\n", + "\n", + " labels = [\n", + " (\"Patient ID\", \"patient_id\"),\n", + " (\"First Name\", \"first_name\"),\n", + " (\"Last Name\", \"last_name\"),\n", + " (\"Email\", \"email\"),\n", + " (\"Phone\", \"phone\")\n", + " ]\n", + "\n", + " for i, (label, key) in enumerate(labels):\n", + " ttk.Label(frame, text=label).grid(row=i, column=0, sticky=\"w\", pady=5)\n", + " entry = ttk.Entry(frame, width=40)\n", + " entry.grid(row=i, column=1, pady=5)\n", + " self.fields[key] = entry\n", + "\n", + " # 👇 Add Gender Combobox (next row)\n", + " gender_row = len(labels)\n", + " \n", + " ttk.Label(frame, text=\"Gender\").grid(row=gender_row, column=0, sticky=\"w\", pady=5)\n", + " \n", + " self.gender_var = tk.StringVar()\n", + " gender_combo = ttk.Combobox(\n", + " frame,\n", + " textvariable=self.gender_var,\n", + " values=[\"Male\", \"Female\", \"Other\"],\n", + " state=\"readonly\",\n", + " width=37\n", + " )\n", + " gender_combo.grid(row=gender_row, column=1, pady=5)\n", + " gender_combo.set(\"Male\") # default value\n", + " \n", + " # Store it like other fields (optional but recommended)\n", + " self.fields[\"gender\"] = gender_combo\n", + "\n", + " # -------------------------\n", + " # Medical Tab\n", + " # -------------------------\n", + " def create_medical_tab(self):\n", + " self.medical_tab = ttk.Frame(self.notebook)\n", + " self.notebook.add(self.medical_tab, text=\"Medical Data\")\n", + "\n", + " # Conditions\n", + " cond_frame = ttk.LabelFrame(self.medical_tab, text=\"Conditions\", padding=10)\n", + " cond_frame.pack(fill=\"both\", expand=True, padx=10, pady=5)\n", + "\n", + " self.conditions_list = tk.Listbox(cond_frame, height=6)\n", + " self.conditions_list.pack(side=\"left\", fill=\"both\", expand=True)\n", + "\n", + " ttk.Button(cond_frame, text=\"Add\", command=self.add_condition).pack(side=\"right\", padx=5)\n", + "\n", + " # Medications\n", + " med_frame = ttk.LabelFrame(self.medical_tab, text=\"Medications\", padding=10)\n", + " med_frame.pack(fill=\"both\", expand=True, padx=10, pady=5)\n", + "\n", + " self.medications_list = tk.Listbox(med_frame, height=6)\n", + " self.medications_list.pack(side=\"left\", fill=\"both\", expand=True)\n", + "\n", + " ttk.Button(med_frame, text=\"Add\", command=self.add_medication).pack(side=\"right\", padx=5)\n", + "\n", + " # -------------------------\n", + " # Nurses Notes Tab\n", + " # -------------------------\n", + " def create_nursesnotes_tab(self):\n", + " self.nursesnotes_tab = ttk.Frame(self.notebook)\n", + " self.notebook.add(self.nursesnotes_tab, text=\"Nursing Notes\")\n", + " \n", + " main_frame = ttk.Frame(self.nursesnotes_tab, padding=10)\n", + " main_frame.pack(fill=\"both\", expand=True)\n", + "\n", + " # Nursing notes text box\n", + " ttk.Label(main_frame, text=\"Nursing Notes:\").pack(anchor=\"w\")\n", + " self.input_text = tk.Text(main_frame, height=10)\n", + " self.input_text.pack(fill=\"x\", pady=5)\n", + "\n", + " ttk.Button(main_frame, text=\"Start Recording\", command=self.start_listening).pack(side=\"left\", padx=5)\n", + " ttk.Button(main_frame, text=\"Stop Recording\", command=self.stop_listening).pack(side=\"left\", padx=5)\n", + " \n", + " # -------------------------\n", + " # Language Translator Tab\n", + " # -------------------------\n", + " def create_languagetranslator_tab(self):\n", + " self.languagetranslator_tab = ttk.Frame(self.notebook)\n", + " self.notebook.add(self.languagetranslator_tab, text=\"Language Translator\")\n", + " \n", + " main_frame = ttk.Frame(self.languagetranslator_tab, padding=10)\n", + " main_frame.pack(fill=\"both\", expand=True)\n", + " \n", + " # Input Text\n", + " ttk.Label(main_frame, text=\"Input Text:\").pack(anchor=\"w\")\n", + " self.input_text = tk.Text(main_frame, height=6)\n", + " self.input_text.pack(fill=\"x\", pady=5)\n", + " \n", + " # Language selection frame (GRID ONLY here)\n", + " control_frame = ttk.Frame(main_frame)\n", + " control_frame.pack(pady=10)\n", + " \n", + " ttk.Label(control_frame, text=\"From:\").grid(row=0, column=0, padx=5)\n", + " self.from_lang = ttk.Combobox(control_frame, values=list(languages.keys()), state=\"readonly\")\n", + " self.from_lang.grid(row=0, column=1, padx=5)\n", + " self.from_lang.set(\"English\")\n", + " \n", + " ttk.Label(control_frame, text=\"To:\").grid(row=0, column=2, padx=5)\n", + " self.to_lang = ttk.Combobox(control_frame, values=list(languages.keys()), state=\"readonly\")\n", + " self.to_lang.grid(row=0, column=3, padx=5)\n", + " self.to_lang.set(\"Spanish\")\n", + " \n", + " ttk.Button(main_frame, text=\"Translate\", command=self.translate_text).pack(pady=10)\n", + " \n", + " # Output Text\n", + " ttk.Label(main_frame, text=\"Translated Text:\").pack(anchor=\"w\")\n", + " self.output_text = tk.Text(main_frame, height=6)\n", + " self.output_text.pack(fill=\"x\", pady=5)\n", + "\n", + " # -------------------------\n", + " # Language Translator Tab Button\n", + " # -------------------------\n", + " def translate_text(self):\n", + " try:\n", + " input_value = self.input_text.get(\"1.0\", tk.END).strip()\n", + " if not input_value:\n", + " messagebox.showwarning(\"Warning\", \"Please enter text to translate.\")\n", + " return\n", + "\n", + " from_language = languages[self.from_lang.get()]\n", + " to_language = languages[self.to_lang.get()]\n", + "\n", + " translated = GoogleTranslator(source=from_language, target=to_language).translate(input_value)\n", + "\n", + " self.output_text.delete(\"1.0\", tk.END)\n", + " self.output_text.insert(tk.END, translated)\n", + "\n", + " except Exception as e:\n", + " messagebox.showerror(\"Error\", str(e))\n", + "\n", + " # -------------------------\n", + " # Validation\n", + " # -------------------------\n", + " def validate(self):\n", + " email = self.fields[\"email\"].get()\n", + " phone = self.fields[\"phone\"].get()\n", + "\n", + " if not re.match(r'^[\\w\\.-]+@[\\w\\.-]+\\.\\w+$', email):\n", + " messagebox.showerror(\"Error\", \"Invalid email\")\n", + " return False\n", + "\n", + " if not re.match(r'^\\+?\\d[\\d\\- ]+$', phone):\n", + " messagebox.showerror(\"Error\", \"Invalid phone\")\n", + " return False\n", + "\n", + " return True\n", + "\n", + " # -------------------------\n", + " # Load JSON\n", + " # -------------------------\n", + " def load_json(self):\n", + " path = filedialog.askopenfilename(filetypes=[(\"JSON\", \"*.json\")])\n", + " if not path:\n", + " return\n", + "\n", + " with open(path) as f:\n", + " self.data = json.load(f)\n", + "\n", + " # Clear fields\n", + " for field in self.fields.values():\n", + " field.delete(0, tk.END)\n", + "\n", + " self.conditions_list.delete(0, tk.END)\n", + " self.medications_list.delete(0, tk.END)\n", + "\n", + " # Populate\n", + " pd = self.data[\"personal_details\"]\n", + " contact = pd[\"contact\"]\n", + "\n", + " self.fields[\"patient_id\"].insert(0, self.data[\"patient_id\"])\n", + " self.fields[\"first_name\"].insert(0, pd[\"first_name\"])\n", + " self.fields[\"last_name\"].insert(0, pd[\"last_name\"])\n", + " self.fields[\"email\"].insert(0, contact[\"email\"])\n", + " self.fields[\"phone\"].insert(0, contact[\"phone\"])\n", + "\n", + " for c in self.data[\"medical_history\"][\"conditions\"]:\n", + " self.conditions_list.insert(tk.END, f\"{c['name']} ({c['status']})\")\n", + "\n", + " for m in self.data[\"medications\"]:\n", + " self.medications_list.insert(tk.END, f\"{m['name']} - {m['dosage']}\")\n", + "\n", + " # -------------------------\n", + " # Add Condition\n", + " # -------------------------\n", + " def add_condition(self):\n", + " win = tk.Toplevel(self.root)\n", + " win.title(\"Add Condition\")\n", + "\n", + " ttk.Label(win, text=\"Name\").pack()\n", + " name = ttk.Entry(win)\n", + " name.pack()\n", + "\n", + " ttk.Label(win, text=\"Status\").pack()\n", + " status = ttk.Entry(win)\n", + " status.pack()\n", + "\n", + " def save():\n", + " self.conditions_list.insert(tk.END, f\"{name.get()} ({status.get()})\")\n", + " win.destroy()\n", + "\n", + " ttk.Button(win, text=\"Save\", command=save).pack()\n", + "\n", + " # -------------------------\n", + " # Add Medication\n", + " # -------------------------\n", + " def add_medication(self):\n", + " win = tk.Toplevel(self.root)\n", + " win.title(\"Add Medication\")\n", + "\n", + " ttk.Label(win, text=\"Name\").pack()\n", + " name = ttk.Entry(win)\n", + " name.pack()\n", + "\n", + " ttk.Label(win, text=\"Dosage\").pack()\n", + " dosage = ttk.Entry(win)\n", + " dosage.pack()\n", + "\n", + " def save():\n", + " self.medications_list.insert(tk.END, f\"{name.get()} - {dosage.get()}\")\n", + " win.destroy()\n", + "\n", + " ttk.Button(win, text=\"Save\", command=save).pack()\n", + "\n", + " # -------------------------\n", + " # Save JSON\n", + " # -------------------------\n", + " def save_json(self):\n", + " if not self.validate():\n", + " return\n", + "\n", + " self.data[\"patient_id\"] = self.fields[\"patient_id\"].get()\n", + "\n", + " pd = self.data[\"personal_details\"]\n", + " contact = pd[\"contact\"]\n", + "\n", + " pd[\"first_name\"] = self.fields[\"first_name\"].get()\n", + " pd[\"last_name\"] = self.fields[\"last_name\"].get()\n", + " pd[\"gender\"] = self.fields[\"gender\"].get()\n", + " contact[\"email\"] = self.fields[\"email\"].get()\n", + " contact[\"phone\"] = self.fields[\"phone\"].get()\n", + "\n", + " # Conditions\n", + " conditions = []\n", + " for item in self.conditions_list.get(0, tk.END):\n", + " name, status = item.split(\" (\")\n", + " conditions.append({\n", + " \"name\": name,\n", + " \"status\": status.replace(\")\", \"\"),\n", + " \"diagnosed_date\": \"\"\n", + " })\n", + "\n", + " self.data[\"medical_history\"][\"conditions\"] = conditions\n", + "\n", + " # Medications\n", + " meds = []\n", + " for item in self.medications_list.get(0, tk.END):\n", + " name, dosage = item.split(\" - \")\n", + " meds.append({\n", + " \"name\": name,\n", + " \"dosage\": dosage,\n", + " \"frequency\": \"\",\n", + " \"start_date\": \"\"\n", + " })\n", + "\n", + " self.data[\"medications\"] = meds\n", + "\n", + " path = filedialog.asksaveasfilename(defaultextension=\".json\")\n", + " if not path:\n", + " return\n", + "\n", + " with open(path, \"w\") as f:\n", + " json.dump(self.data, f, indent=4)\n", + "\n", + " messagebox.showinfo(\"Saved\", \"Record saved successfully\")\n", + "\n", + " def record():\n", + " # ----------------------------\n", + " # Settings\n", + " # ----------------------------\n", + " duration = 10 # seconds to record\n", + " fs = 16000 # sampling frequency (Hz)\n", + " filename = \"recorded_audio.wav\" # output file\n", + " \n", + " # ----------------------------\n", + " # Record audio\n", + " # ----------------------------\n", + " print(\"Recording...\")\n", + " recording = sd.rec(int(duration * fs), samplerate=fs, channels=1)\n", + " sd.wait() # Wait until recording is finished\n", + " print(\"Recording finished!\")\n", + " \n", + " # ----------------------------\n", + " # Save as WAV file\n", + " # ----------------------------\n", + " # Convert to int16 for WAV format\n", + " write(filename, fs, np.int16(recording * 32767))\n", + " print(f\"Audio saved as {filename}\")\n", + " speech_to_text()\n", + "\n", + " def speech_to_text():\n", + " # Load the Vosk model\n", + " model_path = r\"C:\\Users\\graem\\Downloads\\vosk-model-small-en-us-0.15\\vosk-model-small-en-us-0.15\"\n", + " model = Model(model_path)\n", + " \n", + " # Open recorded audio\n", + " wf = wave.open(\"recorded.wav\", \"rb\")\n", + " \n", + " # Initialize recognizer\n", + " rec = KaldiRecognizer(model, wf.getframerate())\n", + " \n", + " transcription = \"\"\n", + " \n", + " # Transcribe\n", + " while True:\n", + " data = wf.readframes(4000)\n", + " \n", + " if len(data) == 0:\n", + " break\n", + " \n", + " if rec.AcceptWaveform(data):\n", + " result = json.loads(rec.Result())\n", + " transcription += result.get(\"text\", \"\") + \" \"\n", + " \n", + " # Final result\n", + " result = json.loads(rec.FinalResult())\n", + " transcription += result.get(\"text\", \"\")\n", + " \n", + " text_notes.insert(tk.END, transcription)\n", + " \n", + " return transcription\n", + "\n", + " #---------------------------------------------------------------------------------\n", + " # Function for speech\n", + " def audio_thread():\n", + " global listening, audio_frames\n", + " \n", + " rec = KaldiRecognizer(model, 16000)\n", + " audio_frames = []\n", + " \n", + " def callback(indata, frames, time, status):\n", + " if listening:\n", + " data = bytes(indata)\n", + " audio_frames.append(data)\n", + " \n", + " if rec.AcceptWaveform(data):\n", + " result = json.loads(rec.Result())\n", + " q.put(result.get(\"text\", \"\"))\n", + " \n", + " with sd.RawInputStream(samplerate=16000,\n", + " blocksize=8000,\n", + " dtype='int16',\n", + " channels=1,\n", + " callback=callback):\n", + " while listening:\n", + " sd.sleep(100) # IMPORTANT: prevents CPU lock\n", + "\n", + " #---------------------------------------------------------------------------------\n", + " def update_textbox():\n", + " \"\"\"Safely update UI from queue\"\"\"\n", + " try:\n", + " while True:\n", + " text = q.get_nowait()\n", + " text_notes.insert(tk.END, text + \"\\n\")\n", + " except queue.Empty:\n", + " pass\n", + " root.after(100, update_textbox)\n", + " \n", + " #---------------------------------------------------------------------------------\n", + " def save_wav(filename=\"recorded.wav\"):\n", + " wf = wave.open(filename, 'wb')\n", + " wf.setnchannels(1)\n", + " wf.setsampwidth(2)\n", + " wf.setframerate(16000)\n", + " wf.writeframes(b''.join(audio_frames))\n", + " wf.close()\n", + " \n", + " #---------------------------------------------------------------------------------\n", + " # Start Recording\n", + " def start_listening():\n", + " global listening\n", + " listening = True\n", + " print(\"Listening...\")\n", + " threading.Thread(target=audio_thread, daemon=True).start()\n", + " \n", + " #---------------------------------------------------------------------------------\n", + " # Stop Recording\n", + " def stop_listening():\n", + " global listening\n", + " listening = False\n", + " \n", + " final = json.loads(KaldiRecognizer(model, 16000).FinalResult())\n", + " q.put(\"FINAL: \" + final.get(\"text\", \"\"))\n", + " \n", + " save_wav()\n", + " print(\"Saved to recorded.wav\")\n", + " \n", + " speech_to_text()\n", + "\n", + "\n", + "\n", + "if __name__ == \"__main__\":\n", + " root = tk.Tk()\n", + " app = MedicalApp(root)\n", + " root.mainloop()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "bb6860bc-ca7f-4f49-9021-1a18d91292c3", + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python [conda env:base] *", + "language": "python", + "name": "conda-base-py" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.12.3" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/AI Guardian/Gopher_AI_FromPDFSp2TextStartstop_v3.ipynb b/AI Guardian/Gopher_AI_FromPDFSp2TextStartstop_v3.ipynb new file mode 100644 index 000000000..bea9f835c --- /dev/null +++ b/AI Guardian/Gopher_AI_FromPDFSp2TextStartstop_v3.ipynb @@ -0,0 +1,1278 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": 32, + "id": "5abbf1de-ad66-4c72-b177-491520b24b94", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Listening...\n", + "Saved to recorded.wav\n", + "facing the recording saying comes out correctly\n" + ] + } + ], + "source": [ + "import json\n", + "import tkinter as tk\n", + "from tkinter import filedialog, messagebox, ttk\n", + "from deep_translator import GoogleTranslator\n", + "from datetime import datetime\n", + "import re\n", + "import wave\n", + "import queue\n", + "import threading\n", + "import sounddevice as sd\n", + "\n", + "from vosk import Model, KaldiRecognizer\n", + "from textblob import TextBlob\n", + "\n", + "# =========================================================\n", + "# PDF IMPORTS\n", + "# =========================================================\n", + "\n", + "from reportlab.lib.pagesizes import letter\n", + "from reportlab.platypus import (\n", + " SimpleDocTemplate,\n", + " Paragraph,\n", + " Spacer\n", + ")\n", + "from reportlab.lib.styles import getSampleStyleSheet\n", + "\n", + "# =========================================================\n", + "# VOSK MODEL\n", + "# =========================================================\n", + "\n", + "model_path = r\"C:\\Users\\graem\\Downloads\\vosk-model-small-en-us-0.15\\vosk-model-small-en-us-0.15\"\n", + "\n", + "model = Model(model_path)\n", + "\n", + "# =========================================================\n", + "# GLOBALS\n", + "# =========================================================\n", + "\n", + "listening = False\n", + "audio_frames = []\n", + "q = queue.Queue()\n", + "\n", + "# Supported languages (can be expanded)\n", + "languages = {\n", + " \"English\": \"en\",\n", + " \"Spanish\": \"es\",\n", + " \"French\": \"fr\",\n", + " \"German\": \"de\",\n", + " \"Chinese (Simplified)\": \"zh-CN\",\n", + " \"Japanese\": \"ja\",\n", + " \"Korean\": \"ko\",\n", + " \"Arabic\": \"ar\",\n", + " \"Hindi\": \"hi\",\n", + " \"Italian\": \"it\"\n", + "}\n", + "\n", + "# =========================================================\n", + "# MAIN APPLICATION\n", + "# =========================================================\n", + "\n", + "class MedicalApp:\n", + "\n", + " def __init__(self, root):\n", + "\n", + " self.root = root\n", + "\n", + " self.root.title(\"Electronic Medical Record System\")\n", + " self.root.geometry(\"1000x700\")\n", + "\n", + " # =====================================================\n", + " # GLOBAL UI SETTINGS\n", + " # =====================================================\n", + "\n", + " root.option_add(\"*Font\", (\"Segoe UI\", 10))\n", + " root.option_add(\"*Background\", \"#66B2B2\")\n", + " root.option_add(\"*Foreground\", \"white\")\n", + "\n", + " # =====================================================\n", + " # COLOURS\n", + " # =====================================================\n", + "\n", + " self.PRIMARY = \"#66B2B2\"\n", + " self.DARK = \"#4C9999\"\n", + " self.LIGHT = \"#A0D6D6\"\n", + " self.WHITE = \"white\"\n", + " self.TEXT = \"black\"\n", + "\n", + " self.root.configure(bg=self.PRIMARY)\n", + "\n", + " # =====================================================\n", + " # STYLE\n", + " # =====================================================\n", + "\n", + " style = ttk.Style()\n", + "\n", + " style.theme_use(\"clam\")\n", + "\n", + " style.configure(\n", + " \".\",\n", + " background=self.PRIMARY,\n", + " foreground=self.WHITE,\n", + " font=(\"Segoe UI\", 10)\n", + " )\n", + "\n", + " style.configure(\n", + " \"TFrame\",\n", + " background=self.PRIMARY\n", + " )\n", + "\n", + " style.configure(\n", + " \"TLabelframe\",\n", + " background=self.PRIMARY,\n", + " bordercolor=self.WHITE,\n", + " borderwidth=2,\n", + " relief=\"solid\"\n", + " )\n", + "\n", + " style.configure(\n", + " \"TLabelframe.Label\",\n", + " background=self.PRIMARY,\n", + " foreground=self.WHITE,\n", + " font=(\"Segoe UI\", 10, \"bold\")\n", + " )\n", + "\n", + " style.configure(\n", + " \"TLabel\",\n", + " background=self.PRIMARY,\n", + " foreground=self.WHITE,\n", + " font=(\"Segoe UI\", 10)\n", + " )\n", + "\n", + " style.configure(\n", + " \"TButton\",\n", + " background=self.LIGHT,\n", + " foreground=self.TEXT,\n", + " font=(\"Segoe UI\", 10, \"bold\"),\n", + " padding=6\n", + " )\n", + "\n", + " style.map(\n", + " \"TButton\",\n", + " background=[(\"active\", self.DARK)],\n", + " foreground=[(\"active\", self.WHITE)]\n", + " )\n", + "\n", + " style.configure(\n", + " \"TNotebook\",\n", + " background=self.PRIMARY\n", + " )\n", + "\n", + " style.configure(\n", + " \"TNotebook.Tab\",\n", + " background=self.LIGHT,\n", + " foreground=self.TEXT,\n", + " font=(\"Segoe UI\", 10, \"bold\"),\n", + " padding=[12, 6]\n", + " )\n", + "\n", + " style.map(\n", + " \"TNotebook.Tab\",\n", + " background=[(\"selected\", self.DARK)],\n", + " foreground=[(\"selected\", self.WHITE)]\n", + " )\n", + "\n", + " style.configure(\n", + " \"TCombobox\",\n", + " fieldbackground=self.WHITE,\n", + " background=self.LIGHT,\n", + " foreground=self.TEXT\n", + " )\n", + "\n", + " # =====================================================\n", + " # BUTTON STYLE\n", + " # =====================================================\n", + "\n", + " self.button_style = {\n", + " \"font\": (\"Segoe UI\", 10, \"bold\"),\n", + " \"bg\": self.LIGHT,\n", + " \"fg\": self.TEXT,\n", + " \"activebackground\": self.DARK,\n", + " \"activeforeground\": self.WHITE,\n", + " \"relief\": \"solid\",\n", + " \"bd\": 1,\n", + " \"highlightthickness\": 2,\n", + " \"highlightbackground\": self.WHITE,\n", + " \"highlightcolor\": self.WHITE,\n", + " \"cursor\": \"hand2\",\n", + " \"padx\": 10,\n", + " \"pady\": 5\n", + " }\n", + "\n", + " # =====================================================\n", + " # TOP FRAME\n", + " # =====================================================\n", + "\n", + " top_frame = tk.Frame(\n", + " root,\n", + " bg=self.PRIMARY\n", + " )\n", + "\n", + " top_frame.pack(\n", + " fill=\"x\",\n", + " padx=10,\n", + " pady=10\n", + " )\n", + "\n", + " tk.Button(\n", + " top_frame,\n", + " text=\"Load Record\",\n", + " command=self.load_json,\n", + " **self.button_style\n", + " ).pack(side=\"left\", padx=5)\n", + "\n", + " tk.Button(\n", + " top_frame,\n", + " text=\"Save Record\",\n", + " command=self.save_json,\n", + " **self.button_style\n", + " ).pack(side=\"left\", padx=5)\n", + "\n", + " tk.Button(\n", + " top_frame,\n", + " text=\"Generate PDF\",\n", + " command=self.generate_pdf,\n", + " **self.button_style\n", + " ).pack(side=\"left\", padx=5)\n", + "\n", + " current_time = datetime.now().strftime(\n", + " \"%d/%m/%Y %H:%M:%S\"\n", + " )\n", + "\n", + " tk.Label(\n", + " top_frame,\n", + " text=current_time,\n", + " bg=self.PRIMARY,\n", + " fg=self.WHITE,\n", + " font=(\"Segoe UI\", 10, \"bold\")\n", + " ).pack(side=\"left\", padx=15)\n", + "\n", + " # =====================================================\n", + " # NOTEBOOK\n", + " # =====================================================\n", + "\n", + " self.notebook = ttk.Notebook(root)\n", + "\n", + " self.notebook.pack(\n", + " fill=\"both\",\n", + " expand=True,\n", + " padx=10,\n", + " pady=10\n", + " )\n", + "\n", + " self.create_patient_tab()\n", + " self.create_medical_tab()\n", + " self.create_notes_tab()\n", + " self.create_translator_tab()\n", + "\n", + " # Start queue updater\n", + " self.update_textbox()\n", + "\n", + " # =========================================================\n", + " # STYLED ENTRY\n", + " # =========================================================\n", + "\n", + " def styled_entry(self, parent, width=40):\n", + "\n", + " return tk.Entry(\n", + " parent,\n", + " width=width,\n", + " font=(\"Segoe UI\", 10),\n", + " bg=\"white\",\n", + " fg=\"black\",\n", + " relief=\"flat\",\n", + " bd=0,\n", + " highlightthickness=2,\n", + " highlightbackground=\"white\",\n", + " highlightcolor=\"white\",\n", + " insertbackground=\"black\"\n", + " )\n", + "\n", + " # =========================================================\n", + " # STYLED TEXT\n", + " # =========================================================\n", + "\n", + " def styled_text(self, parent, height=6):\n", + "\n", + " return tk.Text(\n", + " parent,\n", + " height=height,\n", + " font=(\"Segoe UI\", 10),\n", + " bg=\"white\",\n", + " fg=\"black\",\n", + " relief=\"flat\",\n", + " bd=0,\n", + " highlightthickness=2,\n", + " highlightbackground=\"white\",\n", + " highlightcolor=\"white\",\n", + " insertbackground=\"black\"\n", + " )\n", + "\n", + " # =========================================================\n", + " # STYLED LISTBOX\n", + " # =========================================================\n", + "\n", + " def styled_listbox(self, parent, height=6):\n", + "\n", + " return tk.Listbox(\n", + " parent,\n", + " height=height,\n", + " font=(\"Segoe UI\", 10),\n", + " bg=\"white\",\n", + " fg=\"black\",\n", + " relief=\"flat\",\n", + " bd=0,\n", + " highlightthickness=2,\n", + " highlightbackground=\"white\",\n", + " highlightcolor=\"white\",\n", + " selectbackground=self.DARK,\n", + " selectforeground=\"white\"\n", + " )\n", + "\n", + " # =========================================================\n", + " # STYLE TOPLEVEL\n", + " # =========================================================\n", + "\n", + " def style_toplevel(self, window):\n", + "\n", + " window.configure(bg=self.PRIMARY)\n", + "\n", + " # =========================================================\n", + " # PATIENT TAB\n", + " # =========================================================\n", + "\n", + " def create_patient_tab(self):\n", + "\n", + " self.patient_tab = ttk.Frame(self.notebook)\n", + "\n", + " self.notebook.add(\n", + " self.patient_tab,\n", + " text=\"Patient Details\"\n", + " )\n", + "\n", + " frame = ttk.LabelFrame(\n", + " self.patient_tab,\n", + " text=\"Personal Information\",\n", + " padding=20\n", + " )\n", + "\n", + " frame.pack(\n", + " fill=\"x\",\n", + " padx=10,\n", + " pady=10\n", + " )\n", + "\n", + " self.fields = {}\n", + "\n", + " labels = [\n", + " (\"Patient ID\", \"patient_id\"),\n", + " (\"First Name\", \"first_name\"),\n", + " (\"Last Name\", \"last_name\"),\n", + " (\"Email\", \"email\"),\n", + " (\"Phone\", \"phone\")\n", + " ]\n", + "\n", + " for i, (label, key) in enumerate(labels):\n", + "\n", + " ttk.Label(\n", + " frame,\n", + " text=label\n", + " ).grid(\n", + " row=i,\n", + " column=0,\n", + " sticky=\"w\",\n", + " pady=12,\n", + " padx=10\n", + " )\n", + "\n", + " entry = self.styled_entry(frame)\n", + "\n", + " entry.grid(\n", + " row=i,\n", + " column=1,\n", + " pady=12,\n", + " padx=10,\n", + " ipady=5\n", + " )\n", + "\n", + " self.fields[key] = entry\n", + "\n", + " ttk.Label(\n", + " frame,\n", + " text=\"Gender\"\n", + " ).grid(\n", + " row=len(labels),\n", + " column=0,\n", + " sticky=\"w\",\n", + " pady=12,\n", + " padx=10\n", + " )\n", + "\n", + " self.gender_var = tk.StringVar()\n", + "\n", + " gender_combo = ttk.Combobox(\n", + " frame,\n", + " textvariable=self.gender_var,\n", + " values=[\"Male\", \"Female\", \"Other\"],\n", + " state=\"readonly\",\n", + " width=37\n", + " )\n", + "\n", + " gender_combo.grid(\n", + " row=len(labels),\n", + " column=1,\n", + " pady=12,\n", + " padx=10\n", + " )\n", + "\n", + " gender_combo.set(\"Male\")\n", + "\n", + " # =========================================================\n", + " # MEDICAL TAB\n", + " # =========================================================\n", + "\n", + " def create_medical_tab(self):\n", + "\n", + " self.medical_tab = ttk.Frame(self.notebook)\n", + "\n", + " self.notebook.add(\n", + " self.medical_tab,\n", + " text=\"Medical Data\"\n", + " )\n", + "\n", + " # Conditions\n", + " cond_frame = ttk.LabelFrame(\n", + " self.medical_tab,\n", + " text=\"Conditions\",\n", + " padding=10\n", + " )\n", + "\n", + " cond_frame.pack(\n", + " fill=\"both\",\n", + " expand=True,\n", + " padx=10,\n", + " pady=5\n", + " )\n", + "\n", + " self.conditions_list = self.styled_listbox(\n", + " cond_frame,\n", + " height=6\n", + " )\n", + "\n", + " self.conditions_list.pack(\n", + " side=\"left\",\n", + " fill=\"both\",\n", + " expand=True\n", + " )\n", + "\n", + " tk.Button(\n", + " cond_frame,\n", + " text=\"Add\",\n", + " command=self.add_condition,\n", + " **self.button_style\n", + " ).pack(side=\"right\", padx=5)\n", + "\n", + " # Medications\n", + " med_frame = ttk.LabelFrame(\n", + " self.medical_tab,\n", + " text=\"Medications\",\n", + " padding=10\n", + " )\n", + "\n", + " med_frame.pack(\n", + " fill=\"both\",\n", + " expand=True,\n", + " padx=10,\n", + " pady=5\n", + " )\n", + "\n", + " self.medications_list = self.styled_listbox(\n", + " med_frame,\n", + " height=6\n", + " )\n", + "\n", + " self.medications_list.pack(\n", + " side=\"left\",\n", + " fill=\"both\",\n", + " expand=True\n", + " )\n", + "\n", + " tk.Button(\n", + " med_frame,\n", + " text=\"Add\",\n", + " command=self.add_medication,\n", + " **self.button_style\n", + " ).pack(side=\"right\", padx=5)\n", + "\n", + " # =========================================================\n", + " # NOTES TAB\n", + " # =========================================================\n", + "\n", + " def create_notes_tab(self):\n", + "\n", + " self.notes_tab = ttk.Frame(self.notebook)\n", + "\n", + " self.notebook.add(\n", + " self.notes_tab,\n", + " text=\"Nursing Notes\"\n", + " )\n", + "\n", + " frame = ttk.LabelFrame(\n", + " self.notes_tab,\n", + " text=\"Nursing Notes\",\n", + " padding=15\n", + " )\n", + "\n", + " frame.pack(\n", + " fill=\"both\",\n", + " expand=True,\n", + " padx=10,\n", + " pady=10\n", + " )\n", + "\n", + " self.notes_text = self.styled_text(\n", + " frame,\n", + " height=15\n", + " )\n", + "\n", + " self.notes_text.pack(\n", + " fill=\"both\",\n", + " expand=True\n", + " )\n", + "\n", + " tk.Button(\n", + " frame,\n", + " text=\"Start Recording\",\n", + " command=self.start_listening,\n", + " **self.button_style\n", + " ).pack(side=\"left\", padx=5, pady=10)\n", + "\n", + " tk.Button(\n", + " frame,\n", + " text=\"Stop Recording\",\n", + " command=self.stop_listening,\n", + " **self.button_style\n", + " ).pack(side=\"left\", padx=5, pady=10)\n", + "\n", + " # =========================================================\n", + " # TRANSLATOR TAB\n", + " # =========================================================\n", + "\n", + " # =========================================================\n", + "# TRANSLATOR TAB\n", + "# =========================================================\n", + "\n", + " def create_translator_tab(self):\n", + " \n", + " self.translator_tab = ttk.Frame(self.notebook)\n", + " \n", + " self.notebook.add(\n", + " self.translator_tab,\n", + " text=\"Language Translator\"\n", + " )\n", + " \n", + " frame = ttk.Frame(\n", + " self.translator_tab,\n", + " padding=15\n", + " )\n", + " \n", + " frame.pack(\n", + " fill=\"both\",\n", + " expand=True\n", + " )\n", + "\n", + " # =====================================================\n", + " # LANGUAGE SELECTION FRAME\n", + " # =====================================================\n", + " \n", + " lang_frame = ttk.Frame(frame)\n", + " \n", + " lang_frame.pack(\n", + " fill=\"x\",\n", + " pady=10\n", + " )\n", + "\n", + " # -------------------------\n", + " # FROM LANGUAGE\n", + " # -------------------------\n", + " \n", + " ttk.Label(\n", + " lang_frame,\n", + " text=\"From Language\"\n", + " ).grid(\n", + " row=0,\n", + " column=0,\n", + " padx=10,\n", + " pady=5,\n", + " sticky=\"w\"\n", + " )\n", + "\n", + " self.from_language = tk.StringVar()\n", + " \n", + " from_combo = ttk.Combobox(\n", + " lang_frame,\n", + " textvariable=self.from_language,\n", + " values=list(languages.keys()),\n", + " state=\"readonly\",\n", + " width=25\n", + " )\n", + " \n", + " from_combo.grid(\n", + " row=1,\n", + " column=0,\n", + " padx=10,\n", + " pady=5\n", + " )\n", + "\n", + " from_combo.set(\"English\")\n", + " \n", + " # -------------------------\n", + " # TO LANGUAGE\n", + " # -------------------------\n", + " \n", + " ttk.Label(\n", + " lang_frame,\n", + " text=\"To Language\"\n", + " ).grid(\n", + " row=0,\n", + " column=1,\n", + " padx=10,\n", + " pady=5,\n", + " sticky=\"w\"\n", + " )\n", + "\n", + " self.to_language = tk.StringVar()\n", + " \n", + " to_combo = ttk.Combobox(\n", + " lang_frame,\n", + " textvariable=self.to_language,\n", + " values=list(languages.keys()),\n", + " state=\"readonly\",\n", + " width=25\n", + " )\n", + " \n", + " to_combo.grid(\n", + " row=1,\n", + " column=1,\n", + " padx=10,\n", + " pady=5\n", + " )\n", + " \n", + " to_combo.set(\"Spanish\")\n", + "\n", + " # =====================================================\n", + " # INPUT TEXT\n", + " # =====================================================\n", + " \n", + " ttk.Label(\n", + " frame,\n", + " text=\"Input Text\"\n", + " ).pack(anchor=\"w\")\n", + " \n", + " self.input_text = self.styled_text(\n", + " frame,\n", + " height=6\n", + " )\n", + " \n", + " self.input_text.pack(\n", + " fill=\"x\",\n", + " pady=5\n", + " )\n", + "\n", + " # =====================================================\n", + " # TRANSLATE BUTTON\n", + " # =====================================================\n", + " \n", + " tk.Button(\n", + " frame,\n", + " text=\"Translate\",\n", + " command=self.translate_text,\n", + " **self.button_style\n", + " ).pack(pady=10)\n", + " \n", + " # =====================================================\n", + " # OUTPUT TEXT\n", + " # =====================================================\n", + " \n", + " ttk.Label(\n", + " frame,\n", + " text=\"Translated Text\"\n", + " ).pack(anchor=\"w\")\n", + " \n", + " self.output_text = self.styled_text(\n", + " frame,\n", + " height=6\n", + " )\n", + " \n", + " self.output_text.pack(\n", + " fill=\"x\",\n", + " pady=5\n", + " )\n", + "\n", + " # =========================================================\n", + " # ADD CONDITION\n", + " # =========================================================\n", + "\n", + " def add_condition(self):\n", + "\n", + " win = tk.Toplevel(self.root)\n", + "\n", + " win.title(\"Add Condition\")\n", + "\n", + " self.style_toplevel(win)\n", + "\n", + " ttk.Label(\n", + " win,\n", + " text=\"Name\"\n", + " ).pack(pady=5)\n", + "\n", + " name = self.styled_entry(win)\n", + "\n", + " name.pack(pady=5)\n", + "\n", + " ttk.Label(\n", + " win,\n", + " text=\"Status\"\n", + " ).pack(pady=5)\n", + "\n", + " status = self.styled_entry(win)\n", + "\n", + " status.pack(pady=5)\n", + "\n", + " def save():\n", + "\n", + " self.conditions_list.insert(\n", + " tk.END,\n", + " f\"{name.get()} ({status.get()})\"\n", + " )\n", + "\n", + " win.destroy()\n", + "\n", + " tk.Button(\n", + " win,\n", + " text=\"Save\",\n", + " command=save,\n", + " **self.button_style\n", + " ).pack(pady=10)\n", + "\n", + " # =========================================================\n", + " # ADD MEDICATION\n", + " # =========================================================\n", + "\n", + " def add_medication(self):\n", + "\n", + " win = tk.Toplevel(self.root)\n", + "\n", + " win.title(\"Add Medication\")\n", + "\n", + " self.style_toplevel(win)\n", + "\n", + " ttk.Label(\n", + " win,\n", + " text=\"Name\"\n", + " ).pack(pady=5)\n", + "\n", + " name = self.styled_entry(win)\n", + "\n", + " name.pack(pady=5)\n", + "\n", + " ttk.Label(\n", + " win,\n", + " text=\"Dosage\"\n", + " ).pack(pady=5)\n", + "\n", + " dosage = self.styled_entry(win)\n", + "\n", + " dosage.pack(pady=5)\n", + "\n", + " def save():\n", + "\n", + " self.medications_list.insert(\n", + " tk.END,\n", + " f\"{name.get()} - {dosage.get()}\"\n", + " )\n", + "\n", + " win.destroy()\n", + "\n", + " tk.Button(\n", + " win,\n", + " text=\"Save\",\n", + " command=save,\n", + " **self.button_style\n", + " ).pack(pady=10)\n", + "\n", + " # =========================================================\n", + " # TRANSLATE\n", + " # =========================================================\n", + "\n", + " def translate_text(self):\n", + " \n", + " try:\n", + " \n", + " text = self.input_text.get(\n", + " \"1.0\",\n", + " tk.END\n", + " ).strip()\n", + " \n", + " if not text:\n", + " \n", + " messagebox.showwarning(\n", + " \"Warning\",\n", + " \"Please enter text to translate.\"\n", + " )\n", + " \n", + " return\n", + " \n", + " # Get selected languages\n", + " from_lang_name = self.from_language.get()\n", + " to_lang_name = self.to_language.get()\n", + " \n", + " # Convert names to language codes\n", + " source_lang = languages[from_lang_name]\n", + " target_lang = languages[to_lang_name]\n", + " \n", + " # Translate\n", + " translated = GoogleTranslator(\n", + " source=source_lang,\n", + " target=target_lang\n", + " ).translate(text)\n", + " \n", + " self.output_text.delete(\n", + " \"1.0\",\n", + " tk.END\n", + " )\n", + " \n", + " self.output_text.insert(\n", + " tk.END,\n", + " translated\n", + " )\n", + " \n", + " except Exception as e:\n", + " \n", + " messagebox.showerror(\n", + " \"Translation Error\",\n", + " str(e)\n", + " )\n", + "\n", + " # =========================================================\n", + " # VALIDATE\n", + " # =========================================================\n", + "\n", + " def validate(self):\n", + "\n", + " if not self.fields[\"patient_id\"].get().strip():\n", + "\n", + " messagebox.showerror(\n", + " \"Validation Error\",\n", + " \"Patient ID required\"\n", + " )\n", + "\n", + " return False\n", + "\n", + " return True\n", + "\n", + " # =========================================================\n", + " # SAVE JSON\n", + " # =========================================================\n", + "\n", + " def save_json(self):\n", + "\n", + " if not self.validate():\n", + " return\n", + "\n", + " data = {\n", + " \"patient_id\": self.fields[\"patient_id\"].get(),\n", + " \"first_name\": self.fields[\"first_name\"].get(),\n", + " \"last_name\": self.fields[\"last_name\"].get(),\n", + " \"email\": self.fields[\"email\"].get(),\n", + " \"phone\": self.fields[\"phone\"].get(),\n", + " \"gender\": self.gender_var.get(),\n", + " \"notes\": self.notes_text.get(\"1.0\", tk.END).strip()\n", + " }\n", + "\n", + " path = filedialog.asksaveasfilename(\n", + " defaultextension=\".json\",\n", + " filetypes=[(\"JSON Files\", \"*.json\")]\n", + " )\n", + "\n", + " if not path:\n", + " return\n", + "\n", + " with open(path, \"w\") as f:\n", + "\n", + " json.dump(\n", + " data,\n", + " f,\n", + " indent=4\n", + " )\n", + "\n", + " messagebox.showinfo(\n", + " \"Saved\",\n", + " \"Patient record saved successfully!\"\n", + " )\n", + "\n", + " # =========================================================\n", + " # LOAD JSON\n", + " # =========================================================\n", + "\n", + " def load_json(self):\n", + "\n", + " path = filedialog.askopenfilename(\n", + " filetypes=[(\"JSON Files\", \"*.json\")]\n", + " )\n", + "\n", + " if not path:\n", + " return\n", + "\n", + " with open(path) as f:\n", + "\n", + " data = json.load(f)\n", + "\n", + " self.fields[\"patient_id\"].delete(0, tk.END)\n", + " self.fields[\"patient_id\"].insert(0, data.get(\"patient_id\", \"\"))\n", + "\n", + " self.fields[\"first_name\"].delete(0, tk.END)\n", + " self.fields[\"first_name\"].insert(0, data.get(\"first_name\", \"\"))\n", + "\n", + " self.fields[\"last_name\"].delete(0, tk.END)\n", + " self.fields[\"last_name\"].insert(0, data.get(\"last_name\", \"\"))\n", + "\n", + " self.fields[\"email\"].delete(0, tk.END)\n", + " self.fields[\"email\"].insert(0, data.get(\"email\", \"\"))\n", + "\n", + " self.fields[\"phone\"].delete(0, tk.END)\n", + " self.fields[\"phone\"].insert(0, data.get(\"phone\", \"\"))\n", + "\n", + " self.gender_var.set(\n", + " data.get(\"gender\", \"Male\")\n", + " )\n", + "\n", + " self.notes_text.delete(\n", + " \"1.0\",\n", + " tk.END\n", + " )\n", + "\n", + " self.notes_text.insert(\n", + " tk.END,\n", + " data.get(\"notes\", \"\")\n", + " )\n", + "\n", + " messagebox.showinfo(\n", + " \"Loaded\",\n", + " \"Patient record loaded successfully!\"\n", + " )\n", + "\n", + " # =========================================================\n", + " # GENERATE PDF\n", + " # =========================================================\n", + "\n", + " def generate_pdf(self):\n", + "\n", + " try:\n", + "\n", + " path = filedialog.asksaveasfilename(\n", + " defaultextension=\".pdf\",\n", + " filetypes=[(\"PDF Files\", \"*.pdf\")]\n", + " )\n", + "\n", + " if not path:\n", + " return\n", + "\n", + " doc = SimpleDocTemplate(\n", + " path,\n", + " pagesize=letter\n", + " )\n", + "\n", + " styles = getSampleStyleSheet()\n", + "\n", + " elements = []\n", + "\n", + " title = Paragraph(\n", + " \"Electronic Medical Record\",\n", + " styles[\"Title\"]\n", + " )\n", + "\n", + " elements.append(title)\n", + "\n", + " elements.append(\n", + " Spacer(1, 20)\n", + " )\n", + "\n", + " patient_info = f\"\"\"\n", + " Patient ID: {self.fields['patient_id'].get()}
\n", + " First Name: {self.fields['first_name'].get()}
\n", + " Last Name: {self.fields['last_name'].get()}
\n", + " Email: {self.fields['email'].get()}
\n", + " Phone: {self.fields['phone'].get()}
\n", + " \"\"\"\n", + "\n", + " elements.append(\n", + " Paragraph(\n", + " patient_info,\n", + " styles[\"BodyText\"]\n", + " )\n", + " )\n", + "\n", + " notes = self.notes_text.get(\n", + " \"1.0\",\n", + " tk.END\n", + " ).strip()\n", + "\n", + " elements.append(\n", + " Spacer(1, 20)\n", + " )\n", + "\n", + " elements.append(\n", + " Paragraph(\n", + " \"Nursing Notes\",\n", + " styles[\"Heading2\"]\n", + " )\n", + " )\n", + "\n", + " elements.append(\n", + " Paragraph(\n", + " notes,\n", + " styles[\"BodyText\"]\n", + " )\n", + " )\n", + "\n", + " doc.build(elements)\n", + "\n", + " messagebox.showinfo(\n", + " \"Success\",\n", + " \"PDF generated successfully!\"\n", + " )\n", + "\n", + " except Exception as e:\n", + "\n", + " messagebox.showerror(\n", + " \"PDF Error\",\n", + " str(e)\n", + " )\n", + "\n", + " # =========================================================\n", + " # SPEECH To TEXT\n", + " # =========================================================\n", + "\n", + " def speech_to_text(self):\n", + " # Load the Vosk model\n", + " model_path = r\"C:\\Users\\graem\\Downloads\\vosk-model-small-en-us-0.15\\vosk-model-small-en-us-0.15\"\n", + " model = Model(model_path)\n", + " \n", + " # Open recorded audio\n", + " wf = wave.open(\"recorded.wav\", \"rb\")\n", + " \n", + " # Initialize recognizer\n", + " rec = KaldiRecognizer(model, wf.getframerate())\n", + " \n", + " transcription = \"\"\n", + " \n", + " # Transcribe\n", + " while True:\n", + " data = wf.readframes(4000)\n", + " \n", + " if len(data) == 0:\n", + " break\n", + " \n", + " if rec.AcceptWaveform(data):\n", + " result = json.loads(rec.Result())\n", + " transcription += result.get(\"text\", \"\") + \" \"\n", + " \n", + " # Final result\n", + " result = json.loads(rec.FinalResult())\n", + " transcription += result.get(\"text\", \"\")\n", + "\n", + " corrected = TextBlob(transcription)\n", + "\n", + " print(corrected)\n", + " \n", + " #text_notes.insert(tk.END, transcription)\n", + " self.notes_text.insert(tk.END, corrected)\n", + " \n", + " return corrected\n", + " \n", + " \n", + " # =========================================================\n", + " # SPEECH THREAD\n", + " # =========================================================\n", + "\n", + " def audio_thread(self):\n", + "\n", + " global listening, audio_frames\n", + "\n", + " rec = KaldiRecognizer(\n", + " model,\n", + " 16000\n", + " )\n", + "\n", + " audio_frames = []\n", + "\n", + " def callback(indata, frames, time, status):\n", + "\n", + " if listening:\n", + "\n", + " data = bytes(indata)\n", + "\n", + " audio_frames.append(data)\n", + "\n", + " if rec.AcceptWaveform(data):\n", + "\n", + " result = json.loads(\n", + " rec.Result()\n", + " )\n", + "\n", + " q.put(\n", + " result.get(\"text\", \"\")\n", + " )\n", + "\n", + " with sd.RawInputStream(\n", + " samplerate=16000,\n", + " blocksize=8000,\n", + " dtype='int16',\n", + " channels=1,\n", + " callback=callback\n", + " ):\n", + "\n", + " while listening:\n", + " sd.sleep(100)\n", + "\n", + " # =========================================================\n", + " # UPDATE TEXTBOX\n", + " # =========================================================\n", + "\n", + " def update_textbox(self):\n", + "\n", + " try:\n", + "\n", + " while True:\n", + "\n", + " text = q.get_nowait()\n", + "\n", + " self.notes_text.insert(\n", + " tk.END,\n", + " text + \"\\n\"\n", + " )\n", + "\n", + " self.notes_text.see(tk.END)\n", + "\n", + " except queue.Empty:\n", + " pass\n", + "\n", + " self.root.after(\n", + " 100,\n", + " self.update_textbox\n", + " )\n", + "\n", + " # =========================================================\n", + " # SAVE WAV\n", + " # =========================================================\n", + "\n", + " def save_wav(self, filename=\"recorded.wav\"):\n", + "\n", + " wf = wave.open(\n", + " filename,\n", + " 'wb'\n", + " )\n", + "\n", + " wf.setnchannels(1)\n", + " wf.setsampwidth(2)\n", + " wf.setframerate(16000)\n", + "\n", + " wf.writeframes(\n", + " b''.join(audio_frames)\n", + " )\n", + "\n", + " wf.close()\n", + "\n", + " # =========================================================\n", + " # START LISTENING\n", + " # =========================================================\n", + "\n", + " def start_listening(self):\n", + "\n", + " global listening\n", + "\n", + " listening = True\n", + "\n", + " print(\"Listening...\")\n", + "\n", + " threading.Thread(\n", + " target=self.audio_thread,\n", + " daemon=True\n", + " ).start()\n", + "\n", + " # =========================================================\n", + " # STOP LISTENING\n", + " # =========================================================\n", + "\n", + " def stop_listening(self):\n", + "\n", + " global listening\n", + "\n", + " listening = False\n", + "\n", + " self.save_wav()\n", + "\n", + " print(\"Saved to recorded.wav\")\n", + "\n", + " messagebox.showinfo(\n", + " \"Recording\",\n", + " \"Speech recording stopped.\"\n", + " )\n", + "\n", + " self.speech_to_text()\n", + "\n", + " \n", + "# =========================================================\n", + "# MAIN\n", + "# =========================================================\n", + "\n", + "if __name__ == \"__main__\":\n", + "\n", + " root = tk.Tk()\n", + "\n", + " app = MedicalApp(root)\n", + "\n", + " root.mainloop()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "898e2732-b4d9-4346-88a4-eef13b58ed9a", + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python [conda env:base] *", + "language": "python", + "name": "conda-base-py" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.12.3" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +}