Tuesday, 25 November 2025

#Module 9 – Validation Rules & Business Logic (MCP)

Module 9 – Validation Rules & Business Logic (MCP)

Module 9 – Validation Rules & Business Logic (MCP)

This module implements validation and business rules to ensure data integrity and correct workflow behavior in the Attendance System.

Why Validation Matters

  • Prevent invalid attendance entries (wrong date/hour/subject)
  • Enforce staff–subject authorization before marking attendance
  • Avoid timetable conflicts and duplicate sessions
  • Ensure student exists and belongs to the semester

1. Check: Staff is Assigned to the Subject

# file: mcp_server/validators.py
async def staff_assigned(db, staff_id: int, subject_id: int) -> bool:
    q = "SELECT 1 FROM staff_subjects WHERE staff_id = :sid AND subject_id = :sub"
    row = await db.fetch_one(q, values={'sid': staff_id, 'sub': subject_id})
    return bool(row)

# Usage inside FastAPI route
if not await staff_assigned(db, staff_id, subject_id):
    raise HTTPException(status_code=403, detail='Staff not assigned to this subject')

2. Check: Timetable Slot Exists

-- Verify a timetable slot exists for semester/day/hour/subject
SELECT 1 FROM timetable_slots
WHERE semester_id = :semester_id AND day_of_week = :day AND slot_order = :hour AND subject_id = :subject_id;

3. Check: Student Belongs to Semester

# Python helper
async def student_in_semester(db, student_id: int, semester_id: int) -> bool:
    q = 'SELECT 1 FROM students WHERE id = :sid AND semester = :sem'
    return bool(await db.fetch_one(q, values={'sid': student_id, 'sem': semester_id}))

# Raise error if not
if not await student_in_semester(db, student_id, semester_id):
    raise HTTPException(400, 'Student not enrolled in this semester')

4. Prevent Duplicate Attendance for Same Session

-- Check if attendance row already exists for that student/session/hour
SELECT 1 FROM attendance
WHERE subject_id = :subject_id AND student_id = :student_id AND date = :date AND hour = :hour;

5. Business Rule: Attendance Window

Only allow marking attendance within a sensible window (e.g., same day ±1 day) unless an admin overrides.

from datetime import datetime, timedelta

def within_attendance_window(date_str: str) -> bool:
    d = datetime.fromisoformat(date_str).date()
    today = datetime.utcnow().date()
    return abs((today - d).days) <= 1

if not within_attendance_window(payload['date']):
    raise HTTPException(400, 'Attendance can only be marked within 1 day of class')

6. Conflict Checker for Timetable Changes

-- Prevent two subjects assigned to same semester/day/hour
SELECT COUNT(*) FROM timetable_slots
WHERE semester_id = :semester_id AND day_of_week = :day AND slot_order = :hour;
-- if count > 0 then conflict exists

7. Consolidated FastAPI Example: Mark Attendance (with Validation)

@app.post('/api/attendance/mark')
async def mark_attendance(sessionId: str, studentId: int, status: str, db: Database = Depends(get_db), user=Depends(get_current_user)):
    # fetch session metadata
    ses = await db.fetch_one('SELECT * FROM attendance_sessions WHERE id = :sid', values={'sid': sessionId})
    if not ses:
        raise HTTPException(404, 'Session not found')
    # verify staff
    if not await staff_assigned(db, user.staff_id, ses['subject_id']):
        raise HTTPException(403, 'Not authorized')
    # verify student in semester
    if not await student_in_semester(db, studentId, ses['semester_id']):
        raise HTTPException(400, 'Student not in semester')
    # prevent duplicates
    exists = await db.fetch_one('SELECT 1 FROM attendance WHERE subject_id=:sub AND student_id=:stu AND date=:d AND hour=:h', values={'sub': ses['subject_id'],'stu': studentId,'d': ses['date'],'h': ses['hour']})
    if exists:
        raise HTTPException(409, 'Attendance already marked')
    # insert
    await db.execute('INSERT INTO attendance(subject_id, staff_id, date, hour, student_id, status) VALUES(:sub, :staff, :d, :h, :stu, :status)', values={'sub': ses['subject_id'],'staff': user.staff_id,'d': ses['date'],'h': ses['hour'],'stu': studentId,'status': status})
    return { 'status':'ok' }

Testing Tips

  • Use unit tests to test each validator independently.
  • Simulate edge cases: staff not assigned, wrong date, duplicate submissions.
  • Log and audit all failed validation attempts for security review.

No comments:

Post a Comment

#MCP Index

MCP Tutorial – Module Index MCP Tutorial – 12 Modules Latest Edition for AU-CSE Final year Students Module 1 Introducti...