Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
1 change: 1 addition & 0 deletions app/controllers/main_routes/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ def injectGlobalData():
'lastStaticUpdate': lastStaticUpdate}

from app.controllers.main_routes import main_routes
from app.controllers.main_routes import departmentPortal
from app.controllers.main_routes import laborStatusForm
from app.controllers.main_routes import laborHistory
from app.controllers.main_routes import alterLSF
Expand Down
173 changes: 172 additions & 1 deletion app/controllers/main_routes/departmentPortal.py
Original file line number Diff line number Diff line change
@@ -1 +1,172 @@
from flask import render_template
import re
from datetime import date
from flask import render_template, request, json, redirect, session, url_for, send_file, g, flash, jsonify
from peewee import JOIN, DoesNotExist, fn, Case
from functools import reduce
import operator
from app.logic.userInsertFunctions import createSupervisorFromTracy
from app.models.department import Department
from app.models.supervisor import Supervisor
from app.models.supervisorDepartment import SupervisorDepartment
from app.models.student import Student
from app.models.laborStatusForm import LaborStatusForm
from app.models.formHistory import FormHistory
from app.models.laborReleaseForm import LaborReleaseForm
from app.models.term import Term
from app.controllers.admin_routes.allPendingForms import checkAdjustment
from app.controllers.main_routes import main_bp
from app.logic.download import CSVMaker, saveFormSearchResult, retrieveFormSearchResult
from app.logic.search import getDepartmentsForSupervisor, searchPerson, searchSupervisorPortal
from app.login_manager import require_login, logout
from app.logic.getTableData import getDatatableData
from app.logic.banner import Banner
from flask import abort
from app.logic.manageMembers import supervisorsDbToDict, currentAcademicYear
from app.logic.search import limitSearchByUserDepartment, studentDbToDict, usernameFromEmail
from app.logic.manageMembers import getCurrentDepartment,getDepartmentMembers,getReleasedFormIds,getStudentCounts,attachPositionCounts

@main_bp.route('/department/<org>/<account>/members', methods=['GET'])
def manageMembers(org=None, account=None):
"""Generates the Manage Members page."""
currentUser = require_login()
if not currentUser or not currentUser.supervisor:
if currentUser.student:
return redirect(url_for('main.laborhistory', id=currentUser.student.ID))
return render_template('errors/403.html'), 403

currentSupervisor = Supervisor.get(Supervisor.ID == currentUser.supervisor)
dept = getCurrentDepartment(org, account)

members = getDepartmentMembers(dept)
counts = getStudentCounts(dept)
members = attachPositionCounts(members, counts)

return render_template(
'main/manageMembers.html',
members=members,
department=dept,
currentSupervisor=currentSupervisor,
currentAcademicYear=currentAcademicYear()
)



@main_bp.route('/members/search/<query>', methods=['GET'])
def searchMember(query=None):
"""
Search student table and STUDATA for student results.
"""
currentUser = require_login()
accessAllowed = currentUser and (currentUser.supervisor or currentUser.isLaborAdmin)
if not accessAllowed:
return render_template('errors/403.html'), 403

recordedSupervisors = [] # supervisors recorded in the database
query = query.strip()

displayedSupervisors = Supervisor.select()

# bnumber search
if re.match(r'[Bb]\d+', query):
recordedSupervisors = list(map(supervisorsDbToDict, displayedSupervisors.where(Supervisor.ID % "{}%".format(query.upper()))))


# name search
else:
if " " not in query:
search = query.upper() + "%"
results = displayedSupervisors.where(Supervisor.preferred_name ** search | Supervisor.legal_name ** search | Supervisor.LAST_NAME ** search)
else:
search = query.upper().split()
firstQuery = search[0] + "%"
lastQuery = search[-1] + "%"
results = displayedSupervisors.where((Supervisor.preferred_name ** firstQuery | Supervisor.legal_name ** firstQuery) & Supervisor.LAST_NAME ** lastQuery)

recordedSupervisors = list(map(supervisorsDbToDict, results))


# combine lists, remove duplicates, and then sort
supervisors = list({v['bnumber']:v for v in (recordedSupervisors)}.values())
supervisors = sorted(supervisors, key=lambda f: f['firstName'] + f['lastName'])

return jsonify(supervisors)

@main_bp.route('/members/coordinator_switch', methods=['POST'])
def coordinatorSwitch():
"""
Assigns or unassignes a supervisor as a Labor Coordinator.
"""
data = request.get_json()
supervisorID = data.get("supervisorID")
isCoordinator = data.get("isCoordinator")

departmentID = session.get('current_department_id')
if not departmentID:
return "", 400

member = SupervisorDepartment.get(
(SupervisorDepartment.supervisor == supervisorID) &
(SupervisorDepartment.department == departmentID)
)
member.isCoordinator = isCoordinator
member.save()

return "", 200


@main_bp.route('/members/ban_switch', methods=['POST'])
def elegibilitySwitch():
"""
Updates a supervisor's eligibility status.
"""
data = request.get_json()
supervisorID = data.get("supervisorID")

member = Supervisor.get(Supervisor.ID == supervisorID)

member.isBanned = not member.isBanned
member.save()

return "", 200


@main_bp.route('/members/remove', methods=['DELETE'])
def removeMember():
"""
Removes a staff member from a department.
"""
data = request.get_json()
supervisorID = data.get("supervisorID")

member = SupervisorDepartment.get(
(SupervisorDepartment.supervisor == supervisorID) &
(SupervisorDepartment.department == session['current_department_id'])
)
member.delete_instance()

return "", 200



@main_bp.route('/members/add', methods=['GET', 'POST'])
def addUserToDept():
"""
Adds a user to a department.
"""
userDeptData = request.form
supervisorDeptRecord = SupervisorDepartment.get_or_none(supervisor = userDeptData['supervisorID'], department = userDeptData['departmentID'])
try:
if supervisorDeptRecord:
return "False"

else:
supervisorID = userDeptData['supervisorID']
if not Supervisor.get_or_none(Supervisor.ID == supervisorID):
createSupervisorFromTracy(bnumber=supervisorID)

SupervisorDepartment.create(supervisor=supervisorID, department=userDeptData['departmentID'])
return "True"

except Exception as e:
print(f'Could not add user to department: {e}')
return "", 500
51 changes: 32 additions & 19 deletions app/controllers/main_routes/main_routes.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,10 @@
from app.login_manager import require_login, logout
from app.logic.getTableData import getDatatableData
from app.logic.banner import Banner
from app.logic.tracy import Tracy
from app.logic.userInsertFunctions import createSupervisorFromTracy
from app.models.positionHistory import PositionHistory


@main_bp.route('/logout', methods=['GET'])
def triggerLogout():
Expand Down Expand Up @@ -51,10 +55,14 @@ def supervisorPortal():
@main_bp.route('/department/<org>', methods=['GET'])
@main_bp.route('/department/<org>/<account>', methods=['GET'])
def departmentPortal(org=None,account=None):
try:
dept = Department.get(Department.ORG == org, Department.ACCOUNT == account)
except (NameError, DoesNotExist):
if org and account:
try:
dept = Department.get(Department.ORG == org, Department.ACCOUNT == account)
except (NameError, DoesNotExist):
dept = None
else:
dept = None




Expand All @@ -65,23 +73,28 @@ def departmentPortal(org=None,account=None):

return render_template('main/departmentPortal.html',
departments = departments,
department = dept)

@main_bp.route('/supervisorPortal/addUserToDept', methods=['GET', 'POST'])
def addUserToDept():
userDeptData = request.form
supervisorDeptRecord = SupervisorDepartment.get_or_none(supervisor = userDeptData['supervisorID'], department = userDeptData['departmentID'])
department = dept,
# supervisors = supervisor,
# laborCoordinators=laborCoordinators,
currentUser=g.currentUser,
# positions = positionsList,
# posUrl = posUrl
)

@main_bp.route('/department/<org>/<account>/positions', methods=['GET'])
def managePositions(org, account):
try:
if supervisorDeptRecord:
return "False"

else:
SupervisorDepartment.create(supervisor=userDeptData['supervisorID'], department=userDeptData['departmentID'])
return "True"

except Exception as e:
print(f'Could not add user to department: {e}')
return "", 500
dept = Department.get(Department.ORG == org, Department.ACCOUNT == account)
except DoesNotExist:
return render_template('errors/404.html'), 404

positions = Tracy().getPositionsFromDepartment(org, account)
print(positions)
return render_template('main/managepositions.html',
department = dept,
department_name = dept.DEPT_NAME,
positions = positions
)

@main_bp.route('/supervisorPortal/download', methods=['POST'])
def downloadSupervisorPortalResults():
Expand Down
128 changes: 128 additions & 0 deletions app/logic/manageMembers.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
import re
from datetime import date
from flask import render_template, request, json, redirect, session, url_for, send_file, g, flash, jsonify
from peewee import JOIN, DoesNotExist, fn, Case
from functools import reduce
import operator
from app.logic.userInsertFunctions import createSupervisorFromTracy
from app.models.department import Department
from app.models.supervisor import Supervisor
from app.models.supervisorDepartment import SupervisorDepartment
from app.models.student import Student
from app.models.laborStatusForm import LaborStatusForm
from app.models.formHistory import FormHistory
from app.models.laborReleaseForm import LaborReleaseForm
from app.models.term import Term
from app.controllers.admin_routes.allPendingForms import checkAdjustment
from app.controllers.main_routes import main_bp
from app.logic.download import CSVMaker, saveFormSearchResult, retrieveFormSearchResult
from app.logic.search import getDepartmentsForSupervisor, searchPerson, searchSupervisorPortal
from app.login_manager import require_login, logout
from app.logic.getTableData import getDatatableData
from app.logic.banner import Banner
from flask import abort
from app.logic.search import limitSearchByUserDepartment, studentDbToDict, usernameFromEmail

def supervisorsDbToDict(supervisor):
"""
Given a supervisor object it will return a mapped Dict with supervisor data.
"""
dbToDict = {'username': usernameFromEmail(supervisor.EMAIL.strip()),
'firstName': supervisor.FIRST_NAME.strip(),
'lastName': supervisor.LAST_NAME.strip(),
'bnumber': supervisor.ID.strip(),
'department': supervisor.DEPT_NAME.strip(),
'type': 'Supervisor'}
return dbToDict

def currentAcademicYear():
"""
Finds the current academic year
"""
today = date.today()
currentYear = today.year
if today.month < 7:
currentAcademicYear = (currentYear - 1, currentYear)
else:
currentAcademicYear = (currentYear, currentYear + 1)
# Note that the start of July is
# normally considered the start of a new academic year.

return currentAcademicYear

def getCurrentDepartment(org, account):
"""Look up the department by org/account, stash it in session, or 404."""
try:
dept = Department.get(Department.ORG == org, Department.ACCOUNT == account)
except (NameError, DoesNotExist):
abort(404)
session['current_department_id'] = dept.departmentID
session['current_department'] = dept.DEPT_NAME
return dept


def getDepartmentMembers(dept):
"""Supervisor-department rows for a department, as dicts."""
return list(
SupervisorDepartment.select(SupervisorDepartment, Supervisor)
.where(SupervisorDepartment.department == dept)
.join(Supervisor)
.dicts()
)


def getReleasedFormIds():
"""formIDs for labor release forms already in effect as of today."""
today = date.today()
return (
FormHistory.select(FormHistory.formID)
.join(LaborReleaseForm)
.where(
(FormHistory.historyType == "Labor Release Form") &
(FormHistory.status == "Approved") &
(LaborReleaseForm.releaseDate <= today)
)
)


def getStudentCounts(dept):
"""Active/pending primary/secondary position counts, keyed by (dept, supervisor)."""
releasedForms = getReleasedFormIds()

activePrimaries = (LaborStatusForm.jobType == 'Primary') & (LaborStatusForm.studentConfirmation == True)
pendingPrimaries = (LaborStatusForm.jobType == 'Primary') & (LaborStatusForm.studentConfirmation.is_null(True))
activeSecondaries = (LaborStatusForm.jobType == 'Secondary') & (LaborStatusForm.studentConfirmation == True)
pendingSecondaries = (LaborStatusForm.jobType == 'Secondary') & (LaborStatusForm.studentConfirmation.is_null(True))

rows = list(
LaborStatusForm.select(
fn.SUM(Case(None, ((activePrimaries, 1),), 0)).alias("active_primary_positions"),
fn.SUM(Case(None, ((pendingPrimaries, 1),), 0)).alias("pending_primary_positions"),
fn.SUM(Case(None, ((activeSecondaries, 1),), 0)).alias("active_secondary_positions"),
fn.SUM(Case(None, ((pendingSecondaries, 1),), 0)).alias("pending_secondary_positions"),
LaborStatusForm.department,
LaborStatusForm.supervisor
).where(
(LaborStatusForm.department == dept) &
(LaborStatusForm.laborStatusFormID.not_in(releasedForms))
).group_by(
LaborStatusForm.department, LaborStatusForm.supervisor
).dicts()
)
return {(row["department"], row["supervisor"]): row for row in rows}


def attachPositionCounts(members, counts):
"""Merge counts onto each member dict, defaulting missing values to 0."""
fields = [
"active_primary_positions", "pending_primary_positions",
"active_secondary_positions", "pending_secondary_positions",
]
for member in members:
row = counts.get((member["department"], member["supervisor"]), {})
for field in fields:
member[field] = row.get(field, 0)

return members


Loading