diff --git a/app.py b/app.py index 990974b..201b902 100644 --- a/app.py +++ b/app.py @@ -1,6 +1,6 @@ # This need to be first -import eventlet -eventlet.monkey_patch() +import gevent.monkey +gevent.monkey.patch_all() import logging # noqa: E402 diff --git a/bricktracker/socket.py b/bricktracker/socket.py index 99cd625..1eea532 100644 --- a/bricktracker/socket.py +++ b/bricktracker/socket.py @@ -6,6 +6,8 @@ from flask_socketio import SocketIO from .instructions import BrickInstructions from .instructions_list import BrickInstructionsList +from .peeron_instructions import PeeronPage +from .peeron_pdf import PeeronPDF from .set import BrickSet from .socket_decorator import authenticated_socket, rebrickable_socket from .sql import close as sql_close @@ -18,6 +20,7 @@ MESSAGES: Final[dict[str, str]] = { 'CONNECT': 'connect', 'DISCONNECT': 'disconnect', 'DOWNLOAD_INSTRUCTIONS': 'download_instructions', + 'DOWNLOAD_PEERON_PAGES': 'download_peeron_pages', 'FAIL': 'fail', 'IMPORT_SET': 'import_set', 'LOAD_SET': 'load_set', @@ -70,7 +73,7 @@ class BrickSocket(object): *args, **kwargs, path=app.config['SOCKET_PATH'], - async_mode='eventlet', + async_mode='gevent', ) # Store the socket in the app config @@ -106,6 +109,56 @@ class BrickSocket(object): BrickInstructionsList(force=True) + @self.socket.on(MESSAGES['DOWNLOAD_PEERON_PAGES'], namespace=self.namespace) # noqa: E501 + @authenticated_socket(self) + def download_peeron_pages(data: dict[str, Any], /) -> None: + logger.debug('Socket: DOWNLOAD_PEERON_PAGES={data} (from: {fr})'.format( + data=data, + fr=request.sid, # type: ignore + )) + + try: + # Extract data from the request + set_number = data.get('set', '') + pages_data = data.get('pages', []) + + if not set_number: + raise ValueError("Set number is required") + + if not pages_data: + raise ValueError("No pages selected") + + # Parse set number + if '-' in set_number: + parts = set_number.split('-', 1) + set_num = parts[0] + version_num = parts[1] if len(parts) > 1 else '1' + else: + set_num = set_number + version_num = '1' + + # Convert page data to PeeronPage objects + pages = [] + for page_data in pages_data: + page = PeeronPage( + page_number=page_data.get('page_number', ''), + thumbnail_url=page_data.get('thumbnail_url', ''), + image_url=page_data.get('image_url', ''), + alt_text=page_data.get('alt_text', '') + ) + pages.append(page) + + # Create PDF generator and start download + pdf_generator = PeeronPDF(set_num, version_num, pages, socket=self) + pdf_generator.create_pdf() + + # Refresh instructions list to include new PDF + BrickInstructionsList(force=True) + + except Exception as e: + logger.error(f"Error in download_peeron_pages: {e}") + self.fail(message=f"Error downloading Peeron pages: {e}") + @self.socket.on(MESSAGES['IMPORT_SET'], namespace=self.namespace) @rebrickable_socket(self) def import_set(data: dict[str, Any], /) -> None: diff --git a/wsgi.py b/wsgi.py index 290f731..78381e8 100644 --- a/wsgi.py +++ b/wsgi.py @@ -1,38 +1,18 @@ #!/usr/bin/env python3 """ WSGI entry point for BrickTracker - Production Docker deployment -This ensures proper gevent monkey patching for gunicorn +This ensures proper gevent monkey patching before any imports """ -# CRITICAL: This must be the very first import +# CRITICAL: Monkey patch must be first, before ANY other imports import gevent.monkey gevent.monkey.patch_all() -import logging -import sys -import os +# Now import the regular app factory +from app import create_app -# Add the current directory to Python path -sys.path.insert(0, os.path.dirname(__file__)) +# Create the application - this will be a BrickSocket instance +app_instance = create_app() -# Import Flask and BrickTracker modules directly -from flask import Flask -from bricktracker.app import setup_app -from bricktracker.socket import BrickSocket - -logger = logging.getLogger(__name__) - -# Create the Flask app directly (bypassing app.py to avoid double monkey patching) -app = Flask(__name__) - -# Setup the app -setup_app(app) - -# Create the socket -socket_instance = BrickSocket( - app, - threaded=not app.config['NO_THREADED_SOCKET'], -) - -# Export the Flask app for gunicorn -application = app \ No newline at end of file +# For gunicorn, we need the Flask app, not the BrickSocket wrapper +application = app_instance.app if hasattr(app_instance, 'app') else app_instance \ No newline at end of file