diff --git a/vitallink/backend/__pycache__/database.cpython-39.pyc b/vitallink/backend/__pycache__/database.cpython-39.pyc index e9719f6..51d3263 100644 Binary files a/vitallink/backend/__pycache__/database.cpython-39.pyc and b/vitallink/backend/__pycache__/database.cpython-39.pyc differ 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 0c81117..bd0dab8 100644 Binary files a/vitallink/vitallink.db and b/vitallink/vitallink.db differ