@ -21,7 +21,7 @@ import subprocess
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
from library . interface . form import FormWindow , BookForm , LoginForm , SuccessForm , catch_error_with , error_form
from library . emails import format_reminder_email
# email testing folder creation
@ -35,10 +35,17 @@ 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 " ]
labels = [ " Days (default " + str ( DEFAULT_DAY_VALUE ) + " ) " ]
def _return_values ( self ) :
return self . entries [ 0 ] . value
ret = self . entries [ 0 ] . value
if ret is " " :
return DEFAULT_DAY_VALUE
else :
#If we didn't get valid input, noisily fail
assert ret . isdigit ( ) and int ( ret ) > 0 , \
" Max signed out days is not positive: " + ret . __repr__ ( )
return int ( ret )
class NameForm ( FormWindow ) :
@ -50,6 +57,7 @@ class NameForm(FormWindow):
if self . entries [ 0 ] . value is " " :
return " Librarian "
else :
assert self . entries [ 0 ] . value . isprintable ( )
return self . entries [ 0 ] . value
@ -58,13 +66,15 @@ def _send_email(quest_id: str,
signed_out_date : str ,
max_days_can_be_signed_out : int ,
librarian_name : str ,
book_name_list ,
testing = False ) - > None :
book_name : str ,
testing = False ) - > bool :
""" 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 .
Set testing to true to output emails to the current directory , instead
of actually sending them .
Returns whether an email was sent .
"""
# Determine the days the book has been signed out
@ -72,15 +82,22 @@ def _send_email(quest_id: str,
days_signed_out = ( d . today ( ) - d ) . days
if days_signed_out > max_days_can_be_signed_out :
email_address = quest_id + " @csclub.uwaterloo.ca "
book_name = " " . join ( book_name_list )
#Librarian also gets a copy, so that unreplied emails are more apparent.
email_addresses = [ quest_id + " @csclub.uwaterloo.ca " ,
" librarian@csclub.uwaterloo.ca " ]
email_message = format_reminder_email ( quest_id , days_signed_out , librarian_name , book_name )
if testing :
assert book_name . isprintable ( )
os . makedirs ( " test_emails " , exist_ok = True )
with open ( " test_emails/ {} _ {} .txt " . format ( quest_id , book_name ) , ' w ' ) as f :
#Know that book titles such as "FORTRAN/77" exist, sanitize slashes.
filename = ( " test_emails/ {} _ {} .txt "
. format ( quest_id , book_name . replace ( ' / ' , ' - ' ) ) )
with open ( filename , ' w ' ) as f :
print ( email_message , file = f )
else :
server . sendmail ( " librarian@csclub.uwaterloo.ca " , email_address , email_message )
server . sendmail ( " librarian@csclub.uwaterloo.ca " , email_addresses , email_message )
return True
return False
#Public functions
@ -100,17 +117,17 @@ def sendemails_procedure(w, hb, cy, cx, mx):
w . mvwin ( cy - r / / 2 , cx - c / / 2 )
days = step1 . event_loop ( )
step1 . clear ( )
if days == { } :
return " " #User cancelled the dialog box
#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
#Get the name of the librarian
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 ( )
if librarianName == { } :
return " " #User cancelled the dialog box
#Set up email
global server
@ -149,18 +166,53 @@ def sendemails_procedure(w, hb, cy, cx, mx):
#Get the books that are signed out
signed_out_books = db . get_checkedout_books ( )
earliest_checkout = datetime . now ( )
emails_sent = 0
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 ,
testing )
if _send_email (
str ( quest_id ) ,
str ( date ) , days , str ( librarianName ) , book_name_with_spaces ,
testing ) :
emails_sent + = 1
earliest_checkout = min ( earliest_checkout ,
datetime . strptime ( date , " % Y- % m- %d " ) )
#Exit from the email server
server . quit ( )
#A success message about what emails we just sent
assert earliest_checkout > datetime ( 2010 , 1 , 1 )
time_since_earliest_checkout = ' '
days_since_earliest_checkout = ( datetime . now ( ) . toordinal ( )
- earliest_checkout . toordinal ( ) )
assert days > = 0
month_length = 30 #It's wrong because it's an approximation
if days_since_earliest_checkout < month_length :
time_since_earliest_checkout = ' {} days ago ' . format ( days_since_earliest_checkout )
else :
time_since_earliest_checkout = ' {:0.1f} months ago ' . format ( days_since_earliest_checkout / month_length )
if emails_sent == 0 :
success_text = """
No books are overdue , so all zero reminder emails were vacuously sent successfully .
Either members are ( hopefully ) good at returning books or members don ' t take books out. " " "
else :
success_text = """
Automatically sent out { } individual reminder emails , one per overdue book .
The most overdue book was checked out approximately { } . """ .format(
emails_sent ,
time_since_earliest_checkout
)
step4 = SuccessForm ( w , hb , success_text , width = mx - 20 )
( r , c ) = w . getmaxyx ( )
w . mvwin ( cy - r / / 2 , cx - c / / 2 )
step4 . event_loop ( )
step4 . clear ( )
return " "