From 8d5c03ab218f513446cb9ee519c414c5f6ba2c26 Mon Sep 17 00:00:00 2001 From: smarota17 Date: Thu, 10 Nov 2022 16:53:05 -0500 Subject: [PATCH 01/15] Initial email functionality --- src/account.py | 45 +++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 41 insertions(+), 4 deletions(-) diff --git a/src/account.py b/src/account.py index fe06f17..5a04ffa 100644 --- a/src/account.py +++ b/src/account.py @@ -1,13 +1,16 @@ from account_info import AccountInfo +from datetime import datetime, timedelta +import smtplib, ssl class Account(): - def __init__(self, name='', surname='', birthday='', interests='', wishlist='', friendlist='', ID = None): + def __init__(self, name='', surname='', birthday='', email='', interests='', wishlist='', friendlist='', ID = None): if ID != None: accountMan = AccountInfo() info = accountMan.get_info(ID) self.name = info['Name'] self.surname = info['Surname'] self.birthday = info['Birthday'] + self.email = info['Email'] self.interests = info['Interests'] self.wishlist = info['WishList'] self.friendlist = info['FriendList'] @@ -16,6 +19,7 @@ def __init__(self, name='', surname='', birthday='', interests='', wishlist='', self.name = name self.surname = surname self.birthday = birthday + self.email = email self.interests = interests self.wishlist = wishlist self.friendlist = friendlist @@ -24,23 +28,56 @@ def __init__(self, name='', surname='', birthday='', interests='', wishlist='', def create_account(self): accountMan = AccountInfo() - acc = accountMan.create_account(self.name, self.surname, self.birthday, self.interests, self.wishlist, self.friendlist) + acc = accountMan.create_account(self.name, self.surname, self.birthday, self.email, self.interests, self.wishlist, self.friendlist) return acc def view_account(self): accountMan = AccountInfo() return accountMan.get_info(self.ID) - def update_account(self, name='', surname='', birthday='', interests='', wishlist='', friendlist=''): + def update_account(self, name='', surname='', birthday='', email='', interests='', wishlist='', friendlist=''): self.name = name self.surname = surname self.birthday = birthday + self.email = email self.interests = interests self.wishlist = wishlist self.friendlist = friendlist accountMan = AccountInfo() - accountMan.update_account(self.ID, name, surname, birthday, interests, wishlist, friendlist) + accountMan.update_account(self.ID, name, surname, birthday, email, interests, wishlist, friendlist) + def send_reminder_email(self): + accountMan = AccountInfo() + info = accountMan.get_info(self.ID) + + name = info['Name'] + birthday = info['Birthday'] + email = info['Email'] + friendlist = info['FriendList'] + wishlist = info['WishList'] + + birthdayDate = datetime.datetime.strptime(birthday, "%Y-%m-%d").date() + currDate = datetime.datetime.now().date() + if (birthdayDate - currDate).days < 7: + port = 587 + smtp_server = "outlook.office365.com" + sender_email = email + password = 'G1ft3r#212!' + for friend in friendlist: + receiver_email = friend.email + message = """\ + Subject: """ + name + """'s Birthday is Coming Up + + Buy """ + name + """ the perfect gift for their birthday on """ + birthday + """. + Here are some items on their wishlist: """ + wishlist + """.""" + + context = ssl.create_default_context() + with smtplib.SMTP(smtp_server, port) as server: + server.ehlo() # Can be omitted + server.starttls(context=context) + server.ehlo() # Can be omitted + server.login(sender_email, password) + server.sendmail(sender_email, receiver_email, message) # #acc = Account('Hannah', 'Montana', '05/05/1995', 'Singing, Dancing') From ca7bb8a9f44e3fba17003fad96e0ddcd1c637d92 Mon Sep 17 00:00:00 2001 From: smarota17 Date: Thu, 17 Nov 2022 10:06:42 -0500 Subject: [PATCH 02/15] Updated front/backend to reflect new email parameter. --- data/people_data-copy.csv | 28 ++++++++++++++-------------- src/account_info.py | 19 +++++++++++++++---- src/main.py | 19 +++++++++++++------ 3 files changed, 42 insertions(+), 24 deletions(-) diff --git a/data/people_data-copy.csv b/data/people_data-copy.csv index 771f2df..5f1163f 100644 --- a/data/people_data-copy.csv +++ b/data/people_data-copy.csv @@ -1,14 +1,14 @@ -ID,Name,Surname,Birthday,Interests,WishList,FriendList -1,Yagmur Basak,Bayraktar,12/17/1994,"Dancing, Reading, Cooking, Ballet, Writing","3,5,11,17"," 5, 6, 2, 3" -2,Murat,Bayraktar,9/7/1995,"Basketball, Football, Electronics","3,5",1 -3,Jane,Doe,1/1/2000,"Hiking, Climbing, Bungee Jumping",5,4 -4,John,Doe,2/2/1999,"Painting, Sculpting, Art","2, 3","3, 5" -5,David,Beckham,10/30/2012,"Cookie Art, Puzzles, Pets","1, 4, 2","1, 3" -6,Albert,Einstein,2/5/2004,"Zodiac Signs, Fitness",,4 -7,Taylor,Swift,5/28/1957,"Weaving, DIY, Painting",6, -8,Bruce,Wayne,10/10/2010,Technology,, -9,Tinker,Bell,4/4/2000,Spellcasting,"8, 1, 4, 9, 10",5 -10,Jon,Snow,12/31/1992,,"6, 9,10", -11,Hannah,Montana,5/5/1995,"Singing, Dancing",, -12,Barney,Stinson,8/25/1972,"Women, suits",14,1 -13,Michael Gary,Scott,5/5/1965,"Spy Novels, Improvisation, Sales",, +ID,Name,Surname,Birthday,Email,Interests,WishList,FriendList +1,Yagmur Basak,Bayraktar,12/17/1994,testy@gmail.com,"Dancing, Reading, Cooking, Ballet, Writing","3,5,11,17"," 5, 6, 2, 3" +2,Murat,Bayraktar,9/7/1995,test@gmail.com,"Basketball, Football, Electronics","3,5",1 +3,Jane,Doe,1/1/2000,test@gmail.com,"Hiking, Climbing, Bungee Jumping",5,4 +4,John,Doe,2/2/1999,test@gmail.com,"Painting, Sculpting, Art","2, 3","3, 5" +5,David,Beckham,10/30/2012,test@gmail.com,"Cookie Art, Puzzles, Pets","1, 4, 2","1, 3" +6,Albert,Einstein,2/5/2004,test@gmail.com,"Zodiac Signs, Fitness",,4 +7,Taylor,Swift,5/28/1957,test@gmail.com,"Weaving, DIY, Painting",6, +8,Bruce,Wayne,10/10/2010,test@gmail.com,Technology,, +9,Tinker,Bell,4/4/2000,test@gmail.com,Spellcasting,"8, 1, 4, 9, 10",5 +10,Jon,Snow,12/31/1992,test@gmail.com,,"6, 9,10", +11,Hannah,Montana,5/5/1995,test@gmail.com,"Singing, Dancing",, +12,Barney,Stinson,8/25/1972,test@gmail.com,"Women, suits",14,1 +13,Michael Gary,Scott,5/5/1965,test@gmail.com,"Spy Novels, Improvisation, Sales",, diff --git a/src/account_info.py b/src/account_info.py index 59e5660..663f850 100644 --- a/src/account_info.py +++ b/src/account_info.py @@ -11,7 +11,7 @@ def __init__(self): self.database = '../data/people_data-copy.csv' self.data = pd.read_csv(self.database) - def create_account(self, name, surname='', birthday='', interests='', wishlist='', friendlist=''): + def create_account(self, name, surname='', birthday='', email='', interests='', wishlist='', friendlist=''): id_list = sorted(self.data.ID.tolist(), reverse=True) lastID = id_list[0] if name == '': @@ -23,6 +23,7 @@ def create_account(self, name, surname='', birthday='', interests='', wishlist=' 'Name': name, 'Surname': surname, 'Birthday': birthday, + 'Email': email, 'Interests': interests, 'WishList': wishlist, 'FriendList': friendlist @@ -32,7 +33,7 @@ def create_account(self, name, surname='', birthday='', interests='', wishlist=' print('Account created successfully!') return self.data[self.data['ID']==lastID+1] - def update_account(self, ID, name='', surname='', birthday='', interests='', wishlist='', friendlist=''): + def update_account(self, ID, name='', surname='', birthday='', email='', interests='', wishlist='', friendlist=''): id_list = self.data.ID.tolist() if ID not in id_list: print('User ID:', ID, 'is not in the database!!!') @@ -57,6 +58,7 @@ def update_account(self, ID, name='', surname='', birthday='', interests='', wis 'Name': name, 'Surname': surname, 'Birthday': birthday, + 'Email': email, 'Interests': interests, 'WishList': wishlist, 'FriendList': friendlist @@ -67,6 +69,7 @@ def update_account(self, ID, name='', surname='', birthday='', interests='', wis self.data.loc[index,'Name'] = name self.data.loc[index, 'Surname'] = surname self.data.loc[index, 'Birthday'] = birthday + self.data.loc[index, 'Email'] = email self.data.loc[index, 'Interests'] = interests self.data.loc[index, 'WishList'] = wishlist print(type(wishlist)) @@ -131,6 +134,14 @@ def get_birthday(self, ID): else: result_dict = result.values return result_dict[0][3] + + # def get_email(self, ID): + # result, flag = self.search_ID(ID) + # if flag == -1: + # return -1 + # else: + # result_dict = result.values + # return result_dict[0][4] def get_interests(self, ID): result, flag = self.search_ID(ID) @@ -146,8 +157,8 @@ def get_wishlist(self, ID): return -1 else: result_dict = result.values - return result_dict[0][5] - + return result_dict[0][6] + def add_wishlist(self, ID, items): flag = 1 wl_str = self.get_wishlist(ID) diff --git a/src/main.py b/src/main.py index 463ee4b..ce45923 100644 --- a/src/main.py +++ b/src/main.py @@ -35,10 +35,11 @@ def create_account(): name = form.text_input('Name:') surname = form.text_input('Surname:') birthday = form.text_input('Birthday (MM/DD/YYYY):') + email = form.text_input('Email:') interest = form.text_input('Interests (please enter them comma seperated):') but1 = form.form_submit_button('Submit') if but1: - acc = Account(name, surname, birthday, interest) + acc = Account(name, surname, birthday, email, interest) acc = Account(ID = int(acc.ID)) st.session_state.runpage = 'account' st.session_state.account = acc @@ -70,6 +71,7 @@ def profile_page(): st.write('Name: ' + acc.name.to_string(index=False)) st.write('Surname: ' + acc.surname.to_string(index=False)) st.write('Birthday: ' + acc.birthday.to_string(index=False)) + st.write('Email: ' + acc.email.to_string(index=False)) st.write('Interests: ' + (acc.interests.to_string(index=False)).replace("\"", "")) if st.button("Edit Profile"): st.session_state.runpage = 'editprofile' @@ -85,10 +87,11 @@ def editprofile_page(): name = form.text_input('Name:', value= acc.name.to_string(index=False), placeholder= acc.name.to_string(index=False)) surname = form.text_input('Surname:', value= acc.surname.to_string(index=False), placeholder= acc.surname.to_string(index=False)) birthday = form.text_input('Birthday:', value= acc.birthday.to_string(index=False), placeholder= acc.birthday.to_string(index=False)) + email = form.text_input('Email:', value= acc.email.to_string(index=False), placeholder= acc.email.to_string(index=False)) ints = (acc.interests.to_string(index=False)).replace("\"", "") interests = form.text_input('Interest:', value=ints, placeholder=ints) if form.form_submit_button('Update'): - acc.update_account(name, surname, birthday, interests, acc.wishlist.to_string(index=False), acc.friendlist.to_string(index=False)) + acc.update_account(name, surname, birthday, email, interests, acc.wishlist.to_string(index=False), acc.friendlist.to_string(index=False)) acc = Account(ID = int(acc.ID)) st.session_state.account = acc st.session_state.runpage = 'profile' @@ -141,6 +144,7 @@ def additem_page(): a_name = acc.name.to_string(index=False) a_surname = acc.surname.to_string(index=False) a_birthday = acc.birthday.to_string(index=False) + a_email = acc.email.to_string(index=False) a_interests = acc.interests.to_string(index=False) a_wishlist = acc.wishlist.to_string(index=False) a_friendlist = acc.friendlist.to_string(index=False) @@ -148,7 +152,7 @@ def additem_page(): a_wishlist = str(i.itemID) else: a_wishlist += "," + str(i.itemID) - acc.update_account(a_name, a_surname, a_birthday, a_interests, a_wishlist, a_friendlist) + acc.update_account(a_name, a_surname, a_birthday, a_email, a_interests, a_wishlist, a_friendlist) acc = Account(ID = int(acc.ID)) st.session_state.account = acc st.session_state.runpage = 'wishlist' @@ -189,6 +193,7 @@ def deleteitem_page(): a_name = acc.name.to_string(index=False) a_surname = acc.surname.to_string(index=False) a_birthday = acc.birthday.to_string(index=False) + a_email = acc.email.to_string(index=False) a_interests = acc.interests.to_string(index=False) a_wishlist = acc.wishlist.to_string(index=False) a_friendlist = acc.friendlist.to_string(index=False) @@ -197,7 +202,7 @@ def deleteitem_page(): a_wishlist.remove(str(i.itemID)) a_wishlist = ','.join(a_wishlist) - acc.update_account(a_name, a_surname, a_birthday, a_interests, a_wishlist, a_friendlist) + acc.update_account(a_name, a_surname, a_birthday, a_email, a_interests, a_wishlist, a_friendlist) acc = Account(ID = int(acc.ID)) st.session_state.account = acc st.session_state.runpage = 'wishlist' @@ -268,10 +273,11 @@ def addfriend_page(): a_name = acc.name.to_string(index=False) a_surname = acc.surname.to_string(index=False) a_birthday = acc.birthday.to_string(index=False) + a_email = acc.email.to_string(index=False) a_interests = acc.interests.to_string(index=False) a_wishlist = acc.wishlist.to_string(index=False) a_friendlist = acc.friendlist.to_string(index=False) - acc.update_account(a_name, a_surname, a_birthday, a_interests, a_wishlist, friendlist) + acc.update_account(a_name, a_surname, a_birthday, a_email, a_interests, a_wishlist, friendlist) acc = Account(ID = int(acc.ID)) st.session_state.account = acc st.session_state.runpage = 'friendlist' @@ -291,6 +297,7 @@ def deletefriend_page(): a_name = acc.name.to_string(index=False) a_surname = acc.surname.to_string(index=False) a_birthday = acc.birthday.to_string(index=False) + a_email = acc.email.to_string(index=False) a_interests = acc.interests.to_string(index=False) a_wishlist = acc.wishlist.to_string(index=False) a_friendlist = acc.friendlist.to_string(index=False) @@ -298,7 +305,7 @@ def deletefriend_page(): friends.remove(id) friends = ','.join(friends) - acc.update_account(a_name, a_surname, a_birthday, a_interests, a_wishlist, friends) + acc.update_account(a_name, a_surname, a_birthday, a_email, a_interests, a_wishlist, friends) acc = Account(ID = int(acc.ID)) st.session_state.account = acc st.session_state.runpage = 'friendlist' From 71991ba4cb5d6f55d8c02c057507ea91f925e592 Mon Sep 17 00:00:00 2001 From: smarota17 Date: Thu, 17 Nov 2022 11:18:10 -0500 Subject: [PATCH 03/15] Update account to have email notification param. --- data/people_data-copy.csv | 28 ++++++++--------- src/account.py | 66 +++++++++++++++++++++++---------------- src/main.py | 22 ++++++++----- 3 files changed, 68 insertions(+), 48 deletions(-) diff --git a/data/people_data-copy.csv b/data/people_data-copy.csv index 5f1163f..df181cc 100644 --- a/data/people_data-copy.csv +++ b/data/people_data-copy.csv @@ -1,14 +1,14 @@ -ID,Name,Surname,Birthday,Email,Interests,WishList,FriendList -1,Yagmur Basak,Bayraktar,12/17/1994,testy@gmail.com,"Dancing, Reading, Cooking, Ballet, Writing","3,5,11,17"," 5, 6, 2, 3" -2,Murat,Bayraktar,9/7/1995,test@gmail.com,"Basketball, Football, Electronics","3,5",1 -3,Jane,Doe,1/1/2000,test@gmail.com,"Hiking, Climbing, Bungee Jumping",5,4 -4,John,Doe,2/2/1999,test@gmail.com,"Painting, Sculpting, Art","2, 3","3, 5" -5,David,Beckham,10/30/2012,test@gmail.com,"Cookie Art, Puzzles, Pets","1, 4, 2","1, 3" -6,Albert,Einstein,2/5/2004,test@gmail.com,"Zodiac Signs, Fitness",,4 -7,Taylor,Swift,5/28/1957,test@gmail.com,"Weaving, DIY, Painting",6, -8,Bruce,Wayne,10/10/2010,test@gmail.com,Technology,, -9,Tinker,Bell,4/4/2000,test@gmail.com,Spellcasting,"8, 1, 4, 9, 10",5 -10,Jon,Snow,12/31/1992,test@gmail.com,,"6, 9,10", -11,Hannah,Montana,5/5/1995,test@gmail.com,"Singing, Dancing",, -12,Barney,Stinson,8/25/1972,test@gmail.com,"Women, suits",14,1 -13,Michael Gary,Scott,5/5/1965,test@gmail.com,"Spy Novels, Improvisation, Sales",, +ID,Name,Surname,Birthday,Email,Notifications,Interests,WishList,FriendList +1,Yagmur Basak,Bayraktar,12/17/1994,smarota1861@gmail.com,On,"Dancing, Reading, Cooking, Ballet, Writing","3,5,11,17"," 5, 6, 2, 3" +2,Murat,Bayraktar,9/7/1995,smarota1861@gmail.com,Off,"Basketball, Football, Electronics","3,5",1 +3,Jane,Doe,1/1/2000,smarota1861@gmail.com,On,"Hiking, Climbing, Bungee Jumping",5,4 +4,John,Doe,2/2/1999,smarota1861@gmail.com,Off,"Painting, Sculpting, Art","2, 3","3, 5" +5,David,Beckham,10/30/2012,smarota1861@gmail.com,On,"Cookie Art, Puzzles, Pets","1, 4, 2","1, 3" +6,Albert,Einstein,2/5/2004,smarota1861@gmail.com,Off,"Zodiac Signs, Fitness",,4 +7,Taylor,Swift,5/28/1957,smarota1861@gmail.com,On,"Weaving, DIY, Painting",6, +8,Bruce,Wayne,10/10/2010,smarota1861@gmail.com,Off,Technology,, +9,Tinker,Bell,4/4/2000,smarota1861@gmail.com,On,Spellcasting,"8, 1, 4, 9, 10",5 +10,Jon,Snow,12/31/1992,smarota1861@gmail.com,Off,,"6, 9,10", +11,Hannah,Montana,5/5/1995,smarota1861@gmail.com,On,"Singing, Dancing",, +12,Barney,Stinson,8/25/1972,smarota1861@gmail.com,Off,"Women, suits",14,1 +13,Michael Gary,Scott,5/5/1965,smarota1861@gmail.com,On,"Spy Novels, Improvisation, Sales",, diff --git a/src/account.py b/src/account.py index 5a04ffa..fa389a7 100644 --- a/src/account.py +++ b/src/account.py @@ -3,7 +3,7 @@ import smtplib, ssl class Account(): - def __init__(self, name='', surname='', birthday='', email='', interests='', wishlist='', friendlist='', ID = None): + def __init__(self, name='', surname='', birthday='', email='', notifications='', interests='', wishlist='', friendlist='', ID = None): if ID != None: accountMan = AccountInfo() info = accountMan.get_info(ID) @@ -11,6 +11,7 @@ def __init__(self, name='', surname='', birthday='', email='', interests='', wis self.surname = info['Surname'] self.birthday = info['Birthday'] self.email = info['Email'] + self.notifications = info['Notifications'] self.interests = info['Interests'] self.wishlist = info['WishList'] self.friendlist = info['FriendList'] @@ -20,6 +21,7 @@ def __init__(self, name='', surname='', birthday='', email='', interests='', wis self.surname = surname self.birthday = birthday self.email = email + self.notifications = notifications self.interests = interests self.wishlist = wishlist self.friendlist = friendlist @@ -28,23 +30,24 @@ def __init__(self, name='', surname='', birthday='', email='', interests='', wis def create_account(self): accountMan = AccountInfo() - acc = accountMan.create_account(self.name, self.surname, self.birthday, self.email, self.interests, self.wishlist, self.friendlist) + acc = accountMan.create_account(self.name, self.surname, self.birthday, self.email, self.notifications, self.interests, self.wishlist, self.friendlist) return acc def view_account(self): accountMan = AccountInfo() return accountMan.get_info(self.ID) - def update_account(self, name='', surname='', birthday='', email='', interests='', wishlist='', friendlist=''): + def update_account(self, name='', surname='', birthday='', email='', notifications='', interests='', wishlist='', friendlist=''): self.name = name self.surname = surname self.birthday = birthday self.email = email + self.notifications = notifications self.interests = interests self.wishlist = wishlist self.friendlist = friendlist accountMan = AccountInfo() - accountMan.update_account(self.ID, name, surname, birthday, email, interests, wishlist, friendlist) + accountMan.update_account(self.ID, name, surname, birthday, email, notifications, interests, wishlist, friendlist) def send_reminder_email(self): accountMan = AccountInfo() @@ -53,31 +56,40 @@ def send_reminder_email(self): name = info['Name'] birthday = info['Birthday'] email = info['Email'] - friendlist = info['FriendList'] - wishlist = info['WishList'] + friendlist = info['FriendList'].to_string(index=False) + friendl = friendlist.split(',') + friendobj = [Account(ID=int(f)) for f in friendl] + wishlist = info['WishList'].to_string(index=False) + notifications = info['Notifications'].item() - birthdayDate = datetime.datetime.strptime(birthday, "%Y-%m-%d").date() - currDate = datetime.datetime.now().date() - if (birthdayDate - currDate).days < 7: - port = 587 - smtp_server = "outlook.office365.com" - sender_email = email - password = 'G1ft3r#212!' - for friend in friendlist: - receiver_email = friend.email - message = """\ - Subject: """ + name + """'s Birthday is Coming Up + if (notifications == "Off"): + print("Please turn email notifications on.") + else: + # birthdayDate = datetime.datetime.strptime(birthday, "%Y-%m-%d").date() + # currDate = datetime.datetime.now().date() + # if (birthdayDate - currDate).days < 7: + port = 587 + smtp_server = "outlook.office365.com" + sender_email = email + password = 'G1ft3r#212!' + for friend in friendobj: + print(friend.name) + # if (friend.notifications.item() == "On"): + # print(friend.email) + # receiver_email = friend.email + # message = """\ + # Subject: """ + name + """'s Birthday is Coming Up - Buy """ + name + """ the perfect gift for their birthday on """ + birthday + """. - Here are some items on their wishlist: """ + wishlist + """.""" - - context = ssl.create_default_context() - with smtplib.SMTP(smtp_server, port) as server: - server.ehlo() # Can be omitted - server.starttls(context=context) - server.ehlo() # Can be omitted - server.login(sender_email, password) - server.sendmail(sender_email, receiver_email, message) + # Buy """ + name + """ the perfect gift for their birthday on """ + birthday + """. + # Here are some items on their wishlist: """ + wishlist + """.""" + + # context = ssl.create_default_context() + # with smtplib.SMTP(smtp_server, port) as server: + # server.ehlo() # Can be omitted + # server.starttls(context=context) + # server.ehlo() # Can be omitted + # server.login(sender_email, password) + # server.sendmail(sender_email, receiver_email, message) # #acc = Account('Hannah', 'Montana', '05/05/1995', 'Singing, Dancing') diff --git a/src/main.py b/src/main.py index ce45923..d31634f 100644 --- a/src/main.py +++ b/src/main.py @@ -36,10 +36,11 @@ def create_account(): surname = form.text_input('Surname:') birthday = form.text_input('Birthday (MM/DD/YYYY):') email = form.text_input('Email:') + notifications = form.text_input('Email notifications (enter On or Off):') interest = form.text_input('Interests (please enter them comma seperated):') but1 = form.form_submit_button('Submit') if but1: - acc = Account(name, surname, birthday, email, interest) + acc = Account(name, surname, birthday, email, notifications, interest) acc = Account(ID = int(acc.ID)) st.session_state.runpage = 'account' st.session_state.account = acc @@ -72,6 +73,7 @@ def profile_page(): st.write('Surname: ' + acc.surname.to_string(index=False)) st.write('Birthday: ' + acc.birthday.to_string(index=False)) st.write('Email: ' + acc.email.to_string(index=False)) + st.write('Email Notifications: ' + acc.notifications.to_string(index=False)) st.write('Interests: ' + (acc.interests.to_string(index=False)).replace("\"", "")) if st.button("Edit Profile"): st.session_state.runpage = 'editprofile' @@ -79,6 +81,9 @@ def profile_page(): if st.button("Back"): st.session_state.runpage = 'account' st.experimental_rerun() + if st.button("Send Notifications"): + acc.send_reminder_email() + st.experimental_rerun() def editprofile_page(): st.header('Edit Profile') @@ -88,10 +93,11 @@ def editprofile_page(): surname = form.text_input('Surname:', value= acc.surname.to_string(index=False), placeholder= acc.surname.to_string(index=False)) birthday = form.text_input('Birthday:', value= acc.birthday.to_string(index=False), placeholder= acc.birthday.to_string(index=False)) email = form.text_input('Email:', value= acc.email.to_string(index=False), placeholder= acc.email.to_string(index=False)) + notifications = form.text_input('Email Notifications:', value= acc.notifications.to_string(index=False), placeholder= acc.notifications.to_string(index=False)) ints = (acc.interests.to_string(index=False)).replace("\"", "") interests = form.text_input('Interest:', value=ints, placeholder=ints) if form.form_submit_button('Update'): - acc.update_account(name, surname, birthday, email, interests, acc.wishlist.to_string(index=False), acc.friendlist.to_string(index=False)) + acc.update_account(name, surname, birthday, email, notifications, interests, acc.wishlist.to_string(index=False), acc.friendlist.to_string(index=False)) acc = Account(ID = int(acc.ID)) st.session_state.account = acc st.session_state.runpage = 'profile' @@ -194,6 +200,7 @@ def deleteitem_page(): a_surname = acc.surname.to_string(index=False) a_birthday = acc.birthday.to_string(index=False) a_email = acc.email.to_string(index=False) + a_notifications = acc.notifications.to_string(index=False) a_interests = acc.interests.to_string(index=False) a_wishlist = acc.wishlist.to_string(index=False) a_friendlist = acc.friendlist.to_string(index=False) @@ -202,7 +209,7 @@ def deleteitem_page(): a_wishlist.remove(str(i.itemID)) a_wishlist = ','.join(a_wishlist) - acc.update_account(a_name, a_surname, a_birthday, a_email, a_interests, a_wishlist, a_friendlist) + acc.update_account(a_name, a_surname, a_birthday, a_email, a_notifications, a_interests, a_wishlist, a_friendlist) acc = Account(ID = int(acc.ID)) st.session_state.account = acc st.session_state.runpage = 'wishlist' @@ -234,8 +241,7 @@ def friendlist_page(): st.experimental_rerun() if st.button('Back'): st.session_state.runpage = 'account' - st.experimental_rerun() - + st.experimental_rerun() def viewwishlist_page(): acc = st.session_state.account @@ -274,10 +280,11 @@ def addfriend_page(): a_surname = acc.surname.to_string(index=False) a_birthday = acc.birthday.to_string(index=False) a_email = acc.email.to_string(index=False) + a_notification = acc.notifications.to_string(index=False) a_interests = acc.interests.to_string(index=False) a_wishlist = acc.wishlist.to_string(index=False) a_friendlist = acc.friendlist.to_string(index=False) - acc.update_account(a_name, a_surname, a_birthday, a_email, a_interests, a_wishlist, friendlist) + acc.update_account(a_name, a_surname, a_birthday, a_email, a_notification, a_interests, a_wishlist, friendlist) acc = Account(ID = int(acc.ID)) st.session_state.account = acc st.session_state.runpage = 'friendlist' @@ -298,6 +305,7 @@ def deletefriend_page(): a_surname = acc.surname.to_string(index=False) a_birthday = acc.birthday.to_string(index=False) a_email = acc.email.to_string(index=False) + a_notifications = acc.notifications.to_string(index=False) a_interests = acc.interests.to_string(index=False) a_wishlist = acc.wishlist.to_string(index=False) a_friendlist = acc.friendlist.to_string(index=False) @@ -305,7 +313,7 @@ def deletefriend_page(): friends.remove(id) friends = ','.join(friends) - acc.update_account(a_name, a_surname, a_birthday, a_email, a_interests, a_wishlist, friends) + acc.update_account(a_name, a_surname, a_birthday, a_email, a_notifications, a_interests, a_wishlist, friends) acc = Account(ID = int(acc.ID)) st.session_state.account = acc st.session_state.runpage = 'friendlist' From 06f6b98b298f6a0288dc4d94e8e72c8caecb6921 Mon Sep 17 00:00:00 2001 From: smarota17 Date: Fri, 18 Nov 2022 16:27:03 -0500 Subject: [PATCH 04/15] Added input validation for creating an account. --- src/account_info.py | 7 +++- src/main.py | 99 ++++++++++++++++++++++++++++++++++++++++----- 2 files changed, 93 insertions(+), 13 deletions(-) diff --git a/src/account_info.py b/src/account_info.py index 663f850..c2cbed1 100644 --- a/src/account_info.py +++ b/src/account_info.py @@ -11,7 +11,7 @@ def __init__(self): self.database = '../data/people_data-copy.csv' self.data = pd.read_csv(self.database) - def create_account(self, name, surname='', birthday='', email='', interests='', wishlist='', friendlist=''): + def create_account(self, name, surname='', birthday='', email='', notifications='', interests='', wishlist='', friendlist=''): id_list = sorted(self.data.ID.tolist(), reverse=True) lastID = id_list[0] if name == '': @@ -24,6 +24,7 @@ def create_account(self, name, surname='', birthday='', email='', interests='', 'Surname': surname, 'Birthday': birthday, 'Email': email, + 'Notifications': notifications, 'Interests': interests, 'WishList': wishlist, 'FriendList': friendlist @@ -33,7 +34,7 @@ def create_account(self, name, surname='', birthday='', email='', interests='', print('Account created successfully!') return self.data[self.data['ID']==lastID+1] - def update_account(self, ID, name='', surname='', birthday='', email='', interests='', wishlist='', friendlist=''): + def update_account(self, ID, name='', surname='', birthday='', email='', notifications='', interests='', wishlist='', friendlist=''): id_list = self.data.ID.tolist() if ID not in id_list: print('User ID:', ID, 'is not in the database!!!') @@ -59,6 +60,7 @@ def update_account(self, ID, name='', surname='', birthday='', email='', interes 'Surname': surname, 'Birthday': birthday, 'Email': email, + 'Notifications': notifications, 'Interests': interests, 'WishList': wishlist, 'FriendList': friendlist @@ -70,6 +72,7 @@ def update_account(self, ID, name='', surname='', birthday='', email='', interes self.data.loc[index, 'Surname'] = surname self.data.loc[index, 'Birthday'] = birthday self.data.loc[index, 'Email'] = email + self.data.loc[index, 'Notifications'] = notifications self.data.loc[index, 'Interests'] = interests self.data.loc[index, 'WishList'] = wishlist print(type(wishlist)) diff --git a/src/main.py b/src/main.py index d31634f..e4dc99f 100644 --- a/src/main.py +++ b/src/main.py @@ -6,7 +6,8 @@ from account import Account from account_info import AccountInfo from item import item - +from datetime import datetime +import re def initial_page(): st.header("Gift Finder!") @@ -39,12 +40,51 @@ def create_account(): notifications = form.text_input('Email notifications (enter On or Off):') interest = form.text_input('Interests (please enter them comma seperated):') but1 = form.form_submit_button('Submit') + + # check if birthday is valid format + format = "%m/%d/%Y" + validB = True + try: + validB = bool(datetime.strptime(birthday, format)) + except ValueError: + validB = False + + # check if email is valid format using regex + regex = r'\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Z|a-z]{2,}\b' + validE = True + if not (re.fullmatch(regex, email)): + validE = False + + # when create account button is clicked, check input before creating account if but1: - acc = Account(name, surname, birthday, email, notifications, interest) - acc = Account(ID = int(acc.ID)) - st.session_state.runpage = 'account' - st.session_state.account = acc - st.experimental_rerun() + error = False + errorMessage = "" + if (name == ""): + error = True + errorMessage += "Name cannot be empty.\n" + if (surname == ""): + error = True + errorMessage += "Surname cannot be empty.\n" + if (birthday == "" or validB == False): + error = True + errorMessage += "Birthday should be formatted MM/DD/YYYY.\n" + if (email == "" or validE == False): + error = True + errorMessage += "Please enter a valid email.\n" + if (notifications == "" or notifications != "On"): + if (notifications != "Off"): + error = True + errorMessage += "Email notifications should be either 'On' or 'Off'.\n" + # if there is an error, print the associated messages and allow for user to correct + if (error == True): + st.error(errorMessage) + # if there is not an error, create the account + else: + acc = Account(name, surname, birthday, email, notifications, interest) + acc = Account(ID = int(acc.ID)) + st.session_state.runpage = 'account' + st.session_state.account = acc + st.experimental_rerun() #return account def account_page(): @@ -97,11 +137,48 @@ def editprofile_page(): ints = (acc.interests.to_string(index=False)).replace("\"", "") interests = form.text_input('Interest:', value=ints, placeholder=ints) if form.form_submit_button('Update'): - acc.update_account(name, surname, birthday, email, notifications, interests, acc.wishlist.to_string(index=False), acc.friendlist.to_string(index=False)) - acc = Account(ID = int(acc.ID)) - st.session_state.account = acc - st.session_state.runpage = 'profile' - st.experimental_rerun() + # check if birthday is valid format + format = "%m/%d/%Y" + validB = True + try: + validB = bool(datetime.strptime(birthday, format)) + except ValueError: + validB = False + + # check if email is valid format using regex + regex = r'\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Z|a-z]{2,}\b' + validE = True + if not (re.fullmatch(regex, email)): + validE = False + + error = False + errorMessage = "" + if (name == ""): + error = True + errorMessage += "Name cannot be empty.\n" + if (surname == ""): + error = True + errorMessage += "Surname cannot be empty.\n" + if (birthday == "" or validB == False): + error = True + errorMessage += "Birthday should be formatted MM/DD/YYYY.\n" + if (email == "" or validE == False): + error = True + errorMessage += "Please enter a valid email.\n" + if (notifications == "" or notifications != "On"): + if (notifications != "Off"): + error = True + errorMessage += "Email notifications should be either 'On' or 'Off'.\n" + # if there is an error, print the associated messages and allow for user to correct + if (error == True): + st.error(errorMessage) + # if there is not an error, update the account + else: + acc.update_account(name, surname, birthday, email, notifications, interests, acc.wishlist.to_string(index=False), acc.friendlist.to_string(index=False)) + acc = Account(ID = int(acc.ID)) + st.session_state.account = acc + st.session_state.runpage = 'profile' + st.experimental_rerun() if st.button("Back"): st.session_state.runpage = 'profile' st.experimental_rerun() From 9b8506e572c48430b3d2b15221bb411033ebd62a Mon Sep 17 00:00:00 2001 From: smarota17 Date: Sat, 26 Nov 2022 14:17:08 -0500 Subject: [PATCH 05/15] updating documentation --- src/account.py | 86 +++++++++++++++++++++++++++++++++----------------- src/item.py | 8 ++++- src/main.py | 30 +++++++++++++++++- 3 files changed, 93 insertions(+), 31 deletions(-) diff --git a/src/account.py b/src/account.py index fa389a7..dbff82e 100644 --- a/src/account.py +++ b/src/account.py @@ -1,8 +1,13 @@ from account_info import AccountInfo from datetime import datetime, timedelta import smtplib, ssl +from item import item +from email.mime.text import MIMEText +from email.mime.multipart import MIMEMultipart +# This class represents an account object that a user creates to interact with the code. class Account(): + # Initialization of an account object. def __init__(self, name='', surname='', birthday='', email='', notifications='', interests='', wishlist='', friendlist='', ID = None): if ID != None: accountMan = AccountInfo() @@ -26,17 +31,20 @@ def __init__(self, name='', surname='', birthday='', email='', notifications='', self.wishlist = wishlist self.friendlist = friendlist self.ID = self.create_account()['ID'] - + # This method creates an account. def create_account(self): accountMan = AccountInfo() acc = accountMan.create_account(self.name, self.surname, self.birthday, self.email, self.notifications, self.interests, self.wishlist, self.friendlist) return acc + # This method pulls the account to allow the user the view information. def view_account(self): accountMan = AccountInfo() return accountMan.get_info(self.ID) + # This method is used to update the account based on the supplied information. Input validation + # occurs on the frontend. def update_account(self, name='', surname='', birthday='', email='', notifications='', interests='', wishlist='', friendlist=''): self.name = name self.surname = surname @@ -48,49 +56,69 @@ def update_account(self, name='', surname='', birthday='', email='', notificatio self.friendlist = friendlist accountMan = AccountInfo() accountMan.update_account(self.ID, name, surname, birthday, email, notifications, interests, wishlist, friendlist) - + + # This method is used to send birthday reminder emails if a user has their email notification + # preferences on. The method loops through the friends list, and if the friend has their email + # preferences on as well, an email is sent using SMTP. The method runs every time the program is + # loaded (checks if the birthday is in 1 week) or when the user clicks the "Send Notification" button + # in the profile. def send_reminder_email(self): accountMan = AccountInfo() info = accountMan.get_info(self.ID) name = info['Name'] - birthday = info['Birthday'] - email = info['Email'] + birthday = info['Birthday'].item() + b = birthday.rpartition('/')[0] + birthday.rpartition('/')[1] + b = b[:-1] friendlist = info['FriendList'].to_string(index=False) friendl = friendlist.split(',') friendobj = [Account(ID=int(f)) for f in friendl] + wishlist = info['WishList'].to_string(index=False) - notifications = info['Notifications'].item() + items = wishlist.replace("\"", "").split(",") + items = [int(item) for item in items] + item_objs = [item(ID=int(id)) for id in items] + + notifications = info['Notifications'].item() if (notifications == "Off"): print("Please turn email notifications on.") else: - # birthdayDate = datetime.datetime.strptime(birthday, "%Y-%m-%d").date() - # currDate = datetime.datetime.now().date() - # if (birthdayDate - currDate).days < 7: - port = 587 - smtp_server = "outlook.office365.com" - sender_email = email - password = 'G1ft3r#212!' - for friend in friendobj: - print(friend.name) - # if (friend.notifications.item() == "On"): - # print(friend.email) - # receiver_email = friend.email - # message = """\ - # Subject: """ + name + """'s Birthday is Coming Up + port = 587 + smtp_server = "smtp.office365.com" + sender_email = "gifter-2@outlook.com" + password = "G1ft3r#212!" + + # construct wishlist string + wishlistString = "\n" + for i in item_objs: + wishlistString += " - " + i.title.item() + " ($" + i.cost.item() + "): " + i.link.item() + "\n" + + message = """Buy """ + name.item() + """ the perfect gift for their birthday on """ + b + """.\nHere are some items on their wishlist:\n """ + wishlistString + + for friend in friendobj: + if (friend.notifications.item() == "On"): + receiver_email = friend.email.item() - # Buy """ + name + """ the perfect gift for their birthday on """ + birthday + """. - # Here are some items on their wishlist: """ + wishlist + """.""" - - # context = ssl.create_default_context() - # with smtplib.SMTP(smtp_server, port) as server: - # server.ehlo() # Can be omitted - # server.starttls(context=context) - # server.ehlo() # Can be omitted - # server.login(sender_email, password) - # server.sendmail(sender_email, receiver_email, message) + msg = MIMEMultipart() + msg['Subject'] = "Gifter-2: " + name.item() + "'s Birthday is Coming Up" + msg['From'] = sender_email + msg['To'] = receiver_email + body = MIMEText(message, "plain") + msg.attach(body) + + context = ssl.create_default_context() + with smtplib.SMTP(smtp_server, port) as server: + server.ehlo() + server.starttls(context=context) + server.ehlo() + server.login(sender_email, password) + print("logged in") + print("attempting to send mail") + server.sendmail(sender_email, receiver_email, msg.as_string()) + print("message sent") + server.quit() # #acc = Account('Hannah', 'Montana', '05/05/1995', 'Singing, Dancing') # #acc.view_account() diff --git a/src/item.py b/src/item.py index fe39324..5bd8c87 100644 --- a/src/item.py +++ b/src/item.py @@ -1,5 +1,8 @@ from item_manager import ItemManager + +# This class represents an item object. This item is a gift that a user can add to their wishlist. class item(): + # Initialization of the item object. def __init__(self, title = '', desc = '', link = '', cost = '', ID = None): if ID != None: itemMan = ItemManager() @@ -15,13 +18,14 @@ def __init__(self, title = '', desc = '', link = '', cost = '', ID = None): self.link = link self.cost = cost self.itemID = int(self.create_item()['ItemID']) - + # This method creates an item. def create_item(self): itemMan = ItemManager() item = itemMan.add_item(self.title, self.desc, self.link, self.cost) return item + # This method modifies an item. def modify_item(self, title, desc, link, cost): itemMan = ItemManager() self.title = title @@ -30,10 +34,12 @@ def modify_item(self, title, desc, link, cost): self.cost = cost itemMan.update_item( self.itemID, title, desc, link, cost) + # This pulls an item for viewing. def view_item(self): itemMan = ItemManager() return itemMan.get_item(self.itemID) + # This method deletes an item. def delete_item(self): itemMan = ItemManager() itemMan.delete_item(self.itemID) diff --git a/src/main.py b/src/main.py index e4dc99f..f050c5e 100644 --- a/src/main.py +++ b/src/main.py @@ -9,6 +9,8 @@ from datetime import datetime import re +email_sent = False + def initial_page(): st.header("Gift Finder!") create = st.button('Create Account') @@ -103,7 +105,33 @@ def account_page(): if st.button('Logout'): st.session_state.runpage = 'initial' st.experimental_rerun() - + # check if whether a notification email needs to be sent today (is it 1 week before the account's birthday) + global email_sent + + birthday = acc.birthday.item() + b = birthday.rpartition('/')[0] + birthday.rpartition('/')[1] + b = b[:-1] + month = b.rpartition('/')[0] + day = b.rpartition('/')[2] + currDate = datetime.now().date() + + if (int(currDate.month) > int(month)): + if (int(currDate.day) > int(day)): + birthdayDate = b + "/" + str(currDate.year + 1) + else: + birthdayDate = b + "/" + str(currDate.year) + + birthdayDate = datetime.strptime(birthdayDate, "%m/%d/%Y").date() + + if (birthdayDate - currDate).days == 7 and email_sent == False: + email_sent = True + acc.send_reminder_email() + acc = Account(ID = int(acc.ID)) + st.session_state.account = acc + st.session_state.runpage = 'profile' + st.experimental_rerun() + else: + email_sent = False def profile_page(): st.header('Profile') From 0e86b8293c81e4438c2c53ec3169953e86487c26 Mon Sep 17 00:00:00 2001 From: lichchang <112525744+lichchang@users.noreply.github.com> Date: Sat, 26 Nov 2022 20:53:55 -0500 Subject: [PATCH 06/15] Create proj2-Rubric.md --- docs/proj2-Rubric.md | 117 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 117 insertions(+) create mode 100644 docs/proj2-Rubric.md diff --git a/docs/proj2-Rubric.md b/docs/proj2-Rubric.md new file mode 100644 index 0000000..4a98163 --- /dev/null +++ b/docs/proj2-Rubric.md @@ -0,0 +1,117 @@ +|Notes|Points|Evidence| +|-----|------|---------| +| Video1 | 0 | | +| Video2 | 0 | | +| Workload is spread over the whole team (one team member is often Xtimes more productive than the others but nevertheless, here is a track record that everyone is contributing a lot) | 3 | Everyone contributed to the project. https://github.com/landog893/Gifter-2/graphs/contributors | +|Number of commits| 3 | There were over 161 commits made to the repository https://github.com/landog893/Gifter-2/graphs/commit-activity | +|Number of commits: by different people| 3 | All team members contributed by making over 5 commits with over 200 lines of code added https://github.com/landog893/Gifter-2/graphs/commit-activity, https://github.com/landog893/Gifter-2/pulse | +|Issues reports: there are **many**| 3 | 13 issues created https://github.com/landog893/Gifter-2/issues | +|Issues are being closed| 3 | 13 issues closed https://github.com/landog893/Gifter-2/issues | +|DOI badge: exists| 3 | https://github.com/landog893/Gifter-2#readme | +|Docs: doco generated, format not ugly | 0 | | +|Docs: what: point descriptions of each class/function (in isolation) | 0 | | +|Docs: how: for common use cases X,Y,Z mini-tutorials showing worked examples on how to do X,Y,Z| 0 | | +|Docs: why: docs tell a story, motivate the whole thing, deliver a punchline that makes you want to rush out and use the thing| 0 | | +|Docs: short video, animated, hosted on your repo. That convinces people why they want to work on your code.| 0 | | +|Use of version control tools| 3 | Used Git. | +|Use of style checkers | 0 | | +|Use of code formatters. | 0 | | +|Use of syntax checkers. | 0 | | +|Use of code coverage | 3 | source code tests (https://github.com/landog893/Gifter-2/tree/main/test) and blackbox tests (https://github.com/landog893/Gifter-2/blob/main/docs/BlackBoxTest.md). | +|Other automated analysis tools| 0 | | +|Test cases exist (dozens of tests and those test cases are more than 30% of the code base)| 3 | Blackbox testing (https://github.com/landog893/Gifter-2/blob/main/docs/BlackBoxTest.md) and source code testing (https://github.com/landog893/Gifter-2/tree/main/test). | +|Test cases are routinely executed (E.g. travis-com.com or github actions or something) | 3 | Github | +|The files CONTRIBUTING.md lists coding standards and lots of tips on how to extend the system without screwing things up| 3 | https://github.com/landog893/Gifter-2/blob/main/CONTRIBUTING.md | +|Issues are discussed before they are closed (even if you discuss in slack, need a sumamry statement here) | 3 | https://github.com/landog893/Gifter-2/issues?q=is%3Aissue+is%3Aclosed | +|Chat channel: exists (Link or screenshots)| 3 | https://github.com/landog893/Gifter-2/blob/main/README.md | +|Test cases: a large proportion of the issues related to handling failing cases.(If a test case fails, open an issue and fix it) | 3 | https://github.com/landog893/Gifter-2/issues | +|Evidence that the whole team is using the same tools: everyone can get to all tools and files| 3 | Everyone used Python with Anaconda. | +|Evidence that the whole team is using the same tools (e.g. config files in the repo, updated by lots of different people)| 3 | | +|Evidence that the whole team is using the same tools (e.g. tutor can ask anyone to share screen, they demonstrate the system running on their computer)| 3 | Everyone used Anaconda. | +|Evidence that the members of the team are working across multiple places in the code base| 3 | Members contributed to different parts of the codebase. | +|Short release cycles (hard to see in short projects) project members are committing often enough so that everyone can get your work| 3 | | +|What the software does| | | +|Does the website and documentation provide a clear, high-level overview of the software?| 3 | https://github.com/landog893/Gifter-2/blob/main/README.md | +|Does the website and documentation clearly describe the type of user who should use the software?|3| https://github.com/landog893/Gifter-2/blob/main/README.md | +| Do the authors publish case studies to show how the software has been used by theself and others?| 0 | | +|The project's and software's identity| 3 | https://github.com/landog893/Gifter-2/blob/main/README.md | +|Is the name of the project/software unique?|2| It is indicitive of the intended use for the software https://github.com/landog893/Gifter-2/blob/main/README.md | +|Is the project/software name free from trademark violations?| 0 | It seems there is one trademark for "Gifter" https://www.wegifter.co/ | +|Availability of the software| 3 | Available for anyone to download. | +|Is the software available as a package that can be deployed without building it?|2|Can be downloaded via CMD, https://github.com/landog893/Gifter-2/blob/main/README.md| +|Is the software available for free?|3|Can be downloaded by anyone at no charge. https://github.com/landog893/Gifter-2/blob/main/README.md| +|Is the source code publicly available to download, either as a downloadable bundle or via access to a source code repository?|3| Users can access the source code repo and download from there https://github.com/landog893/Gifter-2| +Is the software hosted in an established, third-party repository like GitHub (https://github.com), BitBucket (https://bitbucket.org),LaunchPad (https://launchpad.net) orSourceForge (https://sourceforge.net)?|3| The project is hosted on GitHub https://github.com/landog893/Gifter-2| +|The software's documentation||| +|Is the documentation clearly available on the website or within the software?|3|Documentation is availible via README.md (https://github.com/landog893/Gifter-2).| +|Does the documentation include a "quick start" guide, that provides a short overview of how to use the software with some basic examples of use?|3|https://github.com/landog893/Gifter-2/blob/main/README.md| +|If the author provide more extensive documentation, does this provide clear, step-by-step instructions on how to deploy and use the software?|3|[https://github.com/landog893/Gifter-2](https://github.com/landog893/Gifter-2/blob/main/USAGE.md)| +|Do the author provide a comprehensive guide to all the software’s commands, functions and options?|3|[https://github.com/landog893/Gifter-2](https://github.com/landog893/Gifter-2/blob/main/USAGE.md)| +|Do the author provide troubleshooting information that describes the symptoms and step-by-step solutions for problems and error messages?|0|https://github.com/landog893/Gifter-2| +|If the software can be used as a library, package or service by other software, do the author provide comprehensive API documentation?|0| Not applicable.| +|Do the author store the documentation under revision control with the source code?|0| | +|Do the author publish the release history e.g. release data, version numbers, key features of each release etc. on the web site or in the documentation?|0|| +|How the author support the software||| +|Does the software describe how a user can get help with using the software?|3|In the Readme.md the authors described the functionalities of the software. https://github.com/landog893/Gifter-2 | +|Does the website and documentation describe what support, if any, the authors provide to users and developers?|0|| +|Does the project have an e-mail address or forum that is solely for supporting users?|0|| +|Are e-mails to the support e-mail address received by more than one person?|0|| +|Does the project have a ticketing system to manage bug reports and feature requests?|3|https://github.com/landog893/Gifter-2/issues | +|Is the project's ticketing system publicly visible to the users, so they can view bug reports and feature requests?|3|https://github.com/landog893/Gifter-2/issues | +|The software's maintainability||| +|Is the software’s architecture and design modular?|0|| +|Does the software use an accepted coding standard or convention?|3|We used Checkstyle to ensure our code follows standard coding practices.| +|Open standards and the software||| +|Does the software allow data to be imported and exported using open data formats? e.g. GIF, SVG, HTML, XML, tar, zip, CSV, JSON, NetCDF, or domain specific ones|3|For new the project allows import from .CSV file https://github.com/landog893/Gifter-2| +| Does the software allow communications using open communications protocols? e.g. HTTP, FTP, XMPP, SOAP over HTTP, or domain-specific ones|3|The project is using HTTP. https://github.com/landog893/Gifter-2| +|The software's portability||| +|Is the software cross-platform compatible? e.g. does it run under two or more of Windows, Unix/Linux and Mac OS X, or can be used from within two or more of Internet Explorer, Chrome, Firefox and Safari?|3|Yes, the app can be used from both Edge and Chrome| +|The software and accessibility||| +|Does the software adhere to appropriate accessibility conventions or standards?|0|| +|Does the documentation adhere to appropriate accessibility conventions or standards?|0|| +|How the authors manage the source code||| +|Is the source code stored in a repository under revision control?|3|Yes, the GitHub repo has revision control. https://github.com/landog893/Gifter-2 | +|Is each source code release a snapshot of the repository?|3|Yes there are releases. https://github.com/landog893/Gifter-2/releases | +|Are releases tagged in the repository?|3|Yes, they are tagged. https://github.com/landog893/Gifter-2/releases | +|Is there a branch of the repository that is always stable? (i.e. tests always pass, code always builds successfully)|3|The main branch. https://github.com/landog893/Gifter-2/tree/main/test | +|Do the authors back-up the repository?|3|Yes, the repo has been cloned multiple times as local back-ups| +|Building and installing the software||| +|Do the authors provide publicly-available instructions for building the software from the source code?|3|In the readme.md file. https://github.com/landog893/Gifter-2/blob/main/README.md | +|Can the authors build, or package, the software using an automated tool? e.g. Make (https://www.gnu.org/software/make/), ANT (http://ant.apache.org/), Maven (https://maven.apache.org/), CMake (https://cmake.org/), Python setuptools (https://pypi.python.org/pypi/setuptools), or R package tools (https://cran.r-project.org/doc/manuals/r-devel/R-extthe authorsrs.html)|3|Yes, using Anaconda. https://github.com/landog893/Gifter-2/blob/main/README.md | +|Do the authors provide publicly-available instructions for deploying the software?|3|In the readme.md file. https://github.com/landog893/Gifter-2/blob/main/README.md | +|Does the documentation list all third-party dependencies? |3| Anaconda https://github.com/landog893/Gifter-2/blob/main/README.md | +|Does the documentation list the version number for all third-party dependencies?|3| https://github.com/landog893/Gifter-2/blob/main/README.md | +|Does the software list the web address, and licences for all third-party dependencies and say whether the dependencies are mandatory or optional?|3|Linked to the readme.md icons https://github.com/landog893/Gifter-2/blob/main/README.md | +|Can the authors download dependencies using a dependency management tool or package manager? e.g. Ivy (http://ant.apache.org/ivy/), Maven (https://maven.apache.org/), Python pip (https://pypi.python.org/pypi/pip) or setuptools (https://pypi.python.org/pypi/setuptools), PHP Composer (https://getcomposer.org/), Ruby gems (https://rubygems.org), or R PackRat (https://rstudio.github.io/packrat/)|3|Yes, by using Anaconda. https://github.com/landog893/Gifter-2/blob/main/README.md | +|Do the authors have tests that can be run after the software has been built or deployed to show whether the build or deployment has been successful?|3|Yes, the blackbox tests and pytest tests| +|How the authors test the software||| +|Do the authors have an automated test suite for the software?|3|Yes, the blackbox tests and pytest tests| +|Do the authors have a framework to periodically (e.g. nightly) run the tests on the latest version of the source code?|0|| +|Do the authors use continuous integration, automatically running tests whenever changes are made to the source code?|0|| +|Are the test results publicly visible?|3|Yes. The Blackbox tests are described and the pytest tests has expected outputs.| +|Are all manually-run tests documented?|3|The blackbox tests. https://github.com/landog893/Gifter-2/blob/main/docs/BlackBoxTest.md | +|How the authors engage with the community|3|| +|Does the project have resources (e.g. blog, Twitter, RSS feed, Facebook page, wiki, mailing list) that are regularly updated with information about the software? e.g. release announcements, publications, workshops, conference presentations|3|The JobTracker README. | +|Does the website state how many projects and users are associated with the project?|0|No.| +|Do the authors provide success stories on the website? |0|There are no success stories| +|Do the authors list the important partners and collaborators on the website?|3| In the README.| +|Do the authors list the project's publications on the website or link to a resource where these are available?|0|There are no publications| +|Do the authors list third-party publications that refer to the software on the website or link to a resource where these are available?|0|There are no publications| +|Can users subscribe to notifications to changes to the source code repository?|3|Yes, with the help of GitHub| +|If the software is developed as an open source project (and, not just a project developing open source software), do the authors have a governance model?|3|Yes. There is a contributing instruction. https://github.com/landog893/Gifter-2/blob/main/CONTRIBUTING.md| +|How the authors manage contributions||| +|Do the authors accept contributions (e.g. bug fixes, enhancements, documentation updates, tutorials) from people who are not part of the project?|3|https://github.com/landog893/Gifter-2/blob/main/CONTRIBUTING.md| +|Do the authors have a contributions policy?|3|https://github.com/landog893/Gifter-2/blob/main/CONTRIBUTING.md| +|Is the contributions' policy publicly available?|3|https://github.com/landog893/Gifter-2/blob/main/CONTRIBUTING.md| +|Do contributors keep the copyright/IP of their contributions?|1|Yes. https://github.com/landog893/Gifter-2/blob/main/LICENSE.md| +|the software's copyright and licensing||| +|Does the website and documentation clearly state the copyright owners of the software and documentation?|3|Yes. https://github.com/landog893/Gifter-2/blob/main/LICENSE.md| +|Does each of the source code files include a copyright statement?|3|There is a License file for all of the source codes. https://github.com/landog893/Gifter-2/blob/main/LICENSE.md| +|Does the website and documentation clearly state the licence of the software?|3|Yes. https://github.com/landog893/Gifter-2/blob/main/LICENSE.md| +|Is the software released under an open source licence?|3|Yes, the Apache License2.0 https://github.com/landog893/Gifter-2/blob/main/LICENSE.md| +|Is the software released under an OSI-approved open-source licence?|3|Yes. https://github.com/landog893/Gifter-2/blob/main/LICENSE.md| +|Does each of the source code files include a licence header?|0|No.| +|Do the authors have a recommended citation for the software?|0| | +|Does the website or documentation include a project roadmap (a list of project and development milestones for the next 3, 6 and 12 months)?|3|In the "Future Features" section of the Readme.md https://github.com/landog893/Gifter-2/blob/main/README.md| +|Does the website or documentation describe how the project is funded, and the period over which funding is guaranteed?|0|No funding| +|Do the authors make timely announcements of the deprecation of components, APIs, etc.?|0|No.| From 1a13c15297f67dab9cbda3df8c36e2a183cf8657 Mon Sep 17 00:00:00 2001 From: sislam8 Date: Sun, 27 Nov 2022 11:48:51 -0500 Subject: [PATCH 07/15] add python package --- src/account_info.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/account_info.py b/src/account_info.py index a3ef887..800326d 100644 --- a/src/account_info.py +++ b/src/account_info.py @@ -1,6 +1,7 @@ import numpy as np import pandas as pd import sys +import streamlit as st class AccountInfo: def __init__(self): @@ -146,7 +147,7 @@ def get_account(self, username,password): if info.Password.values[0] == password: return self.data[self.data['UserName']==username] return -2 - + def search_ID(self, ID): if ID == None: print("ID cannot be empty!!!") From 2f22d9dd8f8087a017acd6687340fb857c49e86b Mon Sep 17 00:00:00 2001 From: sislam8 Date: Sun, 27 Nov 2022 16:43:14 -0500 Subject: [PATCH 08/15] changes in item modify, item delete and item view from item and fixing bugs for databases in account registration and login --- src/account.py | 11 ++- src/account_info.py | 20 ++++- src/main.py | 191 +++++++++++++++++++++++++------------------- 3 files changed, 136 insertions(+), 86 deletions(-) diff --git a/src/account.py b/src/account.py index 0e88403..d51a3fe 100644 --- a/src/account.py +++ b/src/account.py @@ -27,11 +27,14 @@ def __init__(self, name='', surname='', birthday='', username='',password = '', self.interests = interests self.wishlist = wishlist self.friendlist = friendlist - ID = self.create_account() - if ID == None: - st.error("Can not create account, please check the format of information") + acc = self.create_account() + print(acc) + if isinstance(acc, int) == True and acc == -2: + print("Can not create account with same user name") + self.ID = -2 else: - self.ID = ID + self.ID = acc['ID'] + print(self.ID) diff --git a/src/account_info.py b/src/account_info.py index 8d3a3e4..5973270 100644 --- a/src/account_info.py +++ b/src/account_info.py @@ -12,6 +12,7 @@ def create_account(self, name, surname='', birthday='',username='', password = ' query = """Insert Into public."Account" ("Name","Surname","Birthday","UserName","Password","Interests","WishList","FriendList") values(%s,%s,%s,%s,%s,%s,%s,%s) returning "ID" """ conn = None + Checkquery = """Select * From "Account" WHERE "UserName" = %s;""" ID = None try: # initializing connection @@ -19,9 +20,24 @@ def create_account(self, name, surname='', birthday='',username='', password = ' print('Connecting to the PostgreSQL database...') conn = psycopg2.connect(**params) cur = conn.cursor() + # check name field + if name == '': + return -1 + #execute userName check + cur.execute(Checkquery,(username,)) + rows = cur.fetchall() + print(len(rows)) + if len(rows) > 0: + print('User Name already in use. Please use another one!') + st.error("User Name already in use. Please use another one!") + return -2 + cur.close() # execute a statement + cur = conn.cursor() cur.execute(query, (name, surname, birthday, username,password,interests,wishlist,friendlist)) - ID = cur.fetchone()[0] + acc = cur.fetchone() + print("create ID") + print(acc) cur.close() conn.commit() cur.close() @@ -33,7 +49,7 @@ def create_account(self, name, surname='', birthday='',username='', password = ' conn.close() print('Database connection closed.') - return ID + return acc def update_account(self, ID, name='', surname='', birthday='',username='',password = '', interests='', wishlist = '', friendlist= ''): query = """UPDATE "Account" Set "Name" = %s, "Surname" = %s, "Birthday" = %s, "UserName" = %s, "Password" = %s, "Interests" = %s, "WishList" = %s, "FriendList" = %s diff --git a/src/main.py b/src/main.py index 944b247..74fb0c6 100644 --- a/src/main.py +++ b/src/main.py @@ -164,24 +164,51 @@ def wishlist_page(): items = (acc.wishlist).replace("\"", "").split(",") items = [int(item) for item in items if item.isnumeric()] item_objs = [item(ID=id) for id in items] - item_titles = [(i.title).replace("\"", "") for i in item_objs] - item_descs = [(i.desc).replace("\"", "") for i in item_objs] - item_links = [(i.link.replace("\"", "")) for i in item_objs] - item_costs = [i.cost for i in item_objs] + # item_titles = [(i.title).replace("\"", "") for i in item_objs] + # item_descs = [(i.desc).replace("\"", "") for i in item_objs] + # item_links = [(i.link.replace("\"", "")) for i in item_objs] + # item_costs = [i.cost for i in item_objs] + - df = pd.DataFrame(list(zip(items, item_titles, item_descs, item_links, item_costs)), columns=('ID', 'Title', 'Description', 'Link', 'Cost')) - df.set_index('ID', inplace=True) - st.table(df) + # df = pd.DataFrame(list(zip(items, item_titles, item_descs, item_links, item_costs,item_buttons)), columns=('ID', 'Title', 'Description', 'Link', 'Cost','Edit Item')) + # df.set_index('ID', inplace=True) + colms = st.columns((2,2, 2, 2, 2, 2,2)) + fields = ["Item Number","Title", 'Description', 'Link', 'Cost', "Edit Item","Remove Item"] + for col, field_name in zip(colms, fields): + col.write(field_name) + j = 0 + for i in item_objs: + col0,col1, col2, col3, col4, col5,col6 = st.columns((2,2, 2, 2, 2, 2,2)) + col0.write(j+1) + col1.write((i.title).replace("\"", "")) + col2.write((i.desc).replace("\"", "")) + col3.write(i.link.replace("\"", "")) + col4.write(str(i.cost)) + button_phold = col5.empty() + do_action = button_phold.button("Edit",key = items[j]) + if do_action: + st.session_state['edit_key'] = items[j] + st.session_state.runpage = 'modifyitem' + st.experimental_rerun() + button_remove = col6.empty() + do_remove_action = button_remove.button("Delete",key = str(items[j])+str(items[j])) + if do_remove_action: + st.session_state['delete_key'] = items[j] + st.session_state.runpage = 'deleteitem' + st.experimental_rerun() + # col5.write(st.button("Edit"),key=items[j]) + j = j + 1 + # st.table(df) if st.button('Add item'): st.session_state.runpage = 'additem' st.experimental_rerun() - if st.button('Modify item'): - st.session_state.runpage = 'modifyitem' - st.experimental_rerun() - if st.button('Remove item'): - st.session_state.runpage = 'deleteitem' - st.experimental_rerun() + # if st.button('Modify item'): + # st.session_state.runpage = 'modifyitem' + # st.experimental_rerun() + # if st.button('Remove item'): + # st.session_state.runpage = 'deleteitem' + # st.experimental_rerun() if st.button('Back'): st.session_state.runpage = 'account' st.experimental_rerun() @@ -240,92 +267,96 @@ def modifyitem_page(): acc = st.session_state.account items = (acc.wishlist).replace("\"", "").split(",") items = [int(item) for item in items] - id =st.text_input('Please enter ID of the item you want to modify') - if st.button('Confirm'): - case = -1 - try: - i = item(ID=int(id)) - except ValueError: - case = 0 + id = st.session_state['edit_key'] + i = item(ID=int(id)) + # id =st.text_input('Please enter ID of the item you want to modify') + # if st.button('Confirm'): + # case = -1 + # try: + # i = item(ID=int(id)) + # except ValueError: + # case = 0 - try: int(id) - except ValueError: - case = 1 + # try: int(id) + # except ValueError: + # case = 1 - if case == 0: st.error("Item ID does not exist") - elif case == 1: st.error("Item ID must be an integer") - else: - form = st.form(key='ModifyItemForm') - title = form.text_input('Title:', value= i.title, placeholder= i.title) - desc = form.text_input('Description', value= i.desc, placeholder= i.desc) - link = form.text_input('Link', value= i.link, placeholder= i.link) - cost = form.text_input('Cost', value= i.cost, placeholder= i.cost) - chars = set("~!@#$%^&*()_+=") - if form.form_submit_button('Modify item'): - if title == "": - st.error("Title is not nullable") - elif any((c in chars) for c in title): - st.error("Title can not contain symbols") - elif cost != "": - try: - float(cost) - print("modifying item") - i.modify_item(title, desc, link, cost) - st.session_state.runpage = 'wishlist' - st.experimental_rerun() - except ValueError: st.error("Cost must be a number") + # if case == 0: st.error("Item ID does not exist") + # elif case == 1: st.error("Item ID must be an integer") + # else: + form = st.form(key='ModifyItemForm') + title = form.text_input('Title:', value= i.title, placeholder= i.title) + desc = form.text_input('Description', value= i.desc, placeholder= i.desc) + link = form.text_input('Link', value= i.link, placeholder= i.link) + cost = form.text_input('Cost', value= i.cost, placeholder= i.cost) + chars = set("~!@#$%^&*()_+=") + if form.form_submit_button('Modify item'): + if title == "": + st.error("Title is not nullable") + elif any((c in chars) for c in title): + st.error("Title can not contain symbols") + elif cost != "": + try: + float(cost) + print("modifying item") + i.modify_item(title, desc, link, cost) + st.session_state.runpage = 'wishlist' + st.experimental_rerun() + except ValueError: st.error("Cost must be a number") if st.button('Back'): st.session_state.runpage = 'wishlist' st.experimental_rerun() def deleteitem_page(): + id = st.session_state["delete_key"] acc = st.session_state.account items = (acc.wishlist).replace("\"", "").split(",") items = [int(item) for item in items] form = st.form(key='DeleteItemForm') - id =form.text_input('Please enter ID of the item you want to delete', value=items[0]) - case = -1 + i = item(ID=int(id)) + # id =form.text_input('Please enter ID of the item you want to delete', value=items[0]) + # case = -1 - if form.form_submit_button('Delete item'): - try: - i = item(ID=int(id)) - except ValueError: - case = 0 + # if form.form_submit_button('Delete item'): + # try: + # i = item(ID=int(id)) + # except ValueError: + # case = 0 - try: int(id) - except ValueError: - case = 1 + # try: int(id) + # except ValueError: + # case = 1 - if case == 0: st.error("Item ID does not exist") - elif case == 1: st.error("Item ID must be an integer") - else: - acc = st.session_state.account - a_name = acc.name - a_surname = acc.surname - a_birthday = acc.birthday - a_username = acc.username - a_password = acc.password - a_interests = acc.interests - a_wishlist = acc.wishlist - a_friendlist = acc.friendlist + # if case == 0: st.error("Item ID does not exist") + # elif case == 1: st.error("Item ID must be an integer") + # else: + acc = st.session_state.account + a_name = acc.name + a_surname = acc.surname + a_birthday = acc.birthday + a_username = acc.username + a_password = acc.password + a_interests = acc.interests + a_wishlist = acc.wishlist + a_friendlist = acc.friendlist - a_wishlist = a_wishlist.split(",") - a_wishlist.remove(str(i.itemID)) - a_wishlist.remove('') - a_wishlist = ','.join(a_wishlist) + a_wishlist = a_wishlist.split(",") + a_wishlist.remove(str(i.itemID)) + # a_wishlist.remove('') + a_wishlist = ','.join(a_wishlist) - i.delete_item() - acc.update_account(a_name, a_surname, a_birthday,a_username,a_password, a_interests, a_wishlist, a_friendlist) - acc = Account(ID = int(acc.ID)) + i.delete_item() + acc.update_account(a_name, a_surname, a_birthday,a_username,a_password, a_interests, a_wishlist, a_friendlist) + acc = Account(ID = int(acc.ID)) - st.session_state.account = acc - st.session_state.runpage = 'wishlist' - st.experimental_rerun() + st.session_state.account = acc + st.session_state.runpage = 'wishlist' + st.experimental_rerun() - if st.button('Back'): - st.session_state.runpage = 'wishlist' - st.experimental_rerun() + # if st.button('Back'): + # st.session_state.runpage = 'wishlist' + # st.experimental_rerun() def friendlist_page(): st.header('Friend List') From 7c2b8232819337bd3c9a5270d52c6ffbb721d16b Mon Sep 17 00:00:00 2001 From: sislam8 Date: Tue, 29 Nov 2022 00:24:20 -0500 Subject: [PATCH 09/15] bug fixing in registration for getting page load error --- src/account.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/account.py b/src/account.py index d51a3fe..5fec9d0 100644 --- a/src/account.py +++ b/src/account.py @@ -33,7 +33,7 @@ def __init__(self, name='', surname='', birthday='', username='',password = '', print("Can not create account with same user name") self.ID = -2 else: - self.ID = acc['ID'] + self.ID = acc[0] print(self.ID) From 2a41c0d82a794e3a3f966a4e006bc69bf931722d Mon Sep 17 00:00:00 2001 From: sislam8 Date: Tue, 29 Nov 2022 01:05:37 -0500 Subject: [PATCH 10/15] adding new features for friend wishlist and delete --- src/main.py | 155 +++++++++++++++++++++++++++++----------------------- 1 file changed, 86 insertions(+), 69 deletions(-) diff --git a/src/main.py b/src/main.py index 74fb0c6..aec4d0d 100644 --- a/src/main.py +++ b/src/main.py @@ -365,20 +365,47 @@ def friendlist_page(): if friendlist != 'NaN': friendlist = friendlist.split(',') friendobj = [Account(ID=int(f)) for f in friendlist if f.isnumeric()] - friendName = [f.name for f in friendobj] - friendSur = [f.surname for f in friendobj] - df = pd.DataFrame(list(zip(friendlist,friendName,friendSur)), columns=('ID', 'Name', 'Surname')) - df.set_index('ID', inplace=True) - st.table(df) - if st.button('View Wishlist of friend'): - st.session_state.runpage = 'friendwishlist' - st.experimental_rerun() + # friendName = [f.name for f in friendobj] + # friendSur = [f.surname for f in friendobj] + # df = pd.DataFrame(list(zip(friendlist,friendName,friendSur)), columns=('ID', 'First Name', 'Last Name')) + # df.set_index('ID', inplace=True) + # st.table(df) + colms = st.columns((5,5,5,5,5,5)) + fields = ["#Freind","First Name","Last Name", "Birthday","View freinds Wishlist","Delete Friend"] + for col, field_name in zip(colms, fields): + col.write(field_name) + + j = 0 + for i in friendobj: + col0,col1, col2,col5,col3,col4 = st.columns((5,5,5,5,5,5)) + col0.write(j+1) + col1.write((i.name).replace("\"", "")) + col2.write((i.surname).replace("\"", "")) + col5.write((i.birthday)) + button_phold = col3.empty() + do_action = button_phold.button("View Wishlist",key = friendlist[j]) + if do_action: + st.session_state['freindId'] = friendlist[j] + st.session_state.runpage = 'friendwishlist' + st.experimental_rerun() + button_remove = col4.empty() + do_remove_action = button_remove.button("Delete friend",key = str(friendlist[j])+str(friendlist[j])) + if do_remove_action: + st.session_state['delete_friend'] = friendlist[j] + st.session_state.runpage = 'deletefriend' + st.experimental_rerun() + j = j + 1 + + + # if st.button('View Wishlist of friend'): + # st.session_state.runpage = 'friendwishlist' + # st.experimental_rerun() if st.button('Add friend'): st.session_state.runpage = 'addfriend' st.experimental_rerun() - if st.button('Delete friend'): - st.session_state.runpage = 'deletefriend' - st.experimental_rerun() + # if st.button('Delete friend'): + # st.session_state.runpage = 'deletefriend' + # st.experimental_rerun() if st.button('Back'): st.session_state.runpage = 'account' st.experimental_rerun() @@ -389,38 +416,26 @@ def viewwishlist_page(): friendlist = acc.friendlist friendlist = friendlist.split(',') form = st.form(key='Viewwishlistform') - id =form.text_input('Please enter ID of the friend', value=friendlist[0]) - case = -1 - - if form.form_submit_button('See wishlist'): - try: - friend = Account(ID=int(id)) - items = (friend.wishlist).replace("\"", "").split(",") - items = [int(item) for item in items] - item_objs = [item(ID=id) for id in items] - except ValueError: case = 0 - - try: - Account(ID=int(id)) - except ValueError: - case = 1 - - try: int(id) - except ValueError: - case = 2 + id =st.session_state['freindId'] + + try: + friend = Account(ID=int(id)) + items = (friend.wishlist).replace("\"", "").split(",") + items = [int(item) for item in items] + item_objs = [item(ID=id) for id in items] + except ValueError: + st.error("This ID doesn't have any wishlist") - if case == 0: st.error("This ID doesn't have any wishlist") - elif case == 1: st.error("Friend ID does not exist") - elif case == 2: st.error("Friend ID must be an integer") - else: - item_titles = [(i.title).replace("\"", "") for i in item_objs] - item_descs = [(i.desc).replace("\"", "") for i in item_objs] - item_links = [(i.link.replace("\"", "")) for i in item_objs] - item_costs = [i.cost for i in item_objs] + + item_titles = [(i.title).replace("\"", "") for i in item_objs] + item_descs = [(i.desc).replace("\"", "") for i in item_objs] + item_links = [(i.link.replace("\"", "")) for i in item_objs] + item_costs = [i.cost for i in item_objs] - df = pd.DataFrame(list(zip(items, item_titles, item_descs, item_links, item_costs)), columns=('ID', 'Title', 'Description', 'Link', 'Cost')) - df.set_index('ID', inplace=True) - st.dataframe(df) + df = pd.DataFrame(list(zip(items, item_titles, item_descs, item_links, item_costs)), columns=('#Wish', 'Title', 'Description', 'Link', 'Cost')) + df.set_index('#Wish', inplace=True) + st.dataframe(df) + if st.button('Back'): st.session_state.runpage = 'friendlist' st.experimental_rerun() @@ -471,39 +486,41 @@ def deletefriend_page(): acc = st.session_state.account friends = (acc.friendlist).replace("\"", "").split(",") form = st.form(key='DeleteItemForm') - id = form.text_input('Please enter ID of the friend want to delete', value=friends[0]) - case = -1 + id = st.session_state['delete_friend'] + print("friend id") + print(id) + # case = -1 - if form.form_submit_button('Delete friend'): - try: - Account(ID=int(id)) - except ValueError: - case = 0 + # if form.form_submit_button('Delete friend'): + # try: + # Account(ID=int(id)) + # except ValueError: + # case = 0 - try: int(id) - except ValueError: - case = 1 + # try: int(id) + # except ValueError: + # case = 1 - if case == 0: st.error("Friend ID does not exist") - elif case == 1: st.error("Friend ID must be an integer") - else: - a_name = acc.name - a_surname = acc.surname - a_birthday = acc.birthday - a_username = acc.username - a_password = acc.password - a_interests = acc.interests - a_wishlist = acc.wishlist + # if case == 0: st.error("Friend ID does not exist") + # elif case == 1: st.error("Friend ID must be an integer") + # else: + a_name = acc.name + a_surname = acc.surname + a_birthday = acc.birthday + a_username = acc.username + a_password = acc.password + a_interests = acc.interests + a_wishlist = acc.wishlist - friends.remove(id) - friends.remove('') - friends = ','.join(friends) + friends.remove(id) + # friends.remove('') + friends = ','.join(friends) - acc.update_account(a_name, a_surname, a_birthday, a_username, a_password, a_interests, a_wishlist, friends) - acc = Account(ID = int(acc.ID)) - st.session_state.account = acc - st.session_state.runpage = 'friendlist' - st.experimental_rerun() + acc.update_account(a_name, a_surname, a_birthday, a_username, a_password, a_interests, a_wishlist, friends) + acc = Account(ID = int(acc.ID)) + st.session_state.account = acc + st.session_state.runpage = 'friendlist' + st.experimental_rerun() if st.button('Back'): st.session_state.runpage = 'friendlist' st.experimental_rerun() From 97a016595ad63a4aed05888a020d056db77e62e1 Mon Sep 17 00:00:00 2001 From: Shruti Marota <70153150+smarota17@users.noreply.github.com> Date: Wed, 30 Nov 2022 00:34:03 -0500 Subject: [PATCH 11/15] Update README.md --- README.md | 134 +++++++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 117 insertions(+), 17 deletions(-) diff --git a/README.md b/README.md index 7ee2f8b..fcb6de7 100644 --- a/README.md +++ b/README.md @@ -8,27 +8,127 @@ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT) [![Build](https://github.com/yagmurbbayraktar/CSC510_Project1/actions/workflows/python-app.yml/badge.svg)](https://github.com/yagmurbbayraktar/CSC510_Project1/actions/workflows/python-app.yml) -This repository contain the Project 1 of CSC 510 Fall 2022 group 39: Gifter. +# Gifter-2 +[![DOI](https://zenodo.org/badge/535341071.svg)](https://zenodo.org/badge/latestdoi/535341071) +[![License](https://img.shields.io/github/license/landog893/Gifter-2?style=plastic)](https://github.com/landog893/Gifter-2/blob/main/LICENSE.md) +[![java](https://img.shields.io/badge/Made%20with-Java-brightgreen?style=plastic)](https://www.oracle.com/java/technologies/downloads/) +[![size](https://img.shields.io/github/languages/code-size/landog893/Gifter-2?style=plastic)](https://github.com/landog893/Gifter-2) +[![lang](https://img.shields.io/github/languages/count/landog893/Gifter-2?style=plastic)](https://github.com/landog893/Gifter-2/search?l=Java&type=code) +[![contrib](https://img.shields.io/github/contributors/landog893/Gifter-2?style=plastic)](https://github.com/landog893/Gifter-2/graphs/contributors) +[![issue op](https://img.shields.io/github/issues/landog893/Gifter-2?style=plastic)](https://github.com/landog893/Gifter-2/issues) +[![issue cl](https://img.shields.io/github/issues-closed/landog893/Gifter-2?style=plastic)](https://github.com/landog893/Gifter-2/issues?q=is%3Aissue+is%3Aclosed) +[![pull](https://img.shields.io/github/issues-pr/landog893/Gifter-2?style=plastic)](https://github.com/landog893/Gifter-2/pulls?q=is%3Aopen+is%3Apr) +[![pull_closed](https://img.shields.io/github/issues-pr-closed/landog893/Gifter-2?style=plastic)](https://github.com/landog893/Gifter-2/pulls?q=is%3Apr+is%3Aclosed) +[![GitHub Workflow Status](https://img.shields.io/github/workflow/status/landog893/Gifter-2/Build%20Workflow?style=plastic)](https://github.com/landog893/Gifter-2/actions/workflows/build.yml) -## Introduction +
+

-Gifter is a social platform that is built on the idea of gift giving and gift receiving. It runs as web application that allows users to create themselves a wishlist with items they want. The aim of this application is to help users with the process of picking out a gift. The application allows users to add friends and look at their wishlists. Items under the gift registries may also contain the cost and the website of the item. Currently, this project is not deployed on the web and functions on a local file that imitates a database. The project uses Python Smartlit for the GUI. -## Requirements -Please make sure latest versions of Python and Streamlit are installed. For Windows users, use Anaconda Navigator's command prompt. Get the command window to the space where the application is located and use the command. +Gifter-2 is a social platform built on the idea of gift giving and receiving! This application is deployed on the web and is available for individuals of all ages to use! -```streamlit run main.py``` +## Description -## Usage +Gifter-2 is a social platform built on the idea of gift giving and receiving! The goal of this application is to aid in the process of picking out a gift for friends. Users can create wishlists for themselves and add items/gifts that they would like to receive for their birthday. Users can proceed to add friends and view their wishlists as well. Gifter-2 also allows users to opt-in to email notifications which send a user's friends a reminder email regarding their upcoming birthday (if their email notifications are on as well). Items in the wishlists contain the cost of the item as well as the website where it can be purchased. -A user first has to register the application with their name and surname. They the application will provide them with an ID they will be using to log in. User IDs are also used when a user wants to add a friend. The user can create items to add the items they want to receive as gifts. From friends window the user can add or delete friends, and see their wishlists. +To start using Gifter-2, a user must first register by creating an account. The system collects information about a user's name, birthday, email, and interests. All of this information is stored in Gifter-2's database, and is not used by Gifter-2 in any other way. -## Future Work -The future that couldn't be done in the scope of this project consists of: -* An SQL Database -* Smart phone application -* APIs with larger e-commerce stores like Amazon, Target, etc -* ML application to suggest gifts from interests and previous gifts -* A browser extension that allows wishlisting while browsing on e-stores -* A calendar containing birthdays and other 'gift holidays' -* And much more! +## Technologies +

+ + python + + + streamlit + + + postgresql + + anaconda +

+ +* Python +* StreamLit +* PostgreSQL + +## Features + +All new features are in bold. + +* Register as a user. +* Add, edit, and delete items from a wishlist. +* Dashboard displaying friendlist with their associated wishlists. +* **Significantly improved UI experience.** +* **Login using username.** +* **Search for friends using usernames.** +* **Search for items using keywords.** +* **Send reminder emails for upcoming birthdays (if email notifications are on).** +* **Usage of PostgreSQL database to persist information.** +* **Application deployed to web.** + +## Installation + +You will need the following programs and packages installed on your local machine. + +Programs: + +* Python +* StreamLit +* PostgreSQL +* Anaconda Navigator + + 1) Setup and launch Anaconda Navigator. + 2) Navigate to the location of the application and use the command ```streamlit run src/main.py```. + + ** DATABASE?? ** + +Note: In order to use the "Email Notification" functionality for this project, your team must set up an **Outlook** email account, and add the username and password to the send_reminder_email() method in account.py. You cannot use Gmail for this feature because Google set up a new restriction this year that doesn't allow third-party apps to send emails from Gmail accounts. + +## Documentation + +This project is a refactoring of the [Gifter](https://github.com/yagmurbbayraktar/CSC510_Project1) project from 2022. We have added several functionalities to significantly enhance the scope and user experience of this application. + +* The video displaying the functionalities of the original project Gifter can be found [here](). +* The video displaying the new functionalities of Gifter-2 in comparison to the original project Gifter can be found [here](). + +** ADD VIDEO LINKS ** + +### Use Cases +In order to learn more about how to run Gifter-2 and use its features, check out our [USAGE.md]() file! +** ADD USAGE.MD FILE ** + +### Documentation +**Still need to complete** + +### Code Coverage +Gifter-2 uses CodeCov to generate the code coverage of the source code. Additionally, we use [blackbox tests](https://github.com/landog893/Gifter-2/blob/main/docs/BlackBoxTest.md) on the UI to supplement the coverage and ensure that the functionality works as expected. + +## Future Features + +While Gifter-2 is ready for users, there are several enhancements that could be made to amplify user experience. Below, we have listed several scopes of future improvements to Gifter-2 with a brief description. + +* **Smart phone application:** Create an application for mobile devices that allows users to use Gifter-2 on the go. +* **APIs enhancement:** Incorporate the use of APIs from larger e-commerce stores such as Amazon or Target. +* **Machine Learning:** Introduce machine learning algorithms to suggest gifts for users based on interests and previous gifts. +* **Chrome extension:** Create a Chrome extension so users can add items to their wishlist while browsing online. +* **Calendar:** Develop a calendar feature to display user birthdays. + +## Contribution + +Please see the CONTRIBUTING.md for instructions on how to contribute to our repository. + +## License + +This project is licensed under MIT. + +## Team Members +The team members who developed Gifter-2 are: +* Huang-Xing (Jesse) Chen +* Landon Gaddy +* Li-Chia (Jerry) Chang +* Saminur (Sami) Islam +* Shruti Marota + +We communicated via Discord and through weekly in-person meetings. +
+Screenshot (531) From 57681cf5d6e63b94f24d6a82b17a4e66ad518fe4 Mon Sep 17 00:00:00 2001 From: saminur Date: Wed, 30 Nov 2022 00:51:27 -0500 Subject: [PATCH 12/15] variable issue in account_info --- src/account_info.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/account_info.py b/src/account_info.py index 5973270..887ad7c 100644 --- a/src/account_info.py +++ b/src/account_info.py @@ -13,7 +13,7 @@ def create_account(self, name, surname='', birthday='',username='', password = ' values(%s,%s,%s,%s,%s,%s,%s,%s) returning "ID" """ conn = None Checkquery = """Select * From "Account" WHERE "UserName" = %s;""" - ID = None + acc = None try: # initializing connection params = config() From 210ad077ae675afc02c018af4b3d86f7fdab5a6d Mon Sep 17 00:00:00 2001 From: Shruti Marota <70153150+smarota17@users.noreply.github.com> Date: Thu, 1 Dec 2022 08:41:57 -0500 Subject: [PATCH 13/15] Update README.md --- README.md | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index fcb6de7..89ec425 100644 --- a/README.md +++ b/README.md @@ -24,12 +24,9 @@

- -Gifter-2 is a social platform built on the idea of gift giving and receiving! This application is deployed on the web and is available for individuals of all ages to use! - ## Description -Gifter-2 is a social platform built on the idea of gift giving and receiving! The goal of this application is to aid in the process of picking out a gift for friends. Users can create wishlists for themselves and add items/gifts that they would like to receive for their birthday. Users can proceed to add friends and view their wishlists as well. Gifter-2 also allows users to opt-in to email notifications which send a user's friends a reminder email regarding their upcoming birthday (if their email notifications are on as well). Items in the wishlists contain the cost of the item as well as the website where it can be purchased. +Gifter-2 is a social platform built on the idea of gift giving and receiving! The goal of this application is to aid in the process of picking out a gift for friends. Users can create wishlists for themselves and add items/gifts that they would like to receive for their birthday. Users can proceed to add friends and view their wishlists as well. Gifter-2 also allows users to opt-in to email notifications which send a user's friends a reminder email regarding their upcoming birthday (if their email notifications are on as well). Items in the wishlists contain the cost of the item as well as the website where it can be purchased. This application is deployed on the web and is available for individuals of all ages to use! To start using Gifter-2, a user must first register by creating an account. The system collects information about a user's name, birthday, email, and interests. All of this information is stored in Gifter-2's database, and is not used by Gifter-2 in any other way. @@ -57,9 +54,10 @@ All new features are in bold. * Register as a user. * Add, edit, and delete items from a wishlist. +* Add and remove friends from friendslist. * Dashboard displaying friendlist with their associated wishlists. * **Significantly improved UI experience.** -* **Login using username.** +* **Login using a username and password.** * **Search for friends using usernames.** * **Search for items using keywords.** * **Send reminder emails for upcoming birthdays (if email notifications are on).** From 4c4b4c280900f0b2e933b9e564e9dccfedcfc2f5 Mon Sep 17 00:00:00 2001 From: smarota17 Date: Thu, 1 Dec 2022 15:17:03 -0500 Subject: [PATCH 14/15] updated email functionality for database --- src/DBInit.py | 2 ++ src/account.py | 59 ++++++++++++++++++--------------------------- src/account_info.py | 2 +- src/main.py | 29 +++++++++++++--------- 4 files changed, 45 insertions(+), 47 deletions(-) diff --git a/src/DBInit.py b/src/DBInit.py index b2c47a2..1ebb9ca 100644 --- a/src/DBInit.py +++ b/src/DBInit.py @@ -26,6 +26,8 @@ def connect(): "Name" character varying COLLATE pg_catalog."default", "Surname" character varying COLLATE pg_catalog."default", "Birthday" character varying COLLATE pg_catalog."default", + "Email" character varying COLLATE pg_catalog."default", + "Notifications" character varying COLLATE pg_catalog."default", "UserName" character varying COLLATE pg_catalog."default", "Password" character varying COLLATE pg_catalog."default", "Interests" character varying COLLATE pg_catalog."default", diff --git a/src/account.py b/src/account.py index 1bf752f..35c6106 100644 --- a/src/account.py +++ b/src/account.py @@ -75,44 +75,44 @@ def update_account(self, name='', surname='', birthday='', email='', notificatio # loaded (checks if the birthday is in 1 week) or when the user clicks the "Send Notification" button # in the profile. def send_reminder_email(self): - accountMan = AccountInfo() - info = accountMan.get_info(self.ID) - - name = info['Name'] - birthday = info['Birthday'].item() + name = self.name + birthday = self.birthday + print(birthday) b = birthday.rpartition('/')[0] + birthday.rpartition('/')[1] b = b[:-1] - friendlist = info['FriendList'].to_string(index=False) + + friendlist = self.friendlist friendl = friendlist.split(',') - friendobj = [Account(ID=int(f)) for f in friendl] + friendobj = [Account(ID=int(f)) for f in friendl if f.isnumeric()] - wishlist = info['WishList'].to_string(index=False) - items = wishlist.replace("\"", "").split(",") - items = [int(item) for item in items] - item_objs = [item(ID=int(id)) for id in items] - - notifications = info['Notifications'].item() + + items = (self.wishlist).replace("\"", "").split(",") + items = [int(item) for item in items if item.isnumeric()] + item_objs = [item(ID=id) for id in items] + + notifications = self.notifications + port = 587 + smtp_server = "smtp.office365.com" + sender_email = "gifter-2@outlook.com" + password = "G1ft3r#212!" if (notifications == "Off"): print("Please turn email notifications on.") + elif (password == "REPLACE"): + print("Please replace the password field with your Outlook account's password.") else: - port = 587 - smtp_server = "smtp.office365.com" - sender_email = "gifter-2@outlook.com" - password = "G1ft3r#212!" - # construct wishlist string wishlistString = "\n" for i in item_objs: - wishlistString += " - " + i.title.item() + " ($" + i.cost.item() + "): " + i.link.item() + "\n" + wishlistString += " - " + i.title + " ($" + str(i.cost) + "): " + i.link + "\n" - message = """Buy """ + name.item() + """ the perfect gift for their birthday on """ + b + """.\nHere are some items on their wishlist:\n """ + wishlistString + message = """Buy """ + name + """ the perfect gift for their birthday on """ + b + """.\nHere are some items on their wishlist:\n """ + wishlistString for friend in friendobj: - if (friend.notifications.item() == "On"): - receiver_email = friend.email.item() + if (friend.notifications == "On"): + receiver_email = friend.email msg = MIMEMultipart() - msg['Subject'] = "Gifter-2: " + name.item() + "'s Birthday is Coming Up" + msg['Subject'] = "Gifter-2: " + name + "'s Birthday is Coming Up" msg['From'] = sender_email msg['To'] = receiver_email @@ -129,15 +129,4 @@ def send_reminder_email(self): print("attempting to send mail") server.sendmail(sender_email, receiver_email, msg.as_string()) print("message sent") - server.quit() - -# #acc = Account('Hannah', 'Montana', '05/05/1995', 'Singing, Dancing') -# #acc.view_account() -# acc = Account(ID=1) -# ints = (acc.interests.to_string(index=False)).replace("\"", "") -# ints += ", Ballet" -# # print(ints) -# wishes = (acc.wishlist.to_string(index=False)) -# acc.update_account(acc.name.to_string(index=False), acc.surname.to_string(index=False), acc.birthday.to_string(index=False), ints, acc.wishlist.to_string(index=False), acc.friendlist.to_string(index=False)) - - \ No newline at end of file + server.quit() \ No newline at end of file diff --git a/src/account_info.py b/src/account_info.py index c3e2e49..9c941bf 100644 --- a/src/account_info.py +++ b/src/account_info.py @@ -140,7 +140,7 @@ def get_account(self, username,password): if user_info: user_info = user_info[0] - if user_info[6] == password: + if user_info[7] == password: return user_info else: return -2 diff --git a/src/main.py b/src/main.py index 341218d..4fd3aa0 100644 --- a/src/main.py +++ b/src/main.py @@ -132,27 +132,31 @@ def account_page(): # check if whether a notification email needs to be sent today (is it 1 week before the account's birthday) global email_sent - birthday = acc.birthday.item() + birthday = acc.birthday b = birthday.rpartition('/')[0] + birthday.rpartition('/')[1] b = b[:-1] month = b.rpartition('/')[0] day = b.rpartition('/')[2] currDate = datetime.now().date() - if (int(currDate.month) > int(month)): - if (int(currDate.day) > int(day)): - birthdayDate = b + "/" + str(currDate.year + 1) - else: - birthdayDate = b + "/" + str(currDate.year) + birthdayDate = str(month) + "/" + str(day) + if (int(currDate.month) > int(month) and int(currDate.day) > int(day)): + birthdayDate += "/" + str(currDate.year + 1) + else: + birthdayDate += "/" + str(currDate.year) + birthdayDate = datetime.strptime(birthdayDate, "%m/%d/%Y").date() if (birthdayDate - currDate).days == 7 and email_sent == False: email_sent = True - acc.send_reminder_email() - acc = Account(ID = int(acc.ID)) - st.session_state.account = acc - st.session_state.runpage = 'profile' + if (acc.friendlist != 'NaN' and acc.wishlist != 'NaN'): + acc.send_reminder_email() + acc = Account(ID = int(acc.ID)) + st.session_state.account = acc + st.session_state.runpage = 'profile' + else: + st.error("Please ensure you have added items to your wishlist and friends to your friendlist before attempting to send email notifications.") st.experimental_rerun() else: email_sent = False @@ -176,7 +180,10 @@ def profile_page(): st.session_state.runpage = 'account' st.experimental_rerun() if st.button("Send Notifications"): - acc.send_reminder_email() + if (acc.friendlist != 'NaN' and acc.wishlist != 'NaN'): + acc.send_reminder_email() + else: + st.error("Please ensure you have added items to your wishlist and friends to your friendlist before attempting to send email notifications.") st.experimental_rerun() def editprofile_page(): From 2959762a449d4cf60af22524f3733a3b945fbaa9 Mon Sep 17 00:00:00 2001 From: Shruti Marota <70153150+smarota17@users.noreply.github.com> Date: Thu, 1 Dec 2022 16:51:52 -0500 Subject: [PATCH 15/15] Update main.py --- src/main.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/main.py b/src/main.py index 1189ae8..b223f57 100644 --- a/src/main.py +++ b/src/main.py @@ -155,8 +155,6 @@ def account_page(): acc = Account(ID = int(acc.ID)) st.session_state.account = acc st.session_state.runpage = 'profile' - else: - st.error("Please ensure you have added items to your wishlist and friends to your friendlist before attempting to send email notifications.") st.experimental_rerun() else: email_sent = False @@ -659,4 +657,4 @@ def deletefriend_page(): elif st.session_state.runpage == 'addfriend': addfriend_page() elif st.session_state.runpage == 'deletefriend': - deletefriend_page() \ No newline at end of file + deletefriend_page()