Digital Data Real-Time Ingestion Utility 1.0.0

import os
import time
import threading
import tkinter as tk
from tkinter import ttk
from tkcalendar import DateEntry
from datetime import date
from watchdog.observers import Observer
from watchdog.events import FileSystemEventHandler
import oracledb
import getpass

# Use thin mode (pure Python, no Oracle Client needed)
# Oracle DB connection config
DB_USERNAME = "Enter Database Username Here"
DB_PASSWORD = "Enter Database Password Here"
DB_HOST = "Enter Database Hostname Here"
DB_SERVICENAME = "Enter Database Servicename here"
sys_username = getpass.getuser()
BASE_FOLDER = r"folder/path/here"


def create_new_watch_folder(base_folder):
    """Create a new folder with numeric name, starting at 10001, incrementing each run."""
    existing = [f for f in os.listdir(base_folder) if f.isdigit()]
    existing_nums = sorted([int(f) for f in existing])
    global next_num

    if existing_nums:
        next_num = existing_nums[-1] + 1
    else:
        next_num = 10001

    new_folder = os.path.join(base_folder, str(next_num))
    os.makedirs(new_folder, exist_ok=False)
    prompt_user()
    return new_folder

# --- Custom Dialog with Dropdown ---
class CustomInputDialog(tk.Toplevel):
    def __init__(self, parent):
        super().__init__(parent)
        self.title("File Info Input")
        self.result = None
        
        tk.Label(self, text = "Data for patient " +str(next_num)).pack(padx=10,pady=5)

        tk.Label(self, text="Enter the patient's first and last name:").pack(padx=10, pady=5)
        self.name_entry = tk.Entry(self)
        self.name_entry.pack(padx=10, pady=5)

        tk.Label(self, text="Sex of patient:").pack(padx=10, pady=5)
        self.sex_combo = ttk.Combobox(self, values=["Male", "Female"], state="readonly")
        self.sex_combo.current(0)
        self.sex_combo.pack(padx=10, pady=5)
        
        tk.Label(self, text="Race/ethnicity of patient:").pack(padx=10, pady=5)
        self.race_combo = ttk.Combobox(self, values=["American Native", "Asian", "Black", "Hispanic", "Middle Eastern", "Hawaiian or Pacific", "White"], state="readonly")
        self.race_combo.current(0)
        self.race_combo.pack(padx=10, pady=5)
        
        tk.Label(self, text="History of SHD:").pack(padx=10, pady=5)
        self.hiss_combo = ttk.Combobox(self, values=[0, 1], state="readonly")
        self.hiss_combo.current(0)
        self.hiss_combo.pack(padx=10, pady=5)
        
        tk.Label(self, text="History/Presence of AS").pack(padx=10, pady=5)
        self.hisa_combo = ttk.Combobox(self, values=[0, 1, 2, 3], state="readonly")
        self.hisa_combo.current(0)
        self.hisa_combo.pack(padx=10, pady=5)
        
        tk.Label(self, text="Patient date of birth:").pack(pady=5)
        self.date_picker = DateEntry(self, width=12, background='darkblue',foreground='white', borderwidth=2,date_pattern='yyyy-mm-dd')
        self.date_picker.pack(pady=5)

        button_frame = tk.Frame(self)
        button_frame.pack(pady=10)

        tk.Button(button_frame, text="OK", command=self.on_ok).pack(side=tk.LEFT, padx=5)
        tk.Button(button_frame, text="Cancel", command=self.on_cancel).pack(side=tk.LEFT, padx=5)

        self.protocol("WM_DELETE_WINDOW", self.on_cancel)
        self.grab_set()
        self.name_entry.focus_set()
        self.wait_window()

    def on_ok(self):
        self.result = {
            'name': self.name_entry.get(),
            'sex': self.sex_combo.get(),
            'race': self.race_combo.get(),
            'hiss': self.hiss_combo.get(),
            'hisa': self.hisa_combo.get(),
            'dob': self.date_picker.get_date()
        }
        self.destroy()

    def on_cancel(self):
        self.result = None
        self.destroy()

# --- Prompt Function ---
def prompt_user():
    dialog = CustomInputDialog(root)
    if dialog.result is None:
        print("User cancelled input.")
        return

    global user_name 
    user_name = dialog.result['name']
    global sex 
    sex = dialog.result['sex']
    global race 
    race = dialog.result['race']
    global hiss 
    hiss = dialog.result['hiss']
    global hisa 
    hisa = dialog.result['hisa']
    global dob 
    dob = dialog.result['dob']
    today = date.today()
    global age 
    age = (today - dob).days / 365.25
    
def insert_into_db(filename,filepath):
    try:
        with oracledb.connect(user=DB_USERNAME, password=DB_PASSWORD, host=DB_HOST, port=1521, service_name = DB_SERVICENAME) as conn:
            with conn.cursor() as cursor:
                cursor.execute("""
                    INSERT INTO table_name_here 
                        (name, dob, age, sex, race_eth, shd_hist, as_hist, file_path, study_date, usr_info, study_id)
                    VALUES 
                        (:name, :dob, :age, :sex, :race, :hiss, :hisa, :path, SYSTIMESTAMP, :sys_user, :study_id)
                """, {
                    'name': user_name,
                    'dob': dob,
                    'age': age,
                    'sex': sex,
                    'race': race,
                    'hiss': hiss,
                    'hisa': hisa,
                    'path': filepath,
                    'sys_user': sys_username,
                    'study_id': int(filepath[27:32])
                })
            conn.commit()
            print(f"Inserted record for {filename}")
    except oracledb.Error as e:
        print("Oracle error:", e)

# --- File Watcher Class ---
class FileHandler(FileSystemEventHandler):
    def __init__(self, tk_root):
        self.tk_root = tk_root

    def on_created(self, event):
        if event.is_directory:
            return

        filepath = event.src_path
        filename = os.path.basename(filepath)
        
        print(f"[Watchdog] New file detected in {os.path.dirname(filepath)}")
        print(f"    Name: {filename}")

        self.tk_root.after(0, lambda: insert_into_db(filename, filepath))

# --- Main Setup ---
if __name__ == "__main__":
    root = tk.Tk()
    root.withdraw()

    # Step 1: Create new folder
    WATCHED_FOLDER = create_new_watch_folder(BASE_FOLDER)
    print(f"[Setup] Created and monitoring folder: {WATCHED_FOLDER}")

    # Step 2: Start watchdog
    event_handler = FileHandler(root)
    observer = Observer()
    observer.schedule(event_handler, path=WATCHED_FOLDER, recursive=False)
    observer.start()

    try:
        root.mainloop()
    except KeyboardInterrupt:
        observer.stop()
    observer.join()