Added ability for libcom to send emails to those with overdue books

This commit is contained in:
Connor Murphy 2017-02-03 23:55:51 -05:00 committed by Charlie Wang
parent 8e592f3adc
commit 6ed3c35554
3 changed files with 172 additions and 0 deletions

View File

@ -6,6 +6,7 @@ import library.interface.browser as browser
import library.interface.form as form
import library.interface.help_bar as helpBar
import library.interface.checkout as co
import library.interface.sendemails as sendemails
from library import book_data
@ -186,6 +187,11 @@ def catMenu():
cat.eventLoop()
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__":
db.initializeDatabase()
@ -197,6 +203,7 @@ if __name__ == "__main__":
("",exit),
("Check Out a Book", co_menu),
("Return a Book", return_menu),
("Send Overdue Email Reminders", email_menu),
("",exit),
("View Checked Out Books", checkedout_menu),
("View On Shelf Books", onshelf_menu),

View File

@ -107,6 +107,13 @@ class TextEntry:
self.value = self.value[:c] + self.value[c+1:]
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:
@ -276,8 +283,18 @@ class FormWindow:
if self.bt==-1:
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):
caption = "Add a Book"

View File

@ -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 ""