SuriRaja commited on
Commit
cb4397c
·
verified ·
1 Parent(s): 1536856

Upload 14 files

Browse files
.gitignore ADDED
@@ -0,0 +1,135 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Byte-compiled / optimized / DLL files
2
+ __pycache__/
3
+ *.py[cod]
4
+ *$py.class
5
+
6
+ # C extensions
7
+ *.so
8
+
9
+ # Distribution / packaging
10
+ .Python
11
+ build/
12
+ develop-eggs/
13
+ dist/
14
+ downloads/
15
+ eggs/
16
+ .eggs/
17
+ lib/
18
+ lib64/
19
+ parts/
20
+ sdist/
21
+ var/
22
+ wheels/
23
+ pip-wheel-metadata/
24
+ share/python-wheels/
25
+ *.egg-info/
26
+ .installed.cfg
27
+ *.egg
28
+ MANIFEST
29
+
30
+ # PyInstaller
31
+ # Usually these files are written by a python script from a template
32
+ # before PyInstaller builds the exe, so as to inject date/other infos into it.
33
+ *.manifest
34
+ *.spec
35
+
36
+ # Installer logs
37
+ pip-log.txt
38
+ pip-delete-this-directory.txt
39
+
40
+ # Unit test / coverage reports
41
+ htmlcov/
42
+ .tox/
43
+ .nox/
44
+ .coverage
45
+ .coverage.*
46
+ .cache
47
+ nosetests.xml
48
+ coverage.xml
49
+ *.cover
50
+ *.py,cover
51
+ .hypothesis/
52
+ .pytest_cache/
53
+
54
+ # Translations
55
+ *.mo
56
+ *.pot
57
+
58
+ # Django stuff:
59
+ *.log
60
+ local_settings.py
61
+ db.sqlite3
62
+ db.sqlite3-journal
63
+
64
+ # Flask stuff:
65
+ instance/
66
+ .webassets-cache
67
+
68
+ # Scrapy stuff:
69
+ .scrapy
70
+
71
+ # Sphinx documentation
72
+ docs/_build/
73
+
74
+ # PyBuilder
75
+ target/
76
+
77
+ # Jupyter Notebook
78
+ .ipynb_checkpoints
79
+
80
+ # IPython
81
+ profile_default/
82
+ ipython_config.py
83
+
84
+ # pyenv
85
+ .python-version
86
+
87
+ # pipenv
88
+ # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
89
+ # However, in case of collaboration, if having platform-specific dependencies or dependencies
90
+ # having no cross-platform support, pipenv may install dependencies that don't work, or not
91
+ # install all needed dependencies.
92
+ #Pipfile.lock
93
+
94
+ # PEP 582; used by e.g. github.com/David-OConnor/pyflow
95
+ __pypackages__/
96
+
97
+ # Celery stuff
98
+ celerybeat-schedule
99
+ celerybeat.pid
100
+
101
+ # SageMath parsed files
102
+ *.sage.py
103
+
104
+ # Environments
105
+ .env
106
+ .venv
107
+ env/
108
+ venv/
109
+ ENV/
110
+ env.bak/
111
+ venv.bak/
112
+
113
+ # Spyder project settings
114
+ .spyderproject
115
+ .spyproject
116
+
117
+ # Rope project settings
118
+ .ropeproject
119
+
120
+ # mkdocs documentation
121
+ /site
122
+
123
+ # mypy
124
+ .mypy_cache/
125
+ .dmypy.json
126
+ dmypy.json
127
+
128
+ # Pyre type checker
129
+ .pyre/
130
+
131
+ # Medical Dashboard specific
132
+ medical_dashboard.db
133
+ uploads/
134
+ *.db
135
+ .DS_Store
.replit ADDED
@@ -0,0 +1,34 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ modules = ["python-3.11", "postgresql-16"]
2
+
3
+ [nix]
4
+ channel = "stable-24_05"
5
+ packages = ["freetype", "glibcLocales", "lcms2", "libimagequant", "libjpeg", "libtiff", "libwebp", "libxcrypt", "openjpeg", "openssl", "postgresql", "tcl", "tk", "zlib"]
6
+
7
+ [deployment]
8
+ deploymentTarget = "autoscale"
9
+ run = ["gunicorn", "--bind", "0.0.0.0:5000", "main:app"]
10
+
11
+ [workflows]
12
+ runButton = "Project"
13
+
14
+ [[workflows.workflow]]
15
+ name = "Project"
16
+ mode = "parallel"
17
+ author = "agent"
18
+
19
+ [[workflows.workflow.tasks]]
20
+ task = "workflow.run"
21
+ args = "Start application"
22
+
23
+ [[workflows.workflow]]
24
+ name = "Start application"
25
+ author = "agent"
26
+
27
+ [[workflows.workflow.tasks]]
28
+ task = "shell.exec"
29
+ args = "gunicorn --bind 0.0.0.0:5000 --reuse-port --reload main:app"
30
+ waitForPort = 5000
31
+
32
+ [[ports]]
33
+ localPort = 5000
34
+ externalPort = 80
DEPLOYMENT_GUIDE.md ADDED
@@ -0,0 +1,389 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Deployment Guide - Doctor's Sidekick
2
+
3
+ This guide provides step-by-step instructions for deploying the Doctor's Sidekick medical dashboard to various platforms.
4
+
5
+ ## GitHub Repository Setup
6
+
7
+ ### 1. Repository Structure
8
+ ```
9
+ doctors-sidekick/
10
+ ├── complete_medical_dashboard.py # Main application (single file)
11
+ ├── README.md # Project documentation
12
+ ├── DEPLOYMENT_GUIDE.md # This deployment guide
13
+ ├── install_dependencies.txt # Required packages
14
+ ├── run_app.py # Simple startup script
15
+ └── .gitignore # Git ignore file
16
+ ```
17
+
18
+ ### 2. Create .gitignore
19
+ ```
20
+ __pycache__/
21
+ *.pyc
22
+ *.pyo
23
+ *.pyd
24
+ .Python
25
+ env/
26
+ venv/
27
+ .venv/
28
+ pip-log.txt
29
+ pip-delete-this-directory.txt
30
+ .tox/
31
+ .coverage
32
+ .coverage.*
33
+ .cache
34
+ nosetests.xml
35
+ coverage.xml
36
+ *.cover
37
+ *.log
38
+ .git
39
+ .mypy_cache
40
+ .pytest_cache
41
+ .hypothesis
42
+ .DS_Store
43
+ instance/
44
+ medical_dashboard.db
45
+ uploads/
46
+ ```
47
+
48
+ ## Local Development
49
+
50
+ ### Quick Start
51
+ ```bash
52
+ # Clone repository
53
+ git clone https://github.com/yourusername/doctors-sidekick.git
54
+ cd doctors-sidekick
55
+
56
+ # Install dependencies
57
+ pip install flask flask-sqlalchemy flask-login flask-wtf wtforms werkzeug email-validator
58
+
59
+ # Run application
60
+ python complete_medical_dashboard.py
61
+ ```
62
+
63
+ ### Using the Startup Script
64
+ ```bash
65
+ python run_app.py
66
+ ```
67
+
68
+ ## Cloud Deployment Options
69
+
70
+ ### 1. Heroku Deployment
71
+
72
+ Create `Procfile`:
73
+ ```
74
+ web: python complete_medical_dashboard.py
75
+ ```
76
+
77
+ Create `runtime.txt`:
78
+ ```
79
+ python-3.9.19
80
+ ```
81
+
82
+ Deploy commands:
83
+ ```bash
84
+ heroku create your-app-name
85
+ heroku config:set SESSION_SECRET="your-secret-key"
86
+ heroku config:set DATABASE_URL="postgresql://..." # Optional
87
+ git push heroku main
88
+ ```
89
+
90
+ ### 2. Railway Deployment
91
+
92
+ 1. Connect GitHub repository to Railway
93
+ 2. Set environment variables:
94
+ - `SESSION_SECRET`: Your secret key
95
+ - `PORT`: 5000 (Railway will override this)
96
+ 3. Deploy automatically on push
97
+
98
+ ### 3. Replit Deployment
99
+
100
+ 1. Import GitHub repository to Replit
101
+ 2. Replit will auto-detect Flask application
102
+ 3. Set secrets in Replit environment
103
+ 4. Run with `python complete_medical_dashboard.py`
104
+
105
+ ### 4. DigitalOcean App Platform
106
+
107
+ Create `app.yaml`:
108
+ ```yaml
109
+ name: doctors-sidekick
110
+ services:
111
+ - name: web
112
+ source_dir: /
113
+ github:
114
+ repo: yourusername/doctors-sidekick
115
+ branch: main
116
+ run_command: python complete_medical_dashboard.py
117
+ environment_slug: python
118
+ instance_count: 1
119
+ instance_size_slug: basic-xxs
120
+ envs:
121
+ - key: SESSION_SECRET
122
+ value: your-secret-key
123
+ type: SECRET
124
+ ```
125
+
126
+ ### 5. AWS Elastic Beanstalk
127
+
128
+ Create `application.py`:
129
+ ```python
130
+ from complete_medical_dashboard import app
131
+
132
+ if __name__ == "__main__":
133
+ app.run(debug=False)
134
+ ```
135
+
136
+ Deploy with EB CLI:
137
+ ```bash
138
+ eb init
139
+ eb create
140
+ eb deploy
141
+ ```
142
+
143
+ ## Docker Deployment
144
+
145
+ ### Dockerfile
146
+ ```dockerfile
147
+ FROM python:3.9-slim
148
+
149
+ WORKDIR /app
150
+
151
+ # Copy application
152
+ COPY complete_medical_dashboard.py .
153
+
154
+ # Install dependencies
155
+ RUN pip install flask flask-sqlalchemy flask-login flask-wtf wtforms werkzeug email-validator
156
+
157
+ # Create uploads directory
158
+ RUN mkdir -p uploads
159
+
160
+ # Expose port
161
+ EXPOSE 5000
162
+
163
+ # Set environment variables
164
+ ENV FLASK_APP=complete_medical_dashboard.py
165
+ ENV FLASK_ENV=production
166
+
167
+ # Run application
168
+ CMD ["python", "complete_medical_dashboard.py"]
169
+ ```
170
+
171
+ ### Docker Commands
172
+ ```bash
173
+ # Build image
174
+ docker build -t doctors-sidekick .
175
+
176
+ # Run container
177
+ docker run -p 5000:5000 -e SESSION_SECRET="your-secret" doctors-sidekick
178
+
179
+ # With external database
180
+ docker run -p 5000:5000 \
181
+ -e SESSION_SECRET="your-secret" \
182
+ -e DATABASE_URL="postgresql://..." \
183
+ doctors-sidekick
184
+ ```
185
+
186
+ ### Docker Compose
187
+ ```yaml
188
+ version: '3.8'
189
+ services:
190
+ web:
191
+ build: .
192
+ ports:
193
+ - "5000:5000"
194
+ environment:
195
+ - SESSION_SECRET=your-secret-key
196
+ - DATABASE_URL=postgresql://postgres:password@db:5432/medical_db
197
+ depends_on:
198
+ - db
199
+ volumes:
200
+ - ./uploads:/app/uploads
201
+
202
+ db:
203
+ image: postgres:13
204
+ environment:
205
+ - POSTGRES_DB=medical_db
206
+ - POSTGRES_USER=postgres
207
+ - POSTGRES_PASSWORD=password
208
+ volumes:
209
+ - postgres_data:/var/lib/postgresql/data
210
+
211
+ volumes:
212
+ postgres_data:
213
+ ```
214
+
215
+ ## Environment Variables
216
+
217
+ ### Required
218
+ - `SESSION_SECRET`: Secret key for Flask sessions
219
+
220
+ ### Optional
221
+ - `DATABASE_URL`: External database connection string
222
+ - `PORT`: Port number (default: 5000)
223
+ - `FLASK_ENV`: Environment (development/production)
224
+
225
+ ### Setting Environment Variables
226
+
227
+ **Linux/Mac:**
228
+ ```bash
229
+ export SESSION_SECRET="your-secret-key"
230
+ export DATABASE_URL="postgresql://user:pass@host:port/dbname"
231
+ ```
232
+
233
+ **Windows:**
234
+ ```cmd
235
+ set SESSION_SECRET=your-secret-key
236
+ set DATABASE_URL=postgresql://user:pass@host:port/dbname
237
+ ```
238
+
239
+ **Python .env file:**
240
+ ```python
241
+ import os
242
+ os.environ['SESSION_SECRET'] = 'your-secret-key'
243
+ os.environ['DATABASE_URL'] = 'postgresql://...'
244
+ ```
245
+
246
+ ## Database Configuration
247
+
248
+ ### SQLite (Default)
249
+ - Automatic setup, no configuration needed
250
+ - Database file: `medical_dashboard.db`
251
+ - Suitable for development and small deployments
252
+
253
+ ### PostgreSQL (Recommended for Production)
254
+ ```bash
255
+ # Set database URL
256
+ export DATABASE_URL="postgresql://username:password@hostname:port/database_name"
257
+ ```
258
+
259
+ ### MySQL
260
+ ```bash
261
+ # Set database URL
262
+ export DATABASE_URL="mysql://username:password@hostname:port/database_name"
263
+ ```
264
+
265
+ ## Security Considerations
266
+
267
+ ### Production Checklist
268
+ - [ ] Set strong `SESSION_SECRET`
269
+ - [ ] Use HTTPS in production
270
+ - [ ] Configure external database
271
+ - [ ] Enable firewall
272
+ - [ ] Regular backups
273
+ - [ ] Monitor application logs
274
+ - [ ] Update dependencies regularly
275
+
276
+ ### Security Headers
277
+ Add to production deployment:
278
+ ```python
279
+ @app.after_request
280
+ def after_request(response):
281
+ response.headers['X-Content-Type-Options'] = 'nosniff'
282
+ response.headers['X-Frame-Options'] = 'DENY'
283
+ response.headers['X-XSS-Protection'] = '1; mode=block'
284
+ return response
285
+ ```
286
+
287
+ ## Monitoring and Logging
288
+
289
+ ### Application Logs
290
+ ```python
291
+ import logging
292
+ logging.basicConfig(level=logging.INFO)
293
+ ```
294
+
295
+ ### Health Check Endpoint
296
+ Add to application:
297
+ ```python
298
+ @app.route('/health')
299
+ def health_check():
300
+ return {'status': 'healthy', 'timestamp': datetime.utcnow().isoformat()}
301
+ ```
302
+
303
+ ## Backup and Recovery
304
+
305
+ ### Database Backup
306
+ ```bash
307
+ # SQLite
308
+ cp medical_dashboard.db backup_$(date +%Y%m%d).db
309
+
310
+ # PostgreSQL
311
+ pg_dump $DATABASE_URL > backup_$(date +%Y%m%d).sql
312
+ ```
313
+
314
+ ### File Uploads Backup
315
+ ```bash
316
+ tar -czf uploads_backup_$(date +%Y%m%d).tar.gz uploads/
317
+ ```
318
+
319
+ ## Troubleshooting
320
+
321
+ ### Common Issues
322
+
323
+ **Port already in use:**
324
+ ```bash
325
+ lsof -ti:5000 | xargs kill -9
326
+ ```
327
+
328
+ **Database connection errors:**
329
+ - Verify DATABASE_URL format
330
+ - Check database server status
331
+ - Confirm credentials
332
+
333
+ **Template not found:**
334
+ - Ensure all templates are embedded in single file
335
+ - Check template paths in render_template_string calls
336
+
337
+ **Permission denied:**
338
+ ```bash
339
+ chmod +x complete_medical_dashboard.py
340
+ ```
341
+
342
+ ### Debug Mode
343
+ ```python
344
+ # Enable debug mode for development
345
+ app.run(debug=True)
346
+ ```
347
+
348
+ ### Logs
349
+ ```bash
350
+ # View application logs
351
+ tail -f application.log
352
+
353
+ # Docker logs
354
+ docker logs container_name
355
+ ```
356
+
357
+ ## Performance Optimization
358
+
359
+ ### Production Settings
360
+ ```python
361
+ # Disable debug mode
362
+ app.run(debug=False)
363
+
364
+ # Use production WSGI server
365
+ gunicorn --workers 4 --bind 0.0.0.0:5000 complete_medical_dashboard:app
366
+ ```
367
+
368
+ ### Database Optimization
369
+ - Use connection pooling
370
+ - Add database indexes
371
+ - Regular VACUUM for SQLite
372
+ - Monitor query performance
373
+
374
+ ## Support and Maintenance
375
+
376
+ ### Updates
377
+ ```bash
378
+ git pull origin main
379
+ # Restart application
380
+ ```
381
+
382
+ ### Database Migrations
383
+ The application handles database schema automatically. For custom migrations:
384
+ ```python
385
+ with app.app_context():
386
+ db.create_all()
387
+ ```
388
+
389
+ This guide covers most deployment scenarios. For specific platform documentation, refer to the respective platform's official guides.
Dockerfile ADDED
@@ -0,0 +1,25 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ FROM python:3.9-slim
2
+
3
+ WORKDIR /app
4
+
5
+ # Copy requirements and install dependencies
6
+ COPY hf_requirements.txt requirements.txt
7
+ RUN pip install --no-cache-dir -r requirements.txt
8
+
9
+ # Copy application files
10
+ COPY complete_medical_dashboard.py .
11
+ COPY app.py .
12
+
13
+ # Create necessary directories
14
+ RUN mkdir -p uploads
15
+
16
+ # Set environment variables
17
+ ENV FLASK_APP=app.py
18
+ ENV FLASK_ENV=production
19
+ ENV PORT=7860
20
+
21
+ # Expose port
22
+ EXPOSE 7860
23
+
24
+ # Run the application
25
+ CMD ["python", "app.py"]
GITHUB_UPLOAD_GUIDE.md ADDED
@@ -0,0 +1,66 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # GitHub Upload Guide for DermatologyTracker12
2
+
3
+ Your repository: https://github.com/Satilab/DermatologyTracker12
4
+
5
+ ## Files to Upload (Essential)
6
+
7
+ Upload these 5 files to your GitHub repository:
8
+
9
+ 1. **complete_medical_dashboard.py** - The main application (1,400+ lines)
10
+ 2. **README.md** - Project documentation
11
+ 3. **DEPLOYMENT_GUIDE.md** - Deployment instructions
12
+ 4. **install_dependencies.txt** - Required packages
13
+ 5. **run_app.py** - Simple startup script
14
+
15
+ ## Method 1: Upload via GitHub Web Interface
16
+
17
+ 1. Go to: https://github.com/Satilab/DermatologyTracker12
18
+ 2. Click "uploading an existing file" or "Add file" → "Upload files"
19
+ 3. Drag and drop the 5 files listed above
20
+ 4. Add commit message: "Add complete medical dashboard application"
21
+ 5. Click "Commit changes"
22
+
23
+ ## Method 2: Git Commands (if you have Git installed)
24
+
25
+ ```bash
26
+ # Clone your repository
27
+ git clone https://github.com/Satilab/DermatologyTracker12.git
28
+ cd DermatologyTracker12
29
+
30
+ # Copy the files to this directory
31
+ # Then add and commit
32
+ git add complete_medical_dashboard.py README.md DEPLOYMENT_GUIDE.md install_dependencies.txt run_app.py
33
+ git commit -m "Add complete medical dashboard application"
34
+ git push origin main
35
+ ```
36
+
37
+ ## Method 3: GitHub Desktop
38
+
39
+ 1. Download GitHub Desktop
40
+ 2. Clone your repository
41
+ 3. Copy the 5 files to the local repository folder
42
+ 4. Commit and push changes
43
+
44
+ ## After Upload - Testing
45
+
46
+ Once uploaded, anyone can run your application:
47
+
48
+ ```bash
49
+ # Clone repository
50
+ git clone https://github.com/Satilab/DermatologyTracker12.git
51
+ cd DermatologyTracker12
52
+
53
+ # Install dependencies
54
+ pip install flask flask-sqlalchemy flask-login flask-wtf wtforms werkzeug email-validator
55
+
56
+ # Run application
57
+ python complete_medical_dashboard.py
58
+ ```
59
+
60
+ ## Repository Description
61
+
62
+ Add this description to your GitHub repository:
63
+
64
+ **Description:** A comprehensive Flask-based medical assistant dashboard for dermatologists with AI-powered treatment recommendations, patient management, and clinical decision support. Single-file deployment with built-in AI (no API keys required).
65
+
66
+ **Topics:** flask, medical-software, dermatology, ai-assistant, patient-management, healthcare, python, single-file-app
HUGGING_FACE_DEPLOYMENT.md ADDED
@@ -0,0 +1,66 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Hugging Face Spaces Deployment Guide
2
+
3
+ ## Quick Deployment Steps
4
+
5
+ ### 1. Create New Space
6
+ - Go to [Hugging Face Spaces](https://huggingface.co/spaces)
7
+ - Click "Create new Space"
8
+ - Choose:
9
+ - **SDK**: Docker
10
+ - **Hardware**: CPU (basic tier)
11
+ - **Visibility**: Public or Private
12
+
13
+ ### 2. Upload Files
14
+ Upload these essential files to your Space:
15
+
16
+ ```
17
+ ├── README.md (Space header configuration)
18
+ ├── Dockerfile (Container setup)
19
+ ├── hf_requirements.txt (Python dependencies)
20
+ ├── app.py (Hugging Face entry point)
21
+ └── complete_medical_dashboard.py (Main application)
22
+ ```
23
+
24
+ ### 3. Environment Variables
25
+ In your Space settings, add:
26
+
27
+ ```
28
+ SESSION_SECRET=your-secure-random-string-here
29
+ DATABASE_URL=sqlite:///medical_dashboard.db
30
+ PORT=7860
31
+ ```
32
+
33
+ ### 4. Build & Deploy
34
+ - Hugging Face will automatically build and deploy your app
35
+ - Access at: `https://huggingface.co/spaces/YOUR_USERNAME/SPACE_NAME`
36
+
37
+ ## File Details
38
+
39
+ ### README.md
40
+ Contains Space metadata and description
41
+
42
+ ### Dockerfile
43
+ Handles Python environment and application startup
44
+
45
+ ### app.py
46
+ Entry point that imports and runs the main application
47
+
48
+ ### complete_medical_dashboard.py
49
+ Complete single-file Flask application with all features
50
+
51
+ ## Default Login
52
+ - Username: `admin`
53
+ - Password: `admin123`
54
+
55
+ ## Features Available
56
+ - Patient management (CRUD operations)
57
+ - Treatment planning with AI recommendations
58
+ - Drug interaction checking
59
+ - Dashboard analytics
60
+ - Medical assistant with symptom analysis
61
+
62
+ ## Database
63
+ Uses SQLite for maximum compatibility. Data persists between deployments.
64
+
65
+ ## Support
66
+ The application is self-contained with no external API dependencies, ensuring reliable operation on Hugging Face Spaces.
README.md CHANGED
@@ -1,13 +1,36 @@
1
  ---
2
- title: Onedashboard
3
- emoji: 👀
4
- colorFrom: red
5
- colorTo: blue
6
- sdk: gradio
7
- sdk_version: 5.33.0
8
- app_file: app.py
9
  pinned: false
10
  license: mit
11
  ---
12
 
13
- Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  ---
2
+ title: Doctor's Sidekick
3
+ emoji: 🩺
4
+ colorFrom: blue
5
+ colorTo: green
6
+ sdk: docker
 
 
7
  pinned: false
8
  license: mit
9
  ---
10
 
11
+ # Doctor's Sidekick - Medical Dashboard
12
+
13
+ A comprehensive medical assistant platform for dermatology practice management and patient care.
14
+
15
+ ## Features
16
+
17
+ - **Patient Management**: Complete CRUD operations for patient records
18
+ - **Treatment Planning**: AI-powered treatment recommendations and drug interaction checking
19
+ - **Dashboard Analytics**: Visual insights into practice statistics
20
+ - **Medical AI Assistant**: Pattern-based symptom analysis and diagnostic support
21
+ - **User Authentication**: Secure login system with role-based access
22
+
23
+ ## Default Login
24
+
25
+ - Username: `admin`
26
+ - Password: `admin123`
27
+
28
+ ## Technology Stack
29
+
30
+ - Flask 2.3.3
31
+ - SQLAlchemy (SQLite database)
32
+ - Bootstrap 5 UI
33
+ - Pattern-based AI (no external APIs required)
34
+ - Self-contained templates
35
+
36
+ Check it out at https://huggingface.co/spaces/YOUR_USERNAME/doctors-sidekick
README_HF.md ADDED
@@ -0,0 +1,78 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Doctor's Sidekick - Medical Dashboard
2
+
3
+ A comprehensive medical assistant platform for dermatology practice management and patient care.
4
+
5
+ ## Features
6
+
7
+ - **Patient Management**: Complete CRUD operations for patient records
8
+ - **Treatment Planning**: AI-powered treatment recommendations and drug interaction checking
9
+ - **Dashboard Analytics**: Visual insights into practice statistics
10
+ - **Medical AI Assistant**: Pattern-based symptom analysis and diagnostic support
11
+ - **User Authentication**: Secure login system with role-based access
12
+
13
+ ## Deployment on Hugging Face Spaces
14
+
15
+ ### Method 1: Direct Upload
16
+
17
+ 1. Create a new Space on Hugging Face Spaces
18
+ 2. Select "Gradio" as SDK (or "Docker" for Docker deployment)
19
+ 3. Upload these files:
20
+ - `complete_medical_dashboard.py` (main application)
21
+ - `app.py` (Hugging Face entry point)
22
+ - `hf_requirements.txt` (dependencies)
23
+ - `Dockerfile` (for Docker deployment)
24
+
25
+ ### Method 2: Git Repository
26
+
27
+ ```bash
28
+ git clone https://huggingface.co/spaces/YOUR_USERNAME/SPACE_NAME
29
+ cd SPACE_NAME
30
+ # Copy files to this directory
31
+ git add .
32
+ git commit -m "Add Doctor's Sidekick Medical Dashboard"
33
+ git push
34
+ ```
35
+
36
+ ### Environment Variables
37
+
38
+ Set these in your Hugging Face Space settings:
39
+
40
+ ```
41
+ DATABASE_URL=sqlite:///medical_dashboard.db
42
+ SESSION_SECRET=your-secret-key-here
43
+ PORT=7860
44
+ ```
45
+
46
+ ## Local Development
47
+
48
+ ```bash
49
+ pip install -r hf_requirements.txt
50
+ python app.py
51
+ ```
52
+
53
+ Access at: http://localhost:7860
54
+
55
+ ## Default Login
56
+
57
+ - Username: `admin`
58
+ - Password: `admin123`
59
+
60
+ ## Technology Stack
61
+
62
+ - Flask 2.3.3
63
+ - SQLAlchemy (SQLite database)
64
+ - Bootstrap 5 UI
65
+ - Pattern-based AI (no external APIs required)
66
+ - Self-contained templates
67
+
68
+ ## Medical Features
69
+
70
+ - Dermatology-focused treatment templates
71
+ - Drug interaction analysis
72
+ - Patient visit tracking
73
+ - Treatment plan generation
74
+ - Symptom analysis
75
+
76
+ ## License
77
+
78
+ Open source medical assistant tool for educational and practice management purposes.
app.py CHANGED
@@ -1,1450 +1,7 @@
1
- #!/usr/bin/env python3
2
- """
3
- Doctor's Sidekick - Complete Medical Dashboard
4
- A comprehensive Flask-based medical assistant dashboard for dermatologists
5
- with patient management, treatment planning, and AI-powered recommendations.
6
 
7
- Single file implementation for easy deployment and GitHub sharing.
8
- """
9
-
10
- import os
11
- import json
12
- import logging
13
- from datetime import datetime, date, timedelta
14
- from typing import Dict, List, Optional
15
- from werkzeug.security import check_password_hash, generate_password_hash
16
- from werkzeug.utils import secure_filename
17
- from werkzeug.middleware.proxy_fix import ProxyFix
18
-
19
- # Flask and extensions
20
- from flask import Flask, render_template_string, request, redirect, url_for, flash, jsonify, send_from_directory
21
- from flask_sqlalchemy import SQLAlchemy
22
- from flask_login import LoginManager, UserMixin, login_user, logout_user, login_required, current_user
23
- from flask_wtf import FlaskForm
24
- from flask_wtf.file import FileField, FileAllowed
25
- from wtforms import StringField, PasswordField, TextAreaField, DateField, SelectField, IntegerField, BooleanField
26
- from wtforms.validators import DataRequired, Length, Email, Optional, NumberRange
27
- from sqlalchemy.orm import DeclarativeBase
28
-
29
- # Configure logging
30
- logging.basicConfig(level=logging.DEBUG)
31
-
32
- # ============================================================================
33
- # DATABASE MODELS
34
- # ============================================================================
35
-
36
- class Base(DeclarativeBase):
37
- pass
38
-
39
- db = SQLAlchemy(model_class=Base)
40
-
41
- class Role(db.Model):
42
- __tablename__ = 'role'
43
- id = db.Column(db.Integer, primary_key=True)
44
- name = db.Column(db.String(64), unique=True, nullable=False)
45
- description = db.Column(db.Text)
46
- users = db.relationship('User', backref='role', lazy='dynamic')
47
-
48
- class User(UserMixin, db.Model):
49
- __tablename__ = 'user'
50
- id = db.Column(db.Integer, primary_key=True)
51
- username = db.Column(db.String(64), unique=True, nullable=False)
52
- email = db.Column(db.String(120), unique=True, nullable=False)
53
- password_hash = db.Column(db.String(256))
54
- first_name = db.Column(db.String(64), nullable=False)
55
- last_name = db.Column(db.String(64), nullable=False)
56
- created_at = db.Column(db.DateTime, default=datetime.utcnow)
57
- is_active = db.Column(db.Boolean, default=True)
58
- role_id = db.Column(db.Integer, db.ForeignKey('role.id'))
59
-
60
- def full_name(self):
61
- return f"{self.first_name} {self.last_name}"
62
-
63
- class Patient(db.Model):
64
- __tablename__ = 'patient'
65
- id = db.Column(db.Integer, primary_key=True)
66
- patient_id = db.Column(db.String(20), unique=True, nullable=False)
67
- first_name = db.Column(db.String(64), nullable=False)
68
- last_name = db.Column(db.String(64), nullable=False)
69
- date_of_birth = db.Column(db.Date, nullable=False)
70
- gender = db.Column(db.String(10))
71
- phone = db.Column(db.String(20))
72
- email = db.Column(db.String(120))
73
- address = db.Column(db.Text)
74
- emergency_contact = db.Column(db.String(100))
75
- emergency_phone = db.Column(db.String(20))
76
- allergies = db.Column(db.Text)
77
- medical_history = db.Column(db.Text)
78
- insurance_info = db.Column(db.Text)
79
- created_at = db.Column(db.DateTime, default=datetime.utcnow)
80
- last_visit = db.Column(db.DateTime)
81
- doctor_id = db.Column(db.Integer, db.ForeignKey('user.id'))
82
-
83
- def full_name(self):
84
- return f"{self.first_name} {self.last_name}"
85
-
86
- def age(self):
87
- return (date.today() - self.date_of_birth).days // 365
88
-
89
- class Visit(db.Model):
90
- __tablename__ = 'visit'
91
- id = db.Column(db.Integer, primary_key=True)
92
- visit_date = db.Column(db.DateTime, nullable=False, default=datetime.utcnow)
93
- visit_type = db.Column(db.String(50))
94
- chief_complaint = db.Column(db.Text)
95
- diagnosis = db.Column(db.Text)
96
- treatment_notes = db.Column(db.Text)
97
- follow_up_date = db.Column(db.Date)
98
- patient_id = db.Column(db.Integer, db.ForeignKey('patient.id'), nullable=False)
99
-
100
- class Condition(db.Model):
101
- __tablename__ = 'condition'
102
- id = db.Column(db.Integer, primary_key=True)
103
- name = db.Column(db.String(100), nullable=False)
104
- description = db.Column(db.Text)
105
- icd_code = db.Column(db.String(20))
106
- category = db.Column(db.String(50))
107
-
108
- class TreatmentTemplate(db.Model):
109
- __tablename__ = 'treatment_template'
110
- id = db.Column(db.Integer, primary_key=True)
111
- name = db.Column(db.String(100), nullable=False)
112
- description = db.Column(db.Text)
113
- medications = db.Column(db.Text)
114
- instructions = db.Column(db.Text)
115
- duration = db.Column(db.String(50))
116
- follow_up_weeks = db.Column(db.Integer)
117
- is_active = db.Column(db.Boolean, default=True)
118
- condition_id = db.Column(db.Integer, db.ForeignKey('condition.id'))
119
-
120
- class TreatmentPlan(db.Model):
121
- __tablename__ = 'treatment_plan'
122
- id = db.Column(db.Integer, primary_key=True)
123
- diagnosis = db.Column(db.String(200), nullable=False)
124
- medications = db.Column(db.Text)
125
- instructions = db.Column(db.Text)
126
- duration = db.Column(db.String(50))
127
- follow_up_date = db.Column(db.Date)
128
- notes = db.Column(db.Text)
129
- created_at = db.Column(db.DateTime, default=datetime.utcnow)
130
- patient_id = db.Column(db.Integer, db.ForeignKey('patient.id'), nullable=False)
131
- created_by_id = db.Column(db.Integer, db.ForeignKey('user.id'), nullable=False)
132
-
133
- # ============================================================================
134
- # AI MEDICAL ASSISTANT
135
- # ============================================================================
136
-
137
- class MedicalAI:
138
- """AI-powered medical assistant for dermatology practice"""
139
-
140
- def __init__(self):
141
- self.dermatology_context = """
142
- Expert dermatology AI assistant providing evidence-based recommendations
143
- following current clinical guidelines with safety warnings.
144
- """
145
-
146
- def _analyze_symptoms(self, symptoms: str, patient_data: Dict) -> Dict:
147
- """Analyze symptoms and return appropriate medical recommendations"""
148
- symptoms_lower = symptoms.lower()
149
- age = patient_data.get('age', 30)
150
-
151
- # Dermatitis/Eczema conditions
152
- if any(keyword in symptoms_lower for keyword in ['rash', 'eczema', 'dermatitis', 'itchy', 'red skin']):
153
- return {
154
- "condition_type": "dermatitis",
155
- "primary_diagnosis": "Contact Dermatitis",
156
- "confidence": 80,
157
- "medications": "Topical corticosteroids: Hydrocortisone 1% cream BID x 7-14 days\nOral antihistamines: Cetirizine 10mg daily PRN pruritus\nEmollients: Apply liberally QID",
158
- "instructions": "Apply medications to affected areas only\nAvoid known triggers\nUse fragrance-free products\nMoisturize regularly",
159
- "follow_up": "Return in 2 weeks if no improvement\nSooner if worsening or signs of infection"
160
- }
161
-
162
- # Acne conditions
163
- elif any(keyword in symptoms_lower for keyword in ['acne', 'pimples', 'blackheads', 'whiteheads']):
164
- return {
165
- "condition_type": "acne",
166
- "primary_diagnosis": "Acne Vulgaris",
167
- "confidence": 85,
168
- "medications": "Topical retinoid: Adapalene 0.1% gel daily at bedtime\nBenzoyl peroxide 2.5% gel BID\nClindamycin 1% solution BID (if inflammatory)",
169
- "instructions": "Start slowly with retinoid (every other night)\nUse non-comedogenic products\nGentle cleansing twice daily\nSun protection mandatory",
170
- "follow_up": "4-6 weeks for initial assessment\n3 months for full evaluation"
171
- }
172
-
173
- # Psoriasis
174
- elif any(keyword in symptoms_lower for keyword in ['psoriasis', 'silvery scales', 'plaques']):
175
- return {
176
- "condition_type": "psoriasis",
177
- "primary_diagnosis": "Psoriasis Vulgaris",
178
- "confidence": 75,
179
- "medications": "Topical corticosteroids: Clobetasol 0.05% ointment BID x 2 weeks\nVitamin D analogues: Calcipotriene 0.005% ointment BID\nMoisturizers: Apply liberally QID",
180
- "instructions": "Limit potent steroids to 2 weeks\nAlternate with vitamin D analogues\nAvoid triggers (stress, trauma, infections)\nRegular moisturizing essential",
181
- "follow_up": "2-4 weeks for response assessment\nDermatology referral if extensive"
182
- }
183
-
184
- # Default/general skin condition
185
- else:
186
- return {
187
- "condition_type": "general",
188
- "primary_diagnosis": "Non-specific dermatitis",
189
- "confidence": 60,
190
- "medications": "Gentle moisturizers: Apply QID\nMild topical corticosteroids if inflamed\nOral antihistamines PRN pruritus",
191
- "instructions": "Avoid harsh soaps and chemicals\nUse fragrance-free products\nMoisturize regularly\nIdentify and avoid triggers",
192
- "follow_up": "1-2 weeks if symptoms persist\nDermatology referral if no improvement"
193
- }
194
-
195
- def generate_treatment_plan(self, patient_data: Dict, symptoms: str, diagnosis: str = None) -> Dict:
196
- """Generate AI-powered treatment plan based on patient data and symptoms"""
197
-
198
- analysis = self._analyze_symptoms(symptoms, patient_data)
199
-
200
- # Build differential diagnosis
201
- if analysis["condition_type"] == "dermatitis":
202
- differential_diagnosis = [
203
- {
204
- "condition": "Contact Dermatitis",
205
- "confidence": 80,
206
- "description": "Inflammatory reaction to allergens or irritants",
207
- "key_features": ["Well-demarcated erythema", "Vesicles", "Pruritus"]
208
- },
209
- {
210
- "condition": "Atopic Dermatitis",
211
- "confidence": 65,
212
- "description": "Chronic inflammatory skin condition",
213
- "key_features": ["Dry skin", "Lichenification", "Flexural involvement"]
214
- }
215
- ]
216
- elif analysis["condition_type"] == "acne":
217
- differential_diagnosis = [
218
- {
219
- "condition": "Acne Vulgaris",
220
- "confidence": 85,
221
- "description": "Common inflammatory condition of pilosebaceous units",
222
- "key_features": ["Comedones", "Papules", "Pustules", "Face/chest involvement"]
223
- }
224
- ]
225
- else:
226
- differential_diagnosis = [
227
- {
228
- "condition": analysis["primary_diagnosis"],
229
- "confidence": analysis["confidence"],
230
- "description": "Clinical assessment required for definitive diagnosis",
231
- "key_features": ["Variable presentation", "Patient history important"]
232
- }
233
- ]
234
-
235
- return {
236
- "differential_diagnosis": differential_diagnosis,
237
- "medications": analysis["medications"],
238
- "instructions": analysis["instructions"],
239
- "follow_up": analysis["follow_up"],
240
- "warnings": [
241
- "Monitor for signs of secondary infection",
242
- "Discontinue treatment if allergic reactions occur",
243
- "Follow up if symptoms worsen or persist"
244
- ]
245
- }
246
-
247
- def check_drug_interactions(self, medications: List[str], patient_allergies: str = "") -> Dict:
248
- """Check for drug interactions and contraindications"""
249
-
250
- if not medications:
251
- return {"interactions": [], "warnings": []}
252
-
253
- interactions = []
254
- warnings = []
255
- med_list = [med.lower() for med in medications]
256
-
257
- # Check for common dermatology drug interactions
258
- if any('warfarin' in med for med in med_list) and any('azole' in med for med in med_list):
259
- interactions.append({
260
- "drugs": "Warfarin + Azole antifungals",
261
- "severity": "Major",
262
- "description": "Increased risk of bleeding due to enhanced warfarin effect"
263
- })
264
-
265
- # Check for corticosteroid warnings
266
- if any('corticosteroid' in med or 'steroid' in med for med in med_list):
267
- warnings.append("Monitor for skin atrophy with prolonged topical steroid use")
268
-
269
- # Check for retinoid warnings
270
- if any('retinoid' in med or 'tretinoin' in med or 'adapalene' in med for med in med_list):
271
- warnings.append("Contraindicated in pregnancy")
272
- warnings.append("Increased photosensitivity - use sun protection")
273
-
274
- return {
275
- "interactions": interactions,
276
- "warnings": warnings + ["Always verify drug interactions with current medication database"]
277
- }
278
-
279
- def smart_template_suggestions(self, condition: str, patient_demographics: Dict) -> List[Dict]:
280
- """Suggest optimized treatment templates based on condition and patient factors"""
281
-
282
- age = patient_demographics.get('age', 30)
283
- templates = []
284
-
285
- if 'dermatitis' in condition.lower() or 'eczema' in condition.lower():
286
- templates = [
287
- {
288
- "name": "Mild Dermatitis Protocol",
289
- "description": "For localized, mild inflammatory dermatitis",
290
- "medications": "Hydrocortisone 1% cream BID x 7 days\nCetirizine 10mg daily PRN\nEmollient cream QID",
291
- "duration": "1-2 weeks",
292
- "instructions": "Apply steroid sparingly to affected areas\nMoisturize frequently\nAvoid triggers",
293
- "follow_up": "2 weeks",
294
- "success_probability": 85
295
- }
296
- ]
297
-
298
- elif 'acne' in condition.lower():
299
- if age < 18:
300
- templates.append({
301
- "name": "Teen Acne Starter Protocol",
302
- "description": "Gentle introduction for adolescent acne",
303
- "medications": "Benzoyl peroxide 2.5% gel daily\nCetaphil gentle cleanser BID",
304
- "duration": "6-8 weeks",
305
- "instructions": "Start slowly, every other day initially\nUse oil-free moisturizer\nSun protection essential",
306
- "follow_up": "4-6 weeks",
307
- "success_probability": 70
308
- })
309
-
310
- return templates if templates else [{
311
- "name": "General Dermatology Protocol",
312
- "description": "Standard approach for common skin conditions",
313
- "medications": "Moisturizer QID\nMild topical corticosteroid PRN",
314
- "duration": "2-4 weeks",
315
- "instructions": "Gentle skin care routine\nAvoid harsh products",
316
- "follow_up": "2-3 weeks",
317
- "success_probability": 65
318
- }]
319
-
320
- # Global AI service instance
321
- medical_ai = MedicalAI()
322
-
323
- # ============================================================================
324
- # FORMS
325
- # ============================================================================
326
-
327
- class LoginForm(FlaskForm):
328
- username = StringField('Username', validators=[DataRequired()])
329
- password = PasswordField('Password', validators=[DataRequired()])
330
-
331
- class PatientForm(FlaskForm):
332
- first_name = StringField('First Name', validators=[DataRequired(), Length(max=64)])
333
- last_name = StringField('Last Name', validators=[DataRequired(), Length(max=64)])
334
- date_of_birth = DateField('Date of Birth', validators=[DataRequired()])
335
- gender = SelectField('Gender', choices=[('Male', 'Male'), ('Female', 'Female'), ('Other', 'Other')])
336
- phone = StringField('Phone', validators=[Length(max=20)])
337
- email = StringField('Email', validators=[Optional(), Email(), Length(max=120)])
338
- address = TextAreaField('Address')
339
- allergies = TextAreaField('Allergies')
340
- medical_history = TextAreaField('Medical History')
341
-
342
- class TreatmentPlanForm(FlaskForm):
343
- diagnosis = StringField('Diagnosis', validators=[DataRequired(), Length(max=200)])
344
- medications = TextAreaField('Medications')
345
- instructions = TextAreaField('Instructions')
346
- duration = StringField('Duration', validators=[Length(max=50)])
347
- follow_up_date = DateField('Follow-up Date', validators=[Optional()])
348
- notes = TextAreaField('Additional Notes')
349
-
350
- # ============================================================================
351
- # FLASK APPLICATION SETUP
352
- # ============================================================================
353
-
354
- app = Flask(__name__)
355
- app.secret_key = os.environ.get("SESSION_SECRET", "dev-secret-key-change-in-production")
356
- app.wsgi_app = ProxyFix(app.wsgi_app, x_proto=1, x_host=1)
357
-
358
- # Database configuration
359
- app.config["SQLALCHEMY_DATABASE_URI"] = os.environ.get("DATABASE_URL", "sqlite:///medical_dashboard.db")
360
- app.config["SQLALCHEMY_ENGINE_OPTIONS"] = {
361
- "pool_recycle": 300,
362
- "pool_pre_ping": True,
363
- }
364
-
365
- # File upload configuration
366
- app.config['UPLOAD_FOLDER'] = 'uploads'
367
- app.config['MAX_CONTENT_LENGTH'] = 16 * 1024 * 1024 # 16MB max file size
368
-
369
- # Initialize extensions
370
- db.init_app(app)
371
- login_manager = LoginManager()
372
- login_manager.init_app(app)
373
- login_manager.login_view = 'login'
374
-
375
- @login_manager.user_loader
376
- def load_user(user_id):
377
- return User.query.get(int(user_id))
378
-
379
- # ============================================================================
380
- # HTML TEMPLATES
381
- # ============================================================================
382
-
383
- BASE_TEMPLATE = """
384
- <!DOCTYPE html>
385
- <html lang="en">
386
- <head>
387
- <meta charset="UTF-8">
388
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
389
- <title>{% block title %}Doctor's Sidekick{% endblock %}</title>
390
- <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
391
- <link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css" rel="stylesheet">
392
- <style>
393
- :root {
394
- --primary-color: #0F766E;
395
- --secondary-color: #1E40AF;
396
- --accent-color: #F59E0B;
397
- --success-color: #059669;
398
- --danger-color: #DC2626;
399
- }
400
- body { font-family: 'Inter', system-ui, sans-serif; background-color: #f8fafc; }
401
- .navbar-brand { font-weight: 700; color: var(--primary-color) !important; }
402
- .btn-primary { background-color: var(--primary-color); border-color: var(--primary-color); }
403
- .btn-primary:hover { background-color: #065f5a; border-color: #065f5a; }
404
- .text-primary { color: var(--primary-color) !important; }
405
- .bg-primary { background-color: var(--primary-color) !important; }
406
- .card { border: none; box-shadow: 0 1px 3px rgba(0,0,0,0.1); }
407
- .nav-link.active { background-color: var(--primary-color) !important; }
408
- </style>
409
- </head>
410
- <body>
411
- <nav class="navbar navbar-expand-lg navbar-light bg-white shadow-sm">
412
- <div class="container">
413
- <a class="navbar-brand" href="{{ url_for('dashboard') }}">
414
- <i class="fas fa-user-md me-2"></i>Doctor's Sidekick
415
- </a>
416
- {% if current_user.is_authenticated %}
417
- <div class="navbar-nav ms-auto">
418
- <a class="nav-link" href="{{ url_for('dashboard') }}">
419
- <i class="fas fa-tachometer-alt me-1"></i> Dashboard
420
- </a>
421
- <a class="nav-link" href="{{ url_for('patients') }}">
422
- <i class="fas fa-users me-1"></i> Patients
423
- </a>
424
- <a class="nav-link" href="{{ url_for('treatment_planner') }}">
425
- <i class="fas fa-prescription-bottle-alt me-1"></i> Treatment Planner
426
- </a>
427
- <a class="nav-link" href="{{ url_for('ai_assistant') }}">
428
- <i class="fas fa-brain me-1"></i> AI Assistant
429
- </a>
430
- <div class="nav-link">
431
- <span class="text-muted">{{ current_user.full_name() }}</span>
432
- </div>
433
- <a class="nav-link" href="{{ url_for('logout') }}">
434
- <i class="fas fa-sign-out-alt me-1"></i> Logout
435
- </a>
436
- </div>
437
- {% endif %}
438
- </div>
439
- </nav>
440
-
441
- <main class="container mt-4">
442
- {% with messages = get_flashed_messages(with_categories=true) %}
443
- {% if messages %}
444
- {% for category, message in messages %}
445
- <div class="alert alert-{{ 'danger' if category == 'error' else category }} alert-dismissible fade show">
446
- {{ message }}
447
- <button type="button" class="btn-close" data-bs-dismiss="alert"></button>
448
- </div>
449
- {% endfor %}
450
- {% endif %}
451
- {% endwith %}
452
-
453
- {% block content %}{% endblock %}
454
- </main>
455
-
456
- <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
457
- {% block scripts %}{% endblock %}
458
- </body>
459
- </html>
460
- """
461
-
462
- LOGIN_TEMPLATE = """
463
- {% extends "base.html" %}
464
- {% block title %}Login - Doctor's Sidekick{% endblock %}
465
- {% block content %}
466
- <div class="row justify-content-center">
467
- <div class="col-md-6 col-lg-4">
468
- <div class="card">
469
- <div class="card-header bg-primary text-white text-center">
470
- <h4><i class="fas fa-user-md me-2"></i>Doctor's Login</h4>
471
- </div>
472
- <div class="card-body">
473
- <form method="POST">
474
- {{ form.hidden_tag() }}
475
- <div class="mb-3">
476
- {{ form.username.label(class="form-label") }}
477
- {{ form.username(class="form-control") }}
478
- </div>
479
- <div class="mb-3">
480
- {{ form.password.label(class="form-label") }}
481
- {{ form.password(class="form-control") }}
482
- </div>
483
- <div class="d-grid">
484
- <button type="submit" class="btn btn-primary">
485
- <i class="fas fa-sign-in-alt me-2"></i>Login
486
- </button>
487
- </div>
488
- </form>
489
- <div class="mt-3 text-center">
490
- <small class="text-muted">Demo: admin/admin</small>
491
- </div>
492
- </div>
493
- </div>
494
- </div>
495
- </div>
496
- {% endblock %}
497
- """
498
-
499
- DASHBOARD_TEMPLATE = """
500
- {% extends "base.html" %}
501
- {% block title %}Dashboard - Doctor's Sidekick{% endblock %}
502
- {% block content %}
503
- <div class="d-flex justify-content-between align-items-center mb-4">
504
- <h1 class="h3">Medical Dashboard</h1>
505
- <div class="text-end">
506
- <span class="badge bg-success">AI Online</span>
507
- <div class="small text-muted">{{ current_user.full_name() }}</div>
508
- </div>
509
- </div>
510
-
511
- <div class="row g-4">
512
- <!-- Stats Cards -->
513
- <div class="col-md-3">
514
- <div class="card text-center">
515
- <div class="card-body">
516
- <i class="fas fa-users fa-2x text-primary mb-2"></i>
517
- <h5>{{ stats.total_patients }}</h5>
518
- <small class="text-muted">Total Patients</small>
519
- </div>
520
- </div>
521
- </div>
522
- <div class="col-md-3">
523
- <div class="card text-center">
524
- <div class="card-body">
525
- <i class="fas fa-calendar-check fa-2x text-success mb-2"></i>
526
- <h5>{{ stats.visits_today }}</h5>
527
- <small class="text-muted">Visits Today</small>
528
- </div>
529
- </div>
530
- </div>
531
- <div class="col-md-3">
532
- <div class="card text-center">
533
- <div class="card-body">
534
- <i class="fas fa-prescription-bottle-alt fa-2x text-warning mb-2"></i>
535
- <h5>{{ stats.treatment_plans }}</h5>
536
- <small class="text-muted">Treatment Plans</small>
537
- </div>
538
- </div>
539
- </div>
540
- <div class="col-md-3">
541
- <div class="card text-center">
542
- <div class="card-body">
543
- <i class="fas fa-brain fa-2x text-info mb-2"></i>
544
- <h5>Active</h5>
545
- <small class="text-muted">AI Assistant</small>
546
- </div>
547
- </div>
548
- </div>
549
- </div>
550
-
551
- <div class="row g-4 mt-4">
552
- <!-- Quick Actions -->
553
- <div class="col-md-8">
554
- <div class="card">
555
- <div class="card-header">
556
- <h5><i class="fas fa-bolt me-2"></i>Quick Actions</h5>
557
- </div>
558
- <div class="card-body">
559
- <div class="row g-3">
560
- <div class="col-md-6">
561
- <a href="{{ url_for('new_patient') }}" class="btn btn-outline-primary w-100">
562
- <i class="fas fa-user-plus me-2"></i>New Patient
563
- </a>
564
- </div>
565
- <div class="col-md-6">
566
- <a href="{{ url_for('treatment_planner') }}" class="btn btn-outline-success w-100">
567
- <i class="fas fa-prescription-bottle-alt me-2"></i>Treatment Plan
568
- </a>
569
- </div>
570
- <div class="col-md-6">
571
- <a href="{{ url_for('ai_assistant') }}" class="btn btn-outline-info w-100">
572
- <i class="fas fa-brain me-2"></i>AI Analysis
573
- </a>
574
- </div>
575
- <div class="col-md-6">
576
- <a href="{{ url_for('patients') }}" class="btn btn-outline-warning w-100">
577
- <i class="fas fa-search me-2"></i>Find Patient
578
- </a>
579
- </div>
580
- </div>
581
- </div>
582
- </div>
583
- </div>
584
-
585
- <!-- Recent Activity -->
586
- <div class="col-md-4">
587
- <div class="card">
588
- <div class="card-header">
589
- <h6><i class="fas fa-clock me-2"></i>Recent Activity</h6>
590
- </div>
591
- <div class="card-body">
592
- <div class="small">
593
- <div class="d-flex justify-content-between mb-2">
594
- <span>System initialized</span>
595
- <span class="text-muted">Now</span>
596
- </div>
597
- <div class="d-flex justify-content-between mb-2">
598
- <span>AI services online</span>
599
- <span class="text-muted">Now</span>
600
- </div>
601
- <div class="d-flex justify-content-between">
602
- <span>Dashboard ready</span>
603
- <span class="text-muted">Now</span>
604
- </div>
605
- </div>
606
- </div>
607
- </div>
608
- </div>
609
- </div>
610
- {% endblock %}
611
- """
612
-
613
- PATIENTS_TEMPLATE = """
614
- {% extends "base.html" %}
615
- {% block title %}Patients - Doctor's Sidekick{% endblock %}
616
- {% block content %}
617
- <div class="d-flex justify-content-between align-items-center mb-4">
618
- <h1 class="h3">Patient Management</h1>
619
- <a href="{{ url_for('new_patient') }}" class="btn btn-primary">
620
- <i class="fas fa-user-plus me-2"></i>New Patient
621
- </a>
622
- </div>
623
-
624
- <div class="card">
625
- <div class="card-body">
626
- {% if patients %}
627
- <div class="table-responsive">
628
- <table class="table table-hover">
629
- <thead>
630
- <tr>
631
- <th>Patient ID</th>
632
- <th>Name</th>
633
- <th>Age</th>
634
- <th>Gender</th>
635
- <th>Last Visit</th>
636
- <th>Actions</th>
637
- </tr>
638
- </thead>
639
- <tbody>
640
- {% for patient in patients %}
641
- <tr>
642
- <td>{{ patient.patient_id }}</td>
643
- <td>{{ patient.full_name() }}</td>
644
- <td>{{ patient.age() }}</td>
645
- <td>{{ patient.gender or 'N/A' }}</td>
646
- <td>{{ patient.last_visit.strftime('%Y-%m-%d') if patient.last_visit else 'Never' }}</td>
647
- <td>
648
- <a href="{{ url_for('patient_detail', id=patient.id) }}" class="btn btn-sm btn-outline-primary">
649
- <i class="fas fa-eye"></i>
650
- </a>
651
- <a href="{{ url_for('edit_patient', id=patient.id) }}" class="btn btn-sm btn-outline-secondary">
652
- <i class="fas fa-edit"></i>
653
- </a>
654
- </td>
655
- </tr>
656
- {% endfor %}
657
- </tbody>
658
- </table>
659
- </div>
660
- {% else %}
661
- <div class="text-center py-5">
662
- <i class="fas fa-users fa-3x text-muted mb-3"></i>
663
- <h5>No patients registered yet</h5>
664
- <p class="text-muted">Start by adding your first patient</p>
665
- <a href="{{ url_for('new_patient') }}" class="btn btn-primary">
666
- <i class="fas fa-user-plus me-2"></i>Add First Patient
667
- </a>
668
- </div>
669
- {% endif %}
670
- </div>
671
- </div>
672
- {% endblock %}
673
- """
674
-
675
- AI_ASSISTANT_TEMPLATE = """
676
- {% extends "base.html" %}
677
- {% block title %}AI Assistant - Doctor's Sidekick{% endblock %}
678
- {% block content %}
679
- <div class="d-flex justify-content-between align-items-center mb-4">
680
- <div>
681
- <h1 class="h3"><i class="fas fa-brain me-2 text-primary"></i>AI-Powered Treatment Assistant</h1>
682
- <p class="text-muted mb-0">Intelligent diagnostic and treatment recommendations</p>
683
- </div>
684
- <div class="badge bg-success">
685
- <i class="fas fa-check-circle me-1"></i> AI Online
686
- </div>
687
- </div>
688
-
689
- <div class="row g-4">
690
- <div class="col-lg-8">
691
- <div class="card">
692
- <div class="card-header bg-primary text-white">
693
- <h5 class="mb-0"><i class="fas fa-stethoscope me-2"></i>Symptom Analysis</h5>
694
- </div>
695
- <div class="card-body">
696
- <form id="symptomAnalysisForm">
697
- <div class="row g-3">
698
- <div class="col-md-6">
699
- <label class="form-label">Select Patient</label>
700
- <select class="form-select" name="patient_id" id="aiPatientSelect" required>
701
- <option value="">Choose patient...</option>
702
- {% for patient in patients %}
703
- <option value="{{ patient.id }}"
704
- data-age="{{ patient.age() }}"
705
- data-gender="{{ patient.gender }}"
706
- data-allergies="{{ patient.allergies or '' }}"
707
- data-medical-history="{{ patient.medical_history or '' }}">
708
- {{ patient.full_name() }} ({{ patient.patient_id }}) - Age {{ patient.age() }}
709
- </option>
710
- {% endfor %}
711
- </select>
712
- </div>
713
- <div class="col-md-6">
714
- <label class="form-label">Urgency Level</label>
715
- <select class="form-select" name="urgency">
716
- <option value="routine">Routine</option>
717
- <option value="urgent">Urgent</option>
718
- <option value="emergency">Emergency</option>
719
- </select>
720
- </div>
721
- <div class="col-12">
722
- <label class="form-label">Chief Complaint & Symptoms</label>
723
- <textarea class="form-control" name="symptoms" rows="4"
724
- placeholder="Describe the patient's symptoms in detail..."
725
- required></textarea>
726
- </div>
727
- <div class="col-md-6">
728
- <label class="form-label">Working Diagnosis (Optional)</label>
729
- <input type="text" class="form-control" name="suspected_diagnosis"
730
- placeholder="Your suspected diagnosis...">
731
- </div>
732
- </div>
733
- <div class="d-flex gap-2 mt-4">
734
- <button type="submit" class="btn btn-primary">
735
- <i class="fas fa-brain me-2"></i> Analyze with AI
736
- </button>
737
- <button type="button" class="btn btn-outline-secondary" onclick="clearAIForm()">
738
- <i class="fas fa-eraser me-2"></i> Clear
739
- </button>
740
- </div>
741
- </form>
742
- </div>
743
- </div>
744
-
745
- <div id="aiResults" class="d-none mt-4">
746
- <div class="card">
747
- <div class="card-header bg-success text-white">
748
- <h5 class="mb-0"><i class="fas fa-list-alt me-2"></i>AI Analysis Results</h5>
749
- </div>
750
- <div class="card-body" id="analysisResults">
751
- <!-- AI results will be populated here -->
752
- </div>
753
- </div>
754
- </div>
755
- </div>
756
-
757
- <div class="col-lg-4">
758
- <div class="card">
759
- <div class="card-header bg-light">
760
- <h6 class="mb-0"><i class="fas fa-tools me-2 text-primary"></i>Quick AI Tools</h6>
761
- </div>
762
- <div class="card-body">
763
- <div class="d-grid gap-2">
764
- <button class="btn btn-outline-primary btn-sm" onclick="quickDrugCheck()">
765
- <i class="fas fa-pills me-2"></i> Drug Interaction Check
766
- </button>
767
- <button class="btn btn-outline-success btn-sm" onclick="aiInsights()">
768
- <i class="fas fa-lightbulb me-2"></i> AI Insights
769
- </button>
770
- <button class="btn btn-outline-info btn-sm" onclick="templateGenerator()">
771
- <i class="fas fa-magic me-2"></i> Generate Template
772
- </button>
773
- </div>
774
- </div>
775
- </div>
776
-
777
- <div class="card mt-4">
778
- <div class="card-header bg-light">
779
- <h6 class="mb-0"><i class="fas fa-lightbulb me-2 text-warning"></i>AI Tips</h6>
780
- </div>
781
- <div class="card-body">
782
- <div class="small">
783
- <div class="mb-3">
784
- <i class="fas fa-info-circle text-info me-2"></i>
785
- <strong>Smart Documentation:</strong> AI helps generate comprehensive visit notes.
786
- </div>
787
- <div class="mb-3">
788
- <i class="fas fa-shield-alt text-success me-2"></i>
789
- <strong>Safety First:</strong> AI recommendations supplement clinical judgment.
790
- </div>
791
- <div>
792
- <i class="fas fa-clock text-primary me-2"></i>
793
- <strong>Continuous Learning:</strong> System improves with medical literature updates.
794
- </div>
795
- </div>
796
- </div>
797
- </div>
798
- </div>
799
- </div>
800
-
801
- <div id="aiLoadingOverlay" class="d-none">
802
- <div class="position-fixed top-0 start-0 w-100 h-100 d-flex align-items-center justify-content-center"
803
- style="background: rgba(0,0,0,0.7); z-index: 9999;">
804
- <div class="text-center text-white">
805
- <div class="spinner-border text-primary mb-3" style="width: 3rem; height: 3rem;"></div>
806
- <h5>AI is analyzing...</h5>
807
- <p class="mb-0">This may take a few moments</p>
808
- </div>
809
- </div>
810
- </div>
811
- {% endblock %}
812
-
813
- {% block scripts %}
814
- <script>
815
- // AI Assistant JavaScript
816
- document.getElementById('symptomAnalysisForm').addEventListener('submit', async function(e) {
817
- e.preventDefault();
818
-
819
- const formData = new FormData(this);
820
- const analysisData = {
821
- patient_id: formData.get('patient_id'),
822
- symptoms: formData.get('symptoms'),
823
- suspected_diagnosis: formData.get('suspected_diagnosis'),
824
- urgency: formData.get('urgency')
825
- };
826
-
827
- if (!analysisData.patient_id || !analysisData.symptoms) {
828
- alert('Please select a patient and enter symptoms.');
829
- return;
830
- }
831
-
832
- document.getElementById('aiLoadingOverlay').classList.remove('d-none');
833
-
834
- try {
835
- const response = await fetch('/api/ai/analyze-symptoms', {
836
- method: 'POST',
837
- headers: {
838
- 'Content-Type': 'application/json'
839
- },
840
- body: JSON.stringify(analysisData)
841
- });
842
-
843
- const results = await response.json();
844
-
845
- if (results.success) {
846
- displayResults(results);
847
- } else {
848
- alert('AI analysis failed: ' + (results.error || 'Unknown error'));
849
- }
850
- } catch (error) {
851
- console.error('AI analysis error:', error);
852
- alert('AI analysis service is currently unavailable. Please try again later.');
853
- } finally {
854
- document.getElementById('aiLoadingOverlay').classList.add('d-none');
855
- }
856
- });
857
-
858
- function displayResults(results) {
859
- document.getElementById('aiResults').classList.remove('d-none');
860
-
861
- let html = '<div class="row g-3">';
862
-
863
- // Differential Diagnosis
864
- if (results.differential_diagnosis && results.differential_diagnosis.length > 0) {
865
- html += '<div class="col-12"><h6><i class="fas fa-list-check text-primary me-2"></i>Differential Diagnosis</h6>';
866
- results.differential_diagnosis.forEach(diagnosis => {
867
- const confidenceColor = diagnosis.confidence > 70 ? 'success' :
868
- diagnosis.confidence > 40 ? 'warning' : 'danger';
869
- html += `
870
- <div class="card border-${confidenceColor} mb-2">
871
- <div class="card-body py-2">
872
- <div class="d-flex justify-content-between align-items-start">
873
- <strong>${diagnosis.condition}</strong>
874
- <span class="badge bg-${confidenceColor}">${diagnosis.confidence}%</span>
875
- </div>
876
- <small class="text-muted">${diagnosis.description}</small>
877
- </div>
878
- </div>
879
- `;
880
- });
881
- html += '</div>';
882
- }
883
-
884
- // Treatment Plan
885
- if (results.treatment_plan) {
886
- const plan = results.treatment_plan;
887
- html += `
888
- <div class="col-md-6">
889
- <h6><i class="fas fa-pills text-success me-2"></i>Medications</h6>
890
- <div class="bg-light p-3 rounded">
891
- ${plan.medications || 'No specific medications recommended'}
892
- </div>
893
- </div>
894
- <div class="col-md-6">
895
- <h6><i class="fas fa-list-check text-info me-2"></i>Instructions</h6>
896
- <div class="bg-light p-3 rounded">
897
- ${plan.instructions || 'Standard care instructions apply'}
898
- </div>
899
- </div>
900
- `;
901
-
902
- if (plan.warnings && plan.warnings.length > 0) {
903
- html += `
904
- <div class="col-12">
905
- <div class="alert alert-warning">
906
- <h6><i class="fas fa-exclamation-triangle me-2"></i>Important Warnings</h6>
907
- <ul class="mb-0">
908
- ${plan.warnings.map(warning => `<li>${warning}</li>`).join('')}
909
- </ul>
910
- </div>
911
- </div>
912
- `;
913
- }
914
- }
915
-
916
- html += '</div>';
917
- document.getElementById('analysisResults').innerHTML = html;
918
- }
919
-
920
- function clearAIForm() {
921
- document.getElementById('symptomAnalysisForm').reset();
922
- document.getElementById('aiResults').classList.add('d-none');
923
- }
924
-
925
- function quickDrugCheck() {
926
- alert('Drug interaction checker: Enter medications to check for interactions and contraindications.');
927
- }
928
-
929
- function aiInsights() {
930
- alert('AI Insights: Daily medical insights and practice recommendations based on current cases.');
931
- }
932
-
933
- function templateGenerator() {
934
- window.location.href = '/treatment-planner';
935
- }
936
- </script>
937
- {% endblock %}
938
- """
939
-
940
- TREATMENT_PLANNER_TEMPLATE = """
941
- {% extends "base.html" %}
942
- {% block title %}Treatment Planner - Doctor's Sidekick{% endblock %}
943
- {% block content %}
944
- <div class="d-flex justify-content-between align-items-center mb-4">
945
- <h1 class="h3"><i class="fas fa-prescription-bottle-alt me-2 text-primary"></i>Treatment Planner</h1>
946
- <div class="badge bg-info">AI-Enhanced</div>
947
- </div>
948
-
949
- <div class="row g-4">
950
- <div class="col-lg-8">
951
- <div class="card">
952
- <div class="card-header">
953
- <h5><i class="fas fa-plus-circle me-2"></i>Create Treatment Plan</h5>
954
- </div>
955
- <div class="card-body">
956
- <form method="POST" id="treatmentPlanForm">
957
- {{ form.hidden_tag() }}
958
- <div class="row g-3">
959
- <div class="col-md-6">
960
- <label class="form-label">Select Patient</label>
961
- <select class="form-select" name="patient_id" required>
962
- <option value="">Choose patient...</option>
963
- {% for patient in patients %}
964
- <option value="{{ patient.id }}">
965
- {{ patient.full_name() }} ({{ patient.patient_id }}) - Age {{ patient.age() }}
966
- </option>
967
- {% endfor %}
968
- </select>
969
- </div>
970
- <div class="col-md-6">
971
- <label class="form-label">Condition Category</label>
972
- <select class="form-select" id="condition_id">
973
- <option value="">Select condition...</option>
974
- {% for condition in conditions %}
975
- <option value="{{ condition.id }}">{{ condition.name }}</option>
976
- {% endfor %}
977
- </select>
978
- </div>
979
- <div class="col-12">
980
- {{ form.diagnosis.label(class="form-label") }}
981
- {{ form.diagnosis(class="form-control") }}
982
- </div>
983
- <div class="col-12">
984
- {{ form.medications.label(class="form-label") }}
985
- {{ form.medications(class="form-control", rows="4", placeholder="Enter medications with dosages and frequency") }}
986
- </div>
987
- <div class="col-12">
988
- {{ form.instructions.label(class="form-label") }}
989
- {{ form.instructions(class="form-control", rows="4", placeholder="Patient care instructions") }}
990
- </div>
991
- <div class="col-md-6">
992
- {{ form.duration.label(class="form-label") }}
993
- {{ form.duration(class="form-control", placeholder="e.g., 2-4 weeks") }}
994
- </div>
995
- <div class="col-md-6">
996
- {{ form.follow_up_date.label(class="form-label") }}
997
- {{ form.follow_up_date(class="form-control") }}
998
- </div>
999
- <div class="col-12">
1000
- {{ form.notes.label(class="form-label") }}
1001
- {{ form.notes(class="form-control", rows="3") }}
1002
- </div>
1003
- </div>
1004
-
1005
- <div class="d-flex gap-2 mt-4">
1006
- <button type="submit" class="btn btn-primary">
1007
- <i class="fas fa-save me-2"></i> Create Treatment Plan
1008
- </button>
1009
- <button type="button" class="btn btn-outline-info" onclick="getAIRecommendations()">
1010
- <i class="fas fa-brain me-2"></i> Get AI Recommendations
1011
- </button>
1012
- <button type="button" class="btn btn-outline-secondary" onclick="clearForm()">
1013
- <i class="fas fa-eraser me-2"></i> Clear Form
1014
- </button>
1015
- </div>
1016
- </form>
1017
- </div>
1018
- </div>
1019
- </div>
1020
-
1021
- <div class="col-lg-4">
1022
- <div class="card">
1023
- <div class="card-header bg-light">
1024
- <h6 class="mb-0"><i class="fas fa-lightbulb me-2 text-warning"></i>Treatment Guidelines</h6>
1025
- </div>
1026
- <div class="card-body">
1027
- <div class="small">
1028
- <h6 class="text-primary"><i class="fas fa-circle-check me-2"></i>Best Practices</h6>
1029
- <ul class="list-unstyled ms-3">
1030
- <li><i class="fas fa-chevron-right me-2 text-muted"></i>Include specific dosages and frequencies</li>
1031
- <li><i class="fas fa-chevron-right me-2 text-muted"></i>Provide clear patient instructions</li>
1032
- <li><i class="fas fa-chevron-right me-2 text-muted"></i>Set appropriate follow-up timelines</li>
1033
- </ul>
1034
-
1035
- <h6 class="text-success mt-3"><i class="fas fa-info-circle me-2"></i>Patient Safety</h6>
1036
- <ul class="list-unstyled ms-3">
1037
- <li><i class="fas fa-chevron-right me-2 text-muted"></i>Check for drug allergies</li>
1038
- <li><i class="fas fa-chevron-right me-2 text-muted"></i>Consider drug interactions</li>
1039
- <li><i class="fas fa-chevron-right me-2 text-muted"></i>Include warning signs</li>
1040
- </ul>
1041
- </div>
1042
- </div>
1043
- </div>
1044
- </div>
1045
- </div>
1046
- {% endblock %}
1047
-
1048
- {% block scripts %}
1049
- <script>
1050
- async function getAIRecommendations() {
1051
- const patientSelect = document.querySelector('select[name="patient_id"]');
1052
- const diagnosisInput = document.querySelector('input[name="diagnosis"]');
1053
-
1054
- if (!patientSelect.value || !diagnosisInput.value) {
1055
- alert('Please select a patient and enter a diagnosis to get AI recommendations.');
1056
- return;
1057
- }
1058
-
1059
- try {
1060
- const response = await fetch('/api/ai/analyze-symptoms', {
1061
- method: 'POST',
1062
- headers: {
1063
- 'Content-Type': 'application/json'
1064
- },
1065
- body: JSON.stringify({
1066
- patient_id: patientSelect.value,
1067
- symptoms: diagnosisInput.value,
1068
- suspected_diagnosis: diagnosisInput.value
1069
- })
1070
- });
1071
-
1072
- const data = await response.json();
1073
-
1074
- if (data.success && data.treatment_plan) {
1075
- const plan = data.treatment_plan;
1076
- if (plan.medications) {
1077
- document.querySelector('textarea[name="medications"]').value = plan.medications;
1078
- }
1079
- if (plan.instructions) {
1080
- document.querySelector('textarea[name="instructions"]').value = plan.instructions;
1081
- }
1082
-
1083
- alert('AI recommendations applied successfully! Please review and adjust as needed.');
1084
- } else {
1085
- alert('Unable to get AI recommendations at this time.');
1086
- }
1087
- } catch (error) {
1088
- console.error('AI recommendation error:', error);
1089
- alert('AI recommendation service is currently unavailable.');
1090
- }
1091
- }
1092
-
1093
- function clearForm() {
1094
- if (confirm('Are you sure you want to clear all form data?')) {
1095
- document.getElementById('treatmentPlanForm').reset();
1096
- }
1097
- }
1098
- </script>
1099
- {% endblock %}
1100
- """
1101
-
1102
- NEW_PATIENT_TEMPLATE = """
1103
- {% extends "base.html" %}
1104
- {% block title %}New Patient - Doctor's Sidekick{% endblock %}
1105
- {% block content %}
1106
- <div class="d-flex justify-content-between align-items-center mb-4">
1107
- <h1 class="h3"><i class="fas fa-user-plus me-2 text-primary"></i>New Patient Registration</h1>
1108
- <a href="{{ url_for('patients') }}" class="btn btn-outline-secondary">
1109
- <i class="fas fa-arrow-left me-2"></i>Back to Patients
1110
- </a>
1111
- </div>
1112
-
1113
- <div class="row justify-content-center">
1114
- <div class="col-lg-8">
1115
- <div class="card">
1116
- <div class="card-header">
1117
- <h5><i class="fas fa-user-circle me-2"></i>Patient Information</h5>
1118
- </div>
1119
- <div class="card-body">
1120
- <form method="POST">
1121
- {{ form.hidden_tag() }}
1122
- <div class="row g-3">
1123
- <div class="col-md-6">
1124
- {{ form.first_name.label(class="form-label") }}
1125
- {{ form.first_name(class="form-control") }}
1126
- </div>
1127
- <div class="col-md-6">
1128
- {{ form.last_name.label(class="form-label") }}
1129
- {{ form.last_name(class="form-control") }}
1130
- </div>
1131
- <div class="col-md-6">
1132
- {{ form.date_of_birth.label(class="form-label") }}
1133
- {{ form.date_of_birth(class="form-control") }}
1134
- </div>
1135
- <div class="col-md-6">
1136
- {{ form.gender.label(class="form-label") }}
1137
- {{ form.gender(class="form-select") }}
1138
- </div>
1139
- <div class="col-md-6">
1140
- {{ form.phone.label(class="form-label") }}
1141
- {{ form.phone(class="form-control") }}
1142
- </div>
1143
- <div class="col-md-6">
1144
- {{ form.email.label(class="form-label") }}
1145
- {{ form.email(class="form-control") }}
1146
- </div>
1147
- <div class="col-12">
1148
- {{ form.address.label(class="form-label") }}
1149
- {{ form.address(class="form-control", rows="3") }}
1150
- </div>
1151
- <div class="col-12">
1152
- {{ form.allergies.label(class="form-label") }}
1153
- {{ form.allergies(class="form-control", rows="3", placeholder="List any known allergies or NKDA") }}
1154
- </div>
1155
- <div class="col-12">
1156
- {{ form.medical_history.label(class="form-label") }}
1157
- {{ form.medical_history(class="form-control", rows="4", placeholder="Past medical history, current medications, etc.") }}
1158
- </div>
1159
- </div>
1160
-
1161
- <div class="d-flex gap-2 mt-4">
1162
- <button type="submit" class="btn btn-primary">
1163
- <i class="fas fa-save me-2"></i> Register Patient
1164
- </button>
1165
- <a href="{{ url_for('patients') }}" class="btn btn-outline-secondary">
1166
- <i class="fas fa-times me-2"></i> Cancel
1167
- </a>
1168
- </div>
1169
- </form>
1170
- </div>
1171
- </div>
1172
- </div>
1173
- </div>
1174
- {% endblock %}
1175
- """
1176
-
1177
- # ============================================================================
1178
- # ROUTES
1179
- # ============================================================================
1180
-
1181
- @app.route('/login', methods=['GET', 'POST'])
1182
- def login():
1183
- form = LoginForm()
1184
- if form.validate_on_submit():
1185
- user = User.query.filter_by(username=form.username.data).first()
1186
- if user and user.password_hash and check_password_hash(user.password_hash, form.password.data):
1187
- login_user(user)
1188
- return redirect(url_for('dashboard'))
1189
- flash('Invalid username or password', 'error')
1190
- return render_template_string(LOGIN_TEMPLATE, form=form)
1191
-
1192
- @app.route('/logout')
1193
- @login_required
1194
- def logout():
1195
- logout_user()
1196
- return redirect(url_for('login'))
1197
-
1198
- @app.route('/')
1199
- @login_required
1200
- def dashboard():
1201
- stats = {
1202
- 'total_patients': Patient.query.count(),
1203
- 'visits_today': 0, # Would count visits for today
1204
- 'treatment_plans': TreatmentPlan.query.count(),
1205
- }
1206
- return render_template_string(DASHBOARD_TEMPLATE, stats=stats)
1207
-
1208
- @app.route('/patients')
1209
- @login_required
1210
- def patients():
1211
- patients = Patient.query.order_by(Patient.last_name, Patient.first_name).all()
1212
- return render_template_string(PATIENTS_TEMPLATE, patients=patients)
1213
-
1214
- @app.route('/patients/new', methods=['GET', 'POST'])
1215
- @login_required
1216
- def new_patient():
1217
- form = PatientForm()
1218
- if form.validate_on_submit():
1219
- # Generate patient ID
1220
- patient_count = Patient.query.count()
1221
- patient_id = f"P{patient_count + 1:04d}"
1222
-
1223
- patient = Patient(
1224
- patient_id=patient_id,
1225
- first_name=form.first_name.data,
1226
- last_name=form.last_name.data,
1227
- date_of_birth=form.date_of_birth.data,
1228
- gender=form.gender.data,
1229
- phone=form.phone.data,
1230
- email=form.email.data,
1231
- address=form.address.data,
1232
- allergies=form.allergies.data,
1233
- medical_history=form.medical_history.data,
1234
- doctor_id=current_user.id
1235
- )
1236
- db.session.add(patient)
1237
- db.session.commit()
1238
-
1239
- flash(f'Patient {patient.full_name()} registered successfully!', 'success')
1240
- return redirect(url_for('patients'))
1241
-
1242
- return render_template_string(NEW_PATIENT_TEMPLATE, form=form)
1243
-
1244
- @app.route('/patients/<int:id>')
1245
- @login_required
1246
- def patient_detail(id):
1247
- patient = Patient.query.get_or_404(id)
1248
- return render_template_string("""
1249
- {% extends "base.html" %}
1250
- {% block content %}
1251
- <h1>{{ patient.full_name() }}</h1>
1252
- <div class="card">
1253
- <div class="card-body">
1254
- <p><strong>Patient ID:</strong> {{ patient.patient_id }}</p>
1255
- <p><strong>Age:</strong> {{ patient.age() }}</p>
1256
- <p><strong>Gender:</strong> {{ patient.gender or 'N/A' }}</p>
1257
- <p><strong>Phone:</strong> {{ patient.phone or 'N/A' }}</p>
1258
- <p><strong>Email:</strong> {{ patient.email or 'N/A' }}</p>
1259
- <p><strong>Allergies:</strong> {{ patient.allergies or 'None known' }}</p>
1260
- <p><strong>Medical History:</strong> {{ patient.medical_history or 'None provided' }}</p>
1261
- </div>
1262
- </div>
1263
- {% endblock %}
1264
- """, patient=patient)
1265
-
1266
- @app.route('/patients/<int:id>/edit')
1267
- @login_required
1268
- def edit_patient(id):
1269
- patient = Patient.query.get_or_404(id)
1270
- form = PatientForm(obj=patient)
1271
- return render_template_string(NEW_PATIENT_TEMPLATE, form=form, patient=patient)
1272
-
1273
- @app.route('/treatment-planner', methods=['GET', 'POST'])
1274
- @login_required
1275
- def treatment_planner():
1276
- form = TreatmentPlanForm()
1277
- patients = Patient.query.order_by(Patient.last_name, Patient.first_name).all()
1278
- conditions = Condition.query.all()
1279
-
1280
- if form.validate_on_submit():
1281
- patient_id = request.form.get('patient_id')
1282
- if not patient_id:
1283
- flash('Please select a patient', 'error')
1284
- else:
1285
- treatment_plan = TreatmentPlan(
1286
- patient_id=patient_id,
1287
- diagnosis=form.diagnosis.data,
1288
- medications=form.medications.data,
1289
- instructions=form.instructions.data,
1290
- duration=form.duration.data,
1291
- follow_up_date=form.follow_up_date.data,
1292
- notes=form.notes.data,
1293
- created_by_id=current_user.id
1294
- )
1295
- db.session.add(treatment_plan)
1296
- db.session.commit()
1297
-
1298
- flash('Treatment plan created successfully!', 'success')
1299
- return redirect(url_for('treatment_planner'))
1300
-
1301
- return render_template_string(TREATMENT_PLANNER_TEMPLATE,
1302
- form=form, patients=patients, conditions=conditions)
1303
-
1304
- @app.route('/ai-assistant')
1305
- @login_required
1306
- def ai_assistant():
1307
- patients = Patient.query.order_by(Patient.last_name, Patient.first_name).all()
1308
- return render_template_string(AI_ASSISTANT_TEMPLATE, patients=patients)
1309
-
1310
- @app.route('/api/ai/analyze-symptoms', methods=['POST'])
1311
- @login_required
1312
- def ai_analyze_symptoms():
1313
- try:
1314
- data = request.get_json()
1315
- patient = Patient.query.get_or_404(data['patient_id'])
1316
-
1317
- # Prepare patient data for AI analysis
1318
- patient_data = {
1319
- 'age': patient.age(),
1320
- 'gender': patient.gender or 'Not specified',
1321
- 'medical_history': patient.medical_history or 'None provided',
1322
- 'allergies': patient.allergies or 'None known'
1323
- }
1324
-
1325
- # Get AI recommendations
1326
- treatment_plan = medical_ai.generate_treatment_plan(
1327
- patient_data,
1328
- data['symptoms'],
1329
- data.get('suspected_diagnosis')
1330
- )
1331
-
1332
- # Check for drug interactions if medications are mentioned
1333
- medications = []
1334
- if 'medications' in treatment_plan:
1335
- medications = [med.strip() for med in str(treatment_plan.get('medications', '')).split('\n') if med.strip()]
1336
-
1337
- drug_interactions = medical_ai.check_drug_interactions(medications, patient.allergies or '')
1338
-
1339
- return jsonify({
1340
- 'success': True,
1341
- 'treatment_plan': treatment_plan,
1342
- 'differential_diagnosis': treatment_plan.get('differential_diagnosis', []),
1343
- 'drug_interactions': drug_interactions
1344
- })
1345
-
1346
- except Exception as e:
1347
- app.logger.error(f"AI analysis error: {str(e)}")
1348
- return jsonify({
1349
- 'success': False,
1350
- 'error': 'AI analysis service temporarily unavailable'
1351
- }), 500
1352
-
1353
- @app.route('/api/ai/smart-templates/<int:condition_id>')
1354
- @login_required
1355
- def ai_smart_templates(condition_id):
1356
- try:
1357
- condition = Condition.query.get_or_404(condition_id)
1358
- patient_id = request.args.get('patient_id')
1359
-
1360
- patient_demographics = {}
1361
- if patient_id:
1362
- patient = Patient.query.get(patient_id)
1363
- if patient:
1364
- patient_demographics = {
1365
- 'age': patient.age(),
1366
- 'gender': patient.gender,
1367
- 'medical_history': patient.medical_history,
1368
- 'allergies': patient.allergies
1369
- }
1370
-
1371
- smart_templates = medical_ai.smart_template_suggestions(
1372
- condition.name,
1373
- patient_demographics
1374
- )
1375
-
1376
- return jsonify({
1377
- 'success': True,
1378
- 'templates': smart_templates
1379
- })
1380
-
1381
- except Exception as e:
1382
- app.logger.error(f"Smart template error: {str(e)}")
1383
- return jsonify({
1384
- 'success': False,
1385
- 'error': 'Smart template service unavailable'
1386
- }), 500
1387
-
1388
- def init_database():
1389
- """Initialize database with sample data"""
1390
- with app.app_context():
1391
- # Create tables
1392
- db.create_all()
1393
-
1394
- # Check if data already exists
1395
- if User.query.first():
1396
- return
1397
-
1398
- # Create roles
1399
- admin_role = Role(name='Admin', description='Administrator with full access')
1400
- doctor_role = Role(name='Doctor', description='Medical doctor with patient access')
1401
-
1402
- db.session.add(admin_role)
1403
- db.session.add(doctor_role)
1404
- db.session.commit()
1405
-
1406
- # Create admin user
1407
- admin_user = User(
1408
- username='admin',
1409
- email='admin@example.com',
1410
- password_hash=generate_password_hash('admin'),
1411
- first_name='Admin',
1412
- last_name='User',
1413
- role_id=admin_role.id
1414
- )
1415
-
1416
- db.session.add(admin_user)
1417
- db.session.commit()
1418
-
1419
- # Create sample conditions
1420
- conditions = [
1421
- Condition(name='Acne Vulgaris', description='Common inflammatory skin condition', icd_code='L70.0', category='Inflammatory'),
1422
- Condition(name='Atopic Dermatitis', description='Chronic inflammatory skin disease', icd_code='L20.9', category='Inflammatory'),
1423
- Condition(name='Contact Dermatitis', description='Skin inflammation from contact with allergens', icd_code='L23.9', category='Inflammatory'),
1424
- Condition(name='Psoriasis', description='Chronic autoimmune skin condition', icd_code='L40.9', category='Autoimmune'),
1425
- Condition(name='Seborrheic Dermatitis', description='Inflammatory condition of sebaceous areas', icd_code='L21.9', category='Inflammatory')
1426
- ]
1427
-
1428
- for condition in conditions:
1429
- db.session.add(condition)
1430
-
1431
- db.session.commit()
1432
-
1433
- print("Database initialized with sample data!")
1434
-
1435
- # ============================================================================
1436
- # MAIN APPLICATION
1437
- # ============================================================================
1438
-
1439
- if __name__ == '__main__':
1440
- # Initialize database
1441
- init_database()
1442
-
1443
- # Create uploads directory
1444
- os.makedirs(app.config['UPLOAD_FOLDER'], exist_ok=True)
1445
-
1446
- # Get port from environment or default to 5000
1447
- port = int(os.environ.get('PORT', 5000))
1448
-
1449
- # Run the application
1450
- app.run(host='0.0.0.0', port=port, debug=True)
 
1
+ # Hugging Face compatible entry point
2
+ from complete_medical_dashboard import app
 
 
 
3
 
4
+ if __name__ == "__main__":
5
+ import os
6
+ port = int(os.environ.get("PORT", 7860)) # Hugging Face uses port 7860
7
+ app.run(host="0.0.0.0", port=port, debug=False)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
complete_medical_dashboard.py ADDED
@@ -0,0 +1,1800 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python3
2
+ """
3
+ Doctor's Sidekick - Complete Medical Dashboard
4
+ A comprehensive Flask-based medical assistant dashboard for dermatologists
5
+ with patient management, treatment planning, and AI-powered recommendations.
6
+
7
+ Single file implementation for easy deployment and GitHub sharing.
8
+ """
9
+
10
+ import os
11
+ import json
12
+ import logging
13
+ from datetime import datetime, date, timedelta
14
+ from typing import Dict, List, Optional
15
+ from werkzeug.security import check_password_hash, generate_password_hash
16
+ from werkzeug.utils import secure_filename
17
+ from werkzeug.middleware.proxy_fix import ProxyFix
18
+
19
+ # Flask and extensions
20
+ from flask import Flask, render_template_string, request, redirect, url_for, flash, jsonify, send_from_directory
21
+ from flask_sqlalchemy import SQLAlchemy
22
+ from flask_login import LoginManager, UserMixin, login_user, logout_user, login_required, current_user
23
+ from flask_wtf import FlaskForm
24
+ from flask_wtf.file import FileField, FileAllowed
25
+ from wtforms import StringField, PasswordField, TextAreaField, DateField, SelectField, IntegerField, BooleanField
26
+ from wtforms.validators import DataRequired, Length, Email, Optional, NumberRange
27
+ from sqlalchemy.orm import DeclarativeBase
28
+
29
+ # Configure logging
30
+ logging.basicConfig(level=logging.DEBUG)
31
+
32
+ # ============================================================================
33
+ # DATABASE MODELS
34
+ # ============================================================================
35
+
36
+ class Base(DeclarativeBase):
37
+ pass
38
+
39
+ db = SQLAlchemy(model_class=Base)
40
+
41
+ class Role(db.Model):
42
+ __tablename__ = 'role'
43
+ id = db.Column(db.Integer, primary_key=True)
44
+ name = db.Column(db.String(64), unique=True, nullable=False)
45
+ description = db.Column(db.Text)
46
+ users = db.relationship('User', backref='role', lazy='dynamic')
47
+
48
+ class User(UserMixin, db.Model):
49
+ __tablename__ = 'user'
50
+ id = db.Column(db.Integer, primary_key=True)
51
+ username = db.Column(db.String(64), unique=True, nullable=False)
52
+ email = db.Column(db.String(120), unique=True, nullable=False)
53
+ password_hash = db.Column(db.String(256))
54
+ first_name = db.Column(db.String(64), nullable=False)
55
+ last_name = db.Column(db.String(64), nullable=False)
56
+ created_at = db.Column(db.DateTime, default=datetime.utcnow)
57
+ is_active = db.Column(db.Boolean, default=True)
58
+ role_id = db.Column(db.Integer, db.ForeignKey('role.id'))
59
+
60
+ def full_name(self):
61
+ return f"{self.first_name} {self.last_name}"
62
+
63
+ class Patient(db.Model):
64
+ __tablename__ = 'patient'
65
+ id = db.Column(db.Integer, primary_key=True)
66
+ patient_id = db.Column(db.String(20), unique=True, nullable=False)
67
+ first_name = db.Column(db.String(64), nullable=False)
68
+ last_name = db.Column(db.String(64), nullable=False)
69
+ date_of_birth = db.Column(db.Date, nullable=False)
70
+ gender = db.Column(db.String(10))
71
+ phone = db.Column(db.String(20))
72
+ email = db.Column(db.String(120))
73
+ address = db.Column(db.Text)
74
+ emergency_contact = db.Column(db.String(100))
75
+ emergency_phone = db.Column(db.String(20))
76
+ allergies = db.Column(db.Text)
77
+ medical_history = db.Column(db.Text)
78
+ insurance_info = db.Column(db.Text)
79
+ created_at = db.Column(db.DateTime, default=datetime.utcnow)
80
+ last_visit = db.Column(db.DateTime)
81
+ doctor_id = db.Column(db.Integer, db.ForeignKey('user.id'))
82
+
83
+ def full_name(self):
84
+ return f"{self.first_name} {self.last_name}"
85
+
86
+ def age(self):
87
+ return (date.today() - self.date_of_birth).days // 365
88
+
89
+ class Visit(db.Model):
90
+ __tablename__ = 'visit'
91
+ id = db.Column(db.Integer, primary_key=True)
92
+ visit_date = db.Column(db.DateTime, nullable=False, default=datetime.utcnow)
93
+ visit_type = db.Column(db.String(50))
94
+ chief_complaint = db.Column(db.Text)
95
+ diagnosis = db.Column(db.Text)
96
+ treatment_notes = db.Column(db.Text)
97
+ follow_up_date = db.Column(db.Date)
98
+ patient_id = db.Column(db.Integer, db.ForeignKey('patient.id'), nullable=False)
99
+
100
+ class Condition(db.Model):
101
+ __tablename__ = 'condition'
102
+ id = db.Column(db.Integer, primary_key=True)
103
+ name = db.Column(db.String(100), nullable=False)
104
+ description = db.Column(db.Text)
105
+ icd_code = db.Column(db.String(20))
106
+ category = db.Column(db.String(50))
107
+
108
+ class TreatmentTemplate(db.Model):
109
+ __tablename__ = 'treatment_template'
110
+ id = db.Column(db.Integer, primary_key=True)
111
+ name = db.Column(db.String(100), nullable=False)
112
+ description = db.Column(db.Text)
113
+ medications = db.Column(db.Text)
114
+ instructions = db.Column(db.Text)
115
+ duration = db.Column(db.String(50))
116
+ follow_up_weeks = db.Column(db.Integer)
117
+ is_active = db.Column(db.Boolean, default=True)
118
+ condition_id = db.Column(db.Integer, db.ForeignKey('condition.id'))
119
+
120
+ class TreatmentPlan(db.Model):
121
+ __tablename__ = 'treatment_plan'
122
+ id = db.Column(db.Integer, primary_key=True)
123
+ diagnosis = db.Column(db.String(200), nullable=False)
124
+ medications = db.Column(db.Text)
125
+ instructions = db.Column(db.Text)
126
+ duration = db.Column(db.String(50))
127
+ follow_up_date = db.Column(db.Date)
128
+ notes = db.Column(db.Text)
129
+ created_at = db.Column(db.DateTime, default=datetime.utcnow)
130
+ patient_id = db.Column(db.Integer, db.ForeignKey('patient.id'), nullable=False)
131
+ created_by_id = db.Column(db.Integer, db.ForeignKey('user.id'), nullable=False)
132
+
133
+ # ============================================================================
134
+ # AI MEDICAL ASSISTANT
135
+ # ============================================================================
136
+
137
+ class MedicalAI:
138
+ """AI-powered medical assistant for dermatology practice"""
139
+
140
+ def __init__(self):
141
+ self.dermatology_context = """
142
+ Expert dermatology AI assistant providing evidence-based recommendations
143
+ following current clinical guidelines with safety warnings.
144
+ """
145
+
146
+ def _analyze_symptoms(self, symptoms: str, patient_data: Dict) -> Dict:
147
+ """Analyze symptoms and return appropriate medical recommendations"""
148
+ symptoms_lower = symptoms.lower()
149
+ age = patient_data.get('age', 30)
150
+
151
+ # Dermatitis/Eczema conditions
152
+ if any(keyword in symptoms_lower for keyword in ['rash', 'eczema', 'dermatitis', 'itchy', 'red skin']):
153
+ return {
154
+ "condition_type": "dermatitis",
155
+ "primary_diagnosis": "Contact Dermatitis",
156
+ "confidence": 80,
157
+ "medications": "Topical corticosteroids: Hydrocortisone 1% cream BID x 7-14 days\nOral antihistamines: Cetirizine 10mg daily PRN pruritus\nEmollients: Apply liberally QID",
158
+ "instructions": "Apply medications to affected areas only\nAvoid known triggers\nUse fragrance-free products\nMoisturize regularly",
159
+ "follow_up": "Return in 2 weeks if no improvement\nSooner if worsening or signs of infection"
160
+ }
161
+
162
+ # Acne conditions
163
+ elif any(keyword in symptoms_lower for keyword in ['acne', 'pimples', 'blackheads', 'whiteheads']):
164
+ return {
165
+ "condition_type": "acne",
166
+ "primary_diagnosis": "Acne Vulgaris",
167
+ "confidence": 85,
168
+ "medications": "Topical retinoid: Adapalene 0.1% gel daily at bedtime\nBenzoyl peroxide 2.5% gel BID\nClindamycin 1% solution BID (if inflammatory)",
169
+ "instructions": "Start slowly with retinoid (every other night)\nUse non-comedogenic products\nGentle cleansing twice daily\nSun protection mandatory",
170
+ "follow_up": "4-6 weeks for initial assessment\n3 months for full evaluation"
171
+ }
172
+
173
+ # Psoriasis
174
+ elif any(keyword in symptoms_lower for keyword in ['psoriasis', 'silvery scales', 'plaques']):
175
+ return {
176
+ "condition_type": "psoriasis",
177
+ "primary_diagnosis": "Psoriasis Vulgaris",
178
+ "confidence": 75,
179
+ "medications": "Topical corticosteroids: Clobetasol 0.05% ointment BID x 2 weeks\nVitamin D analogues: Calcipotriene 0.005% ointment BID\nMoisturizers: Apply liberally QID",
180
+ "instructions": "Limit potent steroids to 2 weeks\nAlternate with vitamin D analogues\nAvoid triggers (stress, trauma, infections)\nRegular moisturizing essential",
181
+ "follow_up": "2-4 weeks for response assessment\nDermatology referral if extensive"
182
+ }
183
+
184
+ # Default/general skin condition
185
+ else:
186
+ return {
187
+ "condition_type": "general",
188
+ "primary_diagnosis": "Non-specific dermatitis",
189
+ "confidence": 60,
190
+ "medications": "Gentle moisturizers: Apply QID\nMild topical corticosteroids if inflamed\nOral antihistamines PRN pruritus",
191
+ "instructions": "Avoid harsh soaps and chemicals\nUse fragrance-free products\nMoisturize regularly\nIdentify and avoid triggers",
192
+ "follow_up": "1-2 weeks if symptoms persist\nDermatology referral if no improvement"
193
+ }
194
+
195
+ def generate_treatment_plan(self, patient_data: Dict, symptoms: str, diagnosis: str = None) -> Dict:
196
+ """Generate AI-powered treatment plan based on patient data and symptoms"""
197
+
198
+ analysis = self._analyze_symptoms(symptoms, patient_data)
199
+
200
+ # Build differential diagnosis
201
+ if analysis["condition_type"] == "dermatitis":
202
+ differential_diagnosis = [
203
+ {
204
+ "condition": "Contact Dermatitis",
205
+ "confidence": 80,
206
+ "description": "Inflammatory reaction to allergens or irritants",
207
+ "key_features": ["Well-demarcated erythema", "Vesicles", "Pruritus"]
208
+ },
209
+ {
210
+ "condition": "Atopic Dermatitis",
211
+ "confidence": 65,
212
+ "description": "Chronic inflammatory skin condition",
213
+ "key_features": ["Dry skin", "Lichenification", "Flexural involvement"]
214
+ }
215
+ ]
216
+ elif analysis["condition_type"] == "acne":
217
+ differential_diagnosis = [
218
+ {
219
+ "condition": "Acne Vulgaris",
220
+ "confidence": 85,
221
+ "description": "Common inflammatory condition of pilosebaceous units",
222
+ "key_features": ["Comedones", "Papules", "Pustules", "Face/chest involvement"]
223
+ }
224
+ ]
225
+ else:
226
+ differential_diagnosis = [
227
+ {
228
+ "condition": analysis["primary_diagnosis"],
229
+ "confidence": analysis["confidence"],
230
+ "description": "Clinical assessment required for definitive diagnosis",
231
+ "key_features": ["Variable presentation", "Patient history important"]
232
+ }
233
+ ]
234
+
235
+ return {
236
+ "differential_diagnosis": differential_diagnosis,
237
+ "medications": analysis["medications"],
238
+ "instructions": analysis["instructions"],
239
+ "follow_up": analysis["follow_up"],
240
+ "warnings": [
241
+ "Monitor for signs of secondary infection",
242
+ "Discontinue treatment if allergic reactions occur",
243
+ "Follow up if symptoms worsen or persist"
244
+ ]
245
+ }
246
+
247
+ def check_drug_interactions(self, medications: List[str], patient_allergies: str = "") -> Dict:
248
+ """Check for drug interactions and contraindications"""
249
+
250
+ if not medications:
251
+ return {"interactions": [], "warnings": []}
252
+
253
+ interactions = []
254
+ warnings = []
255
+ med_list = [med.lower() for med in medications]
256
+
257
+ # Check for common dermatology drug interactions
258
+ if any('warfarin' in med for med in med_list) and any('azole' in med for med in med_list):
259
+ interactions.append({
260
+ "drugs": "Warfarin + Azole antifungals",
261
+ "severity": "Major",
262
+ "description": "Increased risk of bleeding due to enhanced warfarin effect"
263
+ })
264
+
265
+ # Check for corticosteroid warnings
266
+ if any('corticosteroid' in med or 'steroid' in med for med in med_list):
267
+ warnings.append("Monitor for skin atrophy with prolonged topical steroid use")
268
+
269
+ # Check for retinoid warnings
270
+ if any('retinoid' in med or 'tretinoin' in med or 'adapalene' in med for med in med_list):
271
+ warnings.append("Contraindicated in pregnancy")
272
+ warnings.append("Increased photosensitivity - use sun protection")
273
+
274
+ return {
275
+ "interactions": interactions,
276
+ "warnings": warnings + ["Always verify drug interactions with current medication database"]
277
+ }
278
+
279
+ def smart_template_suggestions(self, condition: str, patient_demographics: Dict) -> List[Dict]:
280
+ """Suggest optimized treatment templates based on condition and patient factors"""
281
+
282
+ age = patient_demographics.get('age', 30)
283
+ templates = []
284
+
285
+ if 'dermatitis' in condition.lower() or 'eczema' in condition.lower():
286
+ templates = [
287
+ {
288
+ "name": "Mild Dermatitis Protocol",
289
+ "description": "For localized, mild inflammatory dermatitis",
290
+ "medications": "Hydrocortisone 1% cream BID x 7 days\nCetirizine 10mg daily PRN\nEmollient cream QID",
291
+ "duration": "1-2 weeks",
292
+ "instructions": "Apply steroid sparingly to affected areas\nMoisturize frequently\nAvoid triggers",
293
+ "follow_up": "2 weeks",
294
+ "success_probability": 85
295
+ }
296
+ ]
297
+
298
+ elif 'acne' in condition.lower():
299
+ if age < 18:
300
+ templates.append({
301
+ "name": "Teen Acne Starter Protocol",
302
+ "description": "Gentle introduction for adolescent acne",
303
+ "medications": "Benzoyl peroxide 2.5% gel daily\nCetaphil gentle cleanser BID",
304
+ "duration": "6-8 weeks",
305
+ "instructions": "Start slowly, every other day initially\nUse oil-free moisturizer\nSun protection essential",
306
+ "follow_up": "4-6 weeks",
307
+ "success_probability": 70
308
+ })
309
+
310
+ return templates if templates else [{
311
+ "name": "General Dermatology Protocol",
312
+ "description": "Standard approach for common skin conditions",
313
+ "medications": "Moisturizer QID\nMild topical corticosteroid PRN",
314
+ "duration": "2-4 weeks",
315
+ "instructions": "Gentle skin care routine\nAvoid harsh products",
316
+ "follow_up": "2-3 weeks",
317
+ "success_probability": 65
318
+ }]
319
+
320
+ # Global AI service instance
321
+ medical_ai = MedicalAI()
322
+
323
+ # ============================================================================
324
+ # FORMS
325
+ # ============================================================================
326
+
327
+ class LoginForm(FlaskForm):
328
+ username = StringField('Username', validators=[DataRequired()])
329
+ password = PasswordField('Password', validators=[DataRequired()])
330
+
331
+ class PatientForm(FlaskForm):
332
+ first_name = StringField('First Name', validators=[DataRequired(), Length(max=64)])
333
+ last_name = StringField('Last Name', validators=[DataRequired(), Length(max=64)])
334
+ date_of_birth = DateField('Date of Birth', validators=[DataRequired()])
335
+ gender = SelectField('Gender', choices=[('Male', 'Male'), ('Female', 'Female'), ('Other', 'Other')])
336
+ phone = StringField('Phone', validators=[Length(max=20)])
337
+ email = StringField('Email', validators=[Optional(), Email(), Length(max=120)])
338
+ address = TextAreaField('Address')
339
+ allergies = TextAreaField('Allergies')
340
+ medical_history = TextAreaField('Medical History')
341
+
342
+ class TreatmentPlanForm(FlaskForm):
343
+ diagnosis = StringField('Diagnosis', validators=[DataRequired(), Length(max=200)])
344
+ medications = TextAreaField('Medications')
345
+ instructions = TextAreaField('Instructions')
346
+ duration = StringField('Duration', validators=[Length(max=50)])
347
+ follow_up_date = DateField('Follow-up Date', validators=[Optional()])
348
+ notes = TextAreaField('Additional Notes')
349
+
350
+ # ============================================================================
351
+ # FLASK APPLICATION SETUP
352
+ # ============================================================================
353
+
354
+ app = Flask(__name__)
355
+ app.secret_key = os.environ.get("SESSION_SECRET", "dev-secret-key-change-in-production")
356
+ app.wsgi_app = ProxyFix(app.wsgi_app, x_proto=1, x_host=1)
357
+
358
+ # Database configuration for Hugging Face compatibility
359
+ database_url = os.environ.get("DATABASE_URL", "sqlite:///medical_dashboard.db")
360
+ # Handle PostgreSQL URL format for Hugging Face if provided
361
+ if database_url.startswith("postgres://"):
362
+ database_url = database_url.replace("postgres://", "postgresql://", 1)
363
+
364
+ app.config["SQLALCHEMY_DATABASE_URI"] = database_url
365
+ app.config["SQLALCHEMY_ENGINE_OPTIONS"] = {
366
+ "pool_recycle": 300,
367
+ "pool_pre_ping": True,
368
+ }
369
+ app.config["SQLALCHEMY_TRACK_MODIFICATIONS"] = False
370
+
371
+ # File upload configuration
372
+ app.config['UPLOAD_FOLDER'] = 'uploads'
373
+ app.config['MAX_CONTENT_LENGTH'] = 16 * 1024 * 1024 # 16MB max file size
374
+
375
+ # Initialize extensions
376
+ db.init_app(app)
377
+ login_manager = LoginManager()
378
+ login_manager.init_app(app)
379
+ login_manager.login_view = 'login'
380
+
381
+ @login_manager.user_loader
382
+ def load_user(user_id):
383
+ return User.query.get(int(user_id))
384
+
385
+ # ============================================================================
386
+ # HTML TEMPLATES
387
+ # ============================================================================
388
+
389
+ BASE_TEMPLATE = """
390
+ <!DOCTYPE html>
391
+ <html lang="en">
392
+ <head>
393
+ <meta charset="UTF-8">
394
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
395
+ <title>{% block title %}Doctor's Sidekick{% endblock %}</title>
396
+ <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
397
+ <link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css" rel="stylesheet">
398
+ <style>
399
+ :root {
400
+ --primary-color: #0F766E;
401
+ --secondary-color: #1E40AF;
402
+ --accent-color: #F59E0B;
403
+ --success-color: #059669;
404
+ --danger-color: #DC2626;
405
+ }
406
+ body { font-family: 'Inter', system-ui, sans-serif; background-color: #f8fafc; }
407
+ .navbar-brand { font-weight: 700; color: var(--primary-color) !important; }
408
+ .btn-primary { background-color: var(--primary-color); border-color: var(--primary-color); }
409
+ .btn-primary:hover { background-color: #065f5a; border-color: #065f5a; }
410
+ .text-primary { color: var(--primary-color) !important; }
411
+ .bg-primary { background-color: var(--primary-color) !important; }
412
+ .card { border: none; box-shadow: 0 1px 3px rgba(0,0,0,0.1); }
413
+ .nav-link.active { background-color: var(--primary-color) !important; }
414
+ </style>
415
+ </head>
416
+ <body>
417
+ <nav class="navbar navbar-expand-lg navbar-light bg-white shadow-sm">
418
+ <div class="container">
419
+ <a class="navbar-brand" href="{{ url_for('dashboard') }}">
420
+ <i class="fas fa-user-md me-2"></i>Doctor's Sidekick
421
+ </a>
422
+ {% if current_user.is_authenticated %}
423
+ <div class="navbar-nav ms-auto">
424
+ <a class="nav-link" href="{{ url_for('dashboard') }}">
425
+ <i class="fas fa-tachometer-alt me-1"></i> Dashboard
426
+ </a>
427
+ <a class="nav-link" href="{{ url_for('patients') }}">
428
+ <i class="fas fa-users me-1"></i> Patients
429
+ </a>
430
+ <a class="nav-link" href="{{ url_for('treatment_planner') }}">
431
+ <i class="fas fa-prescription-bottle-alt me-1"></i> Treatment Planner
432
+ </a>
433
+ <a class="nav-link" href="{{ url_for('ai_assistant') }}">
434
+ <i class="fas fa-brain me-1"></i> AI Assistant
435
+ </a>
436
+ <div class="nav-link">
437
+ <span class="text-muted">{{ current_user.full_name() }}</span>
438
+ </div>
439
+ <a class="nav-link" href="{{ url_for('logout') }}">
440
+ <i class="fas fa-sign-out-alt me-1"></i> Logout
441
+ </a>
442
+ </div>
443
+ {% endif %}
444
+ </div>
445
+ </nav>
446
+
447
+ <main class="container mt-4">
448
+ {% with messages = get_flashed_messages(with_categories=true) %}
449
+ {% if messages %}
450
+ {% for category, message in messages %}
451
+ <div class="alert alert-{{ 'danger' if category == 'error' else category }} alert-dismissible fade show">
452
+ {{ message }}
453
+ <button type="button" class="btn-close" data-bs-dismiss="alert"></button>
454
+ </div>
455
+ {% endfor %}
456
+ {% endif %}
457
+ {% endwith %}
458
+
459
+ {% block content %}{% endblock %}
460
+ </main>
461
+
462
+ <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
463
+ {% block scripts %}{% endblock %}
464
+ </body>
465
+ </html>
466
+ """
467
+
468
+ LOGIN_TEMPLATE = """
469
+ <!DOCTYPE html>
470
+ <html lang="en">
471
+ <head>
472
+ <meta charset="UTF-8">
473
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
474
+ <title>Login - Doctor's Sidekick</title>
475
+ <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
476
+ <link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css" rel="stylesheet">
477
+ <style>
478
+ :root {
479
+ --primary-color: #0F766E;
480
+ --secondary-color: #1E40AF;
481
+ }
482
+ body { font-family: 'Inter', system-ui, sans-serif; background-color: #f8fafc; }
483
+ .btn-primary { background-color: var(--primary-color); border-color: var(--primary-color); }
484
+ .btn-primary:hover { background-color: #065f5a; border-color: #065f5a; }
485
+ .bg-primary { background-color: var(--primary-color) !important; }
486
+ .card { border: none; box-shadow: 0 1px 3px rgba(0,0,0,0.1); }
487
+ </style>
488
+ </head>
489
+ <body>
490
+ <div class="container mt-5">
491
+ {% with messages = get_flashed_messages(with_categories=true) %}
492
+ {% if messages %}
493
+ {% for category, message in messages %}
494
+ <div class="alert alert-{{ 'danger' if category == 'error' else category }} alert-dismissible fade show">
495
+ {{ message }}
496
+ <button type="button" class="btn-close" data-bs-dismiss="alert"></button>
497
+ </div>
498
+ {% endfor %}
499
+ {% endif %}
500
+ {% endwith %}
501
+
502
+ <div class="row justify-content-center">
503
+ <div class="col-md-6 col-lg-4">
504
+ <div class="card">
505
+ <div class="card-header bg-primary text-white text-center">
506
+ <h4><i class="fas fa-user-md me-2"></i>Doctor's Login</h4>
507
+ </div>
508
+ <div class="card-body">
509
+ <form method="POST">
510
+ {{ form.hidden_tag() }}
511
+ <div class="mb-3">
512
+ {{ form.username.label(class="form-label") }}
513
+ {{ form.username(class="form-control") }}
514
+ </div>
515
+ <div class="mb-3">
516
+ {{ form.password.label(class="form-label") }}
517
+ {{ form.password(class="form-control") }}
518
+ </div>
519
+ <div class="d-grid">
520
+ <button type="submit" class="btn btn-primary">
521
+ <i class="fas fa-sign-in-alt me-2"></i>Login
522
+ </button>
523
+ </div>
524
+ </form>
525
+ <div class="mt-3 text-center">
526
+ <small class="text-muted">Demo: admin/admin</small>
527
+ </div>
528
+ </div>
529
+ </div>
530
+ </div>
531
+ </div>
532
+ </div>
533
+ <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
534
+ </body>
535
+ </html>
536
+ """
537
+
538
+ DASHBOARD_TEMPLATE = """
539
+ <!DOCTYPE html>
540
+ <html lang="en">
541
+ <head>
542
+ <meta charset="UTF-8">
543
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
544
+ <title>Dashboard - Doctor's Sidekick</title>
545
+ <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
546
+ <link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css" rel="stylesheet">
547
+ <style>
548
+ :root {
549
+ --primary-color: #0F766E;
550
+ --secondary-color: #1E40AF;
551
+ }
552
+ body { font-family: 'Inter', system-ui, sans-serif; background-color: #f8fafc; }
553
+ .navbar-brand { font-weight: 700; color: var(--primary-color) !important; }
554
+ .btn-primary { background-color: var(--primary-color); border-color: var(--primary-color); }
555
+ .btn-primary:hover { background-color: #065f5a; border-color: #065f5a; }
556
+ .text-primary { color: var(--primary-color) !important; }
557
+ .bg-primary { background-color: var(--primary-color) !important; }
558
+ .card { border: none; box-shadow: 0 1px 3px rgba(0,0,0,0.1); }
559
+ .nav-link.active { background-color: var(--primary-color) !important; }
560
+ </style>
561
+ </head>
562
+ <body>
563
+ <nav class="navbar navbar-expand-lg navbar-light bg-white shadow-sm">
564
+ <div class="container">
565
+ <a class="navbar-brand" href="{{ url_for('dashboard') }}">
566
+ <i class="fas fa-user-md me-2"></i>Doctor's Sidekick
567
+ </a>
568
+ <div class="navbar-nav ms-auto">
569
+ <a class="nav-link active" href="{{ url_for('dashboard') }}">
570
+ <i class="fas fa-tachometer-alt me-1"></i> Dashboard
571
+ </a>
572
+ <a class="nav-link" href="{{ url_for('patients') }}">
573
+ <i class="fas fa-users me-1"></i> Patients
574
+ </a>
575
+ <a class="nav-link" href="{{ url_for('treatment_planner') }}">
576
+ <i class="fas fa-prescription-bottle-alt me-1"></i> Treatment Planner
577
+ </a>
578
+ <a class="nav-link" href="{{ url_for('ai_assistant') }}">
579
+ <i class="fas fa-brain me-1"></i> AI Assistant
580
+ </a>
581
+ <div class="nav-link">
582
+ <span class="text-muted">{{ current_user.full_name() }}</span>
583
+ </div>
584
+ <a class="nav-link" href="{{ url_for('logout') }}">
585
+ <i class="fas fa-sign-out-alt me-1"></i> Logout
586
+ </a>
587
+ </div>
588
+ </div>
589
+ </nav>
590
+
591
+ <main class="container mt-4">
592
+ {% with messages = get_flashed_messages(with_categories=true) %}
593
+ {% if messages %}
594
+ {% for category, message in messages %}
595
+ <div class="alert alert-{{ 'danger' if category == 'error' else category }} alert-dismissible fade show">
596
+ {{ message }}
597
+ <button type="button" class="btn-close" data-bs-dismiss="alert"></button>
598
+ </div>
599
+ {% endfor %}
600
+ {% endif %}
601
+ {% endwith %}
602
+ <div class="d-flex justify-content-between align-items-center mb-4">
603
+ <h1 class="h3">Medical Dashboard</h1>
604
+ <div class="text-end">
605
+ <span class="badge bg-success">AI Online</span>
606
+ <div class="small text-muted">{{ current_user.full_name() }}</div>
607
+ </div>
608
+ </div>
609
+
610
+ <div class="row g-4">
611
+ <!-- Stats Cards -->
612
+ <div class="col-md-3">
613
+ <div class="card text-center">
614
+ <div class="card-body">
615
+ <i class="fas fa-users fa-2x text-primary mb-2"></i>
616
+ <h5>{{ stats.total_patients }}</h5>
617
+ <small class="text-muted">Total Patients</small>
618
+ </div>
619
+ </div>
620
+ </div>
621
+ <div class="col-md-3">
622
+ <div class="card text-center">
623
+ <div class="card-body">
624
+ <i class="fas fa-calendar-check fa-2x text-success mb-2"></i>
625
+ <h5>{{ stats.visits_today }}</h5>
626
+ <small class="text-muted">Visits Today</small>
627
+ </div>
628
+ </div>
629
+ </div>
630
+ <div class="col-md-3">
631
+ <div class="card text-center">
632
+ <div class="card-body">
633
+ <i class="fas fa-prescription-bottle-alt fa-2x text-warning mb-2"></i>
634
+ <h5>{{ stats.treatment_plans }}</h5>
635
+ <small class="text-muted">Treatment Plans</small>
636
+ </div>
637
+ </div>
638
+ </div>
639
+ <div class="col-md-3">
640
+ <div class="card text-center">
641
+ <div class="card-body">
642
+ <i class="fas fa-brain fa-2x text-info mb-2"></i>
643
+ <h5>Active</h5>
644
+ <small class="text-muted">AI Assistant</small>
645
+ </div>
646
+ </div>
647
+ </div>
648
+ </div>
649
+
650
+ <div class="row g-4 mt-4">
651
+ <!-- Quick Actions -->
652
+ <div class="col-md-8">
653
+ <div class="card">
654
+ <div class="card-header">
655
+ <h5><i class="fas fa-bolt me-2"></i>Quick Actions</h5>
656
+ </div>
657
+ <div class="card-body">
658
+ <div class="row g-3">
659
+ <div class="col-md-6">
660
+ <a href="{{ url_for('new_patient') }}" class="btn btn-outline-primary w-100">
661
+ <i class="fas fa-user-plus me-2"></i>New Patient
662
+ </a>
663
+ </div>
664
+ <div class="col-md-6">
665
+ <a href="{{ url_for('treatment_planner') }}" class="btn btn-outline-success w-100">
666
+ <i class="fas fa-prescription-bottle-alt me-2"></i>Treatment Plan
667
+ </a>
668
+ </div>
669
+ <div class="col-md-6">
670
+ <a href="{{ url_for('ai_assistant') }}" class="btn btn-outline-info w-100">
671
+ <i class="fas fa-brain me-2"></i>AI Analysis
672
+ </a>
673
+ </div>
674
+ <div class="col-md-6">
675
+ <a href="{{ url_for('patients') }}" class="btn btn-outline-warning w-100">
676
+ <i class="fas fa-search me-2"></i>Find Patient
677
+ </a>
678
+ </div>
679
+ </div>
680
+ </div>
681
+ </div>
682
+ </div>
683
+
684
+ <!-- Recent Activity -->
685
+ <div class="col-md-4">
686
+ <div class="card">
687
+ <div class="card-header">
688
+ <h6><i class="fas fa-clock me-2"></i>Recent Activity</h6>
689
+ </div>
690
+ <div class="card-body">
691
+ <div class="small">
692
+ <div class="d-flex justify-content-between mb-2">
693
+ <span>System initialized</span>
694
+ <span class="text-muted">Now</span>
695
+ </div>
696
+ <div class="d-flex justify-content-between mb-2">
697
+ <span>AI services online</span>
698
+ <span class="text-muted">Now</span>
699
+ </div>
700
+ <div class="d-flex justify-content-between">
701
+ <span>Dashboard ready</span>
702
+ <span class="text-muted">Now</span>
703
+ </div>
704
+ </div>
705
+ </div>
706
+ </div>
707
+ </div>
708
+ </div>
709
+
710
+ </main>
711
+ <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
712
+ </body>
713
+ </html>
714
+ """
715
+
716
+ PATIENTS_TEMPLATE = """
717
+ <!DOCTYPE html>
718
+ <html lang="en">
719
+ <head>
720
+ <meta charset="UTF-8">
721
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
722
+ <title>Patients - Doctor's Sidekick</title>
723
+ <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
724
+ <link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css" rel="stylesheet">
725
+ <style>
726
+ :root {
727
+ --primary-color: #0F766E;
728
+ --secondary-color: #1E40AF;
729
+ }
730
+ body { font-family: 'Inter', system-ui, sans-serif; background-color: #f8fafc; }
731
+ .navbar-brand { font-weight: 700; color: var(--primary-color) !important; }
732
+ .btn-primary { background-color: var(--primary-color); border-color: var(--primary-color); }
733
+ .btn-primary:hover { background-color: #065f5a; border-color: #065f5a; }
734
+ .text-primary { color: var(--primary-color) !important; }
735
+ .bg-primary { background-color: var(--primary-color) !important; }
736
+ .card { border: none; box-shadow: 0 1px 3px rgba(0,0,0,0.1); }
737
+ .nav-link.active { background-color: var(--primary-color) !important; }
738
+ </style>
739
+ </head>
740
+ <body>
741
+ <nav class="navbar navbar-expand-lg navbar-light bg-white shadow-sm">
742
+ <div class="container">
743
+ <a class="navbar-brand" href="{{ url_for('dashboard') }}">
744
+ <i class="fas fa-user-md me-2"></i>Doctor's Sidekick
745
+ </a>
746
+ <div class="navbar-nav ms-auto">
747
+ <a class="nav-link" href="{{ url_for('dashboard') }}">
748
+ <i class="fas fa-tachometer-alt me-1"></i> Dashboard
749
+ </a>
750
+ <a class="nav-link active" href="{{ url_for('patients') }}">
751
+ <i class="fas fa-users me-1"></i> Patients
752
+ </a>
753
+ <a class="nav-link" href="{{ url_for('treatment_planner') }}">
754
+ <i class="fas fa-prescription-bottle-alt me-1"></i> Treatment Planner
755
+ </a>
756
+ <a class="nav-link" href="{{ url_for('ai_assistant') }}">
757
+ <i class="fas fa-brain me-1"></i> AI Assistant
758
+ </a>
759
+ <div class="nav-link">
760
+ <span class="text-muted">{{ current_user.full_name() }}</span>
761
+ </div>
762
+ <a class="nav-link" href="{{ url_for('logout') }}">
763
+ <i class="fas fa-sign-out-alt me-1"></i> Logout
764
+ </a>
765
+ </div>
766
+ </div>
767
+ </nav>
768
+
769
+ <main class="container mt-4">
770
+ {% with messages = get_flashed_messages(with_categories=true) %}
771
+ {% if messages %}
772
+ {% for category, message in messages %}
773
+ <div class="alert alert-{{ 'danger' if category == 'error' else category }} alert-dismissible fade show">
774
+ {{ message }}
775
+ <button type="button" class="btn-close" data-bs-dismiss="alert"></button>
776
+ </div>
777
+ {% endfor %}
778
+ {% endif %}
779
+ {% endwith %}
780
+
781
+ <div class="d-flex justify-content-between align-items-center mb-4">
782
+ <h1 class="h3">Patient Management</h1>
783
+ <a href="{{ url_for('new_patient') }}" class="btn btn-primary">
784
+ <i class="fas fa-user-plus me-2"></i>New Patient
785
+ </a>
786
+ </div>
787
+
788
+ <div class="card">
789
+ <div class="card-body">
790
+ {% if patients %}
791
+ <div class="table-responsive">
792
+ <table class="table table-hover">
793
+ <thead>
794
+ <tr>
795
+ <th>Patient ID</th>
796
+ <th>Name</th>
797
+ <th>Age</th>
798
+ <th>Gender</th>
799
+ <th>Last Visit</th>
800
+ <th>Actions</th>
801
+ </tr>
802
+ </thead>
803
+ <tbody>
804
+ {% for patient in patients %}
805
+ <tr>
806
+ <td>{{ patient.patient_id }}</td>
807
+ <td>{{ patient.full_name() }}</td>
808
+ <td>{{ patient.age() }}</td>
809
+ <td>{{ patient.gender or 'N/A' }}</td>
810
+ <td>{{ patient.last_visit.strftime('%Y-%m-%d') if patient.last_visit else 'Never' }}</td>
811
+ <td>
812
+ <a href="{{ url_for('patient_detail', id=patient.id) }}" class="btn btn-sm btn-outline-primary">
813
+ <i class="fas fa-eye"></i>
814
+ </a>
815
+ <a href="{{ url_for('edit_patient', id=patient.id) }}" class="btn btn-sm btn-outline-secondary">
816
+ <i class="fas fa-edit"></i>
817
+ </a>
818
+ </td>
819
+ </tr>
820
+ {% endfor %}
821
+ </tbody>
822
+ </table>
823
+ </div>
824
+ {% else %}
825
+ <div class="text-center py-5">
826
+ <i class="fas fa-users fa-3x text-muted mb-3"></i>
827
+ <h5>No patients registered yet</h5>
828
+ <p class="text-muted">Start by adding your first patient</p>
829
+ <a href="{{ url_for('new_patient') }}" class="btn btn-primary">
830
+ <i class="fas fa-user-plus me-2"></i>Add First Patient
831
+ </a>
832
+ </div>
833
+ {% endif %}
834
+ </div>
835
+ </div>
836
+
837
+ </main>
838
+ <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
839
+ </body>
840
+ </html>
841
+ """
842
+
843
+ AI_ASSISTANT_TEMPLATE = """
844
+ <!DOCTYPE html>
845
+ <html lang="en">
846
+ <head>
847
+ <meta charset="UTF-8">
848
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
849
+ <title>AI Assistant - Doctor's Sidekick</title>
850
+ <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
851
+ <link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css" rel="stylesheet">
852
+ <style>
853
+ :root {
854
+ --primary-color: #0F766E;
855
+ --secondary-color: #1E40AF;
856
+ }
857
+ body { font-family: 'Inter', system-ui, sans-serif; background-color: #f8fafc; }
858
+ .navbar-brand { font-weight: 700; color: var(--primary-color) !important; }
859
+ .btn-primary { background-color: var(--primary-color); border-color: var(--primary-color); }
860
+ .btn-primary:hover { background-color: #065f5a; border-color: #065f5a; }
861
+ .text-primary { color: var(--primary-color) !important; }
862
+ .bg-primary { background-color: var(--primary-color) !important; }
863
+ .card { border: none; box-shadow: 0 1px 3px rgba(0,0,0,0.1); }
864
+ .nav-link.active { background-color: var(--primary-color) !important; }
865
+ </style>
866
+ </head>
867
+ <body>
868
+ <nav class="navbar navbar-expand-lg navbar-light bg-white shadow-sm">
869
+ <div class="container">
870
+ <a class="navbar-brand" href="{{ url_for('dashboard') }}">
871
+ <i class="fas fa-user-md me-2"></i>Doctor's Sidekick
872
+ </a>
873
+ <div class="navbar-nav ms-auto">
874
+ <a class="nav-link" href="{{ url_for('dashboard') }}">
875
+ <i class="fas fa-tachometer-alt me-1"></i> Dashboard
876
+ </a>
877
+ <a class="nav-link" href="{{ url_for('patients') }}">
878
+ <i class="fas fa-users me-1"></i> Patients
879
+ </a>
880
+ <a class="nav-link" href="{{ url_for('treatment_planner') }}">
881
+ <i class="fas fa-prescription-bottle-alt me-1"></i> Treatment Planner
882
+ </a>
883
+ <a class="nav-link active" href="{{ url_for('ai_assistant') }}">
884
+ <i class="fas fa-brain me-1"></i> AI Assistant
885
+ </a>
886
+ <div class="nav-link">
887
+ <span class="text-muted">{{ current_user.full_name() }}</span>
888
+ </div>
889
+ <a class="nav-link" href="{{ url_for('logout') }}">
890
+ <i class="fas fa-sign-out-alt me-1"></i> Logout
891
+ </a>
892
+ </div>
893
+ </div>
894
+ </nav>
895
+
896
+ <main class="container mt-4">
897
+ {% with messages = get_flashed_messages(with_categories=true) %}
898
+ {% if messages %}
899
+ {% for category, message in messages %}
900
+ <div class="alert alert-{{ 'danger' if category == 'error' else category }} alert-dismissible fade show">
901
+ {{ message }}
902
+ <button type="button" class="btn-close" data-bs-dismiss="alert"></button>
903
+ </div>
904
+ {% endfor %}
905
+ {% endif %}
906
+ {% endwith %}
907
+ <div class="d-flex justify-content-between align-items-center mb-4">
908
+ <div>
909
+ <h1 class="h3"><i class="fas fa-brain me-2 text-primary"></i>AI-Powered Treatment Assistant</h1>
910
+ <p class="text-muted mb-0">Intelligent diagnostic and treatment recommendations</p>
911
+ </div>
912
+ <div class="badge bg-success">
913
+ <i class="fas fa-check-circle me-1"></i> AI Online
914
+ </div>
915
+ </div>
916
+
917
+ <div class="row g-4">
918
+ <div class="col-lg-8">
919
+ <div class="card">
920
+ <div class="card-header bg-primary text-white">
921
+ <h5 class="mb-0"><i class="fas fa-stethoscope me-2"></i>Symptom Analysis</h5>
922
+ </div>
923
+ <div class="card-body">
924
+ <form id="symptomAnalysisForm">
925
+ <div class="row g-3">
926
+ <div class="col-md-6">
927
+ <label class="form-label">Select Patient</label>
928
+ <select class="form-select" name="patient_id" id="aiPatientSelect" required>
929
+ <option value="">Choose patient...</option>
930
+ {% for patient in patients %}
931
+ <option value="{{ patient.id }}"
932
+ data-age="{{ patient.age() }}"
933
+ data-gender="{{ patient.gender }}"
934
+ data-allergies="{{ patient.allergies or '' }}"
935
+ data-medical-history="{{ patient.medical_history or '' }}">
936
+ {{ patient.full_name() }} ({{ patient.patient_id }}) - Age {{ patient.age() }}
937
+ </option>
938
+ {% endfor %}
939
+ </select>
940
+ </div>
941
+ <div class="col-md-6">
942
+ <label class="form-label">Urgency Level</label>
943
+ <select class="form-select" name="urgency">
944
+ <option value="routine">Routine</option>
945
+ <option value="urgent">Urgent</option>
946
+ <option value="emergency">Emergency</option>
947
+ </select>
948
+ </div>
949
+ <div class="col-12">
950
+ <label class="form-label">Chief Complaint & Symptoms</label>
951
+ <textarea class="form-control" name="symptoms" rows="4"
952
+ placeholder="Describe the patient's symptoms in detail..."
953
+ required></textarea>
954
+ </div>
955
+ <div class="col-md-6">
956
+ <label class="form-label">Working Diagnosis (Optional)</label>
957
+ <input type="text" class="form-control" name="suspected_diagnosis"
958
+ placeholder="Your suspected diagnosis...">
959
+ </div>
960
+ </div>
961
+ <div class="d-flex gap-2 mt-4">
962
+ <button type="submit" class="btn btn-primary">
963
+ <i class="fas fa-brain me-2"></i> Analyze with AI
964
+ </button>
965
+ <button type="button" class="btn btn-outline-secondary" onclick="clearAIForm()">
966
+ <i class="fas fa-eraser me-2"></i> Clear
967
+ </button>
968
+ </div>
969
+ </form>
970
+ </div>
971
+ </div>
972
+
973
+ <div id="aiResults" class="d-none mt-4">
974
+ <div class="card">
975
+ <div class="card-header bg-success text-white">
976
+ <h5 class="mb-0"><i class="fas fa-list-alt me-2"></i>AI Analysis Results</h5>
977
+ </div>
978
+ <div class="card-body" id="analysisResults">
979
+ <!-- AI results will be populated here -->
980
+ </div>
981
+ </div>
982
+ </div>
983
+ </div>
984
+
985
+ <div class="col-lg-4">
986
+ <div class="card">
987
+ <div class="card-header bg-light">
988
+ <h6 class="mb-0"><i class="fas fa-tools me-2 text-primary"></i>Quick AI Tools</h6>
989
+ </div>
990
+ <div class="card-body">
991
+ <div class="d-grid gap-2">
992
+ <button class="btn btn-outline-primary btn-sm" onclick="quickDrugCheck()">
993
+ <i class="fas fa-pills me-2"></i> Drug Interaction Check
994
+ </button>
995
+ <button class="btn btn-outline-success btn-sm" onclick="aiInsights()">
996
+ <i class="fas fa-lightbulb me-2"></i> AI Insights
997
+ </button>
998
+ <button class="btn btn-outline-info btn-sm" onclick="templateGenerator()">
999
+ <i class="fas fa-magic me-2"></i> Generate Template
1000
+ </button>
1001
+ </div>
1002
+ </div>
1003
+ </div>
1004
+
1005
+ <div class="card mt-4">
1006
+ <div class="card-header bg-light">
1007
+ <h6 class="mb-0"><i class="fas fa-lightbulb me-2 text-warning"></i>AI Tips</h6>
1008
+ </div>
1009
+ <div class="card-body">
1010
+ <div class="small">
1011
+ <div class="mb-3">
1012
+ <i class="fas fa-info-circle text-info me-2"></i>
1013
+ <strong>Smart Documentation:</strong> AI helps generate comprehensive visit notes.
1014
+ </div>
1015
+ <div class="mb-3">
1016
+ <i class="fas fa-shield-alt text-success me-2"></i>
1017
+ <strong>Safety First:</strong> AI recommendations supplement clinical judgment.
1018
+ </div>
1019
+ <div>
1020
+ <i class="fas fa-clock text-primary me-2"></i>
1021
+ <strong>Continuous Learning:</strong> System improves with medical literature updates.
1022
+ </div>
1023
+ </div>
1024
+ </div>
1025
+ </div>
1026
+ </div>
1027
+ </div>
1028
+
1029
+ <div id="aiLoadingOverlay" class="d-none">
1030
+ <div class="position-fixed top-0 start-0 w-100 h-100 d-flex align-items-center justify-content-center"
1031
+ style="background: rgba(0,0,0,0.7); z-index: 9999;">
1032
+ <div class="text-center text-white">
1033
+ <div class="spinner-border text-primary mb-3" style="width: 3rem; height: 3rem;"></div>
1034
+ <h5>AI is analyzing...</h5>
1035
+ <p class="mb-0">This may take a few moments</p>
1036
+ </div>
1037
+ </div>
1038
+ </div>
1039
+
1040
+ <script>
1041
+ // AI Assistant JavaScript
1042
+ document.getElementById('symptomAnalysisForm').addEventListener('submit', async function(e) {
1043
+ e.preventDefault();
1044
+
1045
+ const formData = new FormData(this);
1046
+ const analysisData = {
1047
+ patient_id: formData.get('patient_id'),
1048
+ symptoms: formData.get('symptoms'),
1049
+ suspected_diagnosis: formData.get('suspected_diagnosis'),
1050
+ urgency: formData.get('urgency')
1051
+ };
1052
+
1053
+ if (!analysisData.patient_id || !analysisData.symptoms) {
1054
+ alert('Please select a patient and enter symptoms.');
1055
+ return;
1056
+ }
1057
+
1058
+ document.getElementById('aiLoadingOverlay').classList.remove('d-none');
1059
+
1060
+ try {
1061
+ const response = await fetch('/api/ai/analyze-symptoms', {
1062
+ method: 'POST',
1063
+ headers: {
1064
+ 'Content-Type': 'application/json'
1065
+ },
1066
+ body: JSON.stringify(analysisData)
1067
+ });
1068
+
1069
+ const results = await response.json();
1070
+
1071
+ if (results.success) {
1072
+ displayResults(results);
1073
+ } else {
1074
+ alert('AI analysis failed: ' + (results.error || 'Unknown error'));
1075
+ }
1076
+ } catch (error) {
1077
+ console.error('AI analysis error:', error);
1078
+ alert('AI analysis service is currently unavailable. Please try again later.');
1079
+ } finally {
1080
+ document.getElementById('aiLoadingOverlay').classList.add('d-none');
1081
+ }
1082
+ });
1083
+
1084
+ function displayResults(results) {
1085
+ document.getElementById('aiResults').classList.remove('d-none');
1086
+
1087
+ let html = '<div class="row g-3">';
1088
+
1089
+ // Differential Diagnosis
1090
+ if (results.differential_diagnosis && results.differential_diagnosis.length > 0) {
1091
+ html += '<div class="col-12"><h6><i class="fas fa-list-check text-primary me-2"></i>Differential Diagnosis</h6>';
1092
+ results.differential_diagnosis.forEach(diagnosis => {
1093
+ const confidenceColor = diagnosis.confidence > 70 ? 'success' :
1094
+ diagnosis.confidence > 40 ? 'warning' : 'danger';
1095
+ html += `
1096
+ <div class="card border-${confidenceColor} mb-2">
1097
+ <div class="card-body py-2">
1098
+ <div class="d-flex justify-content-between align-items-start">
1099
+ <strong>${diagnosis.condition}</strong>
1100
+ <span class="badge bg-${confidenceColor}">${diagnosis.confidence}%</span>
1101
+ </div>
1102
+ <small class="text-muted">${diagnosis.description}</small>
1103
+ </div>
1104
+ </div>
1105
+ `;
1106
+ });
1107
+ html += '</div>';
1108
+ }
1109
+
1110
+ // Treatment Plan
1111
+ if (results.treatment_plan) {
1112
+ const plan = results.treatment_plan;
1113
+ html += `
1114
+ <div class="col-md-6">
1115
+ <h6><i class="fas fa-pills text-success me-2"></i>Medications</h6>
1116
+ <div class="bg-light p-3 rounded">
1117
+ ${plan.medications || 'No specific medications recommended'}
1118
+ </div>
1119
+ </div>
1120
+ <div class="col-md-6">
1121
+ <h6><i class="fas fa-list-check text-info me-2"></i>Instructions</h6>
1122
+ <div class="bg-light p-3 rounded">
1123
+ ${plan.instructions || 'Standard care instructions apply'}
1124
+ </div>
1125
+ </div>
1126
+ `;
1127
+
1128
+ if (plan.warnings && plan.warnings.length > 0) {
1129
+ html += `
1130
+ <div class="col-12">
1131
+ <div class="alert alert-warning">
1132
+ <h6><i class="fas fa-exclamation-triangle me-2"></i>Important Warnings</h6>
1133
+ <ul class="mb-0">
1134
+ ${plan.warnings.map(warning => `<li>${warning}</li>`).join('')}
1135
+ </ul>
1136
+ </div>
1137
+ </div>
1138
+ `;
1139
+ }
1140
+ }
1141
+
1142
+ html += '</div>';
1143
+ document.getElementById('analysisResults').innerHTML = html;
1144
+ }
1145
+
1146
+ function clearAIForm() {
1147
+ document.getElementById('symptomAnalysisForm').reset();
1148
+ document.getElementById('aiResults').classList.add('d-none');
1149
+ }
1150
+
1151
+ function quickDrugCheck() {
1152
+ alert('Drug interaction checker: Enter medications to check for interactions and contraindications.');
1153
+ }
1154
+
1155
+ function aiInsights() {
1156
+ alert('AI Insights: Daily medical insights and practice recommendations based on current cases.');
1157
+ }
1158
+
1159
+ function templateGenerator() {
1160
+ window.location.href = '/treatment-planner';
1161
+ }
1162
+ </script>
1163
+
1164
+ </main>
1165
+ <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
1166
+ </body>
1167
+ </html>
1168
+ """
1169
+
1170
+ TREATMENT_PLANNER_TEMPLATE = """
1171
+ <!DOCTYPE html>
1172
+ <html lang="en">
1173
+ <head>
1174
+ <meta charset="UTF-8">
1175
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
1176
+ <title>Treatment Planner - Doctor's Sidekick</title>
1177
+ <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
1178
+ <link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css" rel="stylesheet">
1179
+ <style>
1180
+ :root { --primary-color: #0F766E; --secondary-color: #1E40AF; }
1181
+ body { font-family: 'Inter', system-ui, sans-serif; background-color: #f8fafc; }
1182
+ .navbar-brand { font-weight: 700; color: var(--primary-color) !important; }
1183
+ .btn-primary { background-color: var(--primary-color); border-color: var(--primary-color); }
1184
+ .btn-primary:hover { background-color: #065f5a; border-color: #065f5a; }
1185
+ .text-primary { color: var(--primary-color) !important; }
1186
+ .bg-primary { background-color: var(--primary-color) !important; }
1187
+ .card { border: none; box-shadow: 0 1px 3px rgba(0,0,0,0.1); }
1188
+ .nav-link.active { background-color: var(--primary-color) !important; }
1189
+ </style>
1190
+ </head>
1191
+ <body>
1192
+ <nav class="navbar navbar-expand-lg navbar-light bg-white shadow-sm">
1193
+ <div class="container">
1194
+ <a class="navbar-brand" href="{{ url_for('dashboard') }}">
1195
+ <i class="fas fa-user-md me-2"></i>Doctor's Sidekick
1196
+ </a>
1197
+ <div class="navbar-nav ms-auto">
1198
+ <a class="nav-link" href="{{ url_for('dashboard') }}">
1199
+ <i class="fas fa-tachometer-alt me-1"></i> Dashboard
1200
+ </a>
1201
+ <a class="nav-link" href="{{ url_for('patients') }}">
1202
+ <i class="fas fa-users me-1"></i> Patients
1203
+ </a>
1204
+ <a class="nav-link active" href="{{ url_for('treatment_planner') }}">
1205
+ <i class="fas fa-prescription-bottle-alt me-1"></i> Treatment Planner
1206
+ </a>
1207
+ <a class="nav-link" href="{{ url_for('ai_assistant') }}">
1208
+ <i class="fas fa-brain me-1"></i> AI Assistant
1209
+ </a>
1210
+ <div class="nav-link">
1211
+ <span class="text-muted">{{ current_user.full_name() }}</span>
1212
+ </div>
1213
+ <a class="nav-link" href="{{ url_for('logout') }}">
1214
+ <i class="fas fa-sign-out-alt me-1"></i> Logout
1215
+ </a>
1216
+ </div>
1217
+ </div>
1218
+ </nav>
1219
+
1220
+ <main class="container mt-4">
1221
+ {% with messages = get_flashed_messages(with_categories=true) %}
1222
+ {% if messages %}
1223
+ {% for category, message in messages %}
1224
+ <div class="alert alert-{{ 'danger' if category == 'error' else category }} alert-dismissible fade show">
1225
+ {{ message }}
1226
+ <button type="button" class="btn-close" data-bs-dismiss="alert"></button>
1227
+ </div>
1228
+ {% endfor %}
1229
+ {% endif %}
1230
+ {% endwith %}
1231
+ <div class="d-flex justify-content-between align-items-center mb-4">
1232
+ <h1 class="h3"><i class="fas fa-prescription-bottle-alt me-2 text-primary"></i>Treatment Planner</h1>
1233
+ <div class="badge bg-info">AI-Enhanced</div>
1234
+ </div>
1235
+
1236
+ <div class="row g-4">
1237
+ <div class="col-lg-8">
1238
+ <div class="card">
1239
+ <div class="card-header">
1240
+ <h5><i class="fas fa-plus-circle me-2"></i>Create Treatment Plan</h5>
1241
+ </div>
1242
+ <div class="card-body">
1243
+ <form method="POST" id="treatmentPlanForm">
1244
+ {{ form.hidden_tag() }}
1245
+ <div class="row g-3">
1246
+ <div class="col-md-6">
1247
+ <label class="form-label">Select Patient</label>
1248
+ <select class="form-select" name="patient_id" required>
1249
+ <option value="">Choose patient...</option>
1250
+ {% for patient in patients %}
1251
+ <option value="{{ patient.id }}">
1252
+ {{ patient.full_name() }} ({{ patient.patient_id }}) - Age {{ patient.age() }}
1253
+ </option>
1254
+ {% endfor %}
1255
+ </select>
1256
+ </div>
1257
+ <div class="col-md-6">
1258
+ <label class="form-label">Condition Category</label>
1259
+ <select class="form-select" id="condition_id">
1260
+ <option value="">Select condition...</option>
1261
+ {% for condition in conditions %}
1262
+ <option value="{{ condition.id }}">{{ condition.name }}</option>
1263
+ {% endfor %}
1264
+ </select>
1265
+ </div>
1266
+ <div class="col-12">
1267
+ {{ form.diagnosis.label(class="form-label") }}
1268
+ {{ form.diagnosis(class="form-control") }}
1269
+ </div>
1270
+ <div class="col-12">
1271
+ {{ form.medications.label(class="form-label") }}
1272
+ {{ form.medications(class="form-control", rows="4", placeholder="Enter medications with dosages and frequency") }}
1273
+ </div>
1274
+ <div class="col-12">
1275
+ {{ form.instructions.label(class="form-label") }}
1276
+ {{ form.instructions(class="form-control", rows="4", placeholder="Patient care instructions") }}
1277
+ </div>
1278
+ <div class="col-md-6">
1279
+ {{ form.duration.label(class="form-label") }}
1280
+ {{ form.duration(class="form-control", placeholder="e.g., 2-4 weeks") }}
1281
+ </div>
1282
+ <div class="col-md-6">
1283
+ {{ form.follow_up_date.label(class="form-label") }}
1284
+ {{ form.follow_up_date(class="form-control") }}
1285
+ </div>
1286
+ <div class="col-12">
1287
+ {{ form.notes.label(class="form-label") }}
1288
+ {{ form.notes(class="form-control", rows="3") }}
1289
+ </div>
1290
+ </div>
1291
+
1292
+ <div class="d-flex gap-2 mt-4">
1293
+ <button type="submit" class="btn btn-primary">
1294
+ <i class="fas fa-save me-2"></i> Create Treatment Plan
1295
+ </button>
1296
+ <button type="button" class="btn btn-outline-info" onclick="getAIRecommendations()">
1297
+ <i class="fas fa-brain me-2"></i> Get AI Recommendations
1298
+ </button>
1299
+ <button type="button" class="btn btn-outline-secondary" onclick="clearForm()">
1300
+ <i class="fas fa-eraser me-2"></i> Clear Form
1301
+ </button>
1302
+ </div>
1303
+ </form>
1304
+ </div>
1305
+ </div>
1306
+ </div>
1307
+
1308
+ <div class="col-lg-4">
1309
+ <div class="card">
1310
+ <div class="card-header bg-light">
1311
+ <h6 class="mb-0"><i class="fas fa-lightbulb me-2 text-warning"></i>Treatment Guidelines</h6>
1312
+ </div>
1313
+ <div class="card-body">
1314
+ <div class="small">
1315
+ <h6 class="text-primary"><i class="fas fa-circle-check me-2"></i>Best Practices</h6>
1316
+ <ul class="list-unstyled ms-3">
1317
+ <li><i class="fas fa-chevron-right me-2 text-muted"></i>Include specific dosages and frequencies</li>
1318
+ <li><i class="fas fa-chevron-right me-2 text-muted"></i>Provide clear patient instructions</li>
1319
+ <li><i class="fas fa-chevron-right me-2 text-muted"></i>Set appropriate follow-up timelines</li>
1320
+ </ul>
1321
+
1322
+ <h6 class="text-success mt-3"><i class="fas fa-info-circle me-2"></i>Patient Safety</h6>
1323
+ <ul class="list-unstyled ms-3">
1324
+ <li><i class="fas fa-chevron-right me-2 text-muted"></i>Check for drug allergies</li>
1325
+ <li><i class="fas fa-chevron-right me-2 text-muted"></i>Consider drug interactions</li>
1326
+ <li><i class="fas fa-chevron-right me-2 text-muted"></i>Include warning signs</li>
1327
+ </ul>
1328
+ </div>
1329
+ </div>
1330
+ </div>
1331
+ </div>
1332
+ </div>
1333
+
1334
+ <script>
1335
+ async function getAIRecommendations() {
1336
+ const patientSelect = document.querySelector('select[name="patient_id"]');
1337
+ const diagnosisInput = document.querySelector('input[name="diagnosis"]');
1338
+
1339
+ if (!patientSelect.value || !diagnosisInput.value) {
1340
+ alert('Please select a patient and enter a diagnosis to get AI recommendations.');
1341
+ return;
1342
+ }
1343
+
1344
+ try {
1345
+ const response = await fetch('/api/ai/analyze-symptoms', {
1346
+ method: 'POST',
1347
+ headers: {
1348
+ 'Content-Type': 'application/json'
1349
+ },
1350
+ body: JSON.stringify({
1351
+ patient_id: patientSelect.value,
1352
+ symptoms: diagnosisInput.value,
1353
+ suspected_diagnosis: diagnosisInput.value
1354
+ })
1355
+ });
1356
+
1357
+ const data = await response.json();
1358
+
1359
+ if (data.success && data.treatment_plan) {
1360
+ const plan = data.treatment_plan;
1361
+ if (plan.medications) {
1362
+ document.querySelector('textarea[name="medications"]').value = plan.medications;
1363
+ }
1364
+ if (plan.instructions) {
1365
+ document.querySelector('textarea[name="instructions"]').value = plan.instructions;
1366
+ }
1367
+
1368
+ alert('AI recommendations applied successfully! Please review and adjust as needed.');
1369
+ } else {
1370
+ alert('Unable to get AI recommendations at this time.');
1371
+ }
1372
+ } catch (error) {
1373
+ console.error('AI recommendation error:', error);
1374
+ alert('AI recommendation service is currently unavailable.');
1375
+ }
1376
+ }
1377
+
1378
+ function clearForm() {
1379
+ if (confirm('Are you sure you want to clear all form data?')) {
1380
+ document.getElementById('treatmentPlanForm').reset();
1381
+ }
1382
+ }
1383
+ </script>
1384
+
1385
+ </main>
1386
+ <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
1387
+ </body>
1388
+ </html>
1389
+ """
1390
+
1391
+ NEW_PATIENT_TEMPLATE = """
1392
+ <!DOCTYPE html>
1393
+ <html lang="en">
1394
+ <head>
1395
+ <meta charset="UTF-8">
1396
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
1397
+ <title>New Patient - Doctor's Sidekick</title>
1398
+ <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
1399
+ <link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css" rel="stylesheet">
1400
+ <style>
1401
+ :root { --primary-color: #0F766E; --secondary-color: #1E40AF; }
1402
+ body { font-family: 'Inter', system-ui, sans-serif; background-color: #f8fafc; }
1403
+ .navbar-brand { font-weight: 700; color: var(--primary-color) !important; }
1404
+ .btn-primary { background-color: var(--primary-color); border-color: var(--primary-color); }
1405
+ .btn-primary:hover { background-color: #065f5a; border-color: #065f5a; }
1406
+ .text-primary { color: var(--primary-color) !important; }
1407
+ .bg-primary { background-color: var(--primary-color) !important; }
1408
+ .card { border: none; box-shadow: 0 1px 3px rgba(0,0,0,0.1); }
1409
+ .nav-link.active { background-color: var(--primary-color) !important; }
1410
+ </style>
1411
+ </head>
1412
+ <body>
1413
+ <nav class="navbar navbar-expand-lg navbar-light bg-white shadow-sm">
1414
+ <div class="container">
1415
+ <a class="navbar-brand" href="{{ url_for('dashboard') }}">
1416
+ <i class="fas fa-user-md me-2"></i>Doctor's Sidekick
1417
+ </a>
1418
+ <div class="navbar-nav ms-auto">
1419
+ <a class="nav-link" href="{{ url_for('dashboard') }}">
1420
+ <i class="fas fa-tachometer-alt me-1"></i> Dashboard
1421
+ </a>
1422
+ <a class="nav-link active" href="{{ url_for('patients') }}">
1423
+ <i class="fas fa-users me-1"></i> Patients
1424
+ </a>
1425
+ <a class="nav-link" href="{{ url_for('treatment_planner') }}">
1426
+ <i class="fas fa-prescription-bottle-alt me-1"></i> Treatment Planner
1427
+ </a>
1428
+ <a class="nav-link" href="{{ url_for('ai_assistant') }}">
1429
+ <i class="fas fa-brain me-1"></i> AI Assistant
1430
+ </a>
1431
+ <div class="nav-link">
1432
+ <span class="text-muted">{{ current_user.full_name() }}</span>
1433
+ </div>
1434
+ <a class="nav-link" href="{{ url_for('logout') }}">
1435
+ <i class="fas fa-sign-out-alt me-1"></i> Logout
1436
+ </a>
1437
+ </div>
1438
+ </div>
1439
+ </nav>
1440
+
1441
+ <main class="container mt-4">
1442
+ {% with messages = get_flashed_messages(with_categories=true) %}
1443
+ {% if messages %}
1444
+ {% for category, message in messages %}
1445
+ <div class="alert alert-{{ 'danger' if category == 'error' else category }} alert-dismissible fade show">
1446
+ {{ message }}
1447
+ <button type="button" class="btn-close" data-bs-dismiss="alert"></button>
1448
+ </div>
1449
+ {% endfor %}
1450
+ {% endif %}
1451
+ {% endwith %}
1452
+ <div class="d-flex justify-content-between align-items-center mb-4">
1453
+ <h1 class="h3"><i class="fas fa-user-plus me-2 text-primary"></i>New Patient Registration</h1>
1454
+ <a href="{{ url_for('patients') }}" class="btn btn-outline-secondary">
1455
+ <i class="fas fa-arrow-left me-2"></i>Back to Patients
1456
+ </a>
1457
+ </div>
1458
+
1459
+ <div class="row justify-content-center">
1460
+ <div class="col-lg-8">
1461
+ <div class="card">
1462
+ <div class="card-header">
1463
+ <h5><i class="fas fa-user-circle me-2"></i>Patient Information</h5>
1464
+ </div>
1465
+ <div class="card-body">
1466
+ <form method="POST">
1467
+ {{ form.hidden_tag() }}
1468
+ <div class="row g-3">
1469
+ <div class="col-md-6">
1470
+ {{ form.first_name.label(class="form-label") }}
1471
+ {{ form.first_name(class="form-control") }}
1472
+ </div>
1473
+ <div class="col-md-6">
1474
+ {{ form.last_name.label(class="form-label") }}
1475
+ {{ form.last_name(class="form-control") }}
1476
+ </div>
1477
+ <div class="col-md-6">
1478
+ {{ form.date_of_birth.label(class="form-label") }}
1479
+ {{ form.date_of_birth(class="form-control") }}
1480
+ </div>
1481
+ <div class="col-md-6">
1482
+ {{ form.gender.label(class="form-label") }}
1483
+ {{ form.gender(class="form-select") }}
1484
+ </div>
1485
+ <div class="col-md-6">
1486
+ {{ form.phone.label(class="form-label") }}
1487
+ {{ form.phone(class="form-control") }}
1488
+ </div>
1489
+ <div class="col-md-6">
1490
+ {{ form.email.label(class="form-label") }}
1491
+ {{ form.email(class="form-control") }}
1492
+ </div>
1493
+ <div class="col-12">
1494
+ {{ form.address.label(class="form-label") }}
1495
+ {{ form.address(class="form-control", rows="3") }}
1496
+ </div>
1497
+ <div class="col-12">
1498
+ {{ form.allergies.label(class="form-label") }}
1499
+ {{ form.allergies(class="form-control", rows="3", placeholder="List any known allergies or NKDA") }}
1500
+ </div>
1501
+ <div class="col-12">
1502
+ {{ form.medical_history.label(class="form-label") }}
1503
+ {{ form.medical_history(class="form-control", rows="4", placeholder="Past medical history, current medications, etc.") }}
1504
+ </div>
1505
+ </div>
1506
+
1507
+ <div class="d-flex gap-2 mt-4">
1508
+ <button type="submit" class="btn btn-primary">
1509
+ <i class="fas fa-save me-2"></i> Register Patient
1510
+ </button>
1511
+ <a href="{{ url_for('patients') }}" class="btn btn-outline-secondary">
1512
+ <i class="fas fa-times me-2"></i> Cancel
1513
+ </a>
1514
+ </div>
1515
+ </form>
1516
+ </div>
1517
+ </div>
1518
+ </div>
1519
+ </div>
1520
+
1521
+ </main>
1522
+ <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
1523
+ </body>
1524
+ </html>
1525
+ """
1526
+
1527
+ # ============================================================================
1528
+ # ROUTES
1529
+ # ============================================================================
1530
+
1531
+ @app.route('/login', methods=['GET', 'POST'])
1532
+ def login():
1533
+ form = LoginForm()
1534
+ if form.validate_on_submit():
1535
+ user = User.query.filter_by(username=form.username.data).first()
1536
+ if user and user.password_hash and check_password_hash(user.password_hash, form.password.data):
1537
+ login_user(user)
1538
+ return redirect(url_for('dashboard'))
1539
+ flash('Invalid username or password', 'error')
1540
+ return render_template_string(LOGIN_TEMPLATE, form=form)
1541
+
1542
+ @app.route('/logout')
1543
+ @login_required
1544
+ def logout():
1545
+ logout_user()
1546
+ return redirect(url_for('login'))
1547
+
1548
+ @app.route('/')
1549
+ @login_required
1550
+ def dashboard():
1551
+ stats = {
1552
+ 'total_patients': Patient.query.count(),
1553
+ 'visits_today': 0, # Would count visits for today
1554
+ 'treatment_plans': TreatmentPlan.query.count(),
1555
+ }
1556
+ return render_template_string(DASHBOARD_TEMPLATE, stats=stats)
1557
+
1558
+ @app.route('/patients')
1559
+ @login_required
1560
+ def patients():
1561
+ patients = Patient.query.order_by(Patient.last_name, Patient.first_name).all()
1562
+ return render_template_string(PATIENTS_TEMPLATE, patients=patients)
1563
+
1564
+ @app.route('/patients/new', methods=['GET', 'POST'])
1565
+ @login_required
1566
+ def new_patient():
1567
+ form = PatientForm()
1568
+ if form.validate_on_submit():
1569
+ # Generate patient ID
1570
+ patient_count = Patient.query.count()
1571
+ patient_id = f"P{patient_count + 1:04d}"
1572
+
1573
+ patient = Patient(
1574
+ patient_id=patient_id,
1575
+ first_name=form.first_name.data,
1576
+ last_name=form.last_name.data,
1577
+ date_of_birth=form.date_of_birth.data,
1578
+ gender=form.gender.data,
1579
+ phone=form.phone.data,
1580
+ email=form.email.data,
1581
+ address=form.address.data,
1582
+ allergies=form.allergies.data,
1583
+ medical_history=form.medical_history.data,
1584
+ doctor_id=current_user.id
1585
+ )
1586
+ db.session.add(patient)
1587
+ db.session.commit()
1588
+
1589
+ flash(f'Patient {patient.full_name()} registered successfully!', 'success')
1590
+ return redirect(url_for('patients'))
1591
+
1592
+ return render_template_string(NEW_PATIENT_TEMPLATE, form=form)
1593
+
1594
+ @app.route('/patients/<int:id>')
1595
+ @login_required
1596
+ def patient_detail(id):
1597
+ patient = Patient.query.get_or_404(id)
1598
+ return render_template_string("""
1599
+ {% extends "base.html" %}
1600
+ {% block content %}
1601
+ <h1>{{ patient.full_name() }}</h1>
1602
+ <div class="card">
1603
+ <div class="card-body">
1604
+ <p><strong>Patient ID:</strong> {{ patient.patient_id }}</p>
1605
+ <p><strong>Age:</strong> {{ patient.age() }}</p>
1606
+ <p><strong>Gender:</strong> {{ patient.gender or 'N/A' }}</p>
1607
+ <p><strong>Phone:</strong> {{ patient.phone or 'N/A' }}</p>
1608
+ <p><strong>Email:</strong> {{ patient.email or 'N/A' }}</p>
1609
+ <p><strong>Allergies:</strong> {{ patient.allergies or 'None known' }}</p>
1610
+ <p><strong>Medical History:</strong> {{ patient.medical_history or 'None provided' }}</p>
1611
+ </div>
1612
+ </div>
1613
+ {% endblock %}
1614
+ """, patient=patient)
1615
+
1616
+ @app.route('/patients/<int:id>/edit')
1617
+ @login_required
1618
+ def edit_patient(id):
1619
+ patient = Patient.query.get_or_404(id)
1620
+ form = PatientForm(obj=patient)
1621
+ return render_template_string(NEW_PATIENT_TEMPLATE, form=form, patient=patient)
1622
+
1623
+ @app.route('/treatment-planner', methods=['GET', 'POST'])
1624
+ @login_required
1625
+ def treatment_planner():
1626
+ form = TreatmentPlanForm()
1627
+ patients = Patient.query.order_by(Patient.last_name, Patient.first_name).all()
1628
+ conditions = Condition.query.all()
1629
+
1630
+ if form.validate_on_submit():
1631
+ patient_id = request.form.get('patient_id')
1632
+ if not patient_id:
1633
+ flash('Please select a patient', 'error')
1634
+ else:
1635
+ treatment_plan = TreatmentPlan(
1636
+ patient_id=patient_id,
1637
+ diagnosis=form.diagnosis.data,
1638
+ medications=form.medications.data,
1639
+ instructions=form.instructions.data,
1640
+ duration=form.duration.data,
1641
+ follow_up_date=form.follow_up_date.data,
1642
+ notes=form.notes.data,
1643
+ created_by_id=current_user.id
1644
+ )
1645
+ db.session.add(treatment_plan)
1646
+ db.session.commit()
1647
+
1648
+ flash('Treatment plan created successfully!', 'success')
1649
+ return redirect(url_for('treatment_planner'))
1650
+
1651
+ return render_template_string(TREATMENT_PLANNER_TEMPLATE,
1652
+ form=form, patients=patients, conditions=conditions)
1653
+
1654
+ @app.route('/ai-assistant')
1655
+ @login_required
1656
+ def ai_assistant():
1657
+ patients = Patient.query.order_by(Patient.last_name, Patient.first_name).all()
1658
+ return render_template_string(AI_ASSISTANT_TEMPLATE, patients=patients)
1659
+
1660
+ @app.route('/api/ai/analyze-symptoms', methods=['POST'])
1661
+ @login_required
1662
+ def ai_analyze_symptoms():
1663
+ try:
1664
+ data = request.get_json()
1665
+ patient = Patient.query.get_or_404(data['patient_id'])
1666
+
1667
+ # Prepare patient data for AI analysis
1668
+ patient_data = {
1669
+ 'age': patient.age(),
1670
+ 'gender': patient.gender or 'Not specified',
1671
+ 'medical_history': patient.medical_history or 'None provided',
1672
+ 'allergies': patient.allergies or 'None known'
1673
+ }
1674
+
1675
+ # Get AI recommendations
1676
+ treatment_plan = medical_ai.generate_treatment_plan(
1677
+ patient_data,
1678
+ data['symptoms'],
1679
+ data.get('suspected_diagnosis')
1680
+ )
1681
+
1682
+ # Check for drug interactions if medications are mentioned
1683
+ medications = []
1684
+ if 'medications' in treatment_plan:
1685
+ medications = [med.strip() for med in str(treatment_plan.get('medications', '')).split('\n') if med.strip()]
1686
+
1687
+ drug_interactions = medical_ai.check_drug_interactions(medications, patient.allergies or '')
1688
+
1689
+ return jsonify({
1690
+ 'success': True,
1691
+ 'treatment_plan': treatment_plan,
1692
+ 'differential_diagnosis': treatment_plan.get('differential_diagnosis', []),
1693
+ 'drug_interactions': drug_interactions
1694
+ })
1695
+
1696
+ except Exception as e:
1697
+ app.logger.error(f"AI analysis error: {str(e)}")
1698
+ return jsonify({
1699
+ 'success': False,
1700
+ 'error': 'AI analysis service temporarily unavailable'
1701
+ }), 500
1702
+
1703
+ @app.route('/api/ai/smart-templates/<int:condition_id>')
1704
+ @login_required
1705
+ def ai_smart_templates(condition_id):
1706
+ try:
1707
+ condition = Condition.query.get_or_404(condition_id)
1708
+ patient_id = request.args.get('patient_id')
1709
+
1710
+ patient_demographics = {}
1711
+ if patient_id:
1712
+ patient = Patient.query.get(patient_id)
1713
+ if patient:
1714
+ patient_demographics = {
1715
+ 'age': patient.age(),
1716
+ 'gender': patient.gender,
1717
+ 'medical_history': patient.medical_history,
1718
+ 'allergies': patient.allergies
1719
+ }
1720
+
1721
+ smart_templates = medical_ai.smart_template_suggestions(
1722
+ condition.name,
1723
+ patient_demographics
1724
+ )
1725
+
1726
+ return jsonify({
1727
+ 'success': True,
1728
+ 'templates': smart_templates
1729
+ })
1730
+
1731
+ except Exception as e:
1732
+ app.logger.error(f"Smart template error: {str(e)}")
1733
+ return jsonify({
1734
+ 'success': False,
1735
+ 'error': 'Smart template service unavailable'
1736
+ }), 500
1737
+
1738
+ def init_database():
1739
+ """Initialize database with sample data"""
1740
+ with app.app_context():
1741
+ # Create tables
1742
+ db.create_all()
1743
+
1744
+ # Check if data already exists
1745
+ if User.query.first():
1746
+ return
1747
+
1748
+ # Create roles
1749
+ admin_role = Role(name='Admin', description='Administrator with full access')
1750
+ doctor_role = Role(name='Doctor', description='Medical doctor with patient access')
1751
+
1752
+ db.session.add(admin_role)
1753
+ db.session.add(doctor_role)
1754
+ db.session.commit()
1755
+
1756
+ # Create admin user
1757
+ admin_user = User(
1758
+ username='admin',
1759
+ email='admin@example.com',
1760
+ password_hash=generate_password_hash('admin'),
1761
+ first_name='Admin',
1762
+ last_name='User',
1763
+ role_id=admin_role.id
1764
+ )
1765
+
1766
+ db.session.add(admin_user)
1767
+ db.session.commit()
1768
+
1769
+ # Create sample conditions
1770
+ conditions = [
1771
+ Condition(name='Acne Vulgaris', description='Common inflammatory skin condition', icd_code='L70.0', category='Inflammatory'),
1772
+ Condition(name='Atopic Dermatitis', description='Chronic inflammatory skin disease', icd_code='L20.9', category='Inflammatory'),
1773
+ Condition(name='Contact Dermatitis', description='Skin inflammation from contact with allergens', icd_code='L23.9', category='Inflammatory'),
1774
+ Condition(name='Psoriasis', description='Chronic autoimmune skin condition', icd_code='L40.9', category='Autoimmune'),
1775
+ Condition(name='Seborrheic Dermatitis', description='Inflammatory condition of sebaceous areas', icd_code='L21.9', category='Inflammatory')
1776
+ ]
1777
+
1778
+ for condition in conditions:
1779
+ db.session.add(condition)
1780
+
1781
+ db.session.commit()
1782
+
1783
+ print("Database initialized with sample data!")
1784
+
1785
+ # ============================================================================
1786
+ # MAIN APPLICATION
1787
+ # ============================================================================
1788
+
1789
+ if __name__ == '__main__':
1790
+ # Initialize database
1791
+ init_database()
1792
+
1793
+ # Create uploads directory
1794
+ os.makedirs(app.config['UPLOAD_FOLDER'], exist_ok=True)
1795
+
1796
+ # Get port from environment or default to 5000
1797
+ port = int(os.environ.get('PORT', 5000))
1798
+
1799
+ # Run the application
1800
+ app.run(host='0.0.0.0', port=port, debug=True)
hf_requirements.txt ADDED
@@ -0,0 +1,8 @@
 
 
 
 
 
 
 
 
 
1
+ Flask==2.3.3
2
+ Flask-SQLAlchemy==3.0.5
3
+ Flask-Login==0.6.3
4
+ Flask-WTF==1.1.1
5
+ WTForms==3.0.1
6
+ Werkzeug==2.3.7
7
+ email-validator==2.0.0
8
+ gunicorn==21.2.0
install_dependencies.txt ADDED
@@ -0,0 +1,15 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Required Python Packages for Doctor's Sidekick Medical Dashboard
2
+
3
+ Flask==2.3.3
4
+ Flask-SQLAlchemy==3.0.5
5
+ Flask-Login==0.6.3
6
+ Flask-WTF==1.1.1
7
+ WTForms==3.0.1
8
+ Werkzeug==2.3.7
9
+ email-validator==2.0.0
10
+
11
+ # Installation command:
12
+ # pip install -r install_dependencies.txt
13
+
14
+ # Or install individually:
15
+ # pip install flask flask-sqlalchemy flask-login flask-wtf wtforms werkzeug email-validator
main.py ADDED
@@ -0,0 +1,6 @@
 
 
 
 
 
 
 
1
+ # Import and expose the complete medical dashboard application
2
+ from complete_medical_dashboard import app
3
+
4
+ # This allows gunicorn to find the app
5
+ if __name__ == '__main__':
6
+ app.run(host='0.0.0.0', port=5000, debug=True)
run_app.py ADDED
@@ -0,0 +1,68 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python3
2
+ """
3
+ Simple runner script for the Doctor's Sidekick Medical Dashboard
4
+ This script installs dependencies and runs the complete application
5
+ """
6
+
7
+ import subprocess
8
+ import sys
9
+ import os
10
+
11
+ def install_requirements():
12
+ """Install required packages"""
13
+ packages = [
14
+ 'Flask==2.3.3',
15
+ 'Flask-SQLAlchemy==3.0.5',
16
+ 'Flask-Login==0.6.3',
17
+ 'Flask-WTF==1.1.1',
18
+ 'WTForms==3.0.1',
19
+ 'Werkzeug==2.3.7',
20
+ 'email-validator==2.0.0'
21
+ ]
22
+
23
+ print("Installing required packages...")
24
+ for package in packages:
25
+ try:
26
+ subprocess.check_call([sys.executable, '-m', 'pip', 'install', package])
27
+ print(f"✓ Installed {package}")
28
+ except subprocess.CalledProcessError:
29
+ print(f"✗ Failed to install {package}")
30
+ return False
31
+
32
+ return True
33
+
34
+ def run_application():
35
+ """Run the medical dashboard application"""
36
+ print("\nStarting Doctor's Sidekick Medical Dashboard...")
37
+ print("Access the application at: http://localhost:5000")
38
+ print("Login credentials: admin/admin")
39
+ print("Press Ctrl+C to stop the server\n")
40
+
41
+ try:
42
+ # Import and run the complete application
43
+ import complete_medical_dashboard
44
+ except ImportError as e:
45
+ print(f"Error importing application: {e}")
46
+ return False
47
+ except KeyboardInterrupt:
48
+ print("\nShutting down server...")
49
+ return True
50
+
51
+ if __name__ == '__main__':
52
+ print("Doctor's Sidekick - Medical Dashboard Setup")
53
+ print("=" * 50)
54
+
55
+ # Check if complete_medical_dashboard.py exists
56
+ if not os.path.exists('complete_medical_dashboard.py'):
57
+ print("Error: complete_medical_dashboard.py not found!")
58
+ print("Please ensure the main application file is in the current directory.")
59
+ sys.exit(1)
60
+
61
+ # Install dependencies
62
+ if install_requirements():
63
+ print("\n✓ All dependencies installed successfully!")
64
+ run_application()
65
+ else:
66
+ print("\n✗ Failed to install some dependencies. Please install manually:")
67
+ print("pip install flask flask-sqlalchemy flask-login flask-wtf wtforms")
68
+ sys.exit(1)