418 lines
9.3 KiB
Markdown
418 lines
9.3 KiB
Markdown
# Development Guide
|
|
|
|
This guide covers the architecture, code organization, and contribution guidelines for the Romanoff Fire Alarm Management System.
|
|
|
|
## Architecture Overview
|
|
|
|
### Application Structure
|
|
|
|
```
|
|
romanoff/
|
|
├── run.py # Entry point - starts Flask dev server
|
|
├── import_data.py # Data import utility
|
|
├── requirements.txt # Dependencies
|
|
├── app/
|
|
│ ├── __init__.py # Flask app factory
|
|
│ ├── models.py # SQLAlchemy models
|
|
│ ├── routes.py # Web routes & API endpoints
|
|
│ ├── templates/ # Jinja2 templates
|
|
│ └── static/ # CSS, JavaScript assets
|
|
└── instance/ # SQLite database (auto-created)
|
|
```
|
|
|
|
### Design Patterns
|
|
|
|
**Flask App Factory**
|
|
|
|
The application uses the factory pattern in `app/__init__.py`:
|
|
|
|
```python
|
|
def create_app():
|
|
app = Flask(__name__)
|
|
# Configure app
|
|
db.init_app(app)
|
|
# Register blueprints
|
|
app.register_blueprint(main_bp)
|
|
app.register_blueprint(api_bp, url_prefix='/api')
|
|
return app
|
|
```
|
|
|
|
**Blueprint Organization**
|
|
|
|
Routes are organized into two blueprints:
|
|
- `main_bp` - Web routes serving HTML pages
|
|
- `api_bp` - REST API endpoints (prefixed with `/api`)
|
|
|
|
**Model-View Separation**
|
|
|
|
- Models (`models.py`) handle data persistence
|
|
- Routes (`routes.py`) handle request/response logic
|
|
- Templates (`templates/`) handle presentation
|
|
|
|
### Data Flow
|
|
|
|
```
|
|
User Browser
|
|
↓
|
|
Flask Routes (routes.py)
|
|
↓
|
|
SQLAlchemy Models (models.py)
|
|
↓
|
|
SQLite Database (instance/romanoff.db)
|
|
```
|
|
|
|
## Database Schema
|
|
|
|
### Entity Relationship
|
|
|
|
```
|
|
┌─────────────┐
|
|
│ Job │
|
|
├─────────────┤
|
|
│ id (PK) │
|
|
│ job_number │───────┐
|
|
│ job_name │ │
|
|
│ ... │ │
|
|
└─────────────┘ │
|
|
│ │
|
|
│ 1:N │ 1:N
|
|
↓ ↓
|
|
┌─────────────┐ ┌─────────────┐
|
|
│ Phase │ │ Material │
|
|
├─────────────┤ ├─────────────┤
|
|
│ id (PK) │ │ id (PK) │
|
|
│ job_id (FK) │ │ job_id (FK) │
|
|
│ phase_type │ │ part_number │
|
|
│ ... │ │ ... │
|
|
└─────────────┘ └─────────────┘
|
|
```
|
|
|
|
### Models
|
|
|
|
**Job** - Core project entity
|
|
- Contains budget, team assignments, milestones, dates
|
|
- Has many phases and materials (cascade delete)
|
|
|
|
**Phase** - Project schedule phases
|
|
- Types: rough_in, trim, commissioning, final, turnover
|
|
- Tracks dates, completion status, man hours
|
|
|
|
**Material** - Inventory tracking
|
|
- Tracks ordering, receipt, and delivery
|
|
|
|
## Frontend Architecture
|
|
|
|
### Template Hierarchy
|
|
|
|
```
|
|
base.html
|
|
├── dashboard.html - Charts and statistics
|
|
├── jobs.html - Job list with filters
|
|
├── job_detail.html - Single job view
|
|
├── job_form.html - Create/edit job
|
|
├── schedule.html - Phase management
|
|
└── materials.html - Material tracking
|
|
```
|
|
|
|
### JavaScript Strategy
|
|
|
|
**Shared Utilities** (`static/js/app.js`)
|
|
|
|
```javascript
|
|
// API helpers
|
|
async function apiGet(url) { ... }
|
|
async function apiPost(url, data) { ... }
|
|
async function apiPut(url, data) { ... }
|
|
async function apiDelete(url) { ... }
|
|
|
|
// Formatting
|
|
function formatCurrency(value) { ... }
|
|
function formatDate(dateString) { ... }
|
|
|
|
// UI helpers
|
|
function showToast(message, type) { ... }
|
|
function getProgressClass(percent) { ... }
|
|
function debounce(func, wait) { ... }
|
|
function confirmAction(message) { ... }
|
|
```
|
|
|
|
**Page-Specific Scripts**
|
|
|
|
Each template contains embedded `<script>` blocks for page-specific functionality:
|
|
- Data fetching and rendering
|
|
- Event handlers
|
|
- Filter/search logic
|
|
|
|
### CSS Organization
|
|
|
|
**Bootstrap 5** provides the base styling. Custom styles in `static/css/style.css` add:
|
|
- Card shadows and hover effects
|
|
- Progress bar color states
|
|
- Responsive table adjustments
|
|
- Form styling enhancements
|
|
|
|
## Development Setup
|
|
|
|
### Prerequisites
|
|
|
|
- Python 3.8+
|
|
- pip
|
|
|
|
### Local Development
|
|
|
|
1. Create virtual environment:
|
|
```bash
|
|
python -m venv .venv
|
|
source .venv/bin/activate
|
|
```
|
|
|
|
2. Install dependencies:
|
|
```bash
|
|
pip install -r requirements.txt
|
|
```
|
|
|
|
3. Run development server:
|
|
```bash
|
|
python run.py
|
|
```
|
|
|
|
4. Access at `http://localhost:5000`
|
|
|
|
### Database
|
|
|
|
The SQLite database is automatically created on first run in `instance/romanoff.db`. Tables are created via `db.create_all()` in the app factory.
|
|
|
|
To reset the database:
|
|
```bash
|
|
rm instance/romanoff.db
|
|
python run.py
|
|
```
|
|
|
|
### Importing Test Data
|
|
|
|
```bash
|
|
python import_data.py
|
|
```
|
|
|
|
Imports from Excel files (if present):
|
|
- `Raleigh jobs FIRE ALARM INFORMATION.xlsx`
|
|
- `schedule_updated.xlsm`
|
|
|
|
## Code Guidelines
|
|
|
|
### Python Style
|
|
|
|
- Follow PEP 8 style guidelines
|
|
- Use type hints where helpful
|
|
- Keep functions focused and small
|
|
- Document complex logic with comments
|
|
|
|
### Database Operations
|
|
|
|
```python
|
|
# Reading data
|
|
jobs = Job.query.all()
|
|
job = Job.query.get_or_404(job_id)
|
|
|
|
# Creating records
|
|
job = Job(job_number='JOB-001', job_name='New Job')
|
|
db.session.add(job)
|
|
db.session.commit()
|
|
|
|
# Updating records
|
|
job.percent_complete = 0.5
|
|
db.session.commit()
|
|
|
|
# Deleting records
|
|
db.session.delete(job)
|
|
db.session.commit()
|
|
```
|
|
|
|
### API Response Patterns
|
|
|
|
```python
|
|
# Success with data
|
|
return jsonify(job.to_dict())
|
|
|
|
# Success with created resource
|
|
return jsonify(job.to_dict()), 201
|
|
|
|
# Success with no content
|
|
return '', 204
|
|
|
|
# Error
|
|
return jsonify({'error': 'Not found'}), 404
|
|
```
|
|
|
|
### Frontend JavaScript
|
|
|
|
```javascript
|
|
// Fetch pattern
|
|
try {
|
|
const data = await apiGet('/api/jobs');
|
|
renderJobs(data);
|
|
} catch (error) {
|
|
showToast('Error loading jobs', 'danger');
|
|
}
|
|
|
|
// Event handling
|
|
document.getElementById('searchInput')
|
|
.addEventListener('input', debounce(filterJobs, 300));
|
|
```
|
|
|
|
## Adding New Features
|
|
|
|
### Adding a New Model
|
|
|
|
1. Define model in `models.py`:
|
|
```python
|
|
class NewModel(db.Model):
|
|
__tablename__ = 'new_models'
|
|
id = db.Column(db.Integer, primary_key=True)
|
|
# ... fields
|
|
|
|
def to_dict(self):
|
|
return { ... }
|
|
```
|
|
|
|
2. Add API routes in `routes.py`
|
|
|
|
3. Create template if needed
|
|
|
|
4. Restart server (tables auto-create)
|
|
|
|
### Adding a New Page
|
|
|
|
1. Create template in `templates/`
|
|
2. Add web route in `routes.py`:
|
|
```python
|
|
@main_bp.route('/newpage')
|
|
def new_page():
|
|
return render_template('newpage.html')
|
|
```
|
|
3. Add navigation link in `base.html`
|
|
|
|
### Adding API Endpoints
|
|
|
|
1. Add route function in `routes.py`:
|
|
```python
|
|
@api_bp.route('/newresource', methods=['GET'])
|
|
def get_new_resource():
|
|
# ... logic
|
|
return jsonify(data)
|
|
```
|
|
|
|
2. Document in `API.md`
|
|
|
|
## Testing
|
|
|
|
Currently no automated tests are implemented. Recommended testing approach:
|
|
|
|
### Manual Testing
|
|
|
|
- Test all CRUD operations through UI
|
|
- Verify API responses with curl or Postman
|
|
- Test edge cases (empty data, invalid input)
|
|
|
|
### Future Testing Setup
|
|
|
|
```bash
|
|
pip install pytest pytest-flask
|
|
```
|
|
|
|
```python
|
|
# tests/test_api.py
|
|
def test_get_jobs(client):
|
|
response = client.get('/api/jobs')
|
|
assert response.status_code == 200
|
|
```
|
|
|
|
## Deployment Considerations
|
|
|
|
### Production Checklist
|
|
|
|
- [ ] Set `SECRET_KEY` environment variable
|
|
- [ ] Configure production database (PostgreSQL recommended)
|
|
- [ ] Use production WSGI server (Gunicorn, uWSGI)
|
|
- [ ] Enable HTTPS
|
|
- [ ] Add authentication/authorization
|
|
- [ ] Configure logging
|
|
- [ ] Set up database backups
|
|
|
|
### Environment Variables
|
|
|
|
| Variable | Description | Required |
|
|
|----------|-------------|----------|
|
|
| `SECRET_KEY` | Flask secret key | Yes |
|
|
| `DATABASE_URL` | Database connection string | No (defaults to SQLite) |
|
|
|
|
### Example Production Config
|
|
|
|
```python
|
|
# config.py
|
|
import os
|
|
|
|
class ProductionConfig:
|
|
SECRET_KEY = os.environ['SECRET_KEY']
|
|
SQLALCHEMY_DATABASE_URI = os.environ.get('DATABASE_URL')
|
|
SQLALCHEMY_TRACK_MODIFICATIONS = False
|
|
```
|
|
|
|
## Contributing
|
|
|
|
### Workflow
|
|
|
|
1. Create a feature branch from `main`
|
|
2. Make changes following code guidelines
|
|
3. Test thoroughly
|
|
4. Submit pull request with description
|
|
|
|
### Commit Messages
|
|
|
|
Use clear, descriptive commit messages:
|
|
- `Add material delivery tracking feature`
|
|
- `Fix job completion percentage calculation`
|
|
- `Update dashboard chart colors`
|
|
|
|
### Code Review
|
|
|
|
Before merging:
|
|
- Code follows style guidelines
|
|
- No obvious bugs or security issues
|
|
- Documentation updated if needed
|
|
- Changes tested manually
|
|
|
|
## Troubleshooting
|
|
|
|
### Common Issues
|
|
|
|
**Database locked error**
|
|
- Stop any other processes accessing the database
|
|
- Delete `.db` file and restart if corrupted
|
|
|
|
**Import errors**
|
|
- Ensure virtual environment is activated
|
|
- Run `pip install -r requirements.txt`
|
|
|
|
**Template not found**
|
|
- Check file name matches route
|
|
- Verify file is in `templates/` directory
|
|
|
|
**API returns 404**
|
|
- Check URL matches route definition
|
|
- Verify blueprint prefix (`/api`)
|
|
|
|
### Debug Mode
|
|
|
|
Debug mode is enabled by default in development. Check Flask output for:
|
|
- Request/response logging
|
|
- SQL queries (if enabled)
|
|
- Stack traces on errors
|
|
|
|
## Resources
|
|
|
|
- [Flask Documentation](https://flask.palletsprojects.com/)
|
|
- [SQLAlchemy Documentation](https://docs.sqlalchemy.org/)
|
|
- [Bootstrap 5 Documentation](https://getbootstrap.com/docs/5.3/)
|
|
- [Chart.js Documentation](https://www.chartjs.org/docs/)
|