Creating an advanced-level Employee Management System (EMS) using Flask and Bootstrap 5 involves designing a robust database structure, implementing Flask routes, and creating dynamic web pages. Below is a detailed implementation:
1. Database Structure
The database will include tables for employees, departments, roles, and attendance. We'll use SQLAlchemy for ORM.
Database Models
from flask_sqlalchemy import SQLAlchemy
from datetime import datetime
db = SQLAlchemy()
class Department(db.Model):
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(100), nullable=False)
employees = db.relationship('Employee', backref='department', lazy=True)
class Role(db.Model):
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(100), nullable=False)
employees = db.relationship('Employee', backref='role', lazy=True)
class Employee(db.Model):
id = db.Column(db.Integer, primary_key=True)
first_name = db.Column(db.String(100), nullable=False)
last_name = db.Column(db.String(100), nullable=False)
email = db.Column(db.String(120), unique=True, nullable=False)
phone = db.Column(db.String(20), nullable=False)
hire_date = db.Column(db.DateTime, default=datetime.utcnow)
department_id = db.Column(db.Integer, db.ForeignKey('department.id'), nullable=False)
role_id = db.Column(db.Integer, db.ForeignKey('role.id'), nullable=False)
attendance = db.relationship('Attendance', backref='employee', lazy=True)
class Attendance(db.Model):
id = db.Column(db.Integer, primary_key=True)
employee_id = db.Column(db.Integer, db.ForeignKey('employee.id'), nullable=False)
date = db.Column(db.DateTime, default=datetime.utcnow)
status = db.Column(db.String(20), nullable=False) # Present, Absent, Late
Flask Application Structure
Here’s the directory structure for the project:
advanced_employee_management/
│
├── app/
│ ├── __init__.py # Initialize the Flask app and database
│ ├── models.py # Database models (User , Event, Attendance, EventCategory)
│ ├── routes.py # Application routes and views
│ ├── forms.py # Forms for user registration, login, event posting, and attendance
│ ├── static/ # Static files (CSS, JS, images)
│ │ ├── css/
│ │ │ └── styles.css # Custom CSS styles
│ │ └── js/
│ │ └── scripts.js # Custom JavaScript files
│ └── templates/ # HTML templates
│ ├── base.html # Base layout for templates
│ ├── index.html # Home page
│ ├── login.html # Login page
│ ├── register.html # Registration page
│ ├── dashboard.html # User dashboard
│ ├── post_event.html # Event posting page
│ ├── event_detail.html # Event details page
│ └── attendance.html # Attendance management page
│
├── migrations/ # Database migration files (if using Flask-Migrate)
│
├── config.py # Configuration settings for the app
│
├── requirements.txt # Python package dependencies
│
└── run.py # Entry point to run the application
2. Flask Application Setup
Install required packages:
pip install flask flask-sqlalchemy flask-wtf flask-login flask-bootstrap5
Create the Flask app:
from flask import Flask, render_template, redirect, url_for, request, flash
from flask_sqlalchemy import SQLAlchemy
from flask_bootstrap5 import Bootstrap
from flask_login import LoginManager, UserMixin, login_user, login_required, logout_user, current_user
from werkzeug.security import generate_password_hash, check_password_hash
app = Flask(__name__)
app.config['SECRET_KEY'] = 'supersecretkey'
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///ems.db'
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
db = SQLAlchemy(app)
Bootstrap(app)
login_manager = LoginManager(app)
login_manager.login_view = 'login'
# User model for authentication
class User(UserMixin, db.Model):
id = db.Column(db.Integer, primary_key=True)
username = db.Column(db.String(100), unique=True, nullable=False)
password_hash = db.Column(db.String(200), nullable=False)
@login_manager.user_loader
def load_user(user_id):
return User.query.get(int(user_id))
# Create database tables
with app.app_context():
db.create_all()
3. Routes and Views
Implement routes for authentication, employee management, and attendance tracking.
@app.route('/')
@login_required
def index():
employees = Employee.query.all()
return render_template('index.html', employees=employees)
@app.route('/login', methods=['GET', 'POST'])
def login():
if request.method == 'POST':
username = request.form['username']
password = request.form['password']
user = User.query.filter_by(username=username).first()
if user and check_password_hash(user.password_hash, password):
login_user(user)
return redirect(url_for('index'))
flash('Invalid username or password')
return render_template('login.html')
@app.route('/logout')
@login_required
def logout():
logout_user()
return redirect(url_for('login'))
@app.route('/add_employee', methods=['GET', 'POST'])
@login_required
def add_employee():
if request.method == 'POST':
first_name = request.form['first_name']
last_name = request.form['last_name']
email = request.form['email']
phone = request.form['phone']
department_id = request.form['department']
role_id = request.form['role']
new_employee = Employee(
first_name=first_name,
last_name=last_name,
email=email,
phone=phone,
department_id=department_id,
role_id=role_id
)
db.session.add(new_employee)
db.session.commit()
flash('Employee added successfully!')
return redirect(url_for('index'))
departments = Department.query.all()
roles = Role.query.all()
return render_template('add_employee.html', departments=departments, roles=roles)
@app.route('/edit_employee/<int:id>', methods=['GET', 'POST'])
@login_required
def edit_employee(id):
employee = Employee.query.get_or_404(id)
if request.method == 'POST':
employee.first_name = request.form['first_name']
employee.last_name = request.form['last_name']
employee.email = request.form['email']
employee.phone = request.form['phone']
employee.department_id = request.form['department']
employee.role_id = request.form['role']
db.session.commit()
flash('Employee updated successfully!')
return redirect(url_for('index'))
departments = Department.query.all()
roles = Role.query.all()
return render_template('edit_employee.html', employee=employee, departments=departments, roles=roles)
@app.route('/delete_employee/<int:id>', methods=['POST'])
@login_required
def delete_employee(id):
employee = Employee.query.get_or_404(id)
db.session.delete(employee)
db.session.commit()
flash('Employee deleted successfully!')
return redirect(url_for('index'))
@app.route('/attendance', methods=['GET', 'POST'])
@login_required
def attendance():
if request.method == 'POST':
employee_id = request.form['employee_id']
status = request.form['status']
new_attendance = Attendance(employee_id=employee_id, status=status)
db.session.add(new_attendance)
db.session.commit()
flash('Attendance recorded successfully!')
return redirect(url_for('attendance'))
employees = Employee.query.all()
attendance_records = Attendance.query.all()
return render_template('attendance.html', employees=employees, attendance_records=attendance_records)
4. HTML Templates
templates/base.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css">
<title>{% block title %}Employee Management System{% endblock %}</title>
</head>
<body>
<nav class="navbar navbar-expand-lg navbar-light bg-light">
<div class="container-fluid">
<a class="navbar-brand" href="{{ url_for('index') }}">EMS</a>
<div class="collapse navbar-collapse">
<ul class="navbar-nav me-auto">
<li class="nav-item">
<a class="nav-link" href="{{ url_for('index') }}">Home</a>
</li>
<li class="nav-item">
<a class="nav-link" href="{{ url_for('attendance') }}">Attendance</a>
</li>
<li class="nav-item">
<a class="nav-link" href="{{ url_for('logout') }}">Logout</a>
</li>
</ul>
</div>
</div>
</nav>
<div class="container mt-4">
{% with messages = get_flashed_messages() %}
{% if messages %}
<div class="alert alert-info">
{% for message in messages %}
<p>{{ message }}</p>
{% endfor %}
</div>
{% endif %}
{% endwith %}
{% block content %}{% endblock %}
</div>
</body>
</html>
templates/index.html
{% extends 'base.html' %}
{% block title %}Employee List{% endblock %}
{% block content %}
<h1>Employee List</h1>
<a href="{{ url_for('add_employee') }}" class="btn btn-primary">Add Employee</a>
<table class="table mt-3">
<thead>
<tr>
<th>ID</th>
<th>Name</th>
<th>Email</th>
<th>Phone</th>
<th>Actions</th>
</tr>
</thead>
<tbody>
{% for employee in employees %}
<tr>
<td>{{ employee.id }}</td>
<td>{{ employee.first_name }} {{ employee.last_name }}</td>
<td>{{ employee.email }}</td>
<td>{{ employee.phone }}</td>
<td>
<a href="{{ url_for('edit_employee', id=employee.id) }}" class="btn btn-warning">Edit</a>
<form action="{{ url_for('delete_employee', id=employee.id) }}" method="POST" style="display:inline;">
<button type="submit" class="btn btn-danger">Delete</button>
</form>
</td>
</tr>
{% endfor %}
</tbody>
</table>
{% endblock %}
templates/login.html
{% extends 'base.html' %}
{% block title %}Login{% endblock %}
{% block content %}
<h1>Login</h1>
<form method="POST">
<div class="mb-3">
<label for="username" class="form-label">Username</label>
<input type="text" class="form-control" id="username" name="username" required>
</div>
<div class="mb-3">
<label for="password" class="form-label">Password</label>
<input type="password" class="form-control" id="password" name="password" required>
</div>
<button type="submit" class="btn btn-primary">Login</button>
</form>
{% endblock %}
templates/add_employee.html
{% extends 'base.html' %}
{% block title %}Add Employee{% endblock %}
{% block content %}
<h1>Add Employee</h1>
<form method="POST">
<div class="mb-3">
<label for="first_name" class="form-label">First Name</label>
<input type="text" class="form-control" id="first_name" name="first_name" required>
</div>
<div class="mb-3">
<label for="last_name" class="form-label">Last Name</label>
<input type="text" class="form-control" id="last_name" name="last_name" required>
</div>
<div class="mb-3">
<label for="email" class="form-label">Email</label>
<input type="email" class="form-control" id="email" name="email" required>
</div>
<div class="mb-3">
<label for="phone" class="form-label">Phone</label>
<input type="text" class="form-control" id="phone" name="phone" required>
</div>
<div class="mb-3">
<label for="department" class="form-label">Department</label>
<select class="form-select" id="department" name="department" required>
{% for department in departments %}
<option value="{{ department.id }}">{{ department.name }}</option>
{% endfor %}
</select>
</div>
<div class="mb-3">
<label for="role" class="form-label">Role</label>
<select class="form-select" id="role" name="role" required>
{% for role in roles %}
<option value="{{ role.id }}">{{ role.name }}</option>
{% endfor %}
</select>
</div>
<button type="submit" class="btn btn-primary">Add Employee</button>
</form>
{% endblock %}
templates/edit_employee.html
{% extends 'base.html' %}
{% block title %}Edit Employee{% endblock %}
{% block content %}
<h1>Edit Employee</h1>
<form method="POST">
<div class="mb-3">
<label for="first_name" class="form-label">First Name</label>
<input type="text" class="form-control" id="first_name" name="first_name" value="{{ employee.first_name }}" required>
</div>
<div class="mb-3">
<label for="last_name" class="form-label">Last Name</label>
<input type="text" class="form-control" id="last_name" name="last_name" value="{{ employee.last_name }}" required>
</div>
<div class="mb-3">
<label for="email" class="form-label">Email</label>
<input type="email" class="form-control" id="email" name="email" value="{{ employee.email }}" required>
</div>
<div class="mb-3">
<label for="phone" class="form-label">Phone</label>
<input type="text" class="form-control" id="phone" name="phone" value="{{ employee.phone }}" required>
</div>
<div class="mb-3">
<label for="department" class="form-label">Department</label>
<select class="form-select" id="department" name="department" required>
{% for department in departments %}
<option value="{{ department.id }}" {% if department.id == employee.department_id %}selected{% endif %}>{{ department.name }}</option>
{% endfor %}
</select>
</div>
<div class="mb-3">
<label for="role" class="form-label">Role</label>
<select class="form-select" id="role" name="role" required>
{% for role in roles %}
<option value="{{ role.id }}" {% if role.id == employee.role_id %}selected{% endif %}>{{ role.name }}</option>
{% endfor %}
</select>
</div>
<button type="submit" class="btn btn-primary">Update Employee</button>
</form>
{% endblock %}
templates/attendance.html
{% extends 'base.html' %}
{% block title %}Attendance{% endblock %}
{% block content %}
<h1>Attendance</h1>
<form method="POST">
<div class="mb-3">
<label for="employee_id" class="form-label">Select Employee</label>
<select class="form-select" id="employee_id" name="employee_id" required>
{% for employee in employees %}
<option value="{{ employee.id }}">{{ employee.first_name }} {{ employee.last_name }}</option>
{% endfor %}
</select>
</div>
<div class="mb-3">
<label for="status" class="form-label">Status</label>
<select class="form-select" id="status" name="status" required>
<option value="Present">Present</option>
<option value="Absent">Absent</option>
<option value="Late">Late</option>
</select>
</div>
<button type="submit" class="btn btn-primary">Record Attendance</button>
</form>
<table class="table mt-3">
<thead>
<tr>
<th>ID</th>
<th>Employee</th>
<th>Date</th>
<th>Status</th>
</tr>
</thead>
<tbody>
{% for record in attendance_records %}
<tr>
<td>{{ record.id }}</td>
<td>{{ record.employee.first_name }} {{ record.employee.last_name }}</td>
<td>{{ record.date.strftime('%Y-%m-%d') }}</td>
<td>{{ record.status }}</td>
</tr>
{% endfor %}
</tbody>
</table>
{% endblock %}
Conclusion
This structure provides a solid foundation for an advanced Employee Management System using Flask and Bootstrap 5. You can expand upon this by adding features such as user roles, permissions, and more complex reporting functionalities.