-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathadvanced_utils.py
More file actions
345 lines (265 loc) · 12.9 KB
/
Copy pathadvanced_utils.py
File metadata and controls
345 lines (265 loc) · 12.9 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
"""
Utility functions for advanced features.
QR code generation, PDF certificates, symptom screening logic.
"""
import qrcode
from io import BytesIO
import base64
from datetime import datetime, date, timedelta
import json
import secrets
# ──────────────────────────────────────────────
# QR Code Generation
# ──────────────────────────────────────────────
def generate_qr_code(data, size=10, add_logo=False):
"""
Generate QR code for appointment check-in.
Returns base64-encoded image string.
Args:
data: QR code data string
size: Box size for QR code
add_logo: If True, adds favicon logo in center
"""
qr = qrcode.QRCode(
version=1,
error_correction=qrcode.constants.ERROR_CORRECT_H, # High error correction for logo overlay
box_size=size,
border=4,
)
qr.add_data(data)
qr.make(fit=True)
img = qr.make_image(fill_color="black", back_color="white").convert('RGB')
# Add logo in center if requested
if add_logo:
try:
from PIL import Image, ImageDraw
import os
# Get favicon path
logo_path = os.path.join(os.path.dirname(__file__), 'static', 'favicon.ico')
if os.path.exists(logo_path):
logo = Image.open(logo_path)
# Calculate logo size (12% of QR code - smaller for better scanner compatibility)
qr_width, qr_height = img.size
logo_size = int(qr_width * 0.12)
padding = 4 # White padding around logo
# Resize logo (handle different Pillow versions)
try:
logo = logo.resize((logo_size, logo_size), Image.Resampling.LANCZOS)
except AttributeError:
logo = logo.resize((logo_size, logo_size), Image.LANCZOS)
# Convert logo to RGB if needed (in case it's RGBA)
if logo.mode != 'RGB':
logo_bg = Image.new('RGB', (logo_size, logo_size), 'white')
if logo.mode == 'RGBA':
logo_bg.paste(logo, (0, 0), logo)
else:
logo = logo.convert('RGB')
logo_bg = logo
logo = logo_bg
# Create white background box (slightly larger than logo for padding)
bg_size = logo_size + padding * 2
logo_bg_box = Image.new('RGB', (bg_size, bg_size), 'white')
logo_bg_box.paste(logo, (padding, padding))
# Calculate position (center)
logo_pos = ((qr_width - bg_size) // 2, (qr_height - bg_size) // 2)
# Paste logo with white background
img.paste(logo_bg_box, logo_pos)
except Exception as e:
# If logo fails, continue without it
print(f"Warning: Could not add logo to QR code: {e}")
# Convert to base64
buffer = BytesIO()
img.save(buffer, format='PNG')
buffer.seek(0)
img_base64 = base64.b64encode(buffer.read()).decode()
return f'data:image/png;base64,{img_base64}'
def generate_appointment_qr(appointment_id):
"""Generate QR code for appointment check-in with ISUFST logo."""
# Create secure token
token = secrets.token_urlsafe(32)
# QR data includes appointment ID and token (30 days expiration for consistency)
qr_data = json.dumps({
'type': 'appointment_checkin',
'appointment_id': appointment_id,
'token': token,
'expires': (datetime.now() + timedelta(days=30)).isoformat()
})
qr_image = generate_qr_code(qr_data, add_logo=True) # Add branded logo
return qr_image, token
def generate_reservation_qr(reservation_id):
"""Generate QR code for medicine reservation check-in."""
token = secrets.token_urlsafe(32)
qr_data = json.dumps({
'type': 'medicine_reservation',
'reservation_id': reservation_id,
'token': token,
'expires': (datetime.now() + timedelta(days=30)).isoformat()
})
qr_image = generate_qr_code(qr_data, add_logo=True)
return qr_image, token
def verify_qr_checkin(qr_data_json):
"""Verify QR code for check-in."""
try:
data = json.loads(qr_data_json)
# Check expiration
expires = datetime.fromisoformat(data['expires'])
if datetime.now() > expires:
return False, 'QR code expired'
# Verify appointment exists
from models import Appointment
appt = Appointment.query.get(data['appointment_id'])
if not appt:
return False, 'Invalid appointment'
# Verify token matches
from models_extended import AppointmentExtended
ext = AppointmentExtended.query.filter_by(appointment_id=data['appointment_id']).first()
if not ext or ext.qr_code != data['token']:
return False, 'Invalid QR code'
return True, appt
except Exception as e:
return False, str(e)
# ──────────────────────────────────────────────
# Symptom Pre-Screening Logic
# ──────────────────────────────────────────────
SYMPTOM_RULES = {
# Emergency symptoms
'emergency': [
'chest pain', 'difficulty breathing', 'severe bleeding',
'unconscious', 'seizure', 'severe head injury', 'poisoning'
],
# Dental symptoms
'dental': [
'toothache', 'tooth pain', 'gum bleeding', 'wisdom tooth',
'dental cavity', 'broken tooth', 'jaw pain'
],
# Mental health indicators
'mental_health': [
'anxiety', 'depression', 'stress', 'panic attack',
'insomnia', 'suicidal thoughts', 'emotional distress'
],
# Physical therapy indicators
'physical_therapy': [
'sprain', 'muscle pain', 'joint pain', 'back pain',
'sports injury', 'rehabilitation'
],
# Laboratory test needed
'laboratory': [
'blood test', 'urinalysis', 'pregnancy test', 'drug test',
'check up', 'medical clearance'
]
}
def analyze_symptoms(symptoms_text):
"""
Analyze symptoms and recommend service type.
Returns (service_type, severity_level, suggestions).
"""
symptoms_lower = symptoms_text.lower()
# Check for emergency
for symptom in SYMPTOM_RULES['emergency']:
if symptom in symptoms_lower:
return 'Emergency', 1, 'Our analysis indicates high-priority emergency indicators. Please seek immediate evaluation at the nearest hospital Emergency Room or call emergency services right away.'
# Check specific services
if any(symptom in symptoms_lower for symptom in SYMPTOM_RULES['dental']):
return 'Dental', 3, 'Your reported symptoms are highly consistent with dental-related issues. We recommend scheduling a Medical Care appointment with the University Dentist for an oral examination.'
if any(symptom in symptoms_lower for symptom in SYMPTOM_RULES['mental_health']):
return 'Mental Health', 2, 'The patterns in your report suggest significant emotional or psychological distress. Speaking with our Mental Health professionals can provide immediate support and coping strategies.'
if any(symptom in symptoms_lower for symptom in SYMPTOM_RULES['physical_therapy']):
return 'Physical Therapy', 3, 'The localized physical discomfort you described is often best managed through Physical Therapy. A clinical assessment can help determine the best rehabilitation path.'
if any(symptom in symptoms_lower for symptom in SYMPTOM_RULES['laboratory']):
return 'Laboratory', 3, 'To accurately assess your condition, specific diagnostic tests may be required. Please schedule a Laboratory visit so we can gather more data.'
# Default to medical
return 'Medical', 3, 'Your symptoms appear to be non-emergency but warrant a professional evaluation. We suggest booking a Medical Care appointment to discuss these further.'
def calculate_severity_score(symptoms_list):
"""Calculate severity score based on symptom combinations."""
score = 3 # Default: routine
symptoms_text = ' '.join(symptoms_list).lower()
# Emergency indicators
if any(symptom in symptoms_text for symptom in SYMPTOM_RULES['emergency']):
score = 1
# Urgent indicators
elif 'severe' in symptoms_text or 'intense' in symptoms_text or 'unbearable' in symptoms_text:
score = 2
# High fever
elif 'high fever' in symptoms_text or '39' in symptoms_text or '40' in symptoms_text:
score = 2
return score
# ──────────────────────────────────────────────
# Health Certificate PDF Generation
# ──────────────────────────────────────────────
def generate_health_certificate_pdf(certificate):
"""
Generate PDF for health certificate.
Uses ReportLab for PDF generation.
"""
try:
from reportlab.lib.pagesizes import letter
from reportlab.pdfgen import canvas
from reportlab.lib.units import inch
# Create PDF buffer
buffer = BytesIO()
c = canvas.Canvas(buffer, pagesize=letter)
width, height = letter
# Header
c.setFont("Helvetica-Bold", 24)
c.drawCentredString(width / 2, height - inch, "Iloilo State University of Fisheries")
c.drawCentredString(width / 2, height - 1.4*inch, "Science and Technology")
c.setFont("Helvetica-Bold", 18)
c.drawCentredString(width / 2, height - 2*inch, "HEALTH CERTIFICATE")
# Certificate number
c.setFont("Helvetica", 10)
c.drawRightString(width - inch, height - 2.5*inch, f"Certificate No: {certificate.certificate_number}")
# Body
y_position = height - 3*inch
c.setFont("Helvetica", 12)
c.drawString(inch, y_position, f"This is to certify that:")
y_position -= 0.5*inch
c.setFont("Helvetica-Bold", 14)
c.drawString(1.5*inch, y_position, f"{certificate.student.first_name} {certificate.student.last_name}")
y_position -= 0.4*inch
c.setFont("Helvetica", 12)
if certificate.student.student_profile:
c.drawString(1.5*inch, y_position, f"Student ID: {certificate.student.student_profile.student_id_number}")
y_position -= 0.3*inch
c.drawString(1.5*inch, y_position, f"Course: {certificate.student.student_profile.course or 'N/A'}")
y_position -= 0.5*inch
c.drawString(inch, y_position, f"has been examined on {certificate.issued_at.strftime('%B %d, %Y')} and found to be:")
y_position -= 0.4*inch
c.setFont("Helvetica-Bold", 12)
c.drawString(1.5*inch, y_position, "PHYSICALLY FIT")
y_position -= 0.5*inch
c.setFont("Helvetica", 12)
if certificate.medical_findings:
c.drawString(inch, y_position, "Medical Findings:")
y_position -= 0.3*inch
c.drawString(1.5*inch, y_position, certificate.medical_findings[:100])
y_position -= 0.5*inch
c.drawString(inch, y_position, f"Purpose: {certificate.purpose or 'General'}")
y_position -= 0.5*inch
if certificate.valid_until:
c.drawString(inch, y_position, f"Valid Until: {certificate.valid_until.strftime('%B %d, %Y')}")
y_position -= inch
# Signature
y_position -= 0.5*inch
c.drawString(4*inch, y_position, "_" * 30)
y_position -= 0.3*inch
c.drawString(4*inch, y_position, f"{certificate.issuer.first_name} {certificate.issuer.last_name}")
y_position -= 0.2*inch
c.setFont("Helvetica-Oblique", 10)
c.drawString(4*inch, y_position, "University Physician")
# Footer
c.setFont("Helvetica", 8)
c.drawCentredString(width / 2, inch, "This is a computer-generated certificate.")
c.save()
buffer.seek(0)
return buffer.getvalue()
except ImportError:
# If reportlab not installed, return None
return None
def save_certificate_pdf(certificate, output_path):
"""Save certificate PDF to file."""
pdf_data = generate_health_certificate_pdf(certificate)
if pdf_data:
with open(output_path, 'wb') as f:
f.write(pdf_data)
return output_path
return None