From 9a50ce6f2ad45a3fae64fa64e3c0facde269f41e Mon Sep 17 00:00:00 2001 From: Raika Furude Date: Mon, 24 Nov 2025 15:20:45 -0500 Subject: [PATCH] wip-error-fix api post new-p --- .../__pycache__/database.cpython-39.pyc | Bin 16856 -> 17070 bytes vitallink/backend/database.py | 115 +-- vitallink/frontend/dashboard/src/App.jsx | 168 ++++- vitallink/logs/all_pids.txt | 8 +- vitallink/logs/backend.log | 683 +++++++++++++----- vitallink/logs/backend.pid | 2 +- vitallink/logs/dashboard.log | 2 +- vitallink/logs/dashboard.pid | 2 +- vitallink/logs/kiosk.log | 2 +- vitallink/logs/kiosk.pid | 2 +- vitallink/logs/wristbands.pid | 2 +- vitallink/vitallink.db | Bin 53248 -> 53248 bytes 12 files changed, 736 insertions(+), 250 deletions(-) diff --git a/vitallink/backend/__pycache__/database.cpython-39.pyc b/vitallink/backend/__pycache__/database.cpython-39.pyc index e9719f68046affc48743f838c1da55b9b4e50e22..51d3263c2c74478932159a8fa845a12f9c8730fb 100644 GIT binary patch delta 4069 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 W27@FDRIt3vr0;aIVdV zm6C3uj4?5-9I9?wek4CO32Ey(WvG+(W7?=zt$+4wQg`V8?FfX;Tx^T=l4pqirOzr)V?66 zowQYTsSfH`l&O=tE}PREXd6&mbOTV_v<4_{x)~@n)C-haS_c#l@O_Jf`f1>@lJ=^# zG!x=#H1(mu6r+D}6?e4V5NYG67@BXmAW>uKz|iAaJFr%~GQ#Jmya zO;605;b0F<(2duX)g3J~NmF!_Az>-BETzM0csc^dkE*_1|W1tK=u)WA~XvP2{*ACXe+E1!^lN7w=m9fn795J^&{ zG@(*jrX+2moB5EdeqOavJ9ws=I>0kks-h~?34Q~A!rTrsH*moum{r4CFU-)VO4>o& zVYL%HZ3{}&zi6TX8pL>@4Dq$m(4v`!X#`fgfnuzTf^={w&}x7-?@^VYq#9_W@kIr^ z+XzfA?O3H6ytRW8uwo-^frDz*I{4aX5{?G{!X6(8v{5{umA1j$Pn*CITlAWjvc=5I z{%roto=h=wGNY;G#Oi3}lIx+|nj*SmHdD;1`QikHC6h9EZ0&>N4e;E7N4vs(ff)al z$9}RXFPY{DwFaeS0>9t{z$(Ngh1zNLvV5v)S$>HU(`u1>Apd1dwD4k*yZ8_H~Gn{d4&XyVq?}h%DyvqjG(PkK47(Idx*ytsp+g z->+>Xclf_*-wv=QJPTDwnT&ITNJ#PD)U^!Vf}til0UmC$JhEHLiAwRMSqRno0bshy zZV@;o>I;xNE8Bd2(k4#9`01v6;k-DHwcxZWs})WaST3_}caYEf6J&+|%pdbKRUF23 z|27a>6F5sOLemhx8XRj7@;9R9%?JquVbrQKx9|tS82NyQLM<)P3ZaIB&S7q9_bBWI zUj+F~s0B{C8M;kkohYvxVJkup!Zw6ngf2c6Y47+q3^fmc#V+4pi<0>LqC8xABp-~1 z_{Wh5X}S@uGfe5{;po8Tk5G;qKvG1+@7t?gGRXXIx$W6fyeTt7SLn9F3>Dso@ZF9k zLHWDU6Xb>cz+)+d0{|(TNJt~@7!kG^;c2&t*rY5^B!nS^zuFk(KaaVI!rzUxwl-8M zglUWkFflom$)8p++fe)fuZd$aY>K~0Mh1~Ggs>A~7$9ZVtBoC^a@f;4voTn5OBPD% z=9xlh?X<}1JPaxCD@dfp_R@sFFEEI&2+j4iPR`i#Q+5`)lV!uwK!j4fy^Wi*xl zA4A9r(MOJ=!A7az=otF#AlBXx+(Sln+aS@*z6W+&Ib7+C^~N-IF95s1nd=gD^Hib4 zG>Gmm6Byk;B|akq&x|fyVA$J?;I8jyA)yh%iwKWndPc-_ONBPPFWJ!}ihUpIV*93v zF9TIKpDn5EqHdYZuuM*4s}WyJ){{g0cgd6|QYrTHWHTAykrZ0dlWLUR-?W0CZXLIX zgFE@3TO;mq)b}`o5dSUypVk&q=Fzs;z%x1|CX}XXTDFj%&`P;nhFyH>X0w7ICp>pn zF0~yauk@qbmjKqv2h9={^MJ%37Y*BmJefHao*N` z_=K=x0_QIv96|Utg6IK;mU)uAeM)YR=8ciazvSxPi*He5yC5zKX*`01wFVvK)_#23tbM#Xq`oBQCCoGBlXOY6f0UcErV1fdo-18W2!C zT#G^*;dNBvsm_|^pZ0dk_X%(4JJ5R$xptO=AC^ZbAj~3Qt}=X&V`l+$^C@WPMRo-V zS0P}5qz1$HusLCtVB}mTPdh`ZkvB4{!tntAXJ46=_=)We0XGC)!#8Y6whhY1}syvq2sek~$25_*fRiqU07FiJc)&|cSxTCKf5xj-|I%x;N z#~4;V+-`e#cqmRr`M}TucMjQQ_9nJ!`&+<00D{<$`6ojM`0t0RiC~N)v{gGhlP#*6aDj(+_lNcYU$NMTjW|dxJT{Axam`Ef(h)wlyX${cf@RDA diff --git a/vitallink/backend/database.py b/vitallink/backend/database.py index caabe03..2fd8300 100644 --- a/vitallink/backend/database.py +++ b/vitallink/backend/database.py @@ -135,36 +135,47 @@ class VitalLinkDatabase: # PATIENT OPERATIONS # ======================================================================== - async def save_patient(self, patient_data: Dict): - """Save new patient to database""" - await self.conn.execute( - """ - INSERT INTO patients ( - patient_id, band_id, first_name, last_name, dob, - symptoms, severity, check_in_time, current_tier - ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?) - """, - ( - patient_data["patient_id"], - patient_data["band_id"], - patient_data["first_name"], - patient_data["last_name"], - patient_data["dob"], - json.dumps(patient_data["symptoms"]), - patient_data["severity"], - patient_data["check_in_time"], - patient_data.get("current_tier", "NORMAL"), - ), - ) - await self.conn.commit() - # Log event - await self.log_event( - "patient_checkin", +async def save_patient(self, patient_data: Dict): + """Save new patient to database""" + # Convert datetime to ISO string if needed + check_in_time = patient_data["check_in_time"] + if isinstance(check_in_time, datetime): + check_in_time = check_in_time.isoformat() + + await self.conn.execute( + """ + INSERT INTO patients ( + patient_id, band_id, first_name, last_name, dob, + symptoms, severity, check_in_time, current_tier + ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?) + """, + ( patient_data["patient_id"], patient_data["band_id"], - patient_data, - ) + patient_data["first_name"], + patient_data["last_name"], + patient_data["dob"], + json.dumps(patient_data["symptoms"]), + patient_data["severity"], + check_in_time, # Now a string + patient_data.get("current_tier", "NORMAL"), + ), + ) + await self.conn.commit() + + # Log event with serializable data + await self.log_event( + "patient_checkin", + patient_data["patient_id"], + patient_data["band_id"], + { + "first_name": patient_data["first_name"], + "last_name": patient_data["last_name"], + "symptoms": patient_data["symptoms"], + "severity": patient_data["severity"], + }, + ) async def update_patient_tier(self, patient_id: str, new_tier: str): """Update patient's current tier""" @@ -315,23 +326,39 @@ class VitalLinkDatabase: # SYSTEM EVENTS (Audit Trail) # ======================================================================== - async def log_event( - self, - event_type: str, - patient_id: Optional[str], - band_id: Optional[str], - details: Dict, - ): - """Log system event for audit trail""" - await self.conn.execute( - """ - INSERT INTO system_events ( - event_time, event_type, patient_id, band_id, details - ) VALUES (?, ?, ?, ?, ?) - """, - (datetime.now(), event_type, patient_id, band_id, json.dumps(details)), - ) - await self.conn.commit() + +async def log_event( + self, + event_type: str, + patient_id: Optional[str], + band_id: Optional[str], + details: Dict, +): + """Log system event for audit trail""" + + # Ensure details is JSON serializable + serializable_details = {} + for key, value in details.items(): + if isinstance(value, datetime): + serializable_details[key] = value.isoformat() + else: + serializable_details[key] = value + + await self.conn.execute( + """ + INSERT INTO system_events ( + event_time, event_type, patient_id, band_id, details + ) VALUES (?, ?, ?, ?, ?) + """, + ( + datetime.now().isoformat(), # Convert to string + event_type, + patient_id, + band_id, + json.dumps(serializable_details), + ), + ) + await self.conn.commit() async def get_events( self, diff --git a/vitallink/frontend/dashboard/src/App.jsx b/vitallink/frontend/dashboard/src/App.jsx index 09f6d72..2d7ae9a 100644 --- a/vitallink/frontend/dashboard/src/App.jsx +++ b/vitallink/frontend/dashboard/src/App.jsx @@ -365,14 +365,176 @@ function App() { )} ) : ( - /* Wristbands tab - your existing code stays the same */
- {/* ... your existing wristband code ... */} +
+

Wristband Inventory

+ +
+ {wristbands.map(band => ( +
setSelectedWristband(band)} + className={`p-4 rounded-lg border-2 cursor-pointer transition-all ${ + band.status === 'in_use' + ? 'bg-blue-50 border-blue-300 hover:border-blue-400' + : 'bg-gray-50 border-gray-300 hover:border-gray-400' + }`} + > +
+ + {band.type === 'real' ? '🔵' : '🟢'} {band.band_id} + + + {band.status.toUpperCase().replace('_', ' ')} + +
+ + {band.patient_id && ( +
+ Patient: {band.patient_id} +
+ )} + +
+ Packets: {band.packet_count} + {band.is_monitoring && ( + ● LIVE + )} +
+
+ ))} +
+ + {wristbands.length === 0 && ( +

No wristbands configured

+ )} +
+ + {selectedWristband && selectedWristband.last_raw_packet && ( +
+
+

+ Packet Details: {selectedWristband.band_id} + {selectedWristband.type === 'simulated' && ( + MOCK + )} +

+ +
+ +
+

Raw Packet (16 bytes, Hex):

+
+ {selectedWristband.last_raw_packet.hex.toUpperCase().match(/.{1,2}/g).join(' ')} +
+

+ Format: [ver][seq][timestamp][flags][hr][spo2][temp_x100][activity_x100][checksum][rfu] +

+
+ + {selectedWristband.last_raw_packet.decoded && ( + <> +
+

Decoded Fields:

+
+
+
Version
+
0x{selectedWristband.last_raw_packet.decoded.version.toString(16).padStart(2, '0')}
+
+
+
Sequence #
+
{selectedWristband.last_raw_packet.decoded.sequence}
+
+
+
Timestamp (ms)
+
{selectedWristband.last_raw_packet.decoded.timestamp_ms.toLocaleString()}
+
+
+
Heart Rate
+
{selectedWristband.last_raw_packet.decoded.hr_bpm}
+
bpm
+
+
+
SpO₂
+
{selectedWristband.last_raw_packet.decoded.spo2}
+
%
+
+
+
Temperature
+
{selectedWristband.last_raw_packet.decoded.temperature_c.toFixed(2)}
+
°C
+
+
+
Activity
+
{selectedWristband.last_raw_packet.decoded.activity.toFixed(2)}
+
RMS
+
+
+
Checksum
+
{selectedWristband.last_raw_packet.decoded.checksum}
+
+
+
Flags (raw)
+
0x{selectedWristband.last_raw_packet.decoded.flags.raw.toString(16).padStart(2, '0')}
+
+
+
+ + {selectedWristband.last_raw_packet.decoded.flags && ( +
+

Status Flags:

+
+ {selectedWristband.last_raw_packet.decoded.flags.emergency && ( + + 🚨 Bit 4: Emergency + + )} + {selectedWristband.last_raw_packet.decoded.flags.alert && ( + + ⚠️ Bit 3: Alert + + )} + {selectedWristband.last_raw_packet.decoded.flags.sensor_fault && ( + + ⚙️ Bit 2: Sensor Fault + + )} + {selectedWristband.last_raw_packet.decoded.flags.low_battery && ( + + 🔋 Bit 1: Low Battery + + )} + {selectedWristband.last_raw_packet.decoded.flags.motion_artifact && ( + + 👋 Bit 0: Motion Artifact + + )} + {!Object.values(selectedWristband.last_raw_packet.decoded.flags).some(v => v === true) && ( + + ✓ No flags set (all normal) + + )} +
+
+ )} + + )} +
+ )}
)} - {/* Patient Detail Modal - ADD THIS */} + {/* Patient Detail Modal */} {selectedPatient && ( vite - VITE v7.1.10 ready in 225 ms + VITE v7.1.10 ready in 219 ms ➜ Local: http://localhost:5173/ ➜ Network: use --host to expose diff --git a/vitallink/logs/dashboard.pid b/vitallink/logs/dashboard.pid index 1911377..cc1a012 100644 --- a/vitallink/logs/dashboard.pid +++ b/vitallink/logs/dashboard.pid @@ -1 +1 @@ -62581 +68245 diff --git a/vitallink/logs/kiosk.log b/vitallink/logs/kiosk.log index 5b7836f..a6b29a4 100644 --- a/vitallink/logs/kiosk.log +++ b/vitallink/logs/kiosk.log @@ -4,7 +4,7 @@ Port 5173 is in use, trying another one... - VITE v7.1.10 ready in 215 ms + VITE v7.1.10 ready in 250 ms ➜ Local: http://localhost:5174/ ➜ Network: use --host to expose diff --git a/vitallink/logs/kiosk.pid b/vitallink/logs/kiosk.pid index 397d8cf..a1b288b 100644 --- a/vitallink/logs/kiosk.pid +++ b/vitallink/logs/kiosk.pid @@ -1 +1 @@ -62616 +68281 diff --git a/vitallink/logs/wristbands.pid b/vitallink/logs/wristbands.pid index 19f7f43..a5bb77e 100644 --- a/vitallink/logs/wristbands.pid +++ b/vitallink/logs/wristbands.pid @@ -1 +1 @@ -62574 +68237 diff --git a/vitallink/vitallink.db b/vitallink/vitallink.db index 0c81117aa7a906fc53c84532133ce5fe3dd7c778..bd0dab8d1b44d81c24615397f2f6b46667794ae0 100644 GIT binary patch delta 1423 zcmah}Z)^);7{BXvz23EVPcLe0HS3VY64l*kmlW*Q0mP=XHx zBmII962ce8R6-(=WfDG#kCNd73F1#guq{Nw`@D5cQ^K43aL@Dp-skuJe$R8a?*g~) z0(Zj0mYtljge@EHxCC~KJi)+#Jdsgc@8r~ei$(HOO`Td1EQ>KELa`JPUNW>S=$g_S{+}LgO8{P&Zu`$urZOm%lwqv8wpGX>7Knwd+ z)u)BLYB;Kfq9Jv@y0AJD)>f@sk1!zf}G16N@kaPpAcDi=w^QoAHLZ#g@h4##arr$Z9&h+RZqM2yB2iSd;4 zEX(qIiDJrYTD_#v7hm3_#m1KVTTj^fxSo#My2ovE$43YCH{#;Hi%-P#Nqvf_1)}Q0 zXh@5@(l=_n)BS5FziCDFiQ2kuf^4hOKkR)`Kja=jY+V)`RO9ep$`!}=k6C(2nR|EE zygyW~ze2*rJ9Cr^kW$W4)=%vDYHc9c%|q_JFB?U?zp-gz1 zGLS*g;$l`))t9@sENCNjyiI?JM5BYK$%RZIWD>HP(utbVY5vmBvoGU;4OzpUA-t_B zXIKyq*waJ0cbebv>FT*$vgHvz-wnHl{1wRzY zPvv1Kl|RW3fy~nH>Xj_ORAW}v2*1m>D=NFL0*y~aa!cOBt>HHrMx6X zVwaunQ~^CU&rt})c8P^a?1)g9MDOhd$qF=>FHEAFHn%l>=6p}30&^Jn3U6Qx9>EA? z;4++sqmY7rFvw@#2sNO>9OBn^IX!ZnPti|SadJGHGsg_5DP};;Fav6W8BpvaY?6T=Fqu)B--Br71lGcFf*@wt=AZBjV;`K7 delta 178 zcmZozz}&Ead4e>fz(g5mMuCk9OZeFs`KuZDt2YZO6!K55mhS}ezcTQD-7FaJnqQWS zS(-Dz&;SSwc^McOSotCu_}B19@H6wR