diff --git a/README.md b/README.md index 493e38421..b404cfa85 100755 --- a/README.md +++ b/README.md @@ -69,4 +69,4 @@ If you want to test with actual emails, use an email other than outlook to test 1. Set up two factor authentication on your Gmail (Security Settings) 2. Create an App Password through your Gmail. This 16 character password can only be viewed once, so make sure to save it. (NOTE: You won't have the option to create an app password unless step one is completed) 3. Inside of your secret_config.yaml file set the MAIL_USERNAME and MAIL_DEFAULT_SENDER as your Gmail, set the MAIL_PASSWORD as your new app password as, and set ALWAYS_SEND_MAIL as True. If you want emails to go to their real recipients, remove MAIL_OVERRIDE_ALL from your config or set it to "". -4. For testing purposes, change the email of the student and supervisor to match another email that can receive your test emails (or you can use MAIL_OVERRIDE_ALL to send everything to the address specified. +4. For testing purposes, change the email of the student and supervisor to match another email that can receive your test emails (or you can use MAIL_OVERRIDE_ALL to send everything to the address specified. \ No newline at end of file diff --git a/app.py b/app.py index 3ef0ab230..d7a8e73ca 100755 --- a/app.py +++ b/app.py @@ -21,7 +21,7 @@ PORT = 8080 if __name__ == "__main__": - # Print statements go to your log file in production; to your console while developing + # Print statements go to your log file in production; to your console while developing print ("Running server at http://{0}:{1}/".format(IP, PORT)) app.run(host = IP, port = PORT, debug = True, threaded = True) diff --git a/app/__init__.py b/app/__init__.py index 206a5fc7b..61aa37d1f 100755 --- a/app/__init__.py +++ b/app/__init__.py @@ -18,6 +18,7 @@ if 'use_banner' not in app.config.keys(): app.config['use_banner'] = (app.config['ENV'] in ('production','staging')) + # Record and output queries if requested from flask import session from peewee import BaseQuery @@ -29,7 +30,7 @@ def new_execute(*args, **kwargs): session['querycount'] = 0 session['querycount'] += 1 - if app.config.get('show_queries'): # in case we selectively disable + if app.config.get('show_queries'):# in case we selectively disable print("**Running query {}**".format(session['querycount'])) print(args[0]) return old_execute(*args, **kwargs) diff --git a/app/controllers/admin_routes/allPendingForms.py b/app/controllers/admin_routes/allPendingForms.py index ef3499db2..37c75bff2 100644 --- a/app/controllers/admin_routes/allPendingForms.py +++ b/app/controllers/admin_routes/allPendingForms.py @@ -190,6 +190,7 @@ def allPendingForms(formType): @admin.route('/admin/pendingForms/download', methods=['POST']) def downloadAllPendingForms(): + currentUser = require_login() searchResult = retrieveFormSearchResult(request.form.get('downloadId')) if not searchResult: print(f"[ERROR] Missing or invalid download id was provided by the user.") @@ -558,6 +559,7 @@ def sendEmail(): email = emailHandler(historyForm.formHistoryID) link = makeThirdPartyLink("student", request.host, rsp['formHistoryID']) email.LaborOverloadFormStudentReminder(link) + else: if rsp['emailRecipient'] == 'SAASEmail': diff --git a/app/controllers/main_routes/alterLSF.py b/app/controllers/main_routes/alterLSF.py index c6edd850a..6c2f163ea 100644 --- a/app/controllers/main_routes/alterLSF.py +++ b/app/controllers/main_routes/alterLSF.py @@ -14,7 +14,7 @@ from app.models.supervisor import Supervisor from app.login_manager import require_login from app.logic.alterLSF import modifyLSF, adjustLSF - +from app.logic.utils import makeThirdPartyLink @main_bp.route("/alterLSF/", methods=["GET"]) def alterLSF(laborStatusKey): @@ -153,10 +153,11 @@ def submitAlteredLSF(laborStatusKey): for formHistory in formHistoryIDs: try: email = emailHandler(formHistory) + link = makeThirdPartyLink("studentAdjustment", request.host, formHistory) if "supervisor" in fieldsChanged: - email.laborStatusFormAdjusted(fieldsChanged["supervisor"]["newValue"]) + email.laborStatusFormAdjusted(link, newSupervisor=fieldsChanged["supervisor"]["newValue"]) else: - email.laborStatusFormAdjusted() + email.laborStatusFormAdjusted(link) except Exception as e: print("An error occured while attempting to send adjustment form emails: ", e) message = "Your labor adjustment form(s) for {0} {1} have been submitted.".format(student.studentSupervisee.FIRST_NAME, student.studentSupervisee.LAST_NAME) diff --git a/app/controllers/main_routes/laborHistory.py b/app/controllers/main_routes/laborHistory.py index c80cdf9a0..19d704003 100755 --- a/app/controllers/main_routes/laborHistory.py +++ b/app/controllers/main_routes/laborHistory.py @@ -1,9 +1,10 @@ -import datetime +from datetime import date import re import types from fpdf import FPDF from urllib.parse import urlparse +from app.models import status from flask import render_template , flash, redirect, url_for, request, g, session, jsonify, current_app, send_file, json, make_response from flask_login import current_user, login_required from app.controllers.main_routes import * @@ -131,11 +132,18 @@ def populateModal(statusKey): currentUser = require_login() if not currentUser: # Not logged in return render_template('errors/403.html'), 403 + forms = (FormHistory.select().join(LaborReleaseForm, join_type=JOIN.LEFT_OUTER) .where(FormHistory.formID == statusKey).order_by(FormHistory.createdDate.desc(), FormHistory.formHistoryID.desc())) + # Find the FormHistory entry that has an overloadForm (same as overload modal behavior) + overload_history = None + for f in forms: + if f.overloadForm is not None: + overload_history = f + break statusForm = LaborStatusForm.get(LaborStatusForm.laborStatusFormID == statusKey) student = Student.get(Student.ID == statusForm.studentSupervisee) - currentDate = datetime.date.today() + currentDate = date.today() pendingformType = None first = True # temp variable to determine if this is the newest form for form in forms: @@ -178,9 +186,18 @@ def populateModal(statusKey): # Pending release or adjustment forms need the historyType known if (form.releaseForm != None or form.adjustedForm != None) and form.status.statusName == "Pending": pendingformType = form.historyType.historyTypeName + + try: + currentPendingForm = FormHistory.select().where( + (FormHistory.formID == statusForm) & + ((FormHistory.status == "Pending") | (FormHistory.status == "Pre-Student Approval")) + ).get() - approveLink = f"{request.host_url}studentResponse/confirm?token={statusForm.confirmationToken}" - + status = currentPendingForm.status.statusName + except Exception: + status = None + + resp = make_response(render_template('snips/studentHistoryModal.html', forms = forms, currentUser = currentUser, @@ -188,7 +205,8 @@ def populateModal(statusKey): currentDate = currentDate, pendingformType = pendingformType, buttonState = buttonState, - approveLink = approveLink, + status = status, + overload_history = overload_history, )) return (resp) except Exception as e: diff --git a/app/controllers/main_routes/studentOverloadApp.py b/app/controllers/main_routes/studentOverloadApp.py index 683d95e54..ba4aa7fa5 100755 --- a/app/controllers/main_routes/studentOverloadApp.py +++ b/app/controllers/main_routes/studentOverloadApp.py @@ -1,7 +1,7 @@ from datetime import date, datetime +from app.models import overloadForm from playhouse.shortcuts import model_to_dict from flask import json, jsonify, request, redirect, url_for, abort, flash, g - from app.controllers.main_routes import * from app.logic.emailHandler import* from app.logic.utils import makeThirdPartyLink @@ -17,19 +17,38 @@ @main_bp.route('/studentOverloadApp/', methods=['GET']) def studentOverloadApp(formHistoryId): currentUser = require_login() - overloadForm = FormHistory.get_by_id(formHistoryId) + # always load the clicked history entry + currentHistory = FormHistory.get_by_id(formHistoryId) + + # STEP 2 — find the real FormHistory row that holds the overloadForm (reason) + overloadReasonHistory = ( + FormHistory + .select() + .where( + (FormHistory.formID == currentHistory.formID) & + (FormHistory.overloadForm.is_null(False)) + ) + .order_by(FormHistory.createdDate.desc()) + .first() + ) + + # STEP 3 — use whichever record has the overloadForm, fallback to clicked one + if overloadReasonHistory: + overloadHistory = overloadReasonHistory + else: + overloadHistory = currentHistory if not currentUser.isLaborAdmin: if not currentUser: # Not logged in return render_template('errors/403.html'), 403 if not currentUser.student: return render_template('errors/403.html'), 403 - if currentUser.student.ID != overloadForm.formID.studentSupervisee.ID: + if currentUser.student.ID != overloadHistory.formID.studentSupervisee.ID: return render_template('errors/403.html'), 403 lsfForm = (LaborStatusForm.select(LaborStatusForm, Student, Term, Department) .join(Student, attr="studentSupervisee").switch() .join(Term).switch() .join(Department) - .where(LaborStatusForm.laborStatusFormID == overloadForm.formID)).get() + .where(LaborStatusForm.laborStatusFormID == overloadHistory.formID)).get() prefillStudentName = lsfForm.studentSupervisee.FIRST_NAME + " "+ lsfForm.studentSupervisee.LAST_NAME prefillStudentBnum = lsfForm.studentSupervisee.ID prefillStudentCPO = lsfForm.studentSupervisee.STU_CPO @@ -39,7 +58,6 @@ def studentOverloadApp(formHistoryId): prefillPosition = lsfForm.POSN_TITLE prefillHoursOverload = lsfForm.weeklyHours - listOfTerms = [] today = date.today() termYear = today.year * 100 termsInYear = Term.select(Term).where(Term.termCode.between(termYear-1, termYear + 15)) @@ -84,10 +102,25 @@ def studentOverloadApp(formHistoryId): totalCurrentHours += j.formID.weeklyHours totalFormHours = totalCurrentHours + prefillHoursOverload + adjustedField, oldValue, newValue = (None, None, None) + + if overloadHistory.adjustedForm: + adjustmentForm = overloadHistory.adjustedForm + + adjustedField = adjustmentForm.fieldAdjusted + oldValue = adjustmentForm.oldValue + newValue = adjustmentForm.newValue + + if adjustedField == "department": + oldValue = Department.get(Department.ORG == oldValue).DEPT_NAME + newValue = Department.get(Department.ORG == newValue).DEPT_NAME return render_template( 'main/studentOverloadApp.html', title=('student Overload Application'), username = currentUser, - overloadForm = overloadForm, + overloadHistory = overloadHistory, + adjustedField = adjustedField, + oldValue = oldValue, + newValue = newValue, prefillStudentName = prefillStudentName, prefillStudentBnum = prefillStudentBnum, prefillStudentCPO = prefillStudentCPO, @@ -99,26 +132,22 @@ def studentOverloadApp(formHistoryId): currentPrimary = formIDPrimary, currentSecondary = formIDSecondary, totalCurrentHours = totalCurrentHours, - totalFormHours = totalFormHours + totalFormHours = totalFormHours, ) + @main_bp.route('/studentOverloadApp/withdraw/', methods=['POST']) def withdrawRequest(formHistoryId): formHistory = FormHistory.get_by_id(formHistoryId) - if formHistory.historyType_id != "Labor Overload Form": - print("Somehow we reached a non-overload form history entry ({formHistoryId}) from studentOverloadApp.") + if formHistory.historyType_id != "Labor Adjustment Form": abort(500) - # send a withdrawal notification to student and supervisor email = emailHandler(formHistory.formHistoryID) email.LaborOverloadFormWithdrawn() - # TODO should we email financial aid? - - formHistory.overloadForm.delete_instance() + formHistory.adjustedForm.delete_instance() formHistory.formID.delete_instance() #formHistory.delete_instance() - flash("Overload Request Withdrawn", "success") return redirect("/") @@ -129,39 +158,57 @@ def updateDatabase(overloadFormHistoryID): if not overloadReason: abort(500) - oldStatus = Status.get(Status.statusName == "Pre-Student Approval") + # if status is pending that means we have an adjustment + # if status is "pre-student then it means we have an overload" newStatus = Status.get(Status.statusName == "Pending") - overloadFormHistory = FormHistory.get(FormHistory.formHistoryID == overloadFormHistoryID) - originalFormHistory = (FormHistory.select() - .where(FormHistory.formID == overloadFormHistory.formID) - .where(FormHistory.status == oldStatus) - .where(FormHistory.historyType_id == "Labor Status Form")).get() - + originalFormHistory = ( + FormHistory + .select() + .where(FormHistory.formID == overloadFormHistory.formID.laborStatusFormID) + .where((FormHistory.status == "Pre-Student Approval") | (FormHistory.status == "Approved")) + .where(FormHistory.historyType_id.in_([ + "Labor Status Form", + "Labor Overload Form", + "Labor Adjustment Form", + ])) + .first()) + overloadHistoryType, _ = HistoryType.get_or_create( + historyTypeName="Labor Overload Form" + ) with mainDB.atomic() as transaction: - # Update statuses + overloadForm = OverloadForm.create(studentOverloadReason=request.form.get("studentOverloadReason")) + if overloadFormHistory: + overloadFormHistory.overloadForm = overloadForm + overloadFormHistory.save() + else: + overloadFormHistory = FormHistory.create( + formID=originalFormHistory.formID, + historyType=overloadHistoryType, + overloadForm=overloadForm, + createdBy=g.currentUser, + createdDate=datetime.now().date(), + status=newStatus, + ) overloadFormHistory.status = newStatus overloadFormHistory.save() originalFormHistory.status = newStatus originalFormHistory.save() - # Update base student confirmation originalFormHistory.formID.studentResponseDate = datetime.now() originalFormHistory.formID.studentConfirmation = True originalFormHistory.formID.save() - - # Update overload form - overloadForm = overloadFormHistory.overloadForm + overloadForm = overloadFormHistory.overloadForm # should now be non-None overloadForm.studentOverloadReason = overloadReason - overloadForm.save() - + overloadForm.save() # only needed if modified after create email = emailHandler(overloadFormHistory.formHistoryID) link = makeThirdPartyLink("Financial Aid", request.host, overloadFormHistory.formHistoryID) email.overloadVerification("Financial Aid", link) flash("Overload Request Submitted", "success") - return g.currentUser.student.ID - + if g.currentUser.student: + return jsonify({"bnumber": g.currentUser.student.ID}), 200 + return jsonify({"bnumber": None}), 200 except Exception as e: print("ERROR: " + str(e)) abort(500) diff --git a/app/controllers/main_routes/studentResponse.py b/app/controllers/main_routes/studentResponse.py index dc436c280..c29cf0d8a 100644 --- a/app/controllers/main_routes/studentResponse.py +++ b/app/controllers/main_routes/studentResponse.py @@ -17,6 +17,7 @@ def confirm(): .join(FormHistory) .where(LaborStatusForm.confirmationToken == token ,LaborStatusForm.studentSupervisee == g.currentUser.student)) + try: form = forms.get() except DoesNotExist as e: diff --git a/app/logic/emailHandler.py b/app/logic/emailHandler.py index 1de8ef492..35898be60 100644 --- a/app/logic/emailHandler.py +++ b/app/logic/emailHandler.py @@ -91,7 +91,7 @@ def __init__(self, formHistoryKey): # is the 'AttributeError' error. We expect to get the 'AttributeError', # but if we get anything else then we want to print the error if e.__class__.__name__ != "AttributeError": - print (e) + print(e) def send(self, message: Message): if app.config['ENV'] == 'production' or app.config['ALWAYS_SEND_MAIL']: @@ -201,20 +201,18 @@ def laborStatusFormRejected(self): self.checkRecipient("Labor Status Form Rejected For Student", "Primary Position Labor Status Form Rejected") - def laborStatusFormAdjusted(self, newSupervisor=False): - self.checkRecipient("Labor Status Form Adjusted For Student", - "Labor Status Form Adjusted For Supervisor") + def laborStatusFormAdjusted(self, link: str | None = None, newSupervisor: str | None = None): + self.link = link if newSupervisor: self.supervisorEmail = (Supervisor.get(Supervisor.ID == newSupervisor).EMAIL) - self.checkRecipient(False, - "Labor Status Form Adjusted For Supervisor") + self.checkRecipient("Labor Status Form Adjusted For Student", + "Labor Status Form Adjusted For Supervisor") def laborReleaseFormSubmitted(self, adminUserName=None, adminName=None): self.adminName = adminName self.adminEmail = adminUserName + "@berea.edu" emailTemplate = EmailTemplate.get(EmailTemplate.purpose == "Labor Release Form Admin Notification") - self.checkRecipient("Labor Release Form Submitted For Student", - "Labor Release Form Submitted For Supervisor") + self.checkRecipient(False, "Labor Release Form Submitted For Supervisor") self.sendEmail(emailTemplate, "admin") def laborReleaseFormApproved(self): @@ -401,7 +399,6 @@ def replaceText(self, form): form = form.replace("@@WLS@@", self.laborStatusForm.WLS) form = form.replace("@@Term@@", self.term.termName) form = form.replace("@@Admin@@", self.adminName) - if self.formHistory.rejectReason: form = form.replace("@@RejectReason@@", self.formHistory.rejectReason) if self.laborStatusForm.weeklyHours != None: @@ -430,3 +427,4 @@ def replaceText(self, form): if self.laborStatusForm.studentExpirationDate: # handle old cases where we might not have a date form = form.replace("@@StudentConfirmationExpiration@@", self.laborStatusForm.studentExpirationDate.strftime("%B %d, %Y")) return(form) + \ No newline at end of file diff --git a/app/logic/statusFormFunctions.py b/app/logic/statusFormFunctions.py index 4694bad63..92f4b71f6 100644 --- a/app/logic/statusFormFunctions.py +++ b/app/logic/statusFormFunctions.py @@ -78,6 +78,7 @@ def createOverloadFormAndFormHistory(rspFunctional, lsf, creatorID, host=None): email = emailHandler(formOverload.formHistoryID) link = makeThirdPartyLink("student", host, formOverload.formHistoryID) email.LaborOverLoadFormSubmitted(link) + formHistory = FormHistory.create( formID = lsf.laborStatusFormID, historyType = "Labor Status Form", @@ -280,8 +281,6 @@ def createOverloadForm(newWeeklyHours, lsf, currentUser, adjustedForm=None, for # This will delete an overload form after the hours are changed elif previousTotalHours > 15 and newTotalHours <= 15: # If we were overloading and now we aren't print(f"Trying to get formhistory with formID '{lsf.laborStatusFormID}' and history type: 'Labor Overload Form'") - # XXX this breaks if the overload was attached to a different form. ie, this form is the - # primary, but a secondary is what triggered the overload process deleteOverloadForm = FormHistory.get((FormHistory.formID == lsf.laborStatusFormID) & (FormHistory.historyType == "Labor Overload Form")) deleteOverloadForm = OverloadForm.get(OverloadForm.overloadFormID == deleteOverloadForm.overloadForm_id) deleteOverloadForm.delete_instance() # This line also deletes the Form History since it's set to cascade up in the model file diff --git a/app/logic/userInsertFunctions.py b/app/logic/userInsertFunctions.py index 5e77212d3..bd6995412 100644 --- a/app/logic/userInsertFunctions.py +++ b/app/logic/userInsertFunctions.py @@ -175,7 +175,7 @@ def createStudentFromTracy(username=None, bnumber=None): try: return Student.get(Student.ID == tracyStudent.ID.strip()) except DoesNotExist: - #print('Could not find {0} {1} in Student table, creating new entry.'.format(tracyStudent.FIRST_NAME, tracyStudent.LAST_NAME)) + print('Could not find {0} {1} in Student table, creating new entry.'.format(tracyStudent.FIRST_NAME, tracyStudent.LAST_NAME)) return Student.create(ID = tracyStudent.ID.strip(), PIDM = tracyStudent.PIDM, legal_name = tracyStudent.FIRST_NAME, diff --git a/app/logic/utils.py b/app/logic/utils.py index 9bb693958..bef144bc5 100644 --- a/app/logic/utils.py +++ b/app/logic/utils.py @@ -12,7 +12,8 @@ def makeThirdPartyLink(recipient, host, formHistoryId): route = "admin/financialAidOverloadApproval" if recipient == 'student': route = "studentOverloadApp" - + if recipient == "studentAdjustment": + route = "studentOverloadApp" return f"http://{host}/{route}/{formHistoryId}" def setReferrerPath(): diff --git a/app/models/adjustedForm.py b/app/models/adjustedForm.py index 3619bfceb..8abd831a3 100755 --- a/app/models/adjustedForm.py +++ b/app/models/adjustedForm.py @@ -8,3 +8,4 @@ class AdjustedForm(baseModel): oldValue = CharField() newValue = CharField() effectiveDate = DateField() + diff --git a/app/static/js/ckeditor/LICENSE.md b/app/static/js/ckeditor/LICENSE.md index 9ab2d1745..1c71a8768 100644 --- a/app/static/js/ckeditor/LICENSE.md +++ b/app/static/js/ckeditor/LICENSE.md @@ -179,8 +179,8 @@ above, provided that you also meet all of these conditions: a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but - does not normally print such an announcement, your work based on - the Program is not required to print an announcement.) + does not normally such an announcement, your work based on + the Program is not required to print an announcement.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, diff --git a/app/static/js/laborhistory.js b/app/static/js/laborhistory.js index cf6868bb8..adbdd89e7 100755 --- a/app/static/js/laborhistory.js +++ b/app/static/js/laborhistory.js @@ -18,7 +18,6 @@ $('#positionTable tbody tr td').on('click',function(){ function loadFormHistoryModal(formHistory) { $("#modal").modal("show"); $(".loader").show(); - // This function is called when the modal content is loaded $("#modal").find('.modal-content').load('/laborHistory/modal/' + formHistory, function() { $(".loader").fadeOut("slow"); // Hide the loader after content is loaded diff --git a/app/static/js/studentOverloadApp.js b/app/static/js/studentOverloadApp.js index 55559b042..2035c7f4a 100755 --- a/app/static/js/studentOverloadApp.js +++ b/app/static/js/studentOverloadApp.js @@ -50,8 +50,13 @@ function updateDatabase(overloadFormHistoryID){ url: "/studentOverloadApp/update/" + overloadFormHistoryID, method: "POST", data: {"overloadReason": $("#notes").val()}, - success: function(bnumber) { - window.location.replace("/laborHistory/" + bnumber); + success: function(resp) { + const bnumber = resp.bnumber; + if (bnumber) { + window.location.replace("/laborHistory/" + bnumber); + } else { + window.location.replace("/"); + } }, error: function(e) { console.log(e) diff --git a/app/templates/main/studentOverloadApp.html b/app/templates/main/studentOverloadApp.html index e9b90537a..3a0c604c3 100755 --- a/app/templates/main/studentOverloadApp.html +++ b/app/templates/main/studentOverloadApp.html @@ -13,9 +13,15 @@ Click to Skip {% include "snips/freshmanWarning.html" %} + + +
-

Labor Overload Application

+

Labor Overload Application

+ + +
@@ -27,46 +33,52 @@

Labor Overload Application

}
-

A labor overload is defined as more than 15 hours of labor per week during the regular academic year and may not be approved retroactively. All approvals are subject to periodic review. + +

A labor overload is defined as more than 15 hours of labor per week during the regular academic year and may not be approved retroactively. All approvals are subject to periodic review. Approval may be revoked if primary labor hours, labor performance, or academic performance become less than satisfactory.

+

* Marked fields are required

    +

    Guidelines for Approval:

    - + *
    - + *
    - + *
    - + *
    - + *
+ + {% if totalFormHours > 20 %}
    Requests for over 20 hours* are subject to the following additional guidelines:
    - + *
    - + *
{% endif %} +
@@ -95,6 +107,7 @@

Student Information

Requested Positions

+ @@ -109,10 +122,30 @@

Requested Positions

{% for primary in listPrimary %} - + - - + + {% endfor %} @@ -121,24 +154,51 @@

Requested Positions

{% for secondary in listSecondary %} - + - - + + {% endfor %} {% endfor %} - +
{{primary.formID.termCode.termName}}{{primary.formID.department.DEPT_NAME}} + {% if adjustedField == "department" %} + {{ oldValue }}
+ {{ newValue }} + {% else %} + {{primary.formID.department.DEPT_NAME}} + {% endif %} +
{{primary.formID.jobType}}{{primary.formID.POSN_TITLE}}{{primary.formID.weeklyHours}} + {% if adjustedField == "POSN_TITLE" %} + {{ oldValue }}
+ {{ newValue }} + {% else %} + {{primary.formID.POSN_TITLE}} + {% endif %} +
+ {% if adjustedField == "weeklyHours" %} + {{ oldValue }} {{ newValue }} + {% else %} + {{primary.formID.weeklyHours}} + {% endif %} + {{primary.status}}
{{secondary.formID.termCode.termName}}{{secondary.formID.department.DEPT_NAME}} + {% if adjustedField == "department" %} + {{ oldValue }}
+ {{ newValue }} + {% else %} + {{secondary.formID.department.DEPT_NAME}} + {% endif %} +
{{secondary.formID.jobType}}{{secondary.formID.POSN_TITLE}}{{secondary.formID.weeklyHours}} + {% if adjustedField == "POSN_TITLE" %} + {{ oldValue }}
+
{{ newValue }} + {% else %} + {{secondary.formID.POSN_TITLE}} + {% endif %} +
+ {% if adjustedField == "weeklyHours" %} + {{ oldValue }} {{ newValue }} + {% else %} + {{secondary.formID.weeklyHours}} + {% endif %} + {{secondary.status}}
Total Approved Primary and Secondary Hours:{{totalCurrentHours}} + {% if adjustedField == "weeklyHours" %} + {{ oldValue }} {{ newValue }} + {% else %} + {{totalCurrentHours}} + {% endif %} +
- + +
@@ -150,12 +210,15 @@

Requested Positions

- + + - {% with overloadForm=overloadForm %} + + {% with overloadHistory=overloadHistory%} {% include "snips/studentOverloadSubmit.html" %} {% include "snips/studentOverloadWithdraw.html" %} - {% endwith %} + {% endwith %} +
diff --git a/app/templates/snips/pendingOverloadModal.html b/app/templates/snips/pendingOverloadModal.html index fcca4282f..db7a9679c 100644 --- a/app/templates/snips/pendingOverloadModal.html +++ b/app/templates/snips/pendingOverloadModal.html @@ -44,6 +44,7 @@
diff --git a/app/templates/snips/studentOverloadWithdraw.html b/app/templates/snips/studentOverloadWithdraw.html index a782b9bb2..2b0b2e943 100644 --- a/app/templates/snips/studentOverloadWithdraw.html +++ b/app/templates/snips/studentOverloadWithdraw.html @@ -14,7 +14,7 @@
Withdrawing will remove the overload request. A supervisor or admini diff --git a/database/active_position_reader.py b/database/active_position_reader.py index d50d95cd3..e1c75d650 100644 --- a/database/active_position_reader.py +++ b/database/active_position_reader.py @@ -108,12 +108,14 @@ def print_banner_row(row): secondary_indexes = {} # store the secondary position indexes for each B# primary_indexes = {} # store the primary position indexes for each B# supervisor_cache = {} - print("Updating departments...") + get_all_departments() # initial pass, ensuring supervisors and students are in our database and collating student records print("Initial pass through the data...") + + for row in range(len(labor_data)): # clean some data. try not to repeat queries @@ -231,12 +233,12 @@ def print_banner_row(row): ########################################################################################### ### Secondaries ### ########################################################################################### +########################################################################################### print() print("Processing Secondary Records") print("--------------------------") for student_id, banner_forms in secondary_indexes.items(): - print() - print(f"Processing {student_id}") + lsf_forms = (LaborStatusForm.select().where( (LaborStatusForm.studentSupervisee_id == student_id) & diff --git a/database/base_data.py b/database/base_data.py index c081fb752..0ec43946e 100644 --- a/database/base_data.py +++ b/database/base_data.py @@ -256,6 +256,16 @@ "action":"Adjusted", "subject":"Labor Status Form Adjusted", "body":''' +

Dear @@Student@@,

+

 

+

An adjustment has been made to your Labor Status Form by @@Creator@@.

+

Please follow the attached link to verify information needed for the approval of an adjusted form: @@link@@

+

 

+

 

+

Sincerely,

+

Labor Program Office

+

labor_program@berea.edu

+

859-985-3611

''', "audience":"Student" }, @@ -265,6 +275,15 @@ "action":"Adjusted", "subject":"Labor Status Form Adjusted", "body":''' +

Dear @@Supervisor@@,

+

 

+

This is to infrom you that an adjustment has been made to your labor supervisee by @@Creator@@.

+

 

+

 

+

Sincerely,

+

Labor Program Office

+

labor_program@berea.edu

+

859-985-3611

''', "audience":"Supervisor" }, @@ -590,4 +609,5 @@ } ] EmailTemplate.insert_many(emailtemps).on_conflict_replace().execute() -print(" * emailtemplates added") \ No newline at end of file +print(" * emailtemplates added") + diff --git a/database/demo_data.py b/database/demo_data.py index 3bee9c07b..25467c88c 100644 --- a/database/demo_data.py +++ b/database/demo_data.py @@ -146,6 +146,21 @@ "STU_CPO":"420", "LAST_POSN":"TA", "LAST_SUP_PIDM":"7" + }, + { + "ID":"B00251260", + "PIDM":"5", + "FIRST_NAME":"Kafui", + "LAST_NAME":"Gle", + "CLASS_LEVEL":"Junior", + "ACADEMIC_FOCUS":"Computer Science", + "MAJOR":"Computer Science", + "PROBATION":"0", + "ADVISOR":"Scott Heggen", + "STU_EMAIL":"glek@berea.edu", + "STU_CPO":"420", + "LAST_POSN":"TA", + "LAST_SUP_PIDM":"7" } ] diff --git a/database/import-old-data/.DS_Store b/database/import-old-data/.DS_Store new file mode 100644 index 000000000..5008ddfcf Binary files /dev/null and b/database/import-old-data/.DS_Store differ diff --git a/db_test.py b/db_test.py index 2afe0f525..2487aff7b 100644 --- a/db_test.py +++ b/db_test.py @@ -65,6 +65,7 @@ cursor = b.conn.cursor() print(cursor) + # NOT FOR PROD # b.insert(FormHistory.get_by_id(39061)) diff --git a/scripts/addingTemplate.py b/scripts/addingTemplate.py index 6ea1131d1..894ad3b17 100644 --- a/scripts/addingTemplate.py +++ b/scripts/addingTemplate.py @@ -48,6 +48,33 @@ '

Sincerely,
Labor Program Office
labor_program@berea.edu
859-985-3611

' ), "audience":"Student", + }, + "studentWithdrawnNotification":{ + "purpose":'Labor Overload Form Withdrawn For Student', + "formType":"Labor Adjustment Form", + "action":"Withdrew", + "subject":'Labor Adjustment Form Withdrawn', + "body":( + '

Dear @@Student@@,

' + '

This email is to notify that the Labor Adjustment Form submitted by @@Creator@@ for you has been withdrawn.

' + '

 

' + '

Please notify your supervisor @@Supervisor@@ if you want to resubmit the form.

' + '

Sincerely,
Labor Program Office
labor_program@berea.edu
859-985-3611

' + ), + "audience":"Student", + }, + "supervisorWithdrawnNotification":{ + "purpose":'Labor Overload Form Withdrawn For Supervisor', + "formType":"Labor Adjustment Form", + "action":"Withdrew", + "subject":'Labor Adjustment Form Withdrawn', + "body":( + '

Dear @@Supervisor@@,

' + '

This email is to notify that the Labor Adjustment Form submitted by @@Creator@@ for @@Student@@ has been withdrawn.

' + '

 

' + '

Sincerely,
Labor Program Office
labor_program@berea.edu
859-985-3611

' + ), + "audience":"Supervisor", } } diff --git a/scripts/sync_preferred_name.py b/scripts/sync_preferred_name.py index 91999723d..68b95c982 100644 --- a/scripts/sync_preferred_name.py +++ b/scripts/sync_preferred_name.py @@ -43,7 +43,7 @@ def fetch_descriptions(conn, descriptions): conn.search('dc=berea,dc=edu', f"(&{letterQuery}{descriptionQuery})", attributes = ['samaccountname', 'givenname', 'sn', 'employeeid'] ) - print(f"Found {len(conn.entries)} {descriptions} {letters[0]}-{letters[-1]} in AD"); + print(f"Found {len(conn.entries)} {descriptions} {letters[0]}-{letters[-1]} in AD") people += conn.entries return people diff --git a/tests/mail_test.py b/tests/mail_test.py index ca5478670..d9d4537b7 100644 --- a/tests/mail_test.py +++ b/tests/mail_test.py @@ -5,7 +5,7 @@ with app.app_context(): msg = Message("Test Email", recipients=["laborstatusform-aaaaazdxy4j2lsinl67ikumy4y@studentprogrammers.slack.com"],html="

Test

Whooo",sender="support@bereacollege.onmicrosoft.com") mail = Mail(app) - print("Sending") mail.send(msg) print("Sent") +