Initial commit - Fire alarm management application
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
417
DEVELOPMENT.md
Normal file
417
DEVELOPMENT.md
Normal file
@@ -0,0 +1,417 @@
|
||||
# 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/)
|
||||
Reference in New Issue
Block a user