373 lines
9.8 KiB
Markdown
373 lines
9.8 KiB
Markdown
# 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.
|
|
|
|
**Default Behavior: Global Numbering** - All users share the same MOC number sequence, ensuring system-wide unique MOC numbers.
|
|
|
|
## Features
|
|
|
|
### 🤖 Automatic Number Generation
|
|
- **Sequential Numbering**: Automatically generates MOC numbers in sequence (MOC-10000, MOC-10001, MOC-10002, etc.)
|
|
- **Global by Default**: All users share the same sequence for system-wide uniqueness
|
|
- **Conflict-Free**: Ensures no duplicate MOC numbers across the entire system
|
|
- **Customizable Prefix**: Default is "MOC" but can be changed
|
|
- **Optional User-Scoping**: Can scope to individual users if needed
|
|
|
|
### 🔌 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 **'false'** (GLOBAL)
|
|
|
|
**Response:**
|
|
```json
|
|
{
|
|
"success": true,
|
|
"moc_number": "MOC-10001",
|
|
"prefix": "MOC",
|
|
"user_scoped": false
|
|
}
|
|
```
|
|
|
|
**Example Usage:**
|
|
```javascript
|
|
// Get next global MOC number (default)
|
|
fetch('/api/moc/generate')
|
|
.then(response => response.json())
|
|
.then(data => {
|
|
console.log('Next MOC number:', data.moc_number);
|
|
});
|
|
|
|
// Get next user-scoped MOC number (optional)
|
|
fetch('/api/moc/generate?user_scoped=true')
|
|
.then(response => response.json())
|
|
.then(data => {
|
|
console.log('Next user MOC number:', data.moc_number);
|
|
});
|
|
```
|
|
|
|
#### 2. Validate MOC Number
|
|
```
|
|
POST /api/moc/validate
|
|
```
|
|
|
|
**Request Body:**
|
|
```json
|
|
{
|
|
"set_number": "MOC-12345",
|
|
"user_scoped": false
|
|
}
|
|
```
|
|
|
|
**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
|
|
<div class="form-check form-switch mb-3">
|
|
<input class="form-check-input" type="checkbox" id="is_moc" name="is_moc">
|
|
<label class="form-check-label" for="is_moc">
|
|
<strong>This is a MOC (My Own Creation)</strong>
|
|
</label>
|
|
</div>
|
|
|
|
<div id="mocSection" style="display: none;">
|
|
<div class="alert alert-success">
|
|
<i class="bi bi-lightbulb"></i>
|
|
MOC set number will be auto-generated:
|
|
<strong id="mocNumberPreview">Loading...</strong>
|
|
</div>
|
|
</div>
|
|
|
|
<div id="officialSection">
|
|
<label for="set_number">Set Number</label>
|
|
<input type="text" id="set_number" name="set_number" required>
|
|
</div>
|
|
```
|
|
|
|
### 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 (global by default)
|
|
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 GLOBAL MOC number (default)
|
|
moc_number = MOCNumberGenerator.generate_next_number(
|
|
prefix='MOC',
|
|
user_id=None # None = global numbering
|
|
)
|
|
|
|
# Generate next USER-SCOPED MOC number (optional)
|
|
moc_number = MOCNumberGenerator.generate_next_number(
|
|
prefix='MOC',
|
|
user_id=current_user.id # Scope to specific user
|
|
)
|
|
|
|
# Validate MOC number availability (global)
|
|
is_available = MOCNumberGenerator.validate_moc_number(
|
|
'MOC-12345',
|
|
user_id=None # Check globally
|
|
)
|
|
|
|
# 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():
|
|
# Default: user_scoped=false (global numbering)
|
|
user_scoped = request.args.get('user_scoped', 'false').lower() == 'true'
|
|
user_id = current_user.id if user_scoped else None
|
|
|
|
moc_number = MOCNumberGenerator.generate_next_number(
|
|
user_id=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
|
|
```
|
|
|
|
### Numbering Modes
|
|
|
|
**Global Numbering** (Default - **RECOMMENDED**):
|
|
- All users share the same MOC number sequence
|
|
- Ensures system-wide unique MOC numbers
|
|
- MOC-10000 is unique across the entire system
|
|
- **API**: `user_scoped=false` or omit parameter
|
|
- **Use Case**: Single household, organization, or when you want guaranteed unique IDs
|
|
|
|
**Per-User Numbering** (Optional):
|
|
- Each user has their own MOC number sequence
|
|
- MOC-10000 can exist for multiple users
|
|
- **API**: `user_scoped=true`
|
|
- **Use Case**: Multi-tenant systems where users want isolated numbering
|
|
|
|
## Use Cases
|
|
|
|
### Creating a New MOC
|
|
|
|
1. User clicks "Add New Set"
|
|
2. User toggles "This is a MOC" checkbox
|
|
3. System automatically generates next global MOC number (e.g., MOC-10000)
|
|
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:
|
|
# Global numbering - user_id=None
|
|
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. Verify you're using global numbering (default `user_scoped=false`)
|
|
2. Check database constraints on `set_number` field (should be unique)
|
|
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
|
|
- [Feature Update Summary](../FEATURE_UPDATE_SUMMARY.md) - Recent changes
|
|
|
|
## 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.1 (Global Numbering Default)
|
|
**Compatibility**: LEGO Instructions Manager v1.3.0+
|