Add comprehensive documentation for MOC auto-generation feature
This commit is contained in:
348
docs/MOC_AUTO_GENERATION.md
Normal file
348
docs/MOC_AUTO_GENERATION.md
Normal file
@@ -0,0 +1,348 @@
|
||||
# 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
|
||||
<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
|
||||
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+
|
||||
Reference in New Issue
Block a user