# MOC Auto-Generation Feature ## Overview The LEGO Instructions Manager now includes automatic MOC (My Own Creation) set number generation, making it easier to track and organize your custom LEGO builds without manually managing set numbers. ## Features ### 🤖 Automatic Number Generation - **Sequential Numbering**: Automatically generates MOC numbers in sequence (MOC-10000, MOC-10001, MOC-10002, etc.) - **Conflict-Free**: Ensures no duplicate MOC numbers - **Customizable Prefix**: Default is "MOC" but can be changed - **User-Scoped Option**: Can scope MOC numbers per user or globally ### 🔌 API Endpoints #### 1. Generate Next MOC Number ``` GET /api/moc/generate ``` **Query Parameters:** - `prefix` (optional): MOC prefix, default is 'MOC' - `user_scoped` (optional): Scope to current user, default is 'true' **Response:** ```json { "success": true, "moc_number": "MOC-10001", "prefix": "MOC", "user_scoped": true } ``` **Example Usage:** ```javascript fetch('/api/moc/generate') .then(response => response.json()) .then(data => { console.log('Next MOC number:', data.moc_number); }); ``` #### 2. Validate MOC Number ``` POST /api/moc/validate ``` **Request Body:** ```json { "set_number": "MOC-12345", "user_scoped": true } ``` **Response:** ```json { "success": true, "available": true, "set_number": "MOC-12345" } ``` #### 3. Check MOC Format ``` GET /api/moc/check-format?set_number=MOC-10001&prefix=MOC ``` **Response:** ```json { "is_moc": true, "set_number": "MOC-10001", "prefix": "MOC" } ``` ## Database Schema The `Set` model includes MOC support fields: ```python is_moc = db.Column(db.Boolean, default=False, nullable=False, index=True) moc_designer = db.Column(db.String(100), nullable=True) # Designer/creator name moc_description = db.Column(db.Text, nullable=True) # Detailed description ``` ## Frontend Integration ### HTML Form Example When creating a new set, users can toggle MOC mode: ```html
``` ### JavaScript Integration ```javascript // Handle MOC toggle document.getElementById('is_moc').addEventListener('change', function() { const mocSection = document.getElementById('mocSection'); const officialSection = document.getElementById('officialSection'); const setNumberInput = document.getElementById('set_number'); if (this.checked) { // Show MOC section, hide official section mocSection.style.display = 'block'; officialSection.style.display = 'none'; setNumberInput.removeAttribute('required'); // Fetch next MOC number fetchNextMocNumber(); } else { // Show official section, hide MOC section mocSection.style.display = 'none'; officialSection.style.display = 'block'; setNumberInput.setAttribute('required', 'required'); } }); // Fetch next MOC number from API function fetchNextMocNumber() { fetch('/api/moc/generate') .then(response => response.json()) .then(data => { if (data.success) { document.getElementById('mocNumberPreview').textContent = data.moc_number; // Optionally store in hidden field document.getElementById('moc_number_hidden').value = data.moc_number; } }) .catch(error => { console.error('Error fetching MOC number:', error); document.getElementById('mocNumberPreview').textContent = 'Error generating number'; }); } ``` ## Backend Implementation ### Service Layer The `MOCNumberGenerator` service handles all MOC number logic: ```python from app.services.moc_generator import MOCNumberGenerator # Generate next MOC number moc_number = MOCNumberGenerator.generate_next_number( prefix='MOC', user_id=current_user.id # Optional: scope to user ) # Validate MOC number availability is_available = MOCNumberGenerator.validate_moc_number( 'MOC-12345', user_id=current_user.id ) # Check if number follows MOC format is_moc = MOCNumberGenerator.is_moc_number('MOC-10001') ``` ### Route Layer MOC routes are organized in `app/routes/moc.py`: ```python from flask import Blueprint, jsonify from flask_login import login_required, current_user from app.services.moc_generator import MOCNumberGenerator bp = Blueprint('moc', __name__, url_prefix='/api/moc') @bp.route('/generate', methods=['GET']) @login_required def generate_moc_number(): moc_number = MOCNumberGenerator.generate_next_number( user_id=current_user.id ) return jsonify({ 'success': True, 'moc_number': moc_number }) ``` ## Configuration ### Customizing MOC Prefix Edit `app/services/moc_generator.py`: ```python class MOCNumberGenerator: DEFAULT_PREFIX = 'MOC' # Change to 'CUSTOM' or any prefix DEFAULT_START = 10000 # Starting number for MOCs ``` ### Per-User vs Global Numbering **Per-User Numbering** (default): - Each user has their own MOC number sequence - MOC-10000 can exist for multiple users - API: `user_scoped=true` **Global Numbering**: - All users share the same MOC number sequence - Ensures unique MOC numbers across the entire system - API: `user_scoped=false` ## Use Cases ### Creating a New MOC 1. User clicks "Add New Set" 2. User toggles "This is a MOC" checkbox 3. System automatically generates MOC-10000 (or next available) 4. User fills in MOC designer, description, and other details 5. System saves set with is_moc=True ### Importing Multiple MOCs ```python from app.services.moc_generator import MOCNumberGenerator for moc_data in moc_import_list: moc_number = MOCNumberGenerator.generate_next_number() new_set = Set( set_number=moc_number, set_name=moc_data['name'], is_moc=True, moc_designer=moc_data['designer'], theme='MOC', year_released=moc_data['year'] ) db.session.add(new_set) db.session.commit() ``` ### Filtering MOCs ```python # Get all MOCs mocs = Set.query.filter_by(is_moc=True).all() # Get MOCs by designer designer_mocs = Set.query.filter_by( is_moc=True, moc_designer='John Doe' ).all() # Get MOCs by number range mocs_range = Set.query.filter( Set.is_moc == True, Set.set_number.like('MOC-10%') ).all() ``` ## Troubleshooting ### MOC Number Not Generating **Issue**: API returns error or "Error generating number" **Solutions**: 1. Check that user is logged in (endpoint requires authentication) 2. Verify database connection 3. Check browser console for JavaScript errors 4. Ensure `moc_bp` is registered in `app/__init__.py` ### Duplicate MOC Numbers **Issue**: Same MOC number assigned to multiple sets **Solutions**: 1. Check database constraints on `set_number` field 2. Verify `user_scoped` parameter matches your use case 3. Run database migration to add unique constraint if missing ### MOC Toggle Not Working **Issue**: Toggle switch doesn't show/hide sections **Solutions**: 1. Check JavaScript console for errors 2. Verify element IDs match between HTML and JavaScript 3. Ensure Bootstrap JavaScript is loaded (for styling) ## Future Enhancements Potential improvements for the MOC feature: - [ ] Bulk MOC number generation API - [ ] MOC number recycling (reuse deleted MOC numbers) - [ ] Custom number formats (e.g., MOC-YYYY-NNNN for year-based) - [ ] MOC templates for common builds - [ ] MOC sharing/export functionality - [ ] Integration with MOC platforms like Rebrickable ## Related Documentation - [Main README](../README.md) - General application documentation - [Setup Guide](../SETUP_GUIDE.md) - Installation and configuration - [API Documentation](../docs/api.md) - Complete API reference (if exists) - [Database Schema](../docs/database.md) - Database structure (if exists) ## Support For issues or questions about MOC auto-generation: 1. Check existing issues on Gitea 2. Review this documentation 3. Check application logs for errors 4. Create a new issue with: - Steps to reproduce - Expected vs actual behavior - Browser console logs - Server logs (if applicable) --- **Last Updated**: December 2024 **Feature Version**: 1.0 **Compatibility**: LEGO Instructions Manager v1.3.0+