Added ability for libcom to send emails to those with overdue books
This commit is contained in:
parent
8e592f3adc
commit
6ed3c35554
|
@ -6,6 +6,7 @@ import library.interface.browser as browser
|
||||||
import library.interface.form as form
|
import library.interface.form as form
|
||||||
import library.interface.help_bar as helpBar
|
import library.interface.help_bar as helpBar
|
||||||
import library.interface.checkout as co
|
import library.interface.checkout as co
|
||||||
|
import library.interface.sendemails as sendemails
|
||||||
|
|
||||||
from library import book_data
|
from library import book_data
|
||||||
|
|
||||||
|
@ -186,6 +187,11 @@ def catMenu():
|
||||||
cat.eventLoop()
|
cat.eventLoop()
|
||||||
cat.clear()
|
cat.clear()
|
||||||
|
|
||||||
|
def email_menu():
|
||||||
|
w=curses.newwin(1,1)
|
||||||
|
(my,mx)=stdscr.getmaxyx()
|
||||||
|
sendemails.sendemails_procedure(w,hb,my//2,mx//2,mx)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
db.initializeDatabase()
|
db.initializeDatabase()
|
||||||
|
@ -197,6 +203,7 @@ if __name__ == "__main__":
|
||||||
("",exit),
|
("",exit),
|
||||||
("Check Out a Book", co_menu),
|
("Check Out a Book", co_menu),
|
||||||
("Return a Book", return_menu),
|
("Return a Book", return_menu),
|
||||||
|
("Send Overdue Email Reminders", email_menu),
|
||||||
("",exit),
|
("",exit),
|
||||||
("View Checked Out Books", checkedout_menu),
|
("View Checked Out Books", checkedout_menu),
|
||||||
("View On Shelf Books", onshelf_menu),
|
("View On Shelf Books", onshelf_menu),
|
||||||
|
|
|
@ -107,6 +107,13 @@ class TextEntry:
|
||||||
self.value = self.value[:c] + self.value[c+1:]
|
self.value = self.value[:c] + self.value[c+1:]
|
||||||
self._mv_cursor(0)
|
self._mv_cursor(0)
|
||||||
|
|
||||||
|
class PasswordEntry(TextEntry):
|
||||||
|
def redraw(self):
|
||||||
|
self.w.addnstr(self.y,self.x, " "*self.width, self.width)
|
||||||
|
if self.focus:
|
||||||
|
self.w.chgat(self.y, self.x, self.width, curses.A_UNDERLINE)
|
||||||
|
curses.curs_set(1)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class FormWindow:
|
class FormWindow:
|
||||||
|
@ -276,8 +283,18 @@ class FormWindow:
|
||||||
if self.bt==-1:
|
if self.bt==-1:
|
||||||
self.entries[self.hl].handle_input(ch)
|
self.entries[self.hl].handle_input(ch)
|
||||||
|
|
||||||
|
class LoginForm(FormWindow):
|
||||||
|
caption = "Enter your csclub login to access the mail server"
|
||||||
|
blabel = "Login"
|
||||||
|
labels = ["ID", "Password"]
|
||||||
|
|
||||||
|
def _make_entries(self):
|
||||||
|
self.entries = []
|
||||||
|
self.entries.append(TextEntry(self.w))
|
||||||
|
self.entries.append(PasswordEntry(self.w))
|
||||||
|
|
||||||
|
def _return_values(self):
|
||||||
|
return [self.entries[0].value, self.entries[1].value]
|
||||||
|
|
||||||
class BookForm(FormWindow):
|
class BookForm(FormWindow):
|
||||||
caption = "Add a Book"
|
caption = "Add a Book"
|
||||||
|
|
|
@ -0,0 +1,148 @@
|
||||||
|
#Import smtp for the email sending function
|
||||||
|
import smtplib
|
||||||
|
|
||||||
|
#Import argparse to parse command line args
|
||||||
|
import argparse
|
||||||
|
|
||||||
|
#Import sys so that we can exit when given bad command line args
|
||||||
|
import sys
|
||||||
|
|
||||||
|
#Import datetime and time to check dates
|
||||||
|
from datetime import date
|
||||||
|
import time
|
||||||
|
|
||||||
|
#Import getpass for password input
|
||||||
|
import getpass
|
||||||
|
|
||||||
|
#Import subprocess for validation of if a user is in a group
|
||||||
|
import subprocess
|
||||||
|
|
||||||
|
#Import librarian permissions
|
||||||
|
from library import permissions
|
||||||
|
from library.exceptions import *
|
||||||
|
import library.database as db
|
||||||
|
from library.interface.form import FormWindow,BookForm, LoginForm, catch_error_with, error_form
|
||||||
|
|
||||||
|
#Constants
|
||||||
|
DEFAULT_DAY_VALUE = 21
|
||||||
|
MAX_LOGIN_ATTEMPTS = 3
|
||||||
|
|
||||||
|
class DaysForm(FormWindow):
|
||||||
|
caption = "Enter the max number of days a book can be signed out for"
|
||||||
|
blabel = "Enter"
|
||||||
|
labels = ["Days"]
|
||||||
|
|
||||||
|
def _return_values(self):
|
||||||
|
return self.entries[0].value
|
||||||
|
|
||||||
|
|
||||||
|
class NameForm(FormWindow):
|
||||||
|
caption = "Enter the name you want in the signature line for the email"
|
||||||
|
blabel = "Enter"
|
||||||
|
labels = ["Name"]
|
||||||
|
|
||||||
|
def _return_values(self):
|
||||||
|
if self.entries[0].value is "":
|
||||||
|
return "Librarian"
|
||||||
|
else:
|
||||||
|
return self.entries[0].value
|
||||||
|
|
||||||
|
#Private functions
|
||||||
|
def _send_email(quest_id: str, signed_out_date: str, max_days_can_be_signed_out:int, librarianName: str, book_name_list) -> None:
|
||||||
|
"""Sends an email to quest_id@csclub.uwaterloo.ca if the date the book
|
||||||
|
was signed out exceeds the days it is supposed to be signed out for"""
|
||||||
|
|
||||||
|
email_address = quest_id + "@csclub.uwaterloo.ca"
|
||||||
|
book_name = "".join(book_name_list)
|
||||||
|
|
||||||
|
#Determine the days the book has been signed out
|
||||||
|
date_tokens = signed_out_date.split("-")
|
||||||
|
date_signed_out = date(int(date_tokens[0]), int(date_tokens[1]), int(date_tokens[2]))
|
||||||
|
days_signed_out = date.fromtimestamp(time.time()) - date_signed_out
|
||||||
|
|
||||||
|
if days_signed_out.days > max_days_can_be_signed_out:
|
||||||
|
email_message_body = ("Hi " + quest_id + ",\n\n" + "Our records indicate that you "
|
||||||
|
"have had the book " + book_name + " signed out for " + str(str(days_signed_out).split(" ")[:1])[2:-2] +
|
||||||
|
" days.\n\n" + "Please return the book to the CS Club office "
|
||||||
|
"(MC 3036) at your earliest convenience.\n\n" + "Thanks,\n\n" +
|
||||||
|
librarianName + "\n" + "Computer Science Club | University of Waterloo\n"
|
||||||
|
"librarian@csclub.uwaterloo.ca")
|
||||||
|
|
||||||
|
email_message_subject = "Overdue book: {}".format(book_name)
|
||||||
|
email_message = "Subject: {}\n\n{}".format(email_message_subject, email_message_body)
|
||||||
|
|
||||||
|
server.sendmail("librarian@csclub.uwaterloo.ca", email_address, email_message)
|
||||||
|
|
||||||
|
#Public functions
|
||||||
|
@permissions.check_permissions(permissions.PERMISSION_LIBCOM)
|
||||||
|
@catch_error_with(lambda w, hb, *args : (w, hb, None))
|
||||||
|
def sendemails_procedure(w, hb, cy, cx, mx):
|
||||||
|
"""Procedure to send emails to those with overdue books
|
||||||
|
|
||||||
|
w: ncurses window for the routine
|
||||||
|
cy,cx: centre coordinates of the screen
|
||||||
|
mx: max width of screen
|
||||||
|
"""
|
||||||
|
|
||||||
|
#Get the max days a book can be signed out for
|
||||||
|
step1 = DaysForm(w,hb,width=mx-20)
|
||||||
|
(r,c)=w.getmaxyx()
|
||||||
|
w.mvwin(cy-r//2,cx-c//2)
|
||||||
|
days = step1.event_loop()
|
||||||
|
step1.clear()
|
||||||
|
|
||||||
|
#Set the days to a default value if the value given is less than 0
|
||||||
|
if not isinstance(days, int) or days < 0:
|
||||||
|
days = DEFAULT_DAY_VALUE
|
||||||
|
|
||||||
|
#Get the name of the librarain
|
||||||
|
step2 = NameForm(w,hb,width=mx-20)
|
||||||
|
(r,c)=w.getmaxyx()
|
||||||
|
w.mvwin(cy-r//2,cx-c//2)
|
||||||
|
librarianName = step2.event_loop()
|
||||||
|
step2.clear()
|
||||||
|
|
||||||
|
#Set up email
|
||||||
|
global server
|
||||||
|
server = smtplib.SMTP_SSL("mail.csclub.uwaterloo.ca", 465)
|
||||||
|
|
||||||
|
#Attempt to login 3 times
|
||||||
|
loginAttempts = 0
|
||||||
|
while loginAttempts < MAX_LOGIN_ATTEMPTS:
|
||||||
|
|
||||||
|
#Get the user's login info to login to the mail server
|
||||||
|
step3 = LoginForm(w,hb,width=mx-20)
|
||||||
|
(r,c)=w.getmaxyx()
|
||||||
|
w.mvwin(cy-r//2,cx-c//2)
|
||||||
|
loginInfo = step3.event_loop()
|
||||||
|
step3.clear()
|
||||||
|
|
||||||
|
csclubID = loginInfo[0]
|
||||||
|
csclubPwd = loginInfo[1]
|
||||||
|
|
||||||
|
try:
|
||||||
|
server.login(csclubID, csclubPwd)
|
||||||
|
break
|
||||||
|
except smtplib.SMTPAuthenticationError:
|
||||||
|
loginAttempts += 1
|
||||||
|
|
||||||
|
#Check to see if login failed
|
||||||
|
if loginAttempts >= MAX_LOGIN_ATTEMPTS:
|
||||||
|
return ""
|
||||||
|
|
||||||
|
#Get the books that are signed out
|
||||||
|
signed_out_books = db.get_checkedout_books()
|
||||||
|
|
||||||
|
for data in signed_out_books:
|
||||||
|
quest_id = data["uwid"]
|
||||||
|
date = str(data["date"]).split(" ")[0]
|
||||||
|
book_name_with_spaces = data["title"]
|
||||||
|
|
||||||
|
_send_email(str(quest_id), str(date), days, str(librarianName), book_name_with_spaces)
|
||||||
|
|
||||||
|
#Exit from the email server
|
||||||
|
server.quit()
|
||||||
|
|
||||||
|
return ""
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue