Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
43 commits
Select commit Hold shift + click to select a range
62551f4
Started refactoring code to include shift schedulers; need to create …
CidWB Nov 9, 2025
c6dec53
rebased to original commit
CidWB Nov 9, 2025
671036f
rebased to original commit
CidWB Nov 9, 2025
61b8ecb
test
CidWB Nov 9, 2025
9007f84
Merge branch 'cid' into main
CidWB Nov 9, 2025
71f9304
Merge pull request #1 from RostrApp/main
CidWB Nov 9, 2025
60762e3
Merge pull request #2 from RostrApp/cid
CidWB Nov 9, 2025
b4d703d
Started refactoring code to include shift schedulers, details in Trello
CidWB Nov 9, 2025
9fde035
sprint 2 tasks #: 10, 11, 16, 21
augustus1413 Nov 19, 2025
e6961a1
added unit tests and cli commands
augustus1413 Nov 19, 2025
e123d86
test
CidWB Nov 19, 2025
3e607fa
Merge pull request #3 from RostrApp/cid
CidWB Nov 19, 2025
224879b
Added Scheduling services for task #8
CidWB Nov 19, 2025
84a4bfd
Merge pull request #5 from RostrApp/main
CidWB Nov 19, 2025
39c181c
Fixed methods for scheduling strategies
CidWB Nov 19, 2025
8cee2c9
Special Features Classes and controllers implemented
esnotme Nov 20, 2025
fa32aae
Updated Schedule CLI Commands
esnotme Nov 21, 2025
aaacc6f
Tested Cli special feature
esnotme Nov 21, 2025
f4e4e79
Schedule Integration tests implemented and tested
esnotme Nov 21, 2025
9efa0ec
test
LiannMaicoo Nov 21, 2025
f859d12
Revert "test"
LiannMaicoo Nov 21, 2025
a35fec2
Revert "Schedule Integration tests implemented and tested"
LiannMaicoo Nov 21, 2025
67d5389
Revert "Tested Cli special feature"
LiannMaicoo Nov 21, 2025
7b3a5c1
Revert "Updated Schedule CLI Commands"
LiannMaicoo Nov 21, 2025
75063f0
Revert "Special Features Classes and controllers implemented"
LiannMaicoo Nov 21, 2025
1bca20a
Revert "added unit tests and cli commands"
LiannMaicoo Nov 21, 2025
73e81a7
Revert "sprint 2 tasks #: 10, 11, 16, 21"
LiannMaicoo Nov 21, 2025
cf17ae8
#31 Update Schedule Model
LiannMaicoo Nov 23, 2025
fb79c6e
#12 Create Report Controller and functions
LiannMaicoo Nov 23, 2025
9e592f7
#32 Update Shift Model
LiannMaicoo Nov 23, 2025
932fd29
#14 Refactor Admin Controllers
LiannMaicoo Nov 23, 2025
fd5bb38
#11 Refactor Staff Functions
augustus1413 Nov 23, 2025
b5a2830
Revert "#11 Refactor Staff Functions"
augustus1413 Nov 24, 2025
7aafcf9
Merge pull request #13 from RostrApp/schedule-model-fix1
esnotme Nov 24, 2025
e1a3392
Merge pull request #15 from RostrApp/shift-model-fix1
esnotme Nov 24, 2025
7fd9a94
Merge pull request #4 from RostrApp/cid
esnotme Nov 24, 2025
9e5c4e2
Merge pull request #16 from RostrApp/refactor-admin-controllers
esnotme Nov 24, 2025
69b4d07
#11 Refactor Staff Functions
augustus1413 Nov 24, 2025
68ce0d4
#10 Refactor Shift Methods
augustus1413 Nov 24, 2025
c5c4b7a
Revert "#11 Refactor Staff Functions"
augustus1413 Nov 25, 2025
a65c711
Revert "#10 Refactor Shift Methods"
augustus1413 Nov 25, 2025
c64eaa8
#10 Refactor Shift Methods
augustus1413 Nov 25, 2025
4951cb1
#11 Refactor Staff Functions
augustus1413 Nov 25, 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
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 .report import *
61 changes: 41 additions & 20 deletions App/controllers/admin.py
Original file line number Diff line number Diff line change
@@ -1,34 +1,51 @@
from App.models import Shift
from App.database import db
from datetime import datetime
from App.controllers.user import get_user

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

def create_schedule(admin_id, scheduleName): #Not sure why this was missing
#create an empty schedule with start and end date
def create_schedule(start_date, end_date, admin_id):
admin = get_user(admin_id)

if not admin or admin.role != "admin":
raise PermissionError("Only admins can create schedules")

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


new_schedule = Schedule(start_date, end_date, admin_id)

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

return new_schedule

def schedule_shift(admin_id, staff_id, schedule_id, start_time, end_time):
#assign all staff to a weekly schedule using a scheduling strategy
def schedule_week(strategy, schedule_id, staff_list, admin_id):
admin = get_user(admin_id)
staff = get_user(staff_id)

if not admin or admin.role != "admin":
raise PermissionError("Only admins can create schedules")

schedule = db.session.get(Schedule, schedule_id)
if not schedule:
raise ValueError("Invalid schedule ID")

valid_staff = []
for staff_id in staff_list:
staff = get_user(staff_id)
if staff and staff.role == "staff":
valid_staff.append(staff)
else:
print(f"Skipping invalid staff ID: {staff_id}")

if not valid_staff:
raise ValueError("No valid staff members to schedule")

strategy.fill_schedule(valid_staff, schedule)


#create a shift and add it to a schedule
def schedule_shift(schedule_id, start_time, end_time, staff_id, admin_id):
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")
Expand All @@ -49,10 +66,14 @@ def schedule_shift(admin_id, staff_id, schedule_id, start_time, end_time):

return new_shift


def get_shift_report(admin_id):
#view the shift report for a given schedule
def view_report(schedule_id, admin_id):
admin = get_user(admin_id)
if not admin or admin.role != "admin":
raise PermissionError("Only admins can view shift reports")

return [shift.get_json() for shift in Shift.query.order_by(Shift.start_time).all()]
schedule = db.session.get(Schedule, schedule_id)
if not schedule:
raise ValueError("Invalid schedule ID")

return generate_report(schedule_id)
40 changes: 40 additions & 0 deletions App/controllers/report.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
from App.models.schedule import Schedule


# This controller summarises the attendance of staff for a given schedule
def get_summary(scheduleID):
schedule = Schedule.query.get(scheduleID)
if not schedule:
raise ValueError("Schedule not found")

days = {}
for shift in schedule.shifts:
day = shift.start_time.strftime("%Y-%m-%d")

if day not in days:
days[day] = {
"assigned": set(),
"present": set(),
"late": set(),
"missed": set(),
}

staff = shift.staff

# Add assigned staff
days[day]["assigned"].add(staff.username)

# Record staff's attendance
if shift.clock_in:
days[day]["present"].add(staff.username)
if shift.clock_in > shift.start_time:
days[day]["late"].add(staff.username)
else:
days[day]["missed"].add(staff.username)

# Convert sets to lists
for day, record in days.items():
for key in record:
record[key] = list(record[key])

return {"schedule_id": schedule.id, "days": days}
15 changes: 15 additions & 0 deletions App/controllers/schedule.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
from App.controllers.user import get_all_users_by_role, get_all_shifts
from App.models.schedule import Schedule


def create_even_schedule(all_staff, shifts):
# write function
pass

def create_minimum_schedule(all_staff, shifts):
# write function
pass

def create_day_night_schedule(all_staff, shifts):
# write function
pass
50 changes: 37 additions & 13 deletions App/controllers/staff.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,41 +3,65 @@
from datetime import datetime
from App.controllers.user import get_user

def get_combined_roster(staff_id):
def viewShifts(staff_id, schedule_id):
# Shows only the current staff member's shifts for a given schedule

staff = get_user(staff_id)
if not staff or staff.role != "staff":
raise PermissionError("Only staff can view roster")
return [shift.get_json() for shift in Shift.query.order_by(Shift.start_time).all()]

if not staff or staff.role.lower() != "staff":
raise PermissionError("Only staff can view their shifts")

shifts = Shift.query.filter_by(
staff_id=staff_id
).order_by(Shift.start_time).all()

return [s.get_json() for s in shifts]


def viewSchedule(staff_id, schedule_id):
# Combined view of all shifts in a schedule for staff members
# Replaces get_combined_roster()

staff = get_user(staff_id)

if not staff or staff.role.lower() != "staff":
raise PermissionError("Only staff can view schedule rosters")

shifts = Shift.query.filter_by(
schedule_id=schedule_id
).order_by(Shift.start_time).all()

return [s.get_json() for s in shifts]


def clock_in(staff_id, shift_id):
staff = get_user(staff_id)
if not staff or staff.role != "staff":
if not staff or staff.role.lower() != "staff":
raise PermissionError("Only staff can clock in")

shift = db.session.get(Shift, shift_id)
shift = Shift.get_shift(shift_id)

if not shift or shift.staff_id != staff_id:
raise ValueError("Invalid shift for staff")

shift.clock_in = datetime.now()
shift.updateStatus() #automatically updates the status for the clock in
db.session.commit()
return shift


def clock_out(staff_id, shift_id):
staff = get_user(staff_id)
if not staff or staff.role != "staff":
if not staff or staff.role.lower() != "staff":
raise PermissionError("Only staff can clock out")

shift = db.session.get(Shift, shift_id)
shift = Shift.get_shift(shift_id)

if not shift or shift.staff_id != staff_id:
raise ValueError("Invalid shift for staff")

shift.clock_out = datetime.now()
shift.updateStatus() #automatically updates the status for the clock out
db.session.commit()
return shift

def get_shift(shift_id):
shift = db.session.get(Shift, shift_id)

return shift
15 changes: 14 additions & 1 deletion App/controllers/user.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
from App.models import User, Admin, Staff, Shift
from App.database import db
from datetime import datetime

VALID_ROLES = {"user", "staff", "admin"}

Expand Down Expand Up @@ -35,10 +34,24 @@ def get_all_users_json():
return []
return [user.get_json() for user in users]

#added method for scheduling purposes
def get_all_users_by_role(role):
return User.query.filter_by(role=role).all()

def get_all_users_by_role_json(role):
users = get_all_users_by_role(role)
if not users:
return []
return [user.get_json() for user in users]

def update_user(id, username):
user = get_user(id)
if user:
user.username = username
db.session.commit()
return user
return None

#not sure if this method should be added here
def get_all_shifts():
return Shift.query.all()
4 changes: 2 additions & 2 deletions App/models/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,5 @@
from App.models.admin import Admin
from App.models.staff import Staff
from App.models.schedule import Schedule
from App.models.shift import Shift

from App.models.shift import Shift
from App.models.report import Report
9 changes: 9 additions & 0 deletions App/models/day_night_scheduler.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
from App.controllers.user import get_all_users_by_role, get_all_shifts
from App.models.scheduling_strategy import SchedulingStrategy

class DayNightScheduler(SchedulingStrategy):

def schedule_shift(self):
# return Schedule object -> create_day_night_schedule(self.all_staff, self.shifts)
# this method will be in Schedule controller
pass
9 changes: 9 additions & 0 deletions App/models/even_scheduler.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
from App.controllers.user import get_all_users_by_role, get_all_shifts
from App.models.scheduling_strategy import SchedulingStrategy

class EvenScheduler(SchedulingStrategy):

def schedule_shift(self):
# return Schedule object -> create_even_schedule(self.all_staff, self.shifts)
# this method will be in Schedule controller
pass
9 changes: 9 additions & 0 deletions App/models/minimum_scheduler.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
from App.controllers.user import get_all_users_by_role, get_all_shifts
from App.models.scheduling_strategy import SchedulingStrategy

class MinimumScheduler(SchedulingStrategy):

def schedule_shift(self):
# return Schedule object -> create_minimum_schedule(self.all_staff, self.shifts)
# this method will be in Schedule controller
pass
24 changes: 24 additions & 0 deletions App/models/report.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
from App.database import db

class Report(db.Model):
id = db.Column(db.Integer, primary_key=True)
admin_id = db.Column(db.Integer, db.ForeignKey("user.id"), nullable=False)
generated_date = db.Column(db.DateTime, nullable=False)

admin = db.relationship("Admin", backref="reports", foreign_keys=[admin_id])

def get_json(self):
return {
"id": self.id,
"admin_id": self.admin_id,
"admin_name": self.admin.username,
"generated_date": self.generated_date.isoformat()
}

def __init__(self, admin_id, generated_date):
self.id = id
self.admin_id = admin_id
self.generated_date = generated_date



31 changes: 20 additions & 11 deletions App/models/schedule.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,22 +3,31 @@

class Schedule(db.Model):
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(50), nullable=False)
created_at = db.Column(db.DateTime, default=datetime.utcnow)
created_by = db.Column(db.Integer, db.ForeignKey("user.id"), nullable=False)
start_date = db.Column(db.DateTime, nullable=False)
end_date = db.Column(db.DateTime, nullable=False)
admin_id = db.Column(db.Integer, db.ForeignKey("user.id"), nullable=False)
shifts = db.relationship("Shift", backref="schedule", lazy=True)

def shift_count(self):
return len(self.shifts)

def get_json(self):
return {
"id": self.id,
"name": self.name,
"created_at": self.created_at.isoformat(),
"created_by": self.created_by,
"shift_count": self.shift_count(),
"shifts": [shift.get_json() for shift in self.shifts]
"start_date": self.start_date.isoformat(),
"end_date": self.end_date.isoformat(),
"admin_id": self.admin_id,
}

def __init__(self, start_date, end_date, admin_id):
self.start_date = start_date
self.end_date = end_date
self.admin_id = admin_id

def get_shifts(self):
return self.shifts

def add_shift(self, shift):
shift.schedule = self
return self

def remove_shift(self, shift):
shift.schedule = None
return self
15 changes: 15 additions & 0 deletions App/models/scheduling_strategy.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
from abc import ABC, abstractmethod
from App.controllers.user import get_all_users_by_role, get_all_shifts

class SchedulingStrategy(ABC):
# hello
# gets updated list from db upon initialization
def __init__(self):
super().__init__()
self.all_staff = get_all_users_by_role("staff")
self.shifts = get_all_shifts()

@abstractmethod
def schedule_shift(self):
#return Schedule object in concrete classes
pass
Loading