Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
c4f9fe7
revised code and removed auth from cli commands
lillyem Nov 22, 2025
3dc9931
Merge pull request #1 from 2v2-SWEN-Project/Sonali
lillyem Nov 22, 2025
402f003
Refactored Models to match Strategy Pattern
DenelleMohammed Nov 23, 2025
9b247b8
Merge pull request #2 from 2v2-SWEN-Project/Denelle
DenelleMohammed Nov 23, 2025
a7fe3dc
Controller Refactoring
KaveeshRamsarran Nov 23, 2025
7b5a2e1
Refactoring Controller merge pull request #3 from 2v2-SWEN-Project/Ka…
KaveeshRamsarran Nov 23, 2025
7fa882f
updated readme and wsgi.py
lillyem Nov 23, 2025
021a8b1
Merge pull request #4 from 2v2-SWEN-Project/Sonali
lillyem Nov 23, 2025
f56242a
UAT, unit and integration tests implemented
samjssom2703 Nov 25, 2025
9df9007
Merge pull request #5 from 2v2-SWEN-Project/Samuel
samjssom2703 Nov 25, 2025
07dd049
Python version updated
KaveeshRamsarran Nov 30, 2025
8b8f5ce
Python Version Updated from 2v2-SWEN-Project/Kaveesh
KaveeshRamsarran Nov 30, 2025
63ec9a5
Add UI pages and fix routes + create-schedule POST
lillyem Dec 1, 2025
2a19da3
Merge pull request #7 from 2v2-SWEN-Project/Sonali
lillyem Dec 1, 2025
b077a1b
UI Fixes and polish
KaveeshRamsarran Dec 1, 2025
65f3797
Merge pull request #8 from 2v2-SWEN-Project/Kaveesh
KaveeshRamsarran Dec 1, 2025
114bf8e
Added Welcome Page, Added Admin App Routes
DenelleMohammed Dec 2, 2025
66f0380
Merge pull request #9 from 2v2-SWEN-Project/Denelle
DenelleMohammed Dec 2, 2025
1008703
Updated /admin/create-schedule and /admin/view-request
DenelleMohammed Dec 2, 2025
46c0943
Merge pull request #10 from 2v2-SWEN-Project/Denelle
DenelleMohammed Dec 2, 2025
afcf96b
Staff routes updated and UI polishes
samjssom2703 Dec 2, 2025
681fba9
Merge pull request #11 from 2v2-SWEN-Project/Samuel
samjssom2703 Dec 2, 2025
7150a98
updated readme
lillyem Dec 2, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .python-version
Original file line number Diff line number Diff line change
@@ -1 +1 @@
3.9.10
3.910
3 changes: 2 additions & 1 deletion App/controllers/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,5 @@
from .auth import *
from .initialize import *
from .admin import *
from .staff import *
from .staff import *
from .schedule_controller import ScheduleController
101 changes: 75 additions & 26 deletions App/controllers/admin.py
Original file line number Diff line number Diff line change
@@ -1,58 +1,107 @@
from App.models import Shift
from App.models import Shift, Schedule, Staff, ShiftSwapRequest
from App.database import db
from datetime import datetime
from datetime import datetime, timedelta
from App.controllers.user import get_user
from sqlalchemy import func

from App.models import Shift, Schedule
from App.database import db
from datetime import datetime
from App.controllers.user import get_user

def create_schedule(admin_id, scheduleName): #Not sure why this was missing
admin = get_user(admin_id)
if not admin or admin.role != "admin":
raise PermissionError("Only admins can create schedules")

def create_schedule(admin_id, scheduleName):
new_schedule = Schedule(
created_by=admin_id,
name=scheduleName,
created_at=datetime.utcnow()
)

db.session.add(new_schedule)
db.session.commit()

return new_schedule

def schedule_shift(admin_id, staff_id, schedule_id, start_time, end_time):
admin = get_user(admin_id)
staff = get_user(staff_id)

schedule = db.session.get(Schedule, schedule_id)

if not admin or admin.role != "admin":
raise PermissionError("Only admins can schedule shifts")
if not staff or staff.role != "staff":
raise ValueError("Invalid staff member")
raise PermissionError("Only staff can be assigned to a shift.")
if not schedule:
raise ValueError("Invalid schedule ID")

new_shift = Shift(
staff_id=staff_id,
schedule_id=schedule_id,
start_time=start_time,
end_time=end_time
)

db.session.add(new_shift)
db.session.commit()

return new_shift


def get_shift_report(admin_id):
admin = get_user(admin_id)
if not admin or admin.role != "admin":
raise PermissionError("Only admins can view shift reports")
raise PermissionError("Only admin can view shift report")
return [shift.get_json() for shift in Shift.query.order_by(Shift.start_time).all()]

def get_total_staff_count():
"""Count total number of staff members."""
return db.session.query(func.count(Staff.id)).scalar() or 0

def get_shifts_this_week():
"""Count shifts scheduled for this week."""
today = datetime.utcnow()
week_start = today - timedelta(days=today.weekday())
week_end = week_start + timedelta(days=6, hours=23, minutes=59, seconds=59)

return db.session.query(func.count(Shift.id)).filter(
Shift.start_time >= week_start,
Shift.start_time <= week_end
).scalar() or 0

return [shift.get_json() for shift in Shift.query.order_by(Shift.start_time).all()]
def get_pending_swap_requests():
"""Get all pending shift swap requests."""
requests = ShiftSwapRequest.query.filter_by(status="pending").all()
return [req.get_json() for req in requests]

def get_staff_attendance():
"""Get attendance data for all staff (with clock in/out times and hours)."""
staff_members = Staff.query.all()
attendance_data = []

for staff in staff_members:
shifts = Shift.query.filter_by(staff_id=staff.id).all()

for shift in shifts:
hours = 0
status = "Absent"

if shift.clock_in and shift.clock_out:
delta = shift.clock_out - shift.clock_in
hours = round(delta.total_seconds() / 3600, 1)
status = "Present"
elif shift.clock_in and not shift.clock_out:
status = "Clocked In"

attendance_data.append({
"staff_name": staff.username,
"staff_id": staff.id,
"clock_in": shift.clock_in.strftime("%I:%M %p") if shift.clock_in else "—",
"clock_out": shift.clock_out.strftime("%I:%M %p") if shift.clock_out else "—",
"hours": hours,
"status": status,
"shift_id": shift.id
})

return attendance_data

def approve_swap_request(request_id):
"""Approve a shift swap request."""
swap_req = db.session.get(ShiftSwapRequest, request_id)
if not swap_req:
raise ValueError("Swap request not found")
swap_req.status = "approved"
db.session.commit()
return swap_req

def deny_swap_request(request_id):
"""Deny a shift swap request."""
swap_req = db.session.get(ShiftSwapRequest, request_id)
if not swap_req:
raise ValueError("Swap request not found")
swap_req.status = "denied"
db.session.commit()
return swap_req
72 changes: 8 additions & 64 deletions App/controllers/auth.py
Original file line number Diff line number Diff line change
@@ -1,80 +1,24 @@
from flask_jwt_extended import (
create_access_token, jwt_required, JWTManager,
get_jwt_identity, verify_jwt_in_request
)
from App.models import User
from App.database import db

def login(username, password):
result = db.session.execute(db.select(User).filter_by(username=username))
user = result.scalar_one_or_none()
if user and user.check_password(password):
# Store ONLY the user id as a string in JWT 'sub'
return create_access_token(identity=str(user.id))
return None
result = db.session.execute(db.select(User).filter_by(username=username))
user = result.scalar_one_or_none()
if user and user.check_password(password):
return user # Return user object directly, no JWT
return None

def loginCLI(username, password):
result = db.session.execute(db.select(User).filter_by(username=username))
user = result.scalar_one_or_none()

if user and user.check_password(password):

if user.active_token:
return {"message": "User already logged in", "token": user.active_token}

token = create_access_token(identity=str(user.id))
user.active_token = token
db.session.commit()
return {"message": "Login successful", "token": token}

return {"message": "Login successful", "user_id": user.id}
return {"message": "Invalid username or password"}

def logout(username):
# No authentication/session to clear, just a stub
result = db.session.execute(db.select(User).filter_by(username=username))
user = result.scalar_one_or_none()

if not user:
return {"message": "User not found"}

if not user.active_token:
return {"message": f"User {username} is not logged in"}

user.active_token = None
db.session.commit()
return {"message": f"User {username} logged out successfully"}

def setup_jwt(app):
jwt = JWTManager(app)

# Always store a string user id in the JWT identity (sub)
@jwt.user_identity_loader
def user_identity_lookup(identity):
user_id = getattr(identity, "id", identity)
return str(user_id) if user_id is not None else None

@jwt.user_lookup_loader
def user_lookup_callback(_jwt_header, jwt_data):
identity = jwt_data["sub"]
try:
user_id = int(identity)
except (TypeError, ValueError):
return None
return db.session.get(User, user_id)

return jwt

# Context processor to make 'is_authenticated' available to all templates
def add_auth_context(app):
@app.context_processor
def inject_user():
try:
verify_jwt_in_request()
identity = get_jwt_identity()
user_id = int(identity) if identity is not None else None
current_user = db.session.get(User, user_id) if user_id is not None else None
is_authenticated = current_user is not None
except Exception as e:
print(e)
is_authenticated = False
current_user = None
return dict(is_authenticated=is_authenticated, current_user=current_user)
return {"message": f"User {username} logged out (no session)"}
64 changes: 43 additions & 21 deletions App/controllers/initialize.py
Original file line number Diff line number Diff line change
@@ -1,30 +1,52 @@
from .user import create_user
from App.database import db

from App.models import Schedule, Shift
from datetime import datetime, timedelta

def initialize():
db.drop_all()
db.create_all()
create_user('bob', 'bobpass', 'admin')
create_user('jane', 'janepass', 'staff')
create_user('alice', 'alicepass', 'staff')
create_user('tim', 'timpass', 'user')

# db.session.commit()
# Create users
create_user('admin1', 'adminpass', 'admin')
create_user('john_smith', 'password123', 'staff')
create_user('jane_doe', 'password123', 'staff')
create_user('alex_johnson', 'password123', 'staff')
create_user('maria_garcia', 'password123', 'staff')
create_user('robert_chen', 'password123', 'staff')
create_user('emma_davis', 'password123', 'staff')

# Create schedules
schedule1 = Schedule(name="Week 1 Schedule", created_by=1)
schedule2 = Schedule(name="Week 2 Schedule", created_by=1)
db.session.add(schedule1)
db.session.add(schedule2)
db.session.commit()

# # adding dummy schedule data for testing Jane
# schedule = Schedule (
# name = "Morning Shift",
# created_by = 1
# )
# db.session.add(schedule)
# db.session.commit()
# Create shifts for this week
today = datetime.now()
base_date = today.replace(hour=0, minute=0, second=0, microsecond=0)

shifts_data = [
(2, base_date + timedelta(days=0, hours=9), base_date + timedelta(days=0, hours=17)), # Monday
(3, base_date + timedelta(days=0, hours=14), base_date + timedelta(days=0, hours=22)), # Monday
(4, base_date + timedelta(days=1, hours=9), base_date + timedelta(days=1, hours=17)), # Tuesday
(5, base_date + timedelta(days=1, hours=14), base_date + timedelta(days=1, hours=22)), # Tuesday
(6, base_date + timedelta(days=2, hours=9), base_date + timedelta(days=2, hours=17)), # Wednesday
(7, base_date + timedelta(days=2, hours=14), base_date + timedelta(days=2, hours=22)), # Wednesday
(2, base_date + timedelta(days=3, hours=9), base_date + timedelta(days=3, hours=17)), # Thursday
(3, base_date + timedelta(days=3, hours=14), base_date + timedelta(days=3, hours=22)), # Thursday
(4, base_date + timedelta(days=4, hours=9), base_date + timedelta(days=4, hours=17)), # Friday
(5, base_date + timedelta(days=4, hours=14), base_date + timedelta(days=4, hours=22)), # Friday
]

# # adding dummy shifts for Jane
# shift1 = Shift (
# schedule_id = schedule.id,
# staff_id = 2,
# start_time = "2024-10-01 08:00:00",
# end_time = "2024-10-01 12:00:00"
# )
# db.session.add(shift1)
for staff_id, start, end in shifts_data:
shift = Shift(
schedule_id=schedule1.id,
staff_id=staff_id,
start_time=start,
end_time=end
)
db.session.add(shift)

db.session.commit()
Loading