- Added environment variable loading with python-dotenv - Fixed Docker session permissions by using /tmp directory - Updated .dockerignore to include .env file properly - Enhanced docker-compose.yml with env_file directive - Fixed Gunicorn configuration for Docker compatibility - Updated README.md with comprehensive deployment docs - Cleaned up debug logging from API routes - Added DOMAIN_SETUP.md for reverse proxy guidance - All production issues resolved and tested working - Application now accessible at qr.moto-adv.com
146 lines
4.9 KiB
Python
Executable File
146 lines
4.9 KiB
Python
Executable File
"""
|
|
QR Code generation utilities
|
|
"""
|
|
|
|
import os
|
|
import qrcode
|
|
from qrcode.image.styledpil import StyledPilImage
|
|
from qrcode.image.styles.moduledrawers import RoundedModuleDrawer, CircleModuleDrawer, SquareModuleDrawer
|
|
from qrcode.image.svg import SvgPathImage, SvgFragmentImage, SvgFillImage
|
|
from PIL import Image
|
|
import io
|
|
|
|
class QRCodeGenerator:
|
|
def __init__(self):
|
|
self.default_settings = {
|
|
'size': 10,
|
|
'border': 4,
|
|
'error_correction': qrcode.constants.ERROR_CORRECT_M,
|
|
'foreground_color': '#000000',
|
|
'background_color': '#FFFFFF',
|
|
'style': 'square'
|
|
}
|
|
|
|
def generate_qr_code(self, data, settings=None, format='PNG'):
|
|
"""Generate QR code with custom settings in PNG or SVG format"""
|
|
if settings is None:
|
|
settings = self.default_settings.copy()
|
|
else:
|
|
merged_settings = self.default_settings.copy()
|
|
merged_settings.update(settings)
|
|
settings = merged_settings
|
|
|
|
if format.upper() == 'SVG':
|
|
return self._generate_svg_qr_code(data, settings)
|
|
else:
|
|
return self._generate_png_qr_code(data, settings)
|
|
|
|
def _generate_png_qr_code(self, data, settings):
|
|
"""Generate PNG QR code (existing functionality)"""
|
|
# Create QR code instance
|
|
qr = qrcode.QRCode(
|
|
version=1,
|
|
error_correction=settings['error_correction'],
|
|
box_size=settings['size'],
|
|
border=settings['border'],
|
|
)
|
|
|
|
qr.add_data(data)
|
|
qr.make(fit=True)
|
|
|
|
# For styled QR codes with custom module drawer
|
|
if settings['style'] != 'square':
|
|
# Choose module drawer based on style
|
|
module_drawer = None
|
|
if settings['style'] == 'rounded':
|
|
module_drawer = RoundedModuleDrawer()
|
|
elif settings['style'] == 'circle':
|
|
module_drawer = CircleModuleDrawer()
|
|
|
|
# Generate the styled image
|
|
img = qr.make_image(
|
|
image_factory=StyledPilImage,
|
|
module_drawer=module_drawer,
|
|
fill_color=settings['foreground_color'],
|
|
back_color=settings['background_color']
|
|
)
|
|
else:
|
|
# Generate standard image with custom colors
|
|
img = qr.make_image(
|
|
fill_color=settings['foreground_color'],
|
|
back_color=settings['background_color']
|
|
)
|
|
|
|
return img
|
|
|
|
def _generate_svg_qr_code(self, data, settings):
|
|
"""Generate SVG QR code"""
|
|
# Create QR code instance
|
|
qr = qrcode.QRCode(
|
|
version=1,
|
|
error_correction=settings['error_correction'],
|
|
box_size=settings['size'],
|
|
border=settings['border'],
|
|
)
|
|
|
|
qr.add_data(data)
|
|
qr.make(fit=True)
|
|
|
|
# Choose SVG image factory based on style
|
|
if settings['style'] == 'circle':
|
|
# Use SvgFillImage for better circle support
|
|
factory = SvgFillImage
|
|
else:
|
|
# Use SvgPathImage for square and rounded styles
|
|
factory = SvgPathImage
|
|
|
|
# Generate SVG image
|
|
img = qr.make_image(
|
|
image_factory=factory,
|
|
fill_color=settings['foreground_color'],
|
|
back_color=settings['background_color']
|
|
)
|
|
|
|
return img
|
|
|
|
def generate_qr_code_svg_string(self, data, settings=None):
|
|
"""Generate QR code as SVG string"""
|
|
svg_img = self.generate_qr_code(data, settings, format='SVG')
|
|
|
|
# Convert SVG image to string
|
|
svg_buffer = io.BytesIO()
|
|
svg_img.save(svg_buffer)
|
|
svg_buffer.seek(0)
|
|
|
|
return svg_buffer.getvalue().decode('utf-8')
|
|
|
|
def add_logo(self, qr_img, logo_path, logo_size_ratio=0.2):
|
|
"""Add logo to QR code"""
|
|
try:
|
|
logo = Image.open(logo_path)
|
|
|
|
# Calculate logo size
|
|
qr_width, qr_height = qr_img.size
|
|
logo_size = int(min(qr_width, qr_height) * logo_size_ratio)
|
|
|
|
# Resize logo
|
|
logo = logo.resize((logo_size, logo_size), Image.Resampling.LANCZOS)
|
|
|
|
# Create a white background for the logo
|
|
logo_bg = Image.new('RGB', (logo_size + 20, logo_size + 20), 'white')
|
|
logo_bg.paste(logo, (10, 10))
|
|
|
|
# Calculate position to center the logo
|
|
logo_pos = (
|
|
(qr_width - logo_bg.width) // 2,
|
|
(qr_height - logo_bg.height) // 2
|
|
)
|
|
|
|
# Paste logo onto QR code
|
|
qr_img.paste(logo_bg, logo_pos)
|
|
|
|
return qr_img
|
|
except Exception as e:
|
|
print(f"Error adding logo: {e}")
|
|
return qr_img
|