""" Database models for NeuroSight authentication system """ from flask_sqlalchemy import SQLAlchemy from flask_login import UserMixin from werkzeug.security import generate_password_hash, check_password_hash from datetime import datetime import secrets db = SQLAlchemy() class User(UserMixin, db.Model): """User model for doctors and radiologists""" __tablename__ = 'users' id = db.Column(db.Integer, primary_key=True) email = db.Column(db.String(255), unique=True, nullable=False, index=True) password_hash = db.Column(db.String(255), nullable=True) # Nullable for OAuth users full_name = db.Column(db.String(255), nullable=False) role = db.Column(db.String(50), nullable=False) # 'doctor' or 'radiologist' hospital = db.Column(db.String(255)) license_number = db.Column(db.String(100)) phone = db.Column(db.String(20)) is_verified = db.Column(db.Boolean, default=False) is_active = db.Column(db.Boolean, default=True) created_at = db.Column(db.DateTime, default=datetime.utcnow) last_login = db.Column(db.DateTime) # OAuth fields google_id = db.Column(db.String(255), unique=True, index=True) profile_photo_url = db.Column(db.String(500)) # Password reset fields reset_token = db.Column(db.String(255)) reset_token_expiry = db.Column(db.DateTime) # Doctor Details (for onboarding) medical_registration_no = db.Column(db.String(100)) specialization = db.Column(db.String(100)) years_of_experience = db.Column(db.Integer) clinic_timing = db.Column(db.String(255)) # Hospital Details (for onboarding) hospital_id = db.Column(db.String(100)) hospital_address = db.Column(db.Text) department = db.Column(db.String(100)) hospital_logo_url = db.Column(db.String(500)) hospital_phone = db.Column(db.String(20)) # Onboarding tracking onboarding_completed = db.Column(db.Boolean, default=False) # Email verification with OTP email_verified = db.Column(db.Boolean, default=False) otp_code = db.Column(db.String(6)) otp_expiry = db.Column(db.DateTime) otp_attempts = db.Column(db.Integer, default=0) # Relationships analyses = db.relationship('AnalysisHistory', backref='user', lazy='dynamic', cascade='all, delete-orphan') def set_password(self, password): """Hash and set password""" self.password_hash = generate_password_hash(password) def check_password(self, password): """Verify password against hash""" if not self.password_hash: return False return check_password_hash(self.password_hash, password) def generate_reset_token(self): """Generate a secure password reset token""" self.reset_token = secrets.token_urlsafe(32) return self.reset_token def generate_otp(self): """Generate a 6-digit OTP for email verification""" import random from datetime import datetime, timedelta self.otp_code = ''.join([str(random.randint(0, 9)) for _ in range(6)]) self.otp_expiry = datetime.utcnow() + timedelta(minutes=10) # OTP valid for 10 minutes self.otp_attempts = 0 return self.otp_code def verify_otp(self, otp): """Verify the OTP code""" from datetime import datetime # Check if OTP exists if not self.otp_code: return False, "No OTP generated" # Check if OTP has expired if datetime.utcnow() > self.otp_expiry: return False, "OTP has expired" # Check attempt limit (max 5 attempts) if self.otp_attempts >= 5: return False, "Too many failed attempts. Please request a new OTP" # Verify OTP if self.otp_code == otp: self.email_verified = True self.otp_code = None self.otp_expiry = None self.otp_attempts = 0 return True, "Email verified successfully" else: self.otp_attempts += 1 remaining = 5 - self.otp_attempts return False, f"Invalid OTP. {remaining} attempts remaining" def needs_onboarding(self): """Check if user needs to complete onboarding""" return not self.onboarding_completed def has_required_doctor_details(self): """Check if user has all required doctor details""" return all([ self.full_name, self.medical_registration_no, self.specialization, self.phone, self.email, self.years_of_experience is not None ]) def has_required_hospital_details(self): """Check if user has all required hospital details""" return all([ self.hospital, self.hospital_id, self.department ]) def can_complete_onboarding(self): """Check if user can complete onboarding (all required fields filled)""" return self.has_required_doctor_details() and self.has_required_hospital_details() def __repr__(self): return f'' class AnalysisHistory(db.Model): """Analysis history for tracking patient scans""" __tablename__ = 'analysis_history' id = db.Column(db.Integer, primary_key=True) user_id = db.Column(db.Integer, db.ForeignKey('users.id'), nullable=False, index=True) # Patient information patient_name = db.Column(db.String(255)) patient_id = db.Column(db.String(100)) patient_age = db.Column(db.Integer) scan_date = db.Column(db.Date) # Analysis results disease_type = db.Column(db.String(50), nullable=False) # 'ms', 'alzheimer', 'dementia', 'stroke' prediction = db.Column(db.String(255), nullable=False) confidence = db.Column(db.Float) # File paths image_path = db.Column(db.String(500)) report_path = db.Column(db.String(500)) # Metadata created_at = db.Column(db.DateTime, default=datetime.utcnow, index=True) notes = db.Column(db.Text) # Doctor's notes def __repr__(self): return f'' def init_db(app): """Initialize database""" db.init_app(app) with app.app_context(): db.create_all() print("✓ Database tables created successfully")