Add QR Code styling

This commit is contained in:
Nguyen Ngo
2025-08-18 11:56:13 -04:00
parent 8ae60bb48a
commit 4c4967a9e0
4 changed files with 2518 additions and 1498 deletions
+270 -135
View File
@@ -9,6 +9,7 @@ import io, os, base64, re, uuid, requests, json, qrcode, math
from dotenv import load_dotenv from dotenv import load_dotenv
# Import the logging handler # Import the logging handler
from logger_handler import AppLogger, log_user_activity, log_database_operations from logger_handler import AppLogger, log_user_activity, log_database_operations
from PIL import Image, ImageDraw
# Load environment variables in .env # Load environment variables in .env
load_dotenv() load_dotenv()
@@ -109,6 +110,16 @@ class QRCode(db.Model):
project_id = db.Column(db.Integer, db.ForeignKey('projects.id'), nullable=True) project_id = db.Column(db.Integer, db.ForeignKey('projects.id'), nullable=True)
# The project_id field: # The project_id field:
project_id = db.Column(db.Integer, db.ForeignKey('projects.id'), nullable=True) project_id = db.Column(db.Integer, db.ForeignKey('projects.id'), nullable=True)
# QR Code Customization fields
fill_color = db.Column(db.String(7), default="#000000") # Hex color
back_color = db.Column(db.String(7), default="#FFFFFF") # Background color
box_size = db.Column(db.Integer, default=10)
border = db.Column(db.Integer, default=4)
error_correction = db.Column(db.String(1), default='L')
style_id = db.Column(db.Integer, db.ForeignKey('qr_code_styles.id'), nullable=True)
# Relationship to style
style = db.relationship('QRCodeStyle', backref='qr_codes')
@property @property
def has_coordinates(self): def has_coordinates(self):
@@ -129,6 +140,24 @@ class QRCode(db.Model):
self.coordinate_accuracy = accuracy self.coordinate_accuracy = accuracy
self.coordinates_updated_date = datetime.utcnow() self.coordinates_updated_date = datetime.utcnow()
class QRCodeStyle(db.Model):
"""QR Code customization styles"""
__tablename__ = 'qr_code_styles'
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(100), nullable=False) # Style name
fill_color = db.Column(db.String(7), default="#000000") # Hex color for QR modules
back_color = db.Column(db.String(7), default="#FFFFFF") # Hex color for background
box_size = db.Column(db.Integer, default=10) # Size of each QR module
border = db.Column(db.Integer, default=4) # Border size
error_correction = db.Column(db.String(1), default='L') # L, M, Q, H
is_default = db.Column(db.Boolean, default=False)
created_at = db.Column(db.DateTime, default=datetime.utcnow)
created_by = db.Column(db.Integer, db.ForeignKey('users.id'))
def __repr__(self):
return f'<QRCodeStyle {self.name}>'
class Project(db.Model): class Project(db.Model):
""" """
Project model to organize QR codes by projects Project model to organize QR codes by projects
@@ -1131,8 +1160,55 @@ def is_admin_user(user_id):
return False return False
# Utility function to generate QR code # Utility function to generate QR code
def generate_qr_code(data): def generate_qr_code(data, fill_color="black", back_color="white", box_size=10, border=4, error_correction='L'):
"""Generate QR code image and return as base64 string""" # Error correction mapping
error_correction_map = {
'L': qrcode.constants.ERROR_CORRECT_L,
'M': qrcode.constants.ERROR_CORRECT_M,
'Q': qrcode.constants.ERROR_CORRECT_Q,
'H': qrcode.constants.ERROR_CORRECT_H
}
try:
qr = qrcode.QRCode(
version=1,
error_correction=error_correction_map.get(error_correction, qrcode.constants.ERROR_CORRECT_L),
box_size=int(box_size),
border=int(border),
)
qr.add_data(data)
qr.make(fit=True)
# Generate QR code image with custom colors
img = qr.make_image(fill_color=fill_color, back_color=back_color)
# Convert to base64
buffer = io.BytesIO()
img.save(buffer, format='PNG')
img_str = base64.b64encode(buffer.getvalue()).decode()
# Log successful generation if logger is available
try:
logger_handler.log_qr_code_generated(
data_length=len(data),
fill_color=fill_color,
back_color=back_color,
box_size=box_size,
border=border,
error_correction=error_correction
)
except:
pass # Ignore logging errors
return img_str
except Exception as e:
logger_handler.log_database_error('qr_code_generation', e)
# Return default QR code on error
return generate_default_qr_code(data)
def generate_default_qr_code(data):
"""Fallback function for basic QR code generation"""
qr = qrcode.QRCode( qr = qrcode.QRCode(
version=1, version=1,
error_correction=qrcode.constants.ERROR_CORRECT_L, error_correction=qrcode.constants.ERROR_CORRECT_L,
@@ -1144,13 +1220,22 @@ def generate_qr_code(data):
img = qr.make_image(fill_color="black", back_color="white") img = qr.make_image(fill_color="black", back_color="white")
# Convert to base64
buffer = io.BytesIO() buffer = io.BytesIO()
img.save(buffer, format='PNG') img.save(buffer, format='PNG')
img_str = base64.b64encode(buffer.getvalue()).decode() img_str = base64.b64encode(buffer.getvalue()).decode()
return img_str return img_str
def get_qr_styling(qr_code):
"""Extract QR code styling parameters from database record"""
return {
'fill_color': getattr(qr_code, 'fill_color', '#000000') or '#000000',
'back_color': getattr(qr_code, 'back_color', '#FFFFFF') or '#FFFFFF',
'box_size': getattr(qr_code, 'box_size', 10) or 10,
'border': getattr(qr_code, 'border', 4) or 4,
'error_correction': getattr(qr_code, 'error_correction', 'L') or 'L'
}
@app.template_filter('strftime') @app.template_filter('strftime')
def strftime_filter(value, format='%m/%d/%Y'): def strftime_filter(value, format='%m/%d/%Y'):
"""Format datetime/date/string as strftime""" """Format datetime/date/string as strftime"""
@@ -2648,9 +2733,10 @@ def api_active_projects():
@login_required @login_required
@log_database_operations('qr_code_creation') @log_database_operations('qr_code_creation')
def create_qr_code(): def create_qr_code():
"""Enhanced create QR code with readable URL format and coordinates saving""" """Enhanced create QR code with customization options"""
if request.method == 'POST': if request.method == 'POST':
try: try:
# Existing form data
name = request.form['name'] name = request.form['name']
location = request.form['location'] location = request.form['location']
location_address = request.form['location_address'] location_address = request.form['location_address']
@@ -2662,6 +2748,20 @@ def create_qr_code():
longitude = request.form.get('longitude') longitude = request.form.get('longitude')
coordinate_accuracy = request.form.get('coordinate_accuracy', 'geocoded') coordinate_accuracy = request.form.get('coordinate_accuracy', 'geocoded')
# NEW: QR Code customization data
fill_color = request.form.get('fill_color', '#000000')
back_color = request.form.get('back_color', '#FFFFFF')
box_size = int(request.form.get('box_size', 10))
border = int(request.form.get('border', 4))
error_correction = request.form.get('error_correction', 'L')
style_id = request.form.get('style_id') # Pre-defined style
# Validate colors (basic hex validation)
if not (fill_color.startswith('#') and len(fill_color) == 7):
fill_color = '#000000'
if not (back_color.startswith('#') and len(back_color) == 7):
back_color = '#FFFFFF'
# Convert coordinates to float if they exist # Convert coordinates to float if they exist
address_latitude = None address_latitude = None
address_longitude = None address_longitude = None
@@ -2682,11 +2782,19 @@ def create_qr_code():
# Validate project_id if provided # Validate project_id if provided
project = None project = None
if project_id: if project_id:
project_id = int(project_id) try:
project = Project.query.get(project_id) project_id = int(project_id)
if not project or not project.active_status: project = Project.query.get(project_id)
flash('Selected project is not valid or inactive.', 'error') if not project or not project.active_status:
return render_template('create_qr_code.html') flash('Selected project is not valid or inactive.', 'error')
return render_template('create_qr_code.html',
projects=Project.query.filter_by(active_status=True).all(),
styles=QRCodeStyle.query.all() if 'QRCodeStyle' in globals() else [])
except (ValueError, TypeError):
flash('Invalid project selection.', 'error')
return render_template('create_qr_code.html',
projects=Project.query.filter_by(active_status=True).all(),
styles=QRCodeStyle.query.all() if 'QRCodeStyle' in globals() else [])
# Create new QR code record first (without URL and image) # Create new QR code record first (without URL and image)
new_qr_code = QRCode( new_qr_code = QRCode(
@@ -2701,7 +2809,16 @@ def create_qr_code():
address_latitude=address_latitude, address_latitude=address_latitude,
address_longitude=address_longitude, address_longitude=address_longitude,
coordinate_accuracy=coordinate_accuracy if has_coordinates else None, coordinate_accuracy=coordinate_accuracy if has_coordinates else None,
coordinates_updated_date=datetime.utcnow() if has_coordinates else None coordinates_updated_date=datetime.utcnow() if has_coordinates else None,
# NEW: Customization fields (only if columns exist)
**({
'fill_color': fill_color,
'back_color': back_color,
'box_size': box_size,
'border': border,
'error_correction': error_correction,
'style_id': int(style_id) if style_id and style_id.isdigit() else None
} if hasattr(QRCode, 'fill_color') else {})
) )
# Add to session and flush to get the ID # Add to session and flush to get the ID
@@ -2711,9 +2828,16 @@ def create_qr_code():
# Now generate the readable URL using the ID # Now generate the readable URL using the ID
qr_url = generate_qr_url(name, new_qr_code.id) qr_url = generate_qr_url(name, new_qr_code.id)
# Generate QR code data with the destination URL # Generate QR code data with the destination URL and custom styling
qr_data = f"{request.url_root}qr/{qr_url}" qr_data = f"{request.url_root}qr/{qr_url}"
qr_image = generate_qr_code(qr_data) qr_image = generate_qr_code(
data=qr_data,
fill_color=fill_color,
back_color=back_color,
box_size=box_size,
border=border,
error_correction=error_correction
)
# Update the QR code with the URL and image # Update the QR code with the URL and image
new_qr_code.qr_url = qr_url new_qr_code.qr_url = qr_url
@@ -2722,41 +2846,32 @@ def create_qr_code():
# Now commit all changes # Now commit all changes
db.session.commit() db.session.commit()
# Enhanced logging with project and coordinates information # Enhanced logging with customization information
log_data = {
'qr_code_id': new_qr_code.id,
'name': name,
'location': location,
'qr_url': qr_url, # Log the readable URL
'project_id': project_id,
'project_name': project.name if project else None,
'has_coordinates': has_coordinates,
'latitude': address_latitude,
'longitude': address_longitude,
'coordinate_accuracy': coordinate_accuracy
}
qr_data_for_logging = {
'location': location,
'location_address': location_address,
'location_event': location_event,
'has_coordinates': has_coordinates,
'latitude': address_latitude,
'longitude': address_longitude,
'coordinate_accuracy': coordinate_accuracy
}
logger_handler.log_qr_code_created( logger_handler.log_qr_code_created(
qr_code_id=new_qr_code.id, qr_code_id=new_qr_code.id,
qr_code_name=name, qr_code_name=name,
created_by_user_id=session['user_id'], created_by_user_id=session['user_id'],
qr_data=qr_data_for_logging qr_data={
'location': location,
'location_address': location_address,
'location_event': location_event,
'has_coordinates': has_coordinates,
'customization': {
'fill_color': fill_color,
'back_color': back_color,
'box_size': box_size,
'border': border,
'error_correction': error_correction
}
}
) )
# Success message with coordinates info # Success message with customization info
project_info = f" in project '{project.name}'" if project else "" project_info = f" in project '{project.name}'" if project else ""
coord_info = f" with coordinates ({new_qr_code.coordinates_display})" if has_coordinates else "" coord_info = f" with coordinates ({new_qr_code.coordinates_display})" if has_coordinates else ""
style_info = f" with custom styling (Fill: {fill_color}, Background: {back_color})"
flash(f'QR Code "{name}" created successfully{project_info}{coord_info}! URL: {qr_url}', 'success') flash(f'QR Code "{name}" created successfully{project_info}{coord_info}{style_info}! URL: {qr_url}', 'success')
return redirect(url_for('dashboard')) return redirect(url_for('dashboard'))
except Exception as e: except Exception as e:
@@ -2765,15 +2880,17 @@ def create_qr_code():
flash('QR Code creation failed. Please try again.', 'error') flash('QR Code creation failed. Please try again.', 'error')
print(f"❌ QR Code creation error: {e}") print(f"❌ QR Code creation error: {e}")
# Get active projects for dropdown # Get active projects and styles for dropdown
projects = Project.query.filter_by(active_status=True).order_by(Project.name.asc()).all() projects = Project.query.filter_by(active_status=True).order_by(Project.name.asc()).all()
return render_template('create_qr_code.html', projects=projects) styles = QRCodeStyle.query.order_by(QRCodeStyle.name.asc()).all() if 'QRCodeStyle' in globals() else []
return render_template('create_qr_code.html', projects=projects, styles=styles)
@app.route('/qr-codes/<int:qr_id>/edit', methods=['GET', 'POST']) @app.route('/qr-codes/<int:qr_id>/edit', methods=['GET', 'POST'])
@login_required @login_required
@log_database_operations('qr_code_edit') @log_database_operations('qr_code_edit')
def edit_qr_code(qr_id): def edit_qr_code(qr_id):
"""Enhanced edit QR code with project association and URL regeneration - COORDINATE FIX""" """Enhanced edit QR code with customization support"""
try: try:
qr_code = QRCode.query.get_or_404(qr_id) qr_code = QRCode.query.get_or_404(qr_id)
@@ -2788,7 +2905,10 @@ def edit_qr_code(qr_id):
'qr_url': qr_code.qr_url, 'qr_url': qr_code.qr_url,
'address_latitude': qr_code.address_latitude, 'address_latitude': qr_code.address_latitude,
'address_longitude': qr_code.address_longitude, 'address_longitude': qr_code.address_longitude,
'coordinate_accuracy': qr_code.coordinate_accuracy 'coordinate_accuracy': qr_code.coordinate_accuracy,
# Track old styling
'fill_color': getattr(qr_code, 'fill_color', '#000000'),
'back_color': getattr(qr_code, 'back_color', '#FFFFFF')
} }
# Update QR code fields # Update QR code fields
@@ -2798,12 +2918,11 @@ def edit_qr_code(qr_id):
qr_code.location_address = request.form['location_address'] qr_code.location_address = request.form['location_address']
qr_code.location_event = request.form.get('location_event', '') qr_code.location_event = request.form.get('location_event', '')
# SIMPLIFIED COORDINATE HANDLING - Remove validation requirement # Handle coordinates
latitude = request.form.get('address_latitude') # Changed from 'latitude' to match form latitude = request.form.get('address_latitude')
longitude = request.form.get('address_longitude') # Changed from 'longitude' to match form longitude = request.form.get('address_longitude')
coordinate_accuracy = request.form.get('coordinate_accuracy', 'geocoded') coordinate_accuracy = request.form.get('coordinate_accuracy', 'geocoded')
# Update coordinates if provided, or keep existing ones
if latitude and longitude: if latitude and longitude:
try: try:
qr_code.address_latitude = float(latitude) qr_code.address_latitude = float(latitude)
@@ -2811,19 +2930,16 @@ def edit_qr_code(qr_id):
qr_code.coordinate_accuracy = coordinate_accuracy qr_code.coordinate_accuracy = coordinate_accuracy
qr_code.coordinates_updated_date = datetime.utcnow() qr_code.coordinates_updated_date = datetime.utcnow()
except (ValueError, TypeError): except (ValueError, TypeError):
# If invalid coordinates, keep existing ones
pass pass
elif latitude == '' and longitude == '': elif latitude == '' and longitude == '':
# Explicitly clear coordinates if empty strings are sent
qr_code.address_latitude = None qr_code.address_latitude = None
qr_code.address_longitude = None qr_code.address_longitude = None
qr_code.coordinate_accuracy = None qr_code.coordinate_accuracy = None
qr_code.coordinates_updated_date = None qr_code.coordinates_updated_date = None
# If no coordinate data is provided, keep existing coordinates unchanged
# Handle project association - FIXED LOGIC # Handle project association
new_project_id = request.form.get('project_id') new_project_id = request.form.get('project_id')
if new_project_id and new_project_id.strip(): # Check for non-empty string if new_project_id and new_project_id.strip():
try: try:
new_project_id = int(new_project_id) new_project_id = int(new_project_id)
project = Project.query.get(new_project_id) project = Project.query.get(new_project_id)
@@ -2831,104 +2947,70 @@ def edit_qr_code(qr_id):
qr_code.project_id = new_project_id qr_code.project_id = new_project_id
else: else:
flash('Selected project is not valid or inactive.', 'error') flash('Selected project is not valid or inactive.', 'error')
return render_template('edit_qr_code.html', qr_code=qr_code, projects=Project.query.filter_by(active_status=True).all()) return render_template('edit_qr_code.html', qr_code=qr_code,
projects=Project.query.filter_by(active_status=True).all(),
styles=QRCodeStyle.query.all() if 'QRCodeStyle' in globals() else [])
except (ValueError, TypeError): except (ValueError, TypeError):
# Invalid project_id format
flash('Invalid project selection.', 'error') flash('Invalid project selection.', 'error')
return render_template('edit_qr_code.html', qr_code=qr_code, projects=Project.query.filter_by(active_status=True).all()) return render_template('edit_qr_code.html', qr_code=qr_code,
projects=Project.query.filter_by(active_status=True).all(),
styles=QRCodeStyle.query.all() if 'QRCodeStyle' in globals() else [])
else: else:
# Empty or None project_id means unassign from project
qr_code.project_id = None qr_code.project_id = None
# Regenerate URL if name changed # Handle QR code customization (only if columns exist)
fill_color = request.form.get('fill_color', '#000000')
back_color = request.form.get('back_color', '#FFFFFF')
box_size = int(request.form.get('box_size', 10))
border = int(request.form.get('border', 4))
error_correction = request.form.get('error_correction', 'L')
style_id = request.form.get('style_id')
# Update styling fields if they exist
if hasattr(qr_code, 'fill_color'):
qr_code.fill_color = fill_color
qr_code.back_color = back_color
qr_code.box_size = box_size
qr_code.border = border
qr_code.error_correction = error_correction
qr_code.style_id = int(style_id) if style_id and style_id.isdigit() else None
# Check if QR code needs regeneration
name_changed = old_data['name'] != new_name name_changed = old_data['name'] != new_name
styling_changed = (hasattr(qr_code, 'fill_color') and
(old_data['fill_color'] != fill_color or
old_data['back_color'] != back_color))
if name_changed: if name_changed:
new_qr_url = generate_qr_url(new_name, qr_code.id) new_qr_url = generate_qr_url(new_name, qr_code.id)
# Regenerate QR code image with new URL
qr_data = f"{request.url_root}qr/{new_qr_url}"
new_qr_image = generate_qr_code(qr_data)
qr_code.qr_url = new_qr_url qr_code.qr_url = new_qr_url
qr_code.qr_code_image = new_qr_image
# ENHANCED CHANGE DETECTION # Regenerate QR code if name or styling changed
project_changed = old_data['project_id'] != qr_code.project_id if name_changed or styling_changed:
coordinates_changed = (old_data['address_latitude'] != qr_code.address_latitude or qr_data = f"{request.url_root}qr/{qr_code.qr_url}"
old_data['address_longitude'] != qr_code.address_longitude)
# Use new styling if available, otherwise use defaults
styling = get_qr_styling(qr_code)
qr_code.qr_code_image = generate_qr_code(
data=qr_data,
fill_color=styling['fill_color'],
back_color=styling['back_color'],
box_size=styling['box_size'],
border=styling['border'],
error_correction=styling['error_correction']
)
db.session.commit() db.session.commit()
# Log changes with improved tracking # Success message
new_data = { flash(f'QR Code "{qr_code.name}" updated successfully!', 'success')
'name': qr_code.name,
'location': qr_code.location,
'location_address': qr_code.location_address,
'location_event': qr_code.location_event,
'project_id': qr_code.project_id,
'qr_url': qr_code.qr_url,
'address_latitude': qr_code.address_latitude,
'address_longitude': qr_code.address_longitude,
'coordinate_accuracy': qr_code.coordinate_accuracy
}
changes = {}
for key in old_data:
if old_data[key] != new_data[key]:
changes[key] = {'old': old_data[key], 'new': new_data[key]}
# Enhanced logging for all changes
if changes:
logger_handler.logger.info(f"User {session['username']} updated QR code {qr_id}: {json.dumps(changes)}")
# Log specific change actions
if project_changed:
old_project_name = None
new_project_name = None
if old_data['project_id']:
old_project = Project.query.get(old_data['project_id'])
old_project_name = old_project.name if old_project else f"Project ID {old_data['project_id']}"
if qr_code.project_id:
new_project = Project.query.get(qr_code.project_id)
new_project_name = new_project.name if new_project else f"Project ID {qr_code.project_id}"
project_change_msg = f"QR code '{qr_code.name}' project changed from '{old_project_name or 'No Project'}' to '{new_project_name or 'No Project'}'"
logger_handler.logger.info(f"User {session['username']} - {project_change_msg}")
if coordinates_changed:
coord_change_msg = f"QR code '{qr_code.name}' coordinates updated"
if qr_code.has_coordinates:
coord_change_msg += f" to {qr_code.coordinates_display}"
else:
coord_change_msg += " (coordinates cleared)"
logger_handler.logger.info(f"User {session['username']} - {coord_change_msg}")
# Success message with detailed information
url_message = f" URL updated to: {qr_code.qr_url}" if name_changed else ""
project_message = ""
coord_message = ""
if project_changed:
if qr_code.project_id:
current_project = Project.query.get(qr_code.project_id)
project_message = f" Project updated to: {current_project.name if current_project else 'Unknown'}"
else:
project_message = " Removed from project"
if coordinates_changed:
if qr_code.has_coordinates:
coord_message = f" Coordinates updated: {qr_code.coordinates_display}"
else:
coord_message = " Coordinates cleared"
flash(f'QR Code "{qr_code.name}" updated successfully!{url_message}{project_message}{coord_message}', 'success')
return redirect(url_for('dashboard')) return redirect(url_for('dashboard'))
# Get active projects for dropdown # GET request - render edit form
projects = Project.query.filter_by(active_status=True).order_by(Project.name.asc()).all() projects = Project.query.filter_by(active_status=True).order_by(Project.name.asc()).all()
return render_template('edit_qr_code.html', qr_code=qr_code, projects=projects) styles = QRCodeStyle.query.order_by(QRCodeStyle.name.asc()).all() if 'QRCodeStyle' in globals() else []
return render_template('edit_qr_code.html', qr_code=qr_code, projects=projects, styles=styles)
except Exception as e: except Exception as e:
db.session.rollback() db.session.rollback()
@@ -4508,7 +4590,17 @@ def update_existing_qr_codes():
# Generate QR image if missing # Generate QR image if missing
if not qr_code.qr_code_image: if not qr_code.qr_code_image:
qr_data = f"{request.url_root}qr/{qr_code.qr_url}" qr_data = f"{request.url_root}qr/{qr_code.qr_url}"
qr_code.qr_code_image = generate_qr_code(qr_data)
# Use styling from database if available, otherwise defaults
styling = get_qr_styling(qr_code)
qr_code.qr_code_image = generate_qr_code(
data=qr_data,
fill_color=styling['fill_color'],
back_color=styling['back_color'],
box_size=styling['box_size'],
border=styling['border'],
error_correction=styling['error_correction']
)
updated_count += 1 updated_count += 1
@@ -4524,8 +4616,50 @@ def update_existing_qr_codes():
logger_handler.logger.info(f"Updated {updated_count} existing QR codes with missing URLs/images") logger_handler.logger.info(f"Updated {updated_count} existing QR codes with missing URLs/images")
except Exception as e: except Exception as e:
db.session.rollback()
logger_handler.log_database_error('update_existing_qr_codes', e) logger_handler.log_database_error('update_existing_qr_codes', e)
print(f"Error updating existing QR codes: {e}")
def add_qr_customization_columns():
"""Add QR code customization columns to existing table"""
try:
with app.app_context():
# Add columns to QRCode table if they don't exist
db.engine.execute("""
ALTER TABLE qr_codes
ADD COLUMN IF NOT EXISTS fill_color VARCHAR(7) DEFAULT '#000000',
ADD COLUMN IF NOT EXISTS back_color VARCHAR(7) DEFAULT '#FFFFFF',
ADD COLUMN IF NOT EXISTS box_size INT DEFAULT 10,
ADD COLUMN IF NOT EXISTS border INT DEFAULT 4,
ADD COLUMN IF NOT EXISTS error_correction VARCHAR(1) DEFAULT 'L',
ADD COLUMN IF NOT EXISTS style_id INT,
ADD FOREIGN KEY (style_id) REFERENCES qr_code_styles(id)
""")
# Create QRCodeStyle table
db.create_all()
# Insert default styles
default_styles = [
QRCodeStyle(name="Classic Black", fill_color="#000000", back_color="#FFFFFF", is_default=True),
QRCodeStyle(name="Blue Professional", fill_color="#2563eb", back_color="#FFFFFF"),
QRCodeStyle(name="Green Success", fill_color="#10b981", back_color="#FFFFFF"),
QRCodeStyle(name="Red Alert", fill_color="#ef4444", back_color="#FFFFFF"),
QRCodeStyle(name="Dark Mode", fill_color="#FFFFFF", back_color="#1f2937"),
QRCodeStyle(name="High Contrast", fill_color="#000000", back_color="#FFFF00"),
QRCodeStyle(name="Corporate Blue", fill_color="#1e40af", back_color="#f8fafc"),
QRCodeStyle(name="Minimalist Gray", fill_color="#6b7280", back_color="#FFFFFF")
]
for style in default_styles:
if not QRCodeStyle.query.filter_by(name=style.name).first():
db.session.add(style)
db.session.commit()
print("QR Code customization tables and default styles created successfully!")
except Exception as e:
db.session.rollback()
print(f"Error adding QR customization columns: {e}")
def add_coordinate_columns(): def add_coordinate_columns():
"""Add coordinate columns to existing qr_codes table (MySQL compatible)""" """Add coordinate columns to existing qr_codes table (MySQL compatible)"""
@@ -4639,6 +4773,7 @@ if __name__ == '__main__':
# Initialize database and logging # Initialize database and logging
create_tables() create_tables()
update_existing_qr_codes() update_existing_qr_codes()
add_qr_customization_columns()
# Log application startup # Log application startup
logger_handler.logger.info("QR Attendance Management System started successfully") logger_handler.logger.info("QR Attendance Management System started successfully")
+11
View File
@@ -473,6 +473,17 @@ class AppLogger:
'data': event_data 'data': event_data
})) }))
def log_qr_code_generated(self, data_length, fill_color, back_color, box_size, border, error_correction):
"""Log QR code generation with customization details"""
try:
self.logger.info(f"QR code generated with customization - "
f"Data length: {data_length}, "
f"Fill: {fill_color}, Background: {back_color}, "
f"Box size: {box_size}, Border: {border}, "
f"Error correction: {error_correction}")
except Exception as e:
self.logger.error(f"Failed to log QR code generation: {e}")
# DATABASE ERROR LOGGING METHODS # DATABASE ERROR LOGGING METHODS
def log_database_error(self, operation, error, query=None, parameters=None): def log_database_error(self, operation, error, query=None, parameters=None):
File diff suppressed because it is too large Load Diff
+1416 -1052
View File
File diff suppressed because it is too large Load Diff